我大哥给我出了一题pwn heap题,当时现场的时候没解出来,想岔了。事后感概自己还是理解的不够透彻。决定认真再次调试下。用的是2.23版本的how2heap中fastbins_dup.c。
简单用gcc编译下,然后带源码调试:
生成一个a.out的文件:
然后进入pwngdb中调试:
我们把断点断在主函数的位置:
然后r命令执行过去就会进入带源码调试的主函数:
直接讲程序流控制到第一个malloc处:
然后我们直接s命令进入底层去观察做了什么:
这是malloc函数开始的真正位置,一开始为开辟栈帧做准备,接着将[rip+0x33fd63]内存里的东西传给RAX寄存器,继续跟下去,我们看到rax是一个函数:
这个函数的意思是对malloc_hook做一个初始化的工作。接着会跳转到malloc+360的地方执行:
接着会去执行malloc_hook_ini函数:
紧接着在执行malloc_hook_ini的过程中,又调用了其他函数:
通过对汇编以及源码的对照,这个call函数是ptmalloc_init():
到目前为止,程序执行流程大致是glibc在malloc真正执行前,会先去检查是否有用户自定义的hook函数,如果有,就先行调用,如果没有就按正常的malloc执行。
没有hook函数情况下调用链: __libc_malloc --> _ini_mallac --> 执行fastbins分配逻辑
存在hook函数情况下调用链:__libc_malloc --> malloc_hook_ini -->ptmalloc_init --> _ini_malloc->执行fastbins分配逻辑
一些细节上的处理,我没有跟下去,但是在malloc执行前,大致是做了这些步骤。我们程序中传入的参数是8 --> malloc(8)。 我们知道glibc的Malloc函数会根据用户传入的参数判断申请多大的内存。在我们程序第一次分配内存的时候,bins中是没有任何可以用的内存。因此会从top chunk中为我们分割需要的内存:
紧接着执行2个malloc(8):
此时三个堆都分配好了:
而此时bins是没有东西的:
紧接着我们开始执行free函数的时候再来观察:
现在我们要释放a的内存空间:
我们首先得知道,在fastbins中,被释放的chunk都是单链表链接起来的。因此只有fd指针生效。
此时一切都正常。如果我们此时再次释放a这块内存的话,程序会奔溃,因此我们得先释放其他块,接下来释放b这块内存:
此时我们知道了一件事,fastbins组织被free掉的chunk是采用头插头删的。我们可以看看真实内存布局:
到此为止一切OK,接下来就是漏洞的地方,如果我们这个时候接着再次释放a会发生什么:
此时我们在fastbins中会有三个chunk,因为libc没有再这里设置过硬的检查,并且这块空间在free的时候没有置为NULL,因此这片空间能够被当成正常的chunk在fastbins中链接。大致结构图示如下:
因此此时如果申请大小跟fastbins中一样的chunk,将会把a这块空间分配2次:
这就是fastbins的double free漏洞。
利用点的话通常是利用double free来进行任意地址读写,然后利用mallc_hook或者free_hook进行s申请和释放前来执行system("bin/sh")。