手把手教你看懂XXE漏洞,还原XXE漏洞实战场景
好的,我现在需要帮助用户总结一篇文章的内容,控制在100字以内,并且直接写描述,不需要特定的开头。首先,我得仔细阅读用户提供的文章内容,理解其主要结构和重点。 这篇文章主要介绍了XML的基本概念、结构、实体、DTD和XXE漏洞。它详细解释了XML的可扩展性、平台无关性等特性,并举例说明了应用场景。接着,文章深入讲解了XML的组成元素,如声明、元素、属性、文本内容等,并讨论了实体的定义和使用,特别是内部实体和外部实体的区别。最后,文章通过搭建Java环境和编写Servlet代码,演示了如何解析XML,并提到了XXE注入攻击的可能性。 为了总结这篇文章,我需要抓住几个关键点:XML的定义、核心特点、应用场景以及XXE漏洞的介绍。同时,还要提到文章通过实践教学展示了如何解析XML并利用XXE进行攻击。 现在,我需要将这些要点浓缩到100字以内。确保语言简洁明了,涵盖主要概念而不遗漏重要信息。可能的结构是先介绍XML及其特点和应用,然后提到XXE漏洞,并说明文章通过实践教学进行了演示。 最终的总结应该清晰传达文章的核心内容,让用户快速了解文章的主要信息。 </think> 本文介绍了XML的基本概念、结构及应用场景,并详细讲解了XML实体、DTD(文档类型定义)以及XXE(XML外部实体注入)攻击的相关知识。通过搭建Java环境并编写Servlet代码演示了如何解析XML文件,并介绍了如何利用XXE漏洞进行攻击。 2025-12-19 01:47:50 Author: www.freebuf.com(查看原文) 阅读量:4 收藏

引言

XML的双刃剑

