guess_num
来自 xctf 的题目
附件是一个猜数字的程序,只要猜错就会“GG”。
通过 file 查看该文件是 64 位 elf 文件,checksec 发现有堆栈金丝雀保护。
简单分析程序后直接上 ida pro 进行反汇编:
这里使用 srand() 和 rand() 获得伪随机数,如果成功获得就进入 sub_C3E() 里面有 system 函数获得 flag。
对此,我们需要得到种子,这里 seed[0] 存储种子,但是又发现种子是靠 sub_BB0() 函数获得的,而 sub_BB0() 靠 /dev/urandom 获得随机数,基本上就是说种子是随机的,那么随机数也大抵是很随机的……
不过呢,前面输入名字获得字符串使用的是 gets(),给了我们溢出覆盖空间的可能。
点击 v7 看看函数栈里面的布局,发现:
v7(var_30) 往下就是 seed 的空间,它们之间只差了 0x20 字节。显然我们可以通过 gets() 的漏洞改写 seed 内的数据。
改写了 seed 的数据就相当于我们可以自己定义种子,那么随机数也就能够知道了。
由此可以写 python 脚本开始 pwn!
|
|
这里使用 ctypes 来 python 内运行 srand 和 rand 函数。
|
|
使用 libc 运行库,然后运行 srand(0) 种子。后面我们就是要将 seed 种子改写成 0。
|
|
攻击载荷显而易见,0x20 个 a 溢出,然后改写为 0。
这个载荷在输出 “Your name:” 之后注入。
最后是循环 10 次写入对应随机数,随机数计算如下
|
|
用 .encode() 方法把 str 转换为 byte 类型。
10 次结束都正确就进入 sub_C3E() 函数然后获得 flag ,pwn 成功。

后续-第一道没有看 wp 写出来的 pwn!!!
攻防世界的 dice_game
附件带有 libc.so.6 应该是要用到的。首先查看保护:
|
|
64 位,开了 NX 保护和 PIE,所幸没有 canary 保护,可以使用栈溢出。
给可执行权限后跑一下(其实严谨点应该先 patchelf libc 的,但是这里没有,也不需要):
|
|
输入名字就好像是猜数字一样,错一次就退出程序了。
进 ida pro 查看反编译伪 c 代码:
不出所料,发现了 srand 和 seed.
很明显的 srand 初始化种子,种子用的 time(0) 比较的随机,应该寻找到一个地方进行溢出覆盖掉 seed[0]。
看看输入名字的 read,这里 可以输入 0x50,也就是 80 字节的数据,但是 buf 就 55 字节,然后我们查看栈空间:
buf 和 seed 之间差了 0x40 ,也就是 64 字节。实际就是 55 + 9,这里 9 是 var19 和 var_18 ,看 main 好像另有用处,但是我尝试了下直接覆盖掉也没关系?
var_19 对应伪代码的 seed ,var_18 对应 v6 。v6 是 raed 的返回值,直接覆盖掉也没什么事。
|
|
buf[v6-1] = 0 也不要紧。
我们的覆盖载荷应该要 55+9 ,后面就可以直接覆盖 seed[0],直接一个 p64(0) 就可以把种子变成 0,后面我们调用 libc 直接开随机数就好了。
再看看猜数字的过程,当 50 次满后进入 sub_B28 函数,该函数直接读了 flag 文件。
也就是说,我们猜对 50 次后就可以拿到 flag,调用 libc 上一题已经写得很清楚了,直接上 exp:
|
|