php中随机数的缺陷

April 9, 2019 WEB安全 访问: 26 次

之前在逆向中见过这个知识点,这次西湖论剑杯出现在了web上面。

php中随机数函数

mt_srand:

播下一个更好的随机数发生器种子
说明
mt_srand ([ int $seed ] ) : void
用 seed 来给随机数发生器播种。 没有设定 seed 参数时,会被设为随时数。
Note: 自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand() 给随机数发生器播种 ,因为现在是由系统自动完成的。

mt_rand:

mt_rand — 生成更好的随机数
说明
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)。

官方解释的有点多,以我的理解就是,在php中随机数函数生成和在C语言中是一样的,先用mt_srand设置一个种子,然后再用mt_rand来生成随机数,若没有直接声明种子,那么系统会随机设置一个种子,这个种子的范围是0到0x7fffffff
随机数生成规则和C语言是相似的,只要设置的种子一样,那么生成随机数的序列就是一样的
例子:

<?php
mt_srand(123456);
for($i=0;$i<5;$i++)
{
    echo mt_rand()."<br>";
}
?>

页面输出:

1863022934
1105767797
2076010745
1928709656
559367964

多次刷新页面,可以看出来这些数字是不变的
我们就可以通过这个规则,在不知道种子的情况下,爆破出种子的值,利用到的工具是php_mt_seed

可以看出来已经把种子爆破出来了

在php中,如果没有指定设置种子,那么系统默认只设置一次种子

网上找了一个例子:

<?php
function wp_generate_password($length = 12, $special_chars = true) {
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  if ( $special_chars )
  $chars .= '!@#$%^&*()';
  $password = '';
  for ( $i = 0; $i < $length; $i++ )
  $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  return $password;
}
$key = wp_generate_password(16, false);
echo "[*] This is a key for public:".$key."\n";
$private = wp_generate_password(10,false);
echo "[*] Create a private key which you don't know:".$private."\n";
?>

上述代码是根据mt_rand(0,61)来随机生成随机数,没有种子
第一次访问页面输出情况:

[*] This is a key for public:UnHPoeq27cYoJRuL
[*] Create a private key which you don't know:BgEt3qw42E

生成payload:

data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
key = "UnHPoeq27cYoJRuL"
index = []
for x in range(len(key)):
    index.append(data.index(key[x]))
for x in range(len(index)):
    print str(index[x])+" "+str(index[x])+" 0 "+"61",

爆破种子:

然后把爆破出来的种子添加到php代码中

<?php
function wp_generate_password($length = 12, $special_chars = true) {
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  if ( $special_chars )
  $chars .= '!@#$%^&*()';
  $password = '';
  for ( $i = 0; $i < $length; $i++ )
  $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
  return $password;
}
mt_srand(655388788);
$key = wp_generate_password(16, false);
echo "[*] This is a key for public:".$key."<br>";
$private = wp_generate_password(10,false);
echo "[*] Create a private key which you don't know:".$private."<br>";
?>

然后刷新页面,发现生辰过得序列已经是固定的值了,说明爆破出来的就是一开始的种子
再来通过生成第二个字符串来爆破一下,验证一下是否是一个种子

事实证明确实是一个种子来生成的两个字符串
以一个CTF题在 实践一下(稍微有改动):

<?php
function random_str($length = "32")
{
    $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
        "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
        "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
        "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
        "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
    $str = '';
    for ($i = 1; $i <= $length; ++$i) {
        $ch = mt_rand(0, count($set) - 1);
        $str .= $set[$ch];
    }
    return $str;
}
session_start();
$seed = rand(0,999999999);
mt_srand($seed);
$ss = mt_rand();
$hash = md5($ss);
setcookie('SESSI0N', $hash, time() + 3600);
$filename = './uP1O4Ds/'.random_str().".txt";
echo $filename;
?>

题目意思就是在0~999999999随机生成一个数当做种子,然后利用这个种子生成一个随机数,把这个随机数md5加密后存到cookie中,再通过生成随机数来生成一个文件名,这里我直接把文件名输出出来
我们通过session来爆破种子
首先找到session:8638d8dae4ac929376e12f56cee9e782,md5解密得到:990143368,再利用工具php_mt_seed来爆破种子

发现有三个都符合条件,那么我们就一个一个试着生成文件名,当前文件名是:“bx2syTaibaCT5IEyNTsMwlxrIvoUvhv7.txt”

<?php
function random_str($length = "32")
{
    $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
        "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
        "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
        "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
        "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
    $str = '';
    for ($i = 1; $i <= $length; ++$i) {
        $ch = mt_rand(0, count($set) - 1);
        $str .= $set[$ch];
    }
    return $str;
}
mt_srand(738128662);
mt_rand();
echo random_str();
?>

经过测试发现种子应该是:738128662

添加新评论