php伪随机数和mt_rand爆破

伪随机数是用确定性的算法计算出来的随机数序列,它并不真正的随机,但具有类似于随机数的统计特征,如均匀性、独立性等。
在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。伪随机数可以用计算机大量生成,在模拟研究中为了提高模拟效率,一般采用伪随机数代替真正的随机数。模拟中使用的一般是循环周期极长并能通过随机数检验的伪随机数,以保证计算结果的随机性。伪随机数的生成方法有线性同余法、单向散列函数法、密码法等。

php mt_rand

mt_rand就是一个伪随机数生成函数,它由可确定的函数,通过一个种子产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性)。

mt_rand()函数在使用前需要使用mt_srand()进行播种,mt_rand()根据种子产生一系列的随机数。在php>4.2.0的版本中,php可以在第一次使用mt_rand()的时候自动设置种子,不需要手动完成。

php_mt_seed

php_mt_seed是一个破解mt_rand函数种子的工具,对它应用场景的深刻理解和应用能极大的提升漏洞发现的可能和利用的成功率。
在最简单的调用模式下,它能通过mt_rand第一次输出的值寻找mt_rand的seed,在更高级的模式中它能匹配不是第一次输出的和不明确具体输出的情况。

下载地址:https://download.openwall.net/pub/projects/php_mt_seed/

下载之后需要自行编译,它的使用基于命令行,可以使用1,2,4,或者更多的参数。

  • 一个参数:
    当只有一个参数的时候,这个参数代表mt_rand第一次输出的值。
  • 两个参数:
    当有两个参数的时候,他们代表mt_rand第一次输出应该位于什么区间内。(第一个参数为最小值,第二个参数为最大值,若两个参数相同,则等同于一个参数的情况)
  • 四个参数
    前两个参数表示mt_rand第一次输出的区间,后两个参数表示mt_rand输出的区间。
  • 多于五个参数
    每四个参数一组,但最后一组可以是1,2或4个参数,每一组对应一次输出。

一个例子([GWCTF 2019]枯燥的抽奖):

进入题目的check.php可以看到源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");

意思就是使用随机数产生20个随机值,告诉前10个,要我们猜出后10个来。
使用php_mt_seed工具进行爆破种子,知道了种子就可以知道生成的随机数。
先将告诉的前10个字符转换成php_mt_seed能够识别的参数形式:

1
2
3
4
5
6
7
8
9
10
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='YUExElqFii'
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)

结果为:60 60 0 61 56 56 0 61 40 40 0 61 23 23 0 61 40 40 0 61 11 11 0 61 16 16 0 61 41 41 0 61 8 8 0 61 8 8 0 61

使用php_mt_seed爆破:





最后利用得到的种子获得全部字符即可。

文章作者: Dar1in9
文章链接: http://dar1in9s.github.io/2020/01/29/php/php伪随机数和mt_rand爆破/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Dar1in9's Blog