HCTF-2016-fheap 堆技巧use-after-free
常规检查
逆向分析
题目程序较为简单,功能是创建字符串和删除字符串,其中创建字符串用 create ,删除字符串用 free 。
create 函数
当输入的字符串大于 0xf 字符时会另外开辟内存来存储字符串,否则存入 ptr 指针的地址中。通过分析我们还可以得到
- *ptr :在小于等于 0xf 字符时是string的地址,在大于 0xf 字符时是 dest 的指针, dest 指针指新开辟用来存储 stirng 的内存
- *(ptr+3):这个地方存储的是 free 函数地址,当小于等于 0xf 字符时只 free 指针,当 大于 0xf 字符时 free 指针和内容,但是都没有对指针和内容置 0 ,这里就会导致 use-after-free 和 double-free 漏洞
- *(ptr+4):存储 string 的长度。
- *(&unk_2020C0 + 2 * i + 1):存储的是字符串的地址
- *(&unk_2020C0 + 4 * i):通过置 1 说明第 i 个 string 已创建
同时 create 函数 c 层面有个比较难理解的地方
free2 是一个 void 函数,哪里来的返回值呢?我们看下汇编
lea 指令,所以这个语句的意思是将 free2 函数的起始地址赋给 *(ptr+3) 。
delete 函数
*(&unk_2020C0 + 2 * i + 1) 是字符串地址,加上 24 即为 free 函数地址,所以这个语句相当于 free(string) 。同时把 *(&unk_2020C0 + 4 * i) 置 0 说明已经 string 删除。
利用思路
我们首先可以构造两个小的 chunk ,并释放它们,然后 create 大于 0xf 的字符串,这样刚刚被 free 的两个 chunk 又使用到了。同时里面的拥有 free 函数的指针,我们可以覆盖这个指针,泄露出程序运行的基址。虽然程序开启了 PIE ,但是 PIE 是无法改变地址后三位的。
注意到 call put 指令与 free2 函数地址偏移在后三位之内。我们只要把 string 内容中 free2 的地址后三位修改一下为 call put 地址,这样 delete 字符串的时候相当于运行 put 打印出字符串地址开始的内容, call put 的地址就在这串内容里,这样可以泄露程序基址。
线路程序基址后,我们可以获得 printf_plt 的地址,重复上述步骤,利用格式化字符串可以泄露 libc 函数的地址。
最后,根据泄露的 libc 函数地址通过偏移计算 system 与 binsh 的地址,覆盖 free2 函数指针为 system 函数即可 get shell 。