[HDCTF 2023]Makewish
栈迁移+Canary+随机数+栈随机循环
一道开启了 Canary 的 64 位栈题,先搜集信息:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
我们可以填充 buf 大小为 0x30 的数据,恰好可以溢出 8 个字节,显然这个溢出长度太短以至于无法控制执行。
1 | __int64 vuln() |
vuln 函数当中我们同样最多覆盖到 Canary,不过他会在 buf 的末端加上一个字节的 \x00 相当于 Off-By-Null,通过这个,我们可以将 rbp 的最低位覆盖为 \x00。通过控制 rbp 我们就可以尝试栈迁移,问题是溢出长度不够。
细心发现,在 vuln 函数的末尾还有 leave_ret 的指令,这正好解决了溢出长度不够无法进行栈迁移的难题:
1 | int treasure() |
由于只存在 Off-By-Null 而且无法泄露 rbp,所以我们没有办法去确定栈迁移的的地址。
我们只能在 vuln 的 buf 当中布置 ret 到后门的函数地址。抹去最后一个字节后让地址往低跳转,有几率会返回 buf 区域,也就是我们布满 system(‘/bin/sh’);
EXP 思路:
确定种子数
1 | from ctypes import CDLL |
泄露 Canary
1 | randnum=0x2c3 |
这里直接输入 41 位字符串进行覆盖,防止 puts 被 Canary 的 \x00 截断,之后与运算还原低位。
看一下 debug 接收的数据,发现了 Canary 在 0x41-0x48 的位置,所以先接收 40 个字节的数据再收 Canary。
种子数绕过
1 | read(0, &buf_, 4u); |
种子数绕过的前提是该 read 函数只读取了四个字节的数据,所以 p32 传参->
1 | p.sendafter(b"key\n\n",p32(randnum)) |
在 buf 中填满 system(‘/bin/sh’)
1 | payload=p64(elf.sym['treasure'])*11 |
持续接收获取 shell :总 EXP
1 | from pwn import * |
更新: 2026-04-06 16:07:20
原文: https://www.yuque.com/idcm/wnemg9/ucgmw6upsrbg4yom