ctfer

XXE

2026-09-28 #dev

T3-XML格式和常见利用

好的!既然题目里出现了 <阵枢> 这样的中文标签,我们正好借此机会把 XML 的“骨架”彻底拆解一遍。

XML 的本质是一种纯文本的、用来结构化存储和传输数据的格式(你可以把它想象成更严格的 JSON)。它必须遵守一套严格的语法规则。

下面我把 XML 的核心构成要素,结合你题目里的标签给你讲透:

1. XML 的五大核心组成部分

组成部分 作用 你的题目里的体现
① 声明(可选) 告诉解析器版本和编码 <?xml version="1.0"?>
② 根元素(必须) 有且仅有一个最外层的包裹标签 <root> 或你自定义的最外层标签
③ 元素(必须) 成对出现的开始和结束标签 <阵枢>内容</阵枢>
④ 属性(可选) 写在开始标签内,key="value" 形式 <阵枢 id="1">
⑤ 实体(特殊) 用来表示特殊字符或引用外部资源 &xxe;(你 Payload 里的变量)

2. 关键语法规则(必须死记,CTF 常考)

  • 必须有唯一的根元素:所有标签必须被一个最外层的父标签包裹。不能写两个并列的顶级标签。
  • 标签必须严格闭合:有 <阵枢> 就必须有 </阵枢>。如果没有内容,可以简写为 <阵枢/>。
  • 标签名区分大小写:<Array> 和 <array> 是两个不同的标签。
  • 属性值必须加引号:可以是单引号或双引号,但必须存在,例如 <解析 状态="成功">。
  • 嵌套不能交叉:<A><B></A></B> 是绝对错误的,必须 <A><B></B></A>(后进先出)。

3. 你的题目为什么可以用“中文标签”?

因为 XML 底层使用 Unicode(UTF-8) 编码,所以标签名、属性名、文本内容都支持中文。所以在后端代码里写 <阵枢> 是完全合法且有效的,你提交的 Payload 会被 PHP 的 DOMDocument 正常解析。


4. 重点来了:DTD(文档类型定义)—— 这是你解题的关键

你在漏洞利用时,肯定看到了 <!DOCTYPE ...> 这串东西。它不属于普通的 XML 数据,而是 XML 的“前置说明书”,用来定义文档的结构规则和实体(变量)。

DTD 内部实体(普通变量):

1
2
3
<!DOCTYPE 根标签 [
<!ENTITY 变量名 "变量值">
]>

引用方式:&变量名;
例如:

1
2
3
4
<!DOCTYPE root [
<!ENTITY author "小张">
]>
<root><阵枢>&author;</阵枢></root>

解析后,<阵枢> 里的内容会变成 “小张”。

DTD 外部实体(你读取文件用的):
这就是你 Payload 里的核心:

1
2
3
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>

SYSTEM 关键字告诉解析器:“这个变量的值,不在文本里写死,请去我指定的外部路径(系统文件)去读取内容。”
当你写 <阵枢>&xxe;</阵枢> 时,&xxe; 被替换成了 /etc/passwd 的全部文本内容。


5. 把完整结构串起来(标准 XML 文档全貌)

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>   <!-- ① 声明:告诉版本和编码 -->
<!DOCTYPE 阵枢 [ <!-- ② DTD:定义文档规则/实体 -->
<!ENTITY 变量 "这是一个值">
<!ENTITY 文件 SYSTEM "file:///flag">
]>
<阵枢 属性="示例"> <!-- ③ 根元素(带属性),开始 -->
这里是普通文本,&变量; <!-- 内容中引用内部实体 -->
<解析>状态正常</解析> <!-- 子元素 -->
<输出>&文件;</输出> <!-- 子元素引用外部实体 -->
</阵枢> <!-- ④ 根元素闭合 -->

特别提醒(针对本题环境)

你的题目报错 Start tag expected, '<' not found,说明后端接收到的不是以 < 开头的文本(比如你之前只输了 env)。
由于这个 XML 解析器没有禁用外部实体加载(这是漏洞根源),所以只要你的输入格式完全符合上述 5 条核心规则,它就会乖乖去读文件并把内容吐回给你。

现在再回头看你成功读取 /etc/passwd 的 Payload,是不是每个字符的位置和含义都清晰多了?如果还有哪个标签符号觉得模糊,随时问我。😊

评论
也许会发生一些好事!
分享

评论