XML(eXtensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言。它被设计用来结构化、存储和传输信息,同时保持人类可读和机器可读的特性。它被设计为具有自我描述性,并且是W3C的推荐标准。作为数据交换的标准格式,在Web服务、配置文件、数据存储等领域广泛应用。然而,正是其强大的可扩展性设计,隐藏着一个危险的攻击向量——XML外部实体注入(XXE)。

XML的核心特点:

  • 可扩展:用户可以自定义标签
  • 平台无关:不依赖于任何编程语言或操作系统
  • 自描述性:数据和结构一起存储
  • 严格的语法规则:必须格式良好

XML的应用场景

  1. 配置文件:如Java的web.xml、Spring配置文件
  2. 数据交换:如SOAP Web Service
  3. 文档存储:如OpenOffice文档格式
  4. RSS订阅:网站内容聚合
  5. Web服务:SOAP协议使用XML

XML详解

要清楚XXE漏洞,首先需要了解的就是XML文档

XML基本结构

XML文档必须包含一个根元素,该元素是所有其他元素的父元素。XML文档由声明、元素、属性、文本等组成,<?xml version="1.0" encoding="UTF-8"?>必须出现在文档开头,XML版本(通常是1.0或1.1),比如下面的一个常规的xml文档格式:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>aaa</to>
  <from>bbbb</from>
  <heading>email</heading>
  <body>this is a email</body>
</note>

第一行是XML声明,定义了XML版本和编码。接下来是根元素<note>,每个XML文档必须有且仅有一个根元素。根元素包含4个子元素。

XML的核心组成

XML元素

XML元素由开始标签、结束标签和两者之间的内容组成(<标签名>内容</标签名>)。元素可以包含其他元素、文本或两者混合。元素也可以为空。比如如下示例:

<book>
  <title>XML example</title>
  <author>monsterL</author>
  <chapter>
    <title>description</title>
  </chapter>
</book>

XML属性

属性提供有关元素的额外信息。属性在开始标签中定义,以名称-值对的形式出现(<元素名 属性名="属性值">),同一个元素不能有相同名称的属性。比如:

<book name="web安全" pageSize="22">
  <title>XML example</title>
  <author>monsterL</author>
  <chapter>
    <title>description</title>
  </chapter>
</book>

使用属性和元素的比较:

<!-- 使用属性 -->
<book title="XML example" author="monsterL" pageSize="999">
  <chapter title="description"/>
</book>

<!-- 使用子元素 -->
<book>
  <title>XML example</title>
  <author>monsterL</author>
  <pageSize>999</pageSize>
  <chapter>
    <title>description</title>
  </chapter>
</book>

XML命名空间

由于XML元素名称是用户定义的,当两个不同的文档使用相同的元素名时,可能会发生冲突。XML命名空间通过使用URI来避免元素名冲突。

<root xmlns:h="http://www.w3.org/TR/html4/"
      xmlns:f="http://www.w3schools.com/furniture">
  <h:table>
    <h:tr>
      <h:td>Apples</h:td>
      <h:td>Bananas</h:td>
    </h:tr>
  </h:table>
  <f:table>
    <f:name>potato</f:name>
  </f:table>
</root>

文本内容

元素可以包含文本内容,<tag>这里是文本内容</tag>

注释

<!-- 这是XML注释 -->,注释不能出现在声明之前

CDATA区段-了解

包含不需要解析的文本,如脚本代码或包含大量特殊字符的内容。语法如下:

<script><![CDATA[
  function test() {
    if (a < b && c > d) {
      alert("Hello");
    }
  }
]]></script>

‌CDATA 声明‌:<![CDATA[ 和 ]]> 用于包裹 JavaScript 代码,防止浏览器将 < 和 > 误认为 HTML 标签。

以上为XML的基本格式,那么什么又是实体呢。

实体Entity

如果xml文本中特殊字符需要转义,那么转义之后的内容就称之为实体。如下转换规则:它是 HTML/XML 实体编码的一种形式,以下可以看作是内部定义好的实体。

  • <→ &lt;
  • >→ &gt;
  • &→ &amp;
  • "→ &quot;
  • '→ &apos;

在 HTML/XML 中,实体是一种特殊字符的替代表示方式,用于避免直接使用字符本身可能引发的解析错误或歧义

‌实体编码的常见用途:在HTML/XML 内容中使用‌:在标签或属性中嵌入 < 时必须使用 &lt;,例如:

<p>这是一个段落,包含一个标签:&lt;div&gt;</p>

其‌作用‌是防止浏览器将 <误认为标签开始符,导致解析错误。比如将其在浏览器解析出来如下:这也是防止XSS攻击的防御手段之一。

1766040150_6943a256c7833ace35d29.png!small?1766040151420

XML实体的本质

既然有xml中存在内部定义好的实体,那也可以存在自己定义的实体。

‌实体编码‌:根据内部的实体格式,如果将字符串转换为 &name; 或 &#number; 格式的字符串,称之为实体,需要确保内容在不同上下文中正确解析--有点类似于代码中的变量的作用。

实体的定义:以&符号开头,允许在文档中定义可重用的内容。实体分为内部实体和外部实体,其定义格式:以<!ENTITY开头 :通过<!ENTITY>声明的实体,称为自定义实体(Custom Entities),允许开发者在 XML 文档中定义自己的变量或占位符,而如果该自定义实体使用 SYSTEM 或 PUBLIC 关键字引用外部资源(如文件或 URL)则称之为外部实体(External Entities)。

<!ENTITY 内部实体 "固定内容">
<!ENTITY 外部实体 SYSTEM "外部资源URI">

内部实体示例:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
  <!ENTITY writer "monsterL">
]>
<note>
  <author>&writer;</author>
</note>

这里解析就会得到<author>monsterL</author>类似变量的作用

外部实体示例:多用于资源的引用

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
  <!ENTITY file SYSTEM "file:///tmp/note.txt">
]>
<note>
  <content>&file;</content>
</note>

这里解析xml之后将得到 <content>note中的内容</content>

关键区别外部实体可以加载本地文件系统或远程资源内容,这也是XXE攻击的核心。

DTD(文档类型定义)

DTD用于定义XML文档的结构。它定义了一组规则,用来确保XML文档的一致性。

<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to,from,heading,body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>
<note>
  <to>aaa</to>
  <from>bbb</from>
  <heading>email</heading>
  <body>this is a email!</body>
