Confusion Attacks: Exploiting Hidden Semantic Ambiguity In Apache HTTP Server! - Orange Tsai
Conference: Orange Conference / HITCON Conference / BlackHat
講者:Orange Tsai
P.S. 其實 Orange 在 DEVCORE 的 Blog 中已經將這幾個漏洞講得很詳細了,這篇算是幫助自己理解之後再濃縮後的版本。
Filename Confusion
截斷攻擊
mod_rewrite:
mod_rewrite
中的 RewriteRule
可以塞路徑或網址,但不管用哪個最後都會強制把結果當成網址處理
1 | static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) |
從上面的程式碼不難看出, RewriteRule
先將 r->filename
變成 uri
,再把 query
刪掉,看起來蠻合理的,畢竟他是要取 path
但這時如果在請求時塞一些 urlencode
後的編碼,就有可能因為後面被截斷從而繞過檢查:
1 | RewriteEngine On |
如果請求長這樣:
1 | http://example.com/user/orange%2fsecret.yml%3f |
parser
出來的路徑 -> /user/orange%2fsecret.yml%3fmod_rewrite
-> /user/orange/secret.yml?profile.yml -> /user/orange/secret.yml(query
的部分會被刪掉)
到 mod_rewrite
時,因為 r->filename
?後面都被刪除的關係,路徑會變成 /var/user/orange/secret.yml
,導致 information leak
除了讀取敏感文件,強制解析成 url
的特性讓攻擊者可以透過 %3f
截斷,導致 url parser
和 apache 的 mod
前後解釋不一致,從而濫用 mod
的各種功能,例如:
本應將 .php
結尾的檔案解析成 php 的 rewrite rule 可由 path 的 %3f
繞過
1 | RewriteEngine On |
惡意請求:
1 | http://example.com/server/upload/1.gif%3f.php |
parser
出來的路徑 -> upload/1.gif%3f.phpmod_rewrite
-> upload/1.gif?.php -> upload/1.gif 並且符合 regex ,所以會將 1.gif 作為 php 解析,成功執行 webshell
ACL Bypass
mod_proxy:
mod_proxy
會將 r->filename
解析成 url ,但大多數的模組不會做這種事,模組之間的解釋不一就有機會繞過驗證,導致 BAC
,例如:
存取 admin.php
時透過 Files
對其進行身份驗證:
1 | <Files "admin.php"> |
惡意請求:
1 | http://example.com/admin.php%3f.php |
parser
出來的路徑 -> admin.php%3f.php -> 很明顯和admin.php
不符,不需要驗證,又結尾是.php
,因此透過SㄍetHandler
將後續動作轉交由mod_proxy
處理mod_proxy
-> admin.php?.php -> ?.php 被當成query
處理,成功存取admin.php
DocumentRoot Confusion
任意程式碼洩漏
RewriteRule
又是你,這邊有一個先備知識,那就是 RewriteRule
會先嘗試存取 DocumentRoot
下面的路徑,再嘗試使用絕對路徑存取檔案。
因此,假設今天要把所有對於 /html/ 中的請求都加上後綴 .html
,可能會這樣寫:
1 | RewriteEngine On |
那我們就有可能用剛剛學會那招 %3f
直接把後綴蓋掉,從而存取伺服器中的任何原始碼:
1 | http://example.com/html/usr/lib/cgi-bin/download.cgi%3f |
parser
出來的路徑 -> /html/usr/lib/cgi-bin/download.cgi%3fmod_rewrite
-> /html/usr/lib/cgi-bin/download.cgi?.html -> /html/usr/lib/cgi-bin/download.cgi(query
的部分會被刪掉)
這時就可以 leak 出任意原始碼了
Local Gadgets Manipulation
預設 apache 會阻擋除了 DocumentRoot
底下的所有存取,而 Debian/Ubuntu
的 Httpd
會預設允許 /usr/share 的存取
1 | <Directory /usr/share> |
所以其實並不是所有檔案都可以存取,如何利用 /usr/share 下面的東西變成了主要的問題(Orange 提出各種濫用預設存在 /usr/share 底下的各種程式碼範本,這邊就不一一贅述)
Handler Confusion
在正常的流程中瀏覽 http://server/config.php。 首先,mod_mime 會在 type_checker 階段根據 AddType 所設定的附檔名將相對應的內容複製到 r->content_type 中,由於 r->handler 在整個 HTTP 生命週期中並無賦值,於是在執行模組處理器前 ap_invoke_handler() 會將 r->content_type 當成模組處理器,最終呼叫 mod_php 處理請求。
如果可以控制 Response Header 的 Content-Type
欄位,就可以呼叫任何 mod
處理請求,但是問題是什麼呢?問題是需要在執行到 ap_invoke_handler()
前把 r->content_type
覆蓋掉,上面有提到 ap_invoke_handler
會在 type_checker 階段被複製進去,而能夠修改 Content-Type
的地方又全都在這之後,因此直接改 Content-Type
是沒有用的
那要如何利用呢?
在 RFC 3875 中,規定了 CGI 的 Local Redirect Response 行為:
The CGI script can return a URI path and query-string (‘local-pathquery’) for a local resource in a Location header field. This indicates to the server that it should reprocess the request using the path specified.
細看 ap_internal_redirect_handler()
的轉址實作可以發現:
1 | AP_DECLARE(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r) |
Httpd 會先設定 Content-Type
,並在後面呼叫我們心心念念的 ap_invoke_handler()
,這不就是我們要達到的效果嗎
那什麼狀況下會由伺服器端處理轉址並跳到 ap_internal_redirect_handler()
呢?
我們可以在 mod_cgi
的實作中找到答案:
1 | if (location && location[0] == '/' && r->status == 200) { // <------ [2] |
如果回傳的 Status 是 200 以及 Location 標頭欄位是 / 開頭則 mod_cgi
便會把這個回應當成一個伺服器端的轉址並開始處理,因此,如果我們可以控制回應標頭(例如 CRLF Injection or SSRF 之類的漏洞),便可以呼叫任意模組
Arbitrary Handler to Information Disclosure
透過 mod_status
達到 information leak:
1 | http://server/cgi-bin/redir.cgi?r=http:// %0d%0a |
Arbitrary Handler to Misinterpret Scripts
透過 mod_php
強制將任意檔案解析成 php:
1 | http://server/cgi-bin/redir.cgi?r=http:// %0d%0a |
Arbitrary Handler to Full SSRF
呼叫 mod_proxy
存取任何協議以及任意網址:
1 | http://server/cgi-bin/redir.cgi?r=http:// %0d%0a |