当无脑将__malloc_hook覆盖成one_gadget但所有one_gadget都打不通的时候该怎么做
0x01 前言
做到这题的时候想到一个严重的问题,这种菜单堆题以前我都是怎么做的呢?都是通过堆溢出/uaf 在 __malloc_hook
附近伪造一个 fake chunk 然后将其申请下来,再通过堆编辑来将原本的 __malloc_hook
覆盖成 one_gadget。尝试运行一遍打不通,就换一个 one_gadget,但是其实我没有想过为什么会打不通以及当所有 one_gadget 都打不通的时候怎么办?
0x02 如何解决?
学到了一个新的手段,我们可以利用 realloc
来调整栈帧,在网络上搜索到了一篇博客,大佬详细解释了我上面的问题以及如何解决,现在用一道会有这种情况的题目来实际操作一下,题目是PolarCTF2023秋季个人挑战赛的Emo_chunk
0x03 分析:
Checksec:Patch 过了,顺便吐槽一下这个靶场好多堆题都不给 libc 版本,但是看官方解说视频又不说为什么用他那个版本,看到视频里用的是
libc.2.23
就用 2.23 来 patch 了。上 ida:典型菜单堆题,而且有 shell 函数:
但是我一开始没有看到,用了 one_gadget 来打… 而且官方题解视频里也是用 one_gadget,所以现在我们先忽略这个 shell 函数。继续跟进各个函数:
没有 UAF
0x04 堆溢出伪造chunk
但是编辑堆这里会有堆溢出,接下来就是常规操作,先申请一个大堆块,释放之后会被放到
unseorted bin
中,再申请出来就能泄露 libc 基地址,然后申请 3 个堆块,利用堆溢出在 __malloc_hook
附近伪造一个 chunk,将其申请下来,编辑覆写掉 __malloc_hook
:
malloc(0x410)#0 |
0x05 one_gadget打不通了/(ㄒoㄒ)/~~
根据以往的经验,把所有 one_gadget
都试一次,总有一次能打通,但是这次不行了,所以逼自己简单学习一下为什么不行以及如何解决,先看一下 one_gadget 能打通的条件:然后我们开启 gdb,把断点下在最后一次 malloc:
单步到这里,然后步入这个 malloc 函数,最后应该有一个类似 call xxx 的:
然后我们检查一下是否满足 one_gadget 的条件,分别要检查
[rsp+0x30] == NULL
,[rsp+0x50] == NULL
和 [rsp+0x70] == NULL
:都不满足,所以全部 one_gadget 都用不了了。
0x06 realloc抬高栈帧
所以到这里就需要用 realloc
来调整栈帧,也就是改变 rsp
,因为 realloc
函数也存在一个 __realloc_hook
,在执行 realloc
的时候会先判断 realloc
是否为空,不空则执行 realloc
。而且 __realloc_hook
和 __malloc_hook
的地址只相差 8:在覆写
__malloc_hook
的时候可以顺便控制 __realloc_hook
。思路基本上就是 __malloc_hook
写成 __realloc_hook
,__realloc_hook
中写入 one_gadget
。
0x07 pop的反义词是… 可能是push
realloc
是怎么改变栈帧的,我们把 libc 丢到 ida 中:可以看到
realloc
中有很多 push 函数,还有可以直接改变 rsp 的指令,所以这就是 realloc
函数能改变栈帧的原因。能变多少?直接说结论:执行一次realloc函数最少应该抬高0x40个字节(sub rsp,0x38让rsp-0x38再加上call时的压栈指令)
到这里就能够理解官方视频中最后这里的爆破是在做什么了:画个图理解一下:
垃圾数据填充到
__realloc_hook
, 然后分别填入 one_gadget
和 realloc+?
这里 __realloc_hook
地址被覆盖成 one_gadget
了,而 __malloc_hook
则被覆盖成 realloc+?
,下面则是自己写的一个爆破脚本:意思是在
realloc
函数中寻找合适的抬高栈帧的指令的数量,至于为什么是 [2,4,6,8,10]
则是因为:可以看到前几条 push 指令都是占了 2 个字节,为了避免发生 push 指令从中间断开,所以是
[2,4,6,8,10]
,而 i 则是在遍历所有 one_gadget。
唉但是这样也只是权宜之计,理论上的确是能够通过爆破来找到能跑通 one_gadget 组合,我觉得还得是要把大佬的博客理解透彻才对:使用realloc函数来调整栈帧让one_gadget生效,得深入理解其中 realloc
以及其他指令(call
之类)的对rsp的变换。
0x08 Exp:
from pwn import * |