HGAME 2025 Week 2 Writeup
Signin2Heap
Vulnerabilities
存在 off-by-null 漏洞,当 prev_size
域复用时,可置零相邻
chunk 的 prev_inuse
位。
只能申请至多 0xFF
大小的堆块,考虑 fastbin attack。
Exploit
由于程序没有编辑功能,只能使用 add 功能修改堆数据。布置大小分别为
0xf0
, 0x68
, 0xf0
的三个堆块,然后将 0xf0
大小的 tcache bin
填满。此时释放 chunk 0,将进入 unsorted bin
。为了泄露出 libc 有关地址,我们需要利用 show 功能输出 freed chunk
上的指针 (即 fd
)。通过如下操作可以实现类似 UAF 的效果:
- 修改 chunk 2 的
prev_size
和prev_inuse
; - 释放 chunk 2,引起向后合并,此时堆管理器认为 chunk 0 ~ chunk 2
都已经为空闲状态,放入
unsorted bin
; - 先清空优先级更高的
tcache bin
,然后申请 chunk 0 大小的堆,从unsorted bin
中取,此时 fd 移动到 chunk 0 的后面。
经过以上操作后,chunk 1 的位置恰好是 unsorted bin
的头部。但同时程序逻辑上 chunk 1 并没有被释放,引起了 UAF,double
free。
再次填满 tcache bin
,利用 fastbin double free
可实现任意写。
1 | from pwn import * |
Where is the vulnerability
第一次打这么高版本的 libc(原谅我当时脑抽看成 2.29,一堆老漏洞用了半天发现不行 hhh)
禁用 execve
Vulnerabilities
明显的 UAF 漏洞。
只能申请 0x500 ~ 0x900
大小的堆,考虑 large bin
attack。
Exploit
堆块大小限制导致我们只能使用 unsorted bin
和
large bin
,即使通过 UAF 漏洞可以修改堆上的
size
从而使其进入 tcache bin
,但是不能重新申请进行利用。
显而易见的,可以利用 unsorted bin
的特性快速得到 libc
基址。
同时,布置后续的堆块,以进行 large bin attack。
large bin attack 的操作简要描述如下,当然在 how2heap 中有更好更详细的描述:
- 申请两个 chunk,且大小不相同,并在其之后都申请任意大小的堆块,防止释放后合并;
- 释放 chunk 0;
- 申请一个大于 chunk 0 大小的堆,chunk 0 将进入
large bin
; - 释放 chunk 2;
- 修改 chunk 0 的
bk_nextsize
为target - 0x20{sizeof(prev_size + fd + bk + fd_nextsize)}
。 - 重复第三步,chunk 2 将进入
large bin
,由于 chunk 2 更小,导致操作bk_nextsize->fd_nextsize = &chunk2
。
此时就在目标位置写入了 chunk 2 的 prev_size
地址。
通过一种叫做 House of apple 的方式,就可以攻击 IO,劫持程序执行流。
在泄露出 libc 地址后,进而得到 IO_list_all
的地址,利用
large bin attack 将 chunk 地址写入,之后在 chunk 2 上伪造 FILE
结构体。
原理部分请自行查找(毕竟我还没完全弄明白)。我们主要关注伪造 IO 的最后一行,它可以让我们跳转到一个地址,即控制一次
$RIP
。我们的目的是找到一个
gadget,帮助我们实现栈迁移,执行 ROP 链。
可以利用的 gadget 如下:

动态调试可以发现 $rax
指向 fake_io
有关地址,因此可以改变 $rdx
的值。
将 $rdx
改为一处可读写段,执行下一段 gadget:


修改 $rsp
实现栈迁移,注意在后面会将
$rcx=[rdx+0xa8]
入栈,改为一个对后续无影响的可执行地址即可,或者 ROP 的第一个地址。
最后进入 exit()
触发相关调用链,执行
orw(如此有仪式感的操作自然是手动完成)。
1 | from pwn import * |
Hit list
很遗憾本题没有解出,因为前面较少接触的堆题耗费了我挺多心力的,到这已经没什么精力去做了。不过收获很多,是大于遗憾的。
明年见!
平台很好看,出题人很热心,题目很难(
1 | hgame{see_you_next_year!!!} |