京麒CTF 2025 - OldWine
感谢 熙熙 师傅提供的思路!
文件属性
| 属性 | 值 |
|---|---|
| Arch | amd64 |
| RELRO | Full |
| Canary | off |
| NX | on |
| PIE | on |
| strip | no |
| libc | 2.39-0ubuntu8.4 |
seccomp rules
解题思路
程序禁止了one gadget,可以调整rbp,也可以在栈上向后写。类似的思路其实当时西湖论剑的 babytrace 就出现过,然而一下没想到,没做出来,还在那里找各种gadget,这下吃一堑吃一堑了。
原本的栈大小是0x10,加上[rbp]和返回地址总共0x20字节,然后索引从rbp - 0x10开始算。
原始栈布局如下所示:

如果将[rbp]调整为rbp + 0x10,那么栈布局就会变为:

为什么要这么做?反汇编read,如果成功读取,那么有效的指令只有:
1 | cmp byte ptr [rip + __libc_single_threaded], 0 |
因此在call read@plt时,rsp只扩展了8字节,也就是返回地址。查看上面的栈布局,
不难发现,当call时,rsp刚好等于rbp - 0x10,也就是说,我们可以控制参数,
向read函数的返回地址写入数据。将idx和size分别控制为0和一个比较大的值,
由于rsi和rdx没有变化,可以将返回地址劫持到main函数中mov edi, 1; call write@plt;的地方,
这样就可以泄露出栈上的数据,找到libc的值。
后面就简单了,打传统rop,走execveat就可以。
EXPLOIT
1 | from pwn import * |
- 标题: 京麒CTF 2025 - OldWine
- 作者: RocketDev
- 创建于 : 2025-05-28 12:05:00
- 更新于 : 2025-05-28 12:19:00
- 链接: https://rocketma.dev/2025/05/28/OldWine/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论