php反序列化及__toString()

思路:
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

php反序列化及__toString()_第1张图片

接下来,看到后面有一个文件包含,利用该漏洞。可以把文件源码读下来,如下:

?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里面的值最开始是序列化的,前面可以绕过过滤,在这里才打印出来

php反序列化及__toString()_第2张图片

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会有代码审计

你可能感兴趣的:(php反序列化及__toString())