[网鼎杯 2020 青龙组]AreUSerialz WP

[网鼎杯 2020 青龙组]AreUSerialz WP

看到题目,首先进入代码审计

<?php

include("flag.php");//在文件中插入flag.php文件的内容

highlight_file(__FILE__);//highlight_file() 函数对文件进行 PHP 语法高亮显示

class FileHandler {
    
    //定义类

    protected $op;
    protected $filename;
    protected $content;//定义成员变量

    function __construct() {
    
    //如果new了一个FileHandler新对象,机会执行该函数
        $op = "1";
        $filename = "/tmp/tmpfile";//初始化成员变量
        $content = "Hello World!";
        $this->process();//在新对象中调用process()函数
    }

    public function process() {
    
    //定义上文提到的process()函数
        if($this->op == "1") {
    
    
            $this->write();//如果op等于1执行write()函数
        } else if($this->op == "2") {
    
    
            $res = $this->read();
            $this->output($res);
        } else {
    
    
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
    
    //定义上文提到的write()函数
        if(isset($this->filename) && isset($this->content)) {
    
    //使用有filename和content变量,很显然有
            if(strlen((string)$this->content) > 100) {
    
    //如果对象中content字符串长度大于100...很显然小于100
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);//把对象定义的content的内容写入filename文件中,定义res为content的长度
            if($res) $this->output("Successful!");//如果写入成功了即res有长度则输出successful
            else $this->output("Failed!");
        } else {
    
    
            $this->output("Failed!");
        }
    }

    private function read() {
    
    
        $res = "";
        if(isset($this->filename)) {
    
    //如果filename存在
            $res = file_get_contents($this->filename);//函数是用于将文件的内容读入到一个字符串中的首选方法,即将finename文件读入进res
        }
        return $res;
    }

    private function output($s) {
    
    //定义输出结果的格式
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
    
    //反序列化结束后定义op为1,content为空字符,再调用process()函数一次
        if($this->op === "2")//强类型比较,这里输入的op如果不是字符串的2,则false
            $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))//ord() 函数返回字符串中第一个字符的 ASCII 值
            return false;
    return true;//确保字符串中的字符都是asc码在32-125中间的
}

if(isset($_GET{
    
    'str'})) {
    
    //检验是否有字符串输入

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
    
    
        $obj = unserialize($str);//将str字符串反序列化
    }

}

通过如上代码审计,可以发现,如果单纯只进入write()函数没什么用,最后输出的东西只能是succseeful或者fail等没用的东西,而如果进入了read()函数,那么可以利用file_get—contents()函数将开头就导入的flag文件写入res字符串中输出出来。

因此需要进入process,这里我们通过反序列化结束时的__dustruct进入process()函数。

同时进入read()函数有一个关键点:op需要等于2,并且需要绕过强比较,将op=2,而不是op=”2“。

题目中让我们自己输入字符串,由于最后将输入的字符串反序列化,因此输入的应该是一个序列化之后的字符串。

而上面的is_valid()函数又必须让asc码在32-125,因此序列化之后不能存在 * %00之类的东西,所以输入序列化的时候需要进行绕过。

当php版本大于7.1时,对成员变量类型不敏感,而此时可以得到php版本大于7.1,因此在构造字符串时直接使用public格式。

【另外:将payload中,表示字符类型的S进行大写,会将成员变量作为16进制解析。

也可以对不可见字符进行绕过。】

因此构造的字符串如下:

O:11:“FileHandler”:3:{s:2:“op”;i:2;s:8:“filename”;s:8:“flag.php”;s:7:“content”;s:5:“hello”;}

发现啥也没有,然后进入f12去看一眼,找到了。
image-20221130152043190

但是我之后看wp的时候,才发现php伪协议也可以使用,不过我不太懂。

猜你喜欢

转载自blog.csdn.net/m0_56691564/article/details/128124051