看我前面的文章可以发现我在做漏洞利用的总结主要是在glibc2.31以下版本的,这也就导致了在后续版本的glibc的利用方式我也没怎么掌握,后续打算将house of kiwi以及house of Emma写完之后会关注一下glibc2.31之后的how2heap的poc,如果存在差异就会写文章记录。
在学校的一次比赛当中我出了一道题,是glibc2.23版本的,当时的解题关键就是off by null进行多个堆合并,利用方式较为简单。但是就目前的glibc2.32中consolidate的条件是比较苛刻的,因此出现了新的堆风水的方式了(这篇文章也是把以前的坑填了)。
隔块堆合并手法
首先还是先看源码
1 | if (!prev_inuse(p)) { |
1 | static void |
可以看到这里是验证了p位,如果为0那么就检测前一个chunk的size是否等于当前chunk的size,那么就不能单纯的像以前那样利用了,我们还需要伪造前一个chunk的size了,这里需要用到large bin的机制了。
利用方式
既然我们只能进行off by null还需要堆合并,那我们就需要满足上面代码的两项要求,第一就是常规的chunk->fd->bk指向本身,其次就是size==prev_size
此时取出size:0x510,由于残留指针,所以还是存在以下的指向关系
并且此时在fd,bk位置伪造prev_size和size,那么我们在下面off by null的时候计算出prev_size即可绕过对于size的检查了。但是此时又出现了一个问题,fake_chunk的fd的bk以及fake_chunk的bk的fd并不指向它本身。那么现在取出size:0x500的chunk,直接覆盖掉其fd指针,使他指向size:0x510,然后large bin当中只剩下一个size:0x520,它的fd和bk都指向了large bin了,所以我们此时需要再free一个size为0x500的chunk,然后把size:0x520取出来进行覆盖,那么即可绕过consolidate时的验证了。
NULL_FXCK
基本流程
这道题同样是菜单题,但是不同的是在每次选择的时候会验证__malloc_hook和__free_hook以及会清除掉tcache的count。
然后唯一的漏洞点是modify函数,存在一个off by null但是只能执行一次。并且在delete函数会清空指针。
思路
因为只有一次off by null的机会,所以我们能够利用的方式就是上面的堆合并技巧,但是这里的create函数在写入数据的时候总是会把结尾改为\x00并且最小的chunk为0x110所以我们无法直接覆盖内容了,需要利用partial overwrite并且这里的partial overwrite还需要注意一下。我们需要让size:0x510的chunk的地址形式为:0xAAAAAAAAAAAA00AA
形成这样的堆叠,接着申请回来并覆盖值
当然这里由于partial overwrite的缘故第一位是否为0是需要一定概率的(上面的0x1000是因为我还没有计算大小,后续会调整)。
可以看到这里就实现了consolidate。
下面就是泄漏地址了,首先先泄漏堆地址,因为泄漏起来较为简单,在我们consolidate之前我们所显示的堆地址都是以\x00结尾导致无法泄漏,但是在consolidate之后存在以下代码
1 | unlink_chunk (mstate av, mchunkptr p) |
导致堆地址写在了其他索引的chunk当中,所以可以非常轻松的泄漏出来,不过这里的main_arena+96非常恶心,结尾是\x00有因为是strlen计算大小打印就导致泄漏不出来,但是这里使用的方法可以继续延续在unsorted bin当中的思路进行切割,但是下一步就是申请大chunk将我们consolidate的chunk放到largin bin当中。
后面也就是实现任意地址写了,首先想到的肯定就是tcache,虽然题目看起来是没有办法对tcache进行攻击的但是tcache这个结构体也只是因为tls结构存放的指针才起的作用,所以我们可以通过large bin attack来修改tls结构当中的指针,然后在堆块中布置好地址,最后修改地址进行fsop。这里采取的方式就是以前写过的house of kiwi不过以前写的比较匆忙也没有加以实践,可能看起来就会晕头晕脑的,所以这里还是从源码层面分析一边接着放出exp应该会好点。
house of kiwi
先来说一下为什么不能用house of系列中的其他方式,因为这道题的退出函数是_exit然而其他的要求是exit退出或者正常main退出,所以这里只能寻找其他攻击链。
1 | static void |
当assert触发时会调用这一函数,中间调用了fflush
1 | int |
通过调试也可以看到调用关系
并且这里是可读可写的,所以后续就好办了。
exp
1 | from pwn import * |
在前面chunk布局的时候最好多放点chunk,不然就跟我一样后续加很麻烦。
参考文章: