從最近的微信支付看XXE漏洞

FreeBuf2018-07-12 05:34:41

先説下寫這篇文章的初衷吧,最近微信支付java_sdk剛爆發了一次xxe漏洞,然後領導趕快用自家的靜態代碼審計工具做了審計(這裏我就不報名字,本來可以幫公司推廣下產品是很好的,但我怕本文過於基礎會被各位大佬噴出翔來,到時候有辱“司”門就真是罪過罪過了)。

發現能成功找出漏洞點,如下圖。

到目前本來是件極好的事情,但是發現使用自己規則修復後依然能掃描出漏洞,這就很能説明問題,於是老大讓我對微信支付漏洞做漏洞研究並找出產品出問題的原因。所以才有了這篇文章。由於本文的初衷是為了改進產品,所以本文並不是為了深入研究xxe漏洞,更加適合開發人員看。

微信支付的sdk中提供了WXPayUtil這個工具類,該類中實現了xmltoMap和maptoXml這兩個方法,而這次的微信支付的xxe漏洞爆發點就在xmltoMap方法中。


該方法是為了將xml格式的字符串strXML轉化為map。由於strXML可由攻擊者控制,且程序未作任何防護措施(如禁止引用外部實體;過濾關鍵字符串等),導致惡意攻擊者可利用外部實體注入讀取服務器上的文件。當攻擊者獲取支付加密所用的安全密匙後,完全可以實現0元支付商品。

這裏先以微信sdk中的xmlToMap方法為例,復現該xxe漏洞。(由於要完整的實現微信零元支付,需要寫比較完整的程序對接微信支付接口,比較耗時間,暫時先不做)。

出現問題的代碼是:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();org.w3c.dom.Document doc = documentBuilder.parse(stream);

生成documentBuilderFactory後直接去解析流。

構造xml格式的字符串如下:

<?xml version='1.0' encoding='utf-8'?><!DOCTYPE xdsec [<!ELEMENT methodname ANY><!ENTITY xxe SYSTEM 'file:///c:/windows/win.ini'>]><methodcall><methodname>&xxe;</methodname></methodcall>

這樣mapToXml中DOM解析器解析該字符串時,會訪問外部實體中的SYSTEM屬性中標識的URL,並將讀取的文件內容放入methodccall節點中。然後取出放入map中(實際場景中map中的值最後會被攻擊者所獲取,我們這裏以在控制枱輸出為例),能成功讀取系統文件。

一、完全禁用DTDs,生成documentBuilderFactory後對feature進行設置

documentBuilderFactory.setFeature(“http://apache.org/xml/features/disallow-doctype-decl“,true);

效果如下:

程序會報錯,提示將該屬性設為true。

二、生成documentBuilderFactory後對feature進行設置

documentBuilderFactory.setXIncludeAware(false);          documentBuilderFactory.setExpandEntityReferences(false);                                        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities",false);                                 documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);

效果如下:

程序雖然不會報錯,但是已經讀取不出系統文件中的內容了。

三、對關鍵詞做過濾,如ENTITY、 DOCTYPE

對以上三種防範方式做分析:推薦使用第一種,能處理掉絕大多數的xxe漏洞;當有需求不能全部禁用掉DTD時,使用第二種方法;不建議使用第三種方法,如果不然很容易被繞過,如使用ENTIENTITYTY等等情況來繞過。

微信支付sdk中使用的是原生的dom解析xml,接下里分別復現使用原生SAX解析xml、使用dom4j解析xml、使用jdom解析xml這三種實現方式的xxe漏洞以及修復方法(修復原理是一樣的,方法都類似的,但為了方便之後產品規則的完善,這裏全部列舉出來)

使用原生SAX解析xml

問題在於生成SAXParserFactory後直接去解析xml了,修復方法添加屬性

sf.setFeature(“http://apache.org/xml/features/disallow-doctype-decl“,true);

效果如下:

使用dom4j解析xml:

SAXReader reader  = new SAXReader();InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));Document doc = reader.read(stream);

修復方法:

reader.setFeature(“http://apache.org/xml/features/disallow-doctype-decl“,true);

效果如下:

使用Jdom解析xml

SAXBuilder  builder = new SAXBuilder();Document doc = builder.build(stream);

修復方法如下:

builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

效果如下:

最後是對SkyJava審計WxPayAPI結果的分析:

SkyJava是報了兩個xxe漏洞,分別是WXpayUtil.java中的mapToXml和xmlToMap這兩個方法。其中這次的微信支付xxe漏洞爆發點是在xmlToMap,所以兩個中一個是正確的一個是誤報。先分析誤報:

該方法是實現將map中的鍵值對取出後生成xml的節點,並將其放在根節點中,像這種情況,就算map是受攻擊者控制的,生成xml的時候也不會構造出外部實體的引入。其實xxe漏洞都是解析的時候出現問題,單單只是生成有問題的xml,並不能確定是否存在xxe漏洞,關鍵還是得看程序去解析它的時候是否有安全措施(如上面所説的添加禁止外部實體引入的屬性等)。

對於該種誤報我的建議是:不能僅僅因為沒有設置安全屬性就判斷存在漏洞,儘量是先判斷存在解析xml的情況下再根據

  1. 是否有設置安全屬性

  2. Source是否安全

來判斷是否存在漏洞。(感覺不是很好實現,以上兩段話主觀性比較強,僅做參考…..)

再説下SkyJava報的另外一個點,爆發點確定的是正確的。

但頭説修復之後還是會報xxe漏洞,所以我看了下修復之後的方法。

修復的方法中將http://javax.xml.XMLConstants/feature/secure-processing屬性設為true。在本地測試效果如下,發現並不能防禦xxe漏洞,所以不建議使用該方法。

再看官方微信支付sdk修復這個xxe漏洞之後的方法是怎麼樣的

是使用了一個專門的xml工具類來生成DocumentBuilder

該類中設置了一些安全屬性,應該是微信支付為了保險起見吧,同時採用我上面所説的修復方法一和二(畢竟沒有絕對的安全)。但在SkyJava的規則上,我認為審計的時候只要採舉了其中的一種就可以認為不存在xxe漏洞,如果規則上認為兩種措施都用才算不存在漏洞的話可能會導致誤報率較高(畢竟很多程序都只採用第一種方法防範xxe)。

最後,我還是想説我第一次在FreeBuf上發表文章,望各位大佬站在培養新人的點上,輕噴輕噴!

*本文作者:路上路人路過,屬於FreeBuf原創獎勵計劃,未經許可精緻轉載

閲讀原文

TAGS:xxe 漏洞微信支付解析xml外部實體