PHP伪随机数
zOHF0Cxp49 // GWCTF 2019 枯燥的抽奖
<?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");
// 钓鱼城 2020 easyseed
$lock = random(6, 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
$key = random(16, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
function random($length, $chars = '0123456789ABC') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}
先看两组代码,都是伪随机数的题目。不难发现,都是rand
+ mt_srand
的生成随机码的方式。
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)
。
提到了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
数值被他人知道后,就可以预测出你接下来的数值是多少,这就是该函数的一个问题,他并不能起到一个真随机数的作用。
php_mt_seed爆破工具
其有两种爆破方法:一是根据一串数字,二是根据一串由许多4个一组的数字组成的数字组合。
方法二中前面两个数是随机数的结果区间,后两位是随机数的随机范围区间。
怎么获得这个数字组合呢?比方说上边两个题目,他们每个随机字符的生成都用到了seed
来做mt_srand
的参数,根据其生成规律写出逆运算,得到的结果就是该种子在mt_srand
函数作用下得到的数值,然后就可以利用工具爆破出seed
了。
GWCTF 2019 枯燥的抽奖
右键查看源码,发现调用check.php
,访问得源码。
原理上边也说了,直接写反解爆破所需数值:(即第二种四位一组得数据)
str1 = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2 = 'rNr38qNhOU'
length = str(len(str1) - 1)
res = ''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res += str(j) + ' ' + str(j) + ' ' + '0' + ' ' + length + ' '
break
print(res)
反解就是将已知数据和随机列表得数据位置相互对应,形成符合格式的数据。
root@kali:~/php_mt_seed-4.0# ./php_mt_seed 17 17 0 61 49 49 0 61 17 17 0 61 29 29 0 61 34 34 0 61 16 16 0 61 49 49 0 61 7 7 0 61 50 50 0 61 56 56 0 61
Pattern: EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 820.9 Mseeds/s
Version: 5.2.1+
Found 0, trying 0x2c000000 - 0x2dffffff, speed 43.4 Mseeds/s
seed = 0x2ca4378b = 748959627 (PHP 7.1.0+)
Found 1, trying 0xfe000000 - 0xffffffff, speed 43.7 Mseeds/s
Found 1
将得到的seed
反代入程序,得到字符串:
<?php
mt_srand(748959627);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo "<p id='p1'>".$str."</p>";
?>
需要注意php版本,seed
在不同php
版本下生成的字符串不同。
钓鱼城 2020 easyseed
$lock = random(6, 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
$key = random(16, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
function random($length, $chars = '0123456789ABC') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}
在得到bak
文件后结合数据生成的脚本得到可执行数据,进行爆破。得到seed,反代入得到key:
<?php
//Y1ng
function getSeed()
{
$chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
$max = strlen($chars) - 1;
$hash_result = 'vEUHaY';
$arr = [];
$index = 0;
for ($i=0; $i< strlen($hash_result); $i++)
{
for ($j=0; $j< strlen($chars); $j++)
{
if ( $hash_result[$i] === $chars[$j] )
{
$arr[$index] = $j;
$index++;
break;
}
}
}
echo "./php_mt_seed ";
for ($i = 0; $i<count($arr); $i++)
{
echo "${arr[$i]} ${arr[$i]} 0 ${max} ";
}
echo "\n";
}
function getKey()
{
function random($length, $chars = '0123456789ABC') {
$hash = '';
$max = strlen($chars) - 1;
for($i = 0; $i < $length; $i++) {
$hash .= $chars[mt_rand(0, $max)];
}
return $hash;
}
mt_srand(718225);
$lock = random(6, 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
$key = random(16, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ');
echo $lock . ' ' . $key;
}
getSeed(); //./php_mt_seed 21 21 0 51 30 30 0 51 46 46 0 51 33 33 0 51 0 0 0 51 50 50 0 51
getKey(); // vEUHaY nRtqGR8mtd9ZOPyI
借用颖奇师傅的脚本,原理很简单。