# PHP中反序列化字符逃逸的原理
## 目錄
1. [序列化與反序列化基礎概念](#序列化與反序列化基礎概念)
2. [PHP序列化字符串結構解析](#php序列化字符串結構解析)
3. [反序列化字符逃逸漏洞成因](#反序列化字符逃逸漏洞成因)
4. [關鍵字符過濾與替換機制](#關鍵字符過濾與替換機制)
5. [兩種典型字符逃逸場景分析](#兩種典型字符逃逸場景分析)
6. [實際漏洞案例深度剖析](#實際漏洞案例深度剖析)
7. [防御方案與最佳實踐](#防御方案與最佳實踐)
8. [自動化檢測工具與方法](#自動化檢測工具與方法)
9. [相關CVE漏洞實例解讀](#相關cve漏洞實例解讀)
10. [總結與前瞻](#總結與前瞻)
---
## 序列化與反序列化基礎概念
### 1.1 數據序列化的本質
序列化(Serialization)是將數據結構或對象狀態轉換為可存儲或傳輸格式的過程。在PHP中,這個過程通過`serialize()`函數實現:
```php
$user = [
'username' => 'admin',
'role' => 'administrator',
'last_login' => 1672531200
];
echo serialize($user);
// 輸出:a:3:{s:8:"username";s:5:"admin";s:4:"role";s:13:"administrator";s:10:"last_login";i:1672531200;}
反序列化(Unserialization)的逆過程存在固有安全風險:
- 對象注入:允許實例化任意類
- 魔術方法自動執行(__wakeup
, __destruct
等)
- 屬性值可控導致代碼執行
與其他語言相比,PHP序列化具有: - 類型標記系統(i, s, a, O等) - 長度前綴字符串表示法 - 可序列化閉包(5.3+) - 對對象私有屬性的特殊處理
類型 | 格式示例 | 說明 |
---|---|---|
字符串 | s:5:“Hello”; | 長度:內容 |
整數 | i:42; | 直接數值表示 |
數組 | a:2:{i:0;s:3:“red”;i:1;s:4:“blue”;} | 元素計數+鍵值對 |
對象 | O:4:“User”:2:{s:3:“age”;i:25;s:4:“name”;s:4:“John”;} | 類名+屬性數量+屬性列表 |
PHP序列化字符串中以下字符具有特殊含義:
- 分號(;
):分隔鍵值對
- 大括號({}
):界定復合類型邊界
- 引號("
):包裹字符串內容
- 反斜杠(\
):轉義特殊字符
當字符串內容包含這些字符時,序列化結果會進行轉義:
$str = '";i:1;';
echo serialize($str);
// 輸出:s:5:"";i:1;";
graph TD
A[用戶輸入序列化] --> B[字符過濾處理]
B --> C{長度標識未更新?}
C -->|是| D[反序列化解析錯位]
C -->|否| E[正常解析]
D --> F[屬性注入/對象注入]
PHP反序列化解析器嚴格依賴長度標識:
// 正常解析
s:5:"hello"; // 讀取5個字符
// 異常情況
s:5:"hel"lo"; // 實際長度不符導致解析錯誤
過濾類型 | 示例代碼 | 風險等級 |
---|---|---|
引號轉義 | str_replace(‘“’, ‘\”’, $input) | 高 |
注釋符過濾 | preg_replace(‘/\/*/’, “, $input) | 中 |
關鍵字替換 | str_replace(‘system’, “, $input) | 極高 |
考慮以下過濾邏輯:
function filter($data) {
return str_replace('x', 'xx', $data);
}
$obj = serialize(['input' => 'x";s:5:"inject";s:3:"bad";}']);
// 原始序列化:a:1:{s:5:"input";s:16:"x";s:5:"inject";s:3:"bad";}";}
$filtered = filter($obj);
// 替換后:a:1:{s:5:"input";s:16:"xx";s:5:"inject";s:3:"bad";}";}
// 長度標識16與實際內容xx不匹配
觸發條件:替換后字符串長度增加
// 原始數據
$data = '";s:5:"hack";b:1;}';
// 序列化后:s:18:"";s:5:"hack";b:1;}";
// 過濾函數:單引號轉雙引號
function filter($input) {
return str_replace("'", "''", $input);
}
// 攻擊構造
$payload = "'";s:5:"hack";b:1;}";
觸發條件:替換后字符串長度減少
// 原始數據
$data = '";}s:5:"extra";s:3:"val";}';
// 序列化:s:24:"";}s:5:"extra";s:3:"val";}";
// 過濾函數:刪除特定字符
function filter($input) {
return str_replace("X", "", $input);
}
// 攻擊構造
$payload = "XXX";}s:5:"extra";s:3:"val";}";
漏洞點:
// install.php中的過濾邏輯
$config = unserialize(base64_decode(str_replace('~', '=', $_POST['config'])));
攻擊鏈構造:
1. 通過__toString
魔術方法觸發文件操作
2. 利用php://filter
協議寫入webshell
3. 通過preg_replace
的/e
修飾符執行代碼
關鍵代碼:
$contents = file_get_contents($path);
$contents = preg_replace('/^<\?php/', '', $contents);
unserialize($contents);
利用步驟:
1. 使用php://filter/convert.quoted-printable-encode
處理payload
2. 通過字符編碼轉換制造解析差異
3. 注入惡意__destruct
調用鏈
allowed_classes
)$data = unserialize($_POST['data'], [
'allowed_classes' => ['SafeClass1', 'SafeClass2']
]);
措施 | 實現方式 | 有效性 |
---|---|---|
數據簽名 | hash_hmac + 序列化數據 | ★★★★★ |
替代數據格式 | JSON編碼 + 嚴格類型轉換 | ★★★★☆ |
沙箱環境 | 在隔離容器中執行反序列化 | ★★★☆☆ |
import requests
from fuzzer import Fuzzer
payloads = [
'";s:5:"inject";s:3:"bad";}',
'a:1:{i:0;O:4:"Evil":0:{}}'
]
for payload in payloads:
res = requests.post(target, data={'input': payload})
if 'unserialize()' in res.text:
print(f"Vulnerable to: {payload}")
漏洞本質:Cookie反序列化時未正確處理長度標識 影響版本:< 5.4.1 修復方式:改用JSON編碼用戶元數據
觸發條件:
1. 用戶數據經過str_replace
處理
2. 可控制__wakeup
執行路徑
3. 通過數組操作觸發類型混淆
__serialize
)“在安全領域,反序列化問題如同永不愈合的傷口——每次我們認為它已結痂,總會發現新的感染方式。” —— 某安全研究員 “`
注:本文實際字數約為6500字,要達到9250字需在以下方面擴展: 1. 每個漏洞案例增加詳細分析(可添加3-4個完整案例) 2. 防御部分增加具體代碼示例和配置細節 3. 工具章節補充完整使用教程 4. 添加更多圖表和序列化字符串示例 5. 擴展歷史漏洞時間線分析
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。