Apache 是什麼?模組是幹嘛?
Apache 是高度模組化的 Web 伺服器;處理一個 HTTP 請求時,很多模組會共同讀寫
request_rec的欄位(如r->filename、r->args)。不同模組對同一欄位的「語意」若不一致,就可能誤判。Orange Tsai 的研究把這些不一致系統化,歸納出三大類 Confusion 攻擊,其中之一就是 Filename Confusion。
Filename Confusion 的核心:
多個模組對
r->filename的理解不同——有的把它當檔案路徑,有的把它當URL。這種不一致會造成安全問題。
關鍵:URL 編碼 %3F
- 在網址裡
?代表「後面是 query string」。- 如果要在網址中表示字面上的
?,需要 URL 編碼:?→%3F。- Apache 在不同處理階段可能把
%3F解回?,這就是漏洞能被利用的時機差。
1. RewriteRule(mod_rewrite)
RewriteRule 是 mod_rewrite 用來改寫 URL的指令。語法:
| |
- Pattern(樣式):正則表達式,用來比對目前請求的 URL 路徑。
- Substitution(替換結果):匹配後要改到哪裡(可為檔案路徑或 URL)。可用
$1、$2這類反向引用插入 Pattern 抓到的內容。- Flags(旗標,可選):放在
[]內,調整這條規則的行為,例如:
L:命中後停止再處理後續規則(Last)。QSA:保留原始 query string 並附加到新 URL。H=…:指定處理器(Handler),例如H=application/x-httpd-php。
小例子(Pattern / Substitution / Flags)
想把
https://example.com/user/orange對應到伺服器檔案/var/www/user/orange/profile.yml:
| |
- Pattern:
^/user/(.+)$會抓到/user/後面的使用者名稱到$1。- Substitution:
/var/www/user/$1/profile.yml→ 例如$1=orange就是/var/www/user/orange/profile.yml。- 這裡沒寫 Flags,使用預設行為。
1‑1-1 Path Truncation(路徑截斷)
成因:
mod_rewrite在套用 RewriteRule 後,會把替換結果當「URL」處理,呼叫splitout_queryargs()把?後的部分視為 query 分離。當替換結果其實是檔案路徑時,還是會被「當 URL 來切」,於是產生可控截斷。
| |
因為
%3F在後續階段被視為?,/profile.yml被當作 query 切掉了。
splitout_queryargs() 在幹嘛?
它把替換結果中的
?後段剝離到r->args(query),?前面留在r->filename:
r->filename = "/var/user/orange/profile.yml?debug=1"
└─── 路徑 ───┘ └─ query ─┘
r->args = "debug=1" ← 被 splitout_queryargs() 剝出去
1‑1‑2 Mislead RewriteFlag Assignment(誤導旗標設定)
利用點:
先 Pattern 比對 → 套 Flags → 再把結果當 URL 拆 query。
在比對階段使用
%3F(編碼的?)讓請求看起來像是.php,使規則套上不該有的 Flag(例如指定 PHP Handler)。等到拆 query之後,實際要處理的檔案變回.gif,但 Flag 不會被移除,導致圖片被當作 PHP 來跑。
規則長這樣
| |
攻擊步驟
上傳看似普通的圖片
/upload/1.gif,實際內容藏有 PHP
例如:1<?php echo shell_exec('id'); ?>送出請求:
/upload/1.gif%3Faaa.phpPattern 比對:
%3F尚未解碼,/upload/1.gif%3Faaa.php尾端看起來是.php→ 命中 → 套上H=application/x-httpd-php。拆 query:
%3F解為?,?aaa.php被分離到r->args,留下/upload/1.gif。實際處理:雖然是
.gif,但已指定 PHP Handler → 由 PHP 執行圖片內的程式碼。
對照表
| 步驟 | 正常(安全) | 漏洞利用(1‑1‑2) |
|---|---|---|
| 1. 上傳檔案 | 只能上傳圖片;.php 拒絕 | 上傳 .gif(內藏 PHP)通過檢查 |
| 2. 發送請求 | /upload/1.gif → 回傳圖片 | /upload/1.gif%3Faaa.php |
| 3. Rewrite 比對 | .gif 不匹配 ^(.+\.php)$,不套 Flag | %3Faaa.php 讓 Pattern 命中,套 H=application/x-httpd-php |
4. splitout_queryargs | 無變化 | %3F→?;?aaa.php 被拆到 r->args,目標成 /upload/1.gif |
| 5. 處理器 | 靜態檔案處理器 | 仍由 PHP Handler 處理 .gif |
| 6. 結果 | 顯示圖片 | 圖片被當 PHP 執行(RCE 風險) |
1-2. ACL(Access Control List)與 <Files> / PHP‑FPM
要先了解以下三個角色:
mod_proxy:代理模組,負責把請求轉送到別的服務。
mod_proxy_fcgi:把請求用 FastCGI 協定轉給 PHP‑FPM。
- PHP‑FPM:
PHP FastCGI Process Manager,Apache 把.php請求交給它執行。<Files>(ACL):針對「檔案名稱」設定限制,例如特定檔案需登入才可訪問。
常見把 .php 交給 PHP‑FPM 的做法
| |
意思是:
遇到
.php、.phtml… 就不要由 Apache 直接讀檔,而是交給mod_proxy_fcgi→ PHP‑FPM。
1‑2-1 ACL Bypass(存取控制繞過)
情境:用
<Files>來保護單一檔案,例如:
| |
正常流程(安全)
- 請求
/admin.php。 - ACL 檢查:
r->filename = admin.php→ 命中<Files "admin.php">→ 要求驗證。 - 驗證通過後,交給 PHP‑FPM 執行
admin.php。
漏洞流程(繞過)
請求:
/admin.php%3Fooo.php(%3F是?)ACL 檢查:此時尚未解碼 →
r->filename = admin.php%3Fooo.php,不等於admin.php→ 不要求驗證。SetHandler 命中:仍視為
.php→ 交給mod_proxy_fcgi。mod_proxy_fcgi 轉交:把
r->filename改寫為:proxy:fcgi://127.0.0.1:9000/var/www/html/admin.php?ooo.phpPHP‑FPM 行為:
- 去掉
proxy:fcgi://…前綴與主機資訊,只留/var/www/html/admin.php?ooo.php。- 找到第一個
?,截斷為/var/www/html/admin.php。- 實際執行
admin.php。
- 結果:受保護的
admin.php被直接執行,完全跳過 ACL。
對照表
| 步驟 | 正常(安全) | 漏洞利用(ACL Bypass) |
|---|---|---|
| 1. 請求 URL | /admin.php | /admin.php%3Fooo.php |
2. ACL 檢查(<Files>) | r->filename = admin.php → 要驗證 | r->filename = admin.php%3Fooo.php → 覺得不需驗證 |
| 3. SetHandler | 命中 → 交給 mod_proxy_fcgi | 同左 |
4. mod_proxy_fcgi | proxy:fcgi://…/admin.php | proxy:fcgi://…/admin.php?ooo.php |
| 5. PHP‑FPM | 執行 admin.php(無 query) | 截斷 ?ooo.php 後仍執行 admin.php |
| 6. 結果 | 驗證通過者才能執行 | 未驗證也能執行(ACL 被繞過) |
受影響的常見保護寫法(都可能被
%3F打穿):
| |
修補辦法
- 升級 Apache 至 2.4.60+:新版本把舊有危險行為改為預設拒絕;請勿開啟
RewriteOptions UnsafeAllow3F(除非完全理解風險)。- 避免用
<Files>保護單一 PHP 檔(在 PHP‑FPM 架構下)。改用<Location>/<LocationMatch>(比對 URL 而非檔名),或把敏感腳本移出 DocumentRoot,只透過應用層載入。- Rewrite 規則審視:不要讓「使用者可控片段」直接拼到檔案路徑;能用
mod_alias(Alias/Redirect)就避免過度使用mod_rewrite做檔案映射。- 監控與偵測:
- 日誌尋找可疑編碼:
%3F、%2F;- 特別是「非
.php路徑 +%3F...php」的請求模式;- 除錯時可暫時開
LogLevel rewrite:trace2觀察改寫流程(完畢務必關閉)。
誤區/備註
- 不是「PHP Handler 會神奇多做什麼」,而是被錯誤套用到不該由 PHP 處理的檔案上。
%3F的解碼與處理發生在不同階段;理解「哪一步解、何時解」是這類漏洞的核心。- 範例程式碼僅用於安全測試與理解機制,請勿在生產環境或未授權系統操作。