pwn题堆利用的一些姿势 – malloc_hook
pwn题堆利用的一些姿势 – IO_FILE
pwn题堆利用的一些姿势 – setcontext
pwn题堆利用的一些姿势 – exit_hook
概述
在做堆题的时候,经常会遇到保护全开的情况,其中对利用者影响最大的是PIE保护和Full RELRO,NX保护和栈保护对堆利用来说影响都不大,一般利用也不会往这方面靠。开了PIE保护的话代码段的地址会变,需要泄露代码段基地址才能利用存储在bss段上的堆指针;开了Full RELRO的话,则意味着我们无法修改got表,导致无法修改其它函数got表指向system,进一步获取到shell。因此也就有了我这一系列文章的目的,在got表无法被修改时,我们往往利用的就是下面的一些hook+onegadget来达到我们的目的。
主要分为以下几个系列:malloc_hook --> free_hook --> IO_FILE --> setcontext。
初级必备姿势
下面介绍free_hook的初级必备姿势,free_hook是调用free函数之前会执行的钩子函数,利用方式和malloc_hook大同小异,所以建议学习这种利用方式之前先看下malloc_hook。熟练掌握malloc_hook的利用方式后,再看free_hook会觉得十分轻松,基本上感觉是换汤不换药。当然一般利用先想malloc_hook,毕竟堆题肯定是有malloc函数的,但却不一定有free函数,另外free_hook往上的内存数据中不一定存在0x7f的字节数据来帮助我们实现fast bin attack。
好,废话不多说,接下来以2019年ciscn_en_3的pwn题为例,介绍free_hook的利用姿势,远程环境可以在BUUCTF上找到。该题目环境为Ubuntu18,需要利用tcache机制中的double free,不熟悉的读者可以参见这篇博文,pwn题堆入门 – Tcache bin。
程序分析:该题目保护全开,64位程序,使用libc-2.27.so,程序本身去除了符号名,可以自己修复一下,接下来我们直接定位漏洞,其余不重要的细节此处不再赘述。如下图所示,程序主逻辑中开头中存在格式化字符串漏洞,但由于开启了FORTIFY保护,所以对利用方式有点影响。这里可以简单总结一下开了FORTIFY保护下的格式化字符串漏洞利用,首先这里无法再使用%n来实现任意地址写,然后是无法使用%d$p(d代表大于1的数字)进行地址泄露;但是可以像这样连续使用%p%p%p%p来泄露数据。具体利用方式参见下面的wp。
另外查阅了一下网上相关的wp,发现有前辈是利用了puts(s)函数的溢出,即如下图所示,栈上"aaaaaaaa"的部分即是s的位置,我们将此处填满到8字节,由于puts在打印时遇到"\x00"才会停止,所以会将紧邻的setbuffer函数的地址打印出来,这样我们也能获得libc的地址。
如下图所示,是程序主逻辑的地方,主要实现了增删查改四个功能,但其中edit和show并没有实现具体功能,然后add是正常实现了堆块的分配,不存在溢出,delete存在uaf漏洞,再下一张图即是delete功能的具体实现。
漏洞利用:首先利用printf格式化字符串漏洞泄露libc地址,计算出free_hook地址,然后利用tcache的double free将堆块分配到free_hook上,填写为one_gadget。
如下图所示,我们先看gdb调试的结果,首先是printf_chk的格式化利用,这里即将call的就是printf_chk,由于加了保护,所以该函数有两个参数,第一个参数0x1代表保护级别存入寄存器rdi,第二个参数才是我们输入的格式化利用字符串%p-%p-%p
存入寄存器rsi。我们可以看到寄存器rcx存放的是read+17的地址,根据64位linux下前6个寄存器传参的习惯(rdi/rsi/rdx/rcx/r8/r9),此处已经用了两个寄存器,所以第二个%p将会打印出rcx存放的值。到这里我们就获取到了libc上的地址,再根据libc计算出基地址和free_hook的地址即可。
如下图所示,第一张图展示了wp的执行结果,通过在wp中打印地址可以方便我们直接在gdb中进行断点调试和验证。第二张图展示了gdb的中结果,可以发现free_hook处已经被修改为了one_gadget的地址,当然这里运气不错,one_gadget的执行条件满足,接下来我们继续执行就可以获取到shell了。
这里给出wp。
from pwn import *
ld_path = "/home/fanxinli/libc-so/libc-27/ld-2.27.so"
libc_path = "/home/fanxinli/libc-so/libc-2.27-64.so"
p = process([ld_path, "./ciscn_2019_en_3"], env={
"LD_PRELOAD":libc_path})
def add(size, con)