【Pwn#0x05】pwnable.tw hacknote writeup
UAF,以及发现了使用system()的小技巧。
程序分析
保护:
1 | [*] '/home/nss/Desktop/pwnable.tw/hacknote/hacknote' |
作为一个菜单题,本题菜单如下:
1 | ---------------------- |
Add note:
申请一个0x16的区块(malloc(0x8))和一个用户决定大小的区块(malloc(size)),我将其称为控制区块和用户区块。
在控制区块的8字节用户可用空间中,分别塞入一个函数地址、和用户区块的地址。
然后进行read(0, malloc(size), size),允许用户输入内容至用户区块。
在一个固定地址(0x0804a050),存放着一个指针数组,用来存储历次分配产生的控制区块的地址。
为了防止溢出,程序在每次add note时都会先检查全局变量note_count(自己取的名),并且在分配成功后让note_count++。最多可以分配5个区块。
Delete note:
输入index,将对应的用户数组和控制数组释放(注意先后顺序)。
但这里并不会让note_count–,也不会从全局指针数组中删除控制区块。只会free而已。
Print note:
输入index,将对应控制区块的用户可用空间地址压栈(也就是函数的指针的地址),调用对应控制数组存放的函数(该函数会将arg0加4后作为一个char*调用puts输出)。
漏洞分析
本题没有buffer overflow(都是调用read完成),也没有栈上的漏洞。
唯一的漏洞在于UAF——程序在使用delete note来free区块之后,依然能够使用print note来使用对应区块存放的数据(具体来说,调用存放的函数指针)。
所以只需要构造一下,使得已经被释放的控制区块被分配作用户区块,这样就可以在控制区块中写入信息,控制控制流。
利用 Exploitation
我先随意add_note两次并释放之,两个原控制区块都被free到fast bin中。然后再add_note(8),那么此时原来的两个控制区块,一个被用作新的控制区块,另一个被用作了用户区块。
这时,我往用户区块中塞入两个4Bytes:一个是程序特化的打印函数,会把参数当作地址+4后,调用puts函数打印之;还有一个是got表中puts的地址(这里任意got表中函数都可以)。
然后调用print note,这样程序就会把got表中puts的地址打印出来,那么libc基地址就有了。
之后可以故技重施,再把那两个受害者区块free再malloc,此时再往用户区块中塞入两个4Bytes:一个是system()地址,另一个是b’sh\x00’。
由于print note会把对应控制区块的用户可用空间压栈,也就是此时的&system(),所以实际上我执行的是system(pack(system()) + “;sh”),通过分号来bypass前四个字节的不可打印字符。
(除该技巧外,别的部分早就想出来了,就该技巧卡了好久,最后看网上wp学到了……)
EXP如下:
1 | from pwn import * |