[MRCTF2020]Ezaudit

[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 = KVQP0LdJKRaV3n9Dpublic_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

image-20211217191437265

得到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();

运行上述代码的时候一定要注意两点

  1. php版本一定要是在PHP 5.2.1 to 7.0.x中,程序运行中给出了这个版本,不同版本通过种子生成随机数的算法不同
  2. 一定要先输出一遍public_key,再输出private_key,这样是为了确保种子生成随机数的顺序保持一致

然后直接使用万能钥匙就可以获得flag了,这里比较简单就不过多阐述了(这里被坑了,已知以为要给index.php传参,还搞了半天

image-20211217200111944

posted @ 2023-01-08 23:18  seizer-zyx  阅读(55)  评论(0编辑  收藏  举报