鉴于最近的CTF比赛中常出现关于mt_rand()伪随机函数的题目,本周斗哥就给大家讲解一下关于mt_rand()函数的问题。
mt_rand()工作原理:
这里引用一下php手册
mt_rand( void) : int
mt_rand( int $min, int $max) : int
很多老的 libc 的随机数发生器具有一些不确定和未知的特性而且很慢。PHP 的 rand() 函数默认使用 libc 随机数发生器。mt_rand() 函数是非正式用来替换它的。该函数用了 » Mersenne Twister 中已知的特性作为随机数发生器,它可以产生随机数值的平均速度比 libc 提供的 rand() 快四倍。
如果没有提供可选参数 min 和 max,mt_rand() 返回 0 到 mt_getrandmax() 之间的伪随机数。例如想要 5 到 15(包括 5 和 15)之间的随机数,用 mt_rand(5, 15)。
提到了mt_rand()就要说一下mt_srand()函数:
mt_srand([ int $seed] ) : void
用 seed 来给随机数发生器播种。 没有设定 seed 参数时,会被设为随时数。使用者在进行一次mt_srand()操作后,seed数值将被固定下来,给接下来的mt_rand()函数使用。
mt_rand()存在的问题:
由于mt_rand()的生成的随机数只跟seed和调用该函数的次数有关。举一个简单的例子来说明一下这个问题,假设使用mt_srand(1111111)进行了一次播种操作,接下来调用mt_rand()函数,第一次生成的数值为a,第二次生成的为b,第三次生成的为c。任何一个人拿到这样的一串代码,所执行的结果都是跟刚刚描述的一样。所以当你的seed数值被他人知道后,就可以预测出你接下来的数值是多少,这就是该函数的一个问题,他并不能起到一个真随机数的作用。
seed的获取:
seed获取需要利用一个爆破工具。
https://github.com/lepiaf/php_mt_seed
下载后有这三个文件:
在linux中直接命令make就可以生成可执行文件。
php_mt_seed是一个可以根据一串int 或者一串由许多4个一组的数字组成(等下在说明)。
这里直接用这个工具页面里面的例子来说明。
上图就是直接由一串int数值来爆破的seed值,当然这样爆破出来的seed可能有多个,那么就需要你去验证一下。这种直接根据int数值来爆破是第一种方法。
第二种重点介绍一下:
这后面的一整串数值每四个为一组,中间以一个空格为间隔,前面两个数是随机数的结果区间,后两位是随机数的随机范围区间。具体如何获取这一串数值,等等将在例题中为大家解答。
以上2种都是直接获取seed的用法。
CTF原题:
就前段时间西湖论剑线上赛的web第一题题目就有利用到这个seed这个点,他的最后一步需要通过php_mt_seed这个工具来获取seed,然后flag存放于这个爆破出来的seed.txt文件夹中,对这题有兴趣的可以找找这个题目。
NJCTF也是一道很好的题目。
接下来讲一下SWPUCTF的其中一道web题目。
这些都是当初做的时候的截图了。
具体解法不赘述了,就来说一下整个题目的流程以及关于mt_rand函数的点。
首先注册登录会有一个15位的优惠码,但是优惠码全长为24位,也就是你需要预测出这个值,通过扫描发现了源码,下图就是跟本知识点有关的代码。
先分析一下代码,首先在开头通过一个rand随机获取了一个seed数值,然后采用优惠码的方法,该方法先将seed用作mt_srand()的参数,seed就是mt_srand()播种的种子,接下来每次调用的mt_rand()函数都会根据这个seed来生成数值,解题思路其实很明确,就是爆破到seed然后生成优惠码来完成。
这个脚本是根据源码所写成的脚本,根据原来的生成规律来写的,源码中逻辑是前半段从左往右数X,拿1个字符,取完一半,从后半段从右往左数X,拿一个字符完成优惠码的生成。每次取字符的时候都会使用到mt_rand()来获取位置,也就是每次读取到的位置其实就是mt_rand()函数产生的数值,所以在脚本中用了strpos()函数来获取数值位置,返回的结果实际上就是mt_rand()生成的结果,根据php_mt_seed的格式生成了字符串,爆破得到seed。
得到seed后就可以将seed放入源码中的mt_srand(seed),得到24位优惠码,最后绕过正则得到flag。
小总结:
关于伪随基数的介绍就到这里啦!欢迎各位小伙伴们给斗哥留言探讨哇~