</note>

1-7行表示定义了需要使用到的标签名,后面就只能使用这些标签,9-10就是对定义的标签的使用。

XML Schema(XSD

XML Schema是DTD的替代者,它使用XML语法,功能更强大。如下示例定义:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="note">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="to" type="xs:string"/>
        <xs:element name="from" type="xs:string"/>
        <xs:element name="heading" type="xs:string"/>
        <xs:element name="body" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

其实这有点像DTD+XML属性的结合使用,在定义的时候,同时定义了其对应的属性。

XXE注入实践

理论结束,现在开始实践教学(请问神乐哪里最可爱吖!不好意思走错片场了)

先来搭建环境吧:

新建一个java项目,导入相关的依赖:pom.xml--这里可以看到XML的实际应用,配置文件。

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>
</dependencies>

使用tomcat启动web应用。

1766046167_6943b9d7cca37f7a1b0c8.png!small?1766046168240

XML解析器

既然有xml格式的内容,那么就应该有对应的解析器:常见的XML解析方式有两种:DOM和SAX。

DOM:将整个XML文档加载到内存,形成一棵树,可以方便地遍历和修改,但耗内存。

SAX:事件驱动,逐行读取,内存消耗小,但无法随机访问。

模拟服务端解析XML

新建一个servlet---DomXMLServlet.java代码如下:

package org.example;

import org.w3c.dom.Document;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * XML解析Servlet - 提供基于DOM的XML解析服务
 * 支持GET请求展示测试页面,POST请求解析XML内容
 */
@WebServlet("/parse-xml")
public class DomXMLServlet extends HttpServlet {
    /**
     * 处理POST请求 - 接收并解析XML内容
     * @param request HTTP请求对象,包含待解析的XML数据
     * @param response HTTP响应对象,返回解析结果
     * @throws IOException IO异常
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        // 设置响应内容类型为文本格式,支持中文编码
        response.setContentType("text/plain;charset=UTF-8");
        PrintWriter out = response.getWriter();

        try {

            // 创建DocumentBuilderFactory实例用于配置XML解析器
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // 创建DocumentBuilder实例用于解析XML文档
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 从HTTP请求读取XML内容
            Document doc = builder.parse(request.getInputStream());

            // 输出解析内容
            String textContent = doc.getDocumentElement().getTextContent();
            out.println("解析结果: " + textContent);

        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            out.println("解析失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 处理GET请求 - 返回XML解析测试页面
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     * @throws IOException IO异常
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<html><head><title>XML解析测试</title></head><body>");
        out.println("<h2>XML解析服务</h2>");
        out.println("<p>使用以下表单发送XML:</p>");
        out.println("<textarea id='xmlInput' rows='10' cols='50'>&lt;root&gt;测试XML内容&lt;/root&gt;</textarea><br>");
        out.println("<button onclick='sendXML()'>解析XML</button>");
        out.println("<div id='result'></div>");
        out.println("<script>");
        out.println("function sendXML() {");
        out.println("  var xmlContent = document.getElementById('xmlInput').value;");
        out.println("  fetch('parse-xml', {");
        out.println("    method: 'POST',");
        out.println("    headers: { 'Content-Type': 'text/xml' },");
        out.println("    body: xmlContent");
        out.println("  }).then(response => response.text())");
        out.println("    .then(text => document.getElementById('result').innerText = text)");
        out.println("    .catch(err => document.getElementById('result').innerText = '错误:' + err)");
        out.println("}");
        out.println("</script>");
        out.println("</body></html>");
    }
}

代码说明:

  • 该Servlet提供XML解析服务,支持GET和POST两种请求方式
  • GET请求返回测试页面,包含XML输入框和解析按钮
  • POST请求接收XML内容,使用DOM解析器提取文本内容并返回结果
  • 前端使用原生JavaScript实现异步请求和结果展示

使用tomcat启动当前应用,访问这个servlet:http://localhost:8000/xxe//parse-xml


文章来源: https://www.freebuf.com/articles/web/462819.html
如有侵权请联系:admin#unsafe.sh