**说到随机函数的应用,作为一个菜鸟,理解的也不是很深刻,在这里之作为一个笔记来记录,以后慢慢将其掌握之后,再在内容上面进行加深。

随机函数的作用,常常是用来生成验证码、随机文件名、订单号,如果用来做安全验证的话常常用来生成加密key、token等等。**

一、常见的随机函数

1、rand()
常用的随机函数,默认生成0-getrandmax()之间的随机数,不过因为性能问题,已经被mt_rand()函数替代
相关函数:
rand(int $min,int $max)
srand(int $seed),生成时间种子,同一个时间种子下随机生成的随机数值是相同的。
getrandmax()获取最大随机数,这里获取的随机数会随系统的不同而不同。如linux最大2147483647
2、mt_rand
常用的随机函数,默认生成0-mt_getrandmax()之间的随机数, Mersenne Twister 算法生成随机整数
相关函数:
mt_srand(),生成种子,同一个种子下随机生成的随机数值是相同的。
该函数是产生随机值的更好选择,返回结果的速度是 rand() 函数的 4 倍(手册是是这么写的),我个人并不认同的,我感觉他说的4倍是很多年前的事了。因为mt_rand()使用的Mersenne Twister algorythm是1997的事,所以在很多年前,和rand()在速度上的差异可能是(4倍),自2004年,rand()已经开始使用algorythm,所以现在它们速度上没有太大的区别.
有时候手册也是骗人的,就像一会要说的这个函数造成的问题。
3、uniqid()
生成唯一ID的函数,精确到了微妙,较mt_rand精确。适用场景生成token和生成uuid
具体细节没做研究
4、openssl_random_pseudo_bytes()
适用于生成token,具体详情没做研究

二、mt_rand()函数造成的问题所在

--
今天重点记录一下mt_rand带来的问题,和在CTF中的具体解法。对于这个函数的介绍是有两个版本的,一个是英文版,一个是中文版,去对比一下,在英文版中会多出一个警告:

Caution:This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.

意思是注意函数的安全,不能用来生成密码安全值,不要引用于加密,如果需要可以适用等等函数代替。其实函数本身是没有问题的,只是使用的方式不当而已。
我没有挖漏洞的经验,所以也不清楚大佬说的这方面的漏洞多不多,但以我个人而言,肯定只会选择看中文版的介绍,并且使用此函数去生成密码安全值。这样就不知道警告,也就会造成安全问题。

首先我们要知道,每一次调用mt_rand()函数的时候,都会检查一下系统有没有播种。(播种是由mt_srand()函数完成的),当随机种子生成后,后面生成的随机数都会根据这个随机种子生成。所以前面也说到,同一个种子下随机生成的随机数值是相同的。同时,也解释了我们破解随机种子的可行性。如果每次调用mt_rand()函数都需要生成一个随机种子的话,那根本就没办法破解。
做一个简单的测试,测试随机数种子相同,后面的每次执行的随机数也相同
脚本:

<?php
mt_srand(45678913);
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
?>

php随机函数mt_rand()产生的小问题大漏洞
两次执行结果:
php随机函数mt_rand()产生的小问题大漏洞
可以看到得到的随机数是相同的。
同时我们应该注意,mt_srand()函数播种的时候,只有在第一次调用mt_rand()函数的时候才会使用。所以如果我们知道了第一次生成的随机数值,就可能爆破出随机数种子。

--
接下来就来验证一下
首先爆破出随机数种子,利用工具php_mt_srand
工具链接:https://github.com/lepiaf/php_mt_seed
这个工具的具体用法,也不再着解释,说起来还是挺复杂的,在这里推荐一个文章吧,看完就看一了解个大概,同时还有助于理解随机数

https://×××w.openwall.com/php_mt_seed/README
爆破随机数种子,我们将得到的第一组数进行爆破
php随机函数mt_rand()产生的小问题大漏洞
得到三组seed值,里面就有我们使用的随机数种子,当然在正常情况下我们是不知道这个数值的,所以还需要去验证。

--
如何验证?
1、只需要将这个种子通过mt_srand()函数生成数值后再调用几组mt_rand()函数生成几组随机数\
2、然后将随机数和我们刚开始得到的随机数对比即可。
测试第一组
php随机函数mt_rand()产生的小问题大漏洞

php随机函数mt_rand()产生的小问题大漏洞
测试第二组
php随机函数mt_rand()产生的小问题大漏洞

php随机函数mt_rand()产生的小问题大漏洞
测试第三组
php随机函数mt_rand()产生的小问题大漏洞

php随机函数mt_rand()产生的小问题大漏洞
可以看到只有第二组的随机数和原来的相同,到这里便成功获取到了seed值。

三、CTF中的题目

题目来自于成都大学网络***演练平台--随机数
题目链接:http://ctf.cdusec.org/challenges
题目非常简单,直接就给出了题目随机数的源码以及前几组随机数:

<?php
echo "PHP 5.4.26";
mt_srand(xxxxxxxx);
#We can't tell you what is xxxxxxxx!
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo mt_rand()."<br/>";
echo "echo flag{".mt_rand()."}"
?>
984489752
619387123
2070958802
2105559368
1909473866
1679323715
1910332168
640569646
1103001695
1871111424
flag
So, Please guess the flag!

理解了上面所说的爆破步骤的话,对这个题目简直太容易了。
1、因为我们不知道××××代表那些数字,但是给出了第一组随机数。上面也说了,只有第一次调用mt_rand()函数的时候才会自动播种,接下来的就会根据这个种子生成随机数。所以我们来利用第一组爆破
php随机函数mt_rand()产生的小问题大漏洞
2、将seed数值带入到脚本mt_srand()函数当中,去跑flag,因为只要得到seed,接下来不管怎么跑,跑几次,得到的几组数值都是相同的。这里得到两组seed值,测试两次,将得到的flag递交看一下那个正确即可。
php随机函数mt_rand()产生的小问题大漏洞

四、应当注意的问题。

随机数这个东西在系统之间和php版本之间是有削微的区别的,比如就CTF这道题目而言,开始我并没有注意php版本问题,当时的测试环境是php7的版本,同样的做法,答案却是不同的,所以结果也一直出不来。
同时也告诉我们,细节决定成败。

因为是刚刚学习这一知识点,可能讲解的也不够详细或者有些许错误。哪位大佬看到不准确的希望给指出,谢谢!

相关链接:
http://php.net/manual/en/function.mt-rand.php
http://php.net/manual/zh/function.mt-rand.php