为了防止把事情搞复杂,先从一个常见的文件读取xxe payload示例开始
第一行是 XML 声明。它定义 XML 的版本(1.0)
接下来的部分是DTD
!DOCTYPE
用来声明文档类型,!ELEMENT
定义一个元素(XML 标签名)这里类型为ANY
!ENTITY
声明一个实体(XML 标签中的内容)
第六行则是xml树结构了 上面的DTD 用来定义一个 XML 文档的合法元素
下图是一个菜鸟教程给出的xml示例 https://www.runoob.com/try/xml/note.xml
CDATA字符数据(character data),是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
这里只介绍XXE中会用到的类型,上面提到了内部和外部实体是从定义的方式区别的,从作用域来分别的话又可分为通用实体和参数实体
用 &实体名; 引用的实体,他在DTD 中定义,在 XML 文档中引用
定义和使用均在 DTD 中,但只有在 DTD 文件中,参数实体的定义才能引用其他实体
有五个常用内置实体,通常应用在嵌套定义的时候,DTD中支持单双引号,所以可以通过单双引号间隔使用作为区分嵌套实体和实体之间的关系;在实际使用中,我们通常需要再嵌套一个参数实体,%号是需要处理成 %
• &符号:&
• 单引号:'
• 大于号:>
• 小于号:<
• 双引号:"
XXE(XML External Entity Injection) XML外部实体注入
XML注入是通过闭合标签插入恶意的XML元素进行注册管理员用户等逻辑漏洞攻击
而XXE是将XML元素注入变成外部实体注入,上面的基础知识部分已经提到了在DTD中声明外部实体,在xml中实体的作用相当于是一个已经定义的变量,可以在标签内使用,通过 & 符号进行引用
这里的效果是在win7虚拟机的ie中打开的,现在的浏览器安全策略已经不允许这种简单粗暴的方式了
这里通过注入引入外部实体,以类似变量引用方式将本地文件读取
这是一个有回显的XXE,这里读的文件是比较理想的,没有会被xml解析的字符,如果像下图中文件的内容一样就会引起xml解析报错(如Linux的/etc/fstab文件其中包含一些看起来像 XML 的字符),XML 解析器会尝试解析这些元素,但其不是一个有效的 XML 文档
将读取文件内容的实体通过拼接放入标签当中,使得文件内容不会被解析器解析。
XML 规范不允许将外部实体与内部实体结合使用
需要在DTD中分别使用%start和%end参数实体将文件内容包装在CDATA标记中完成拼接后,存储在另一个名为all的通用实体中,最后再在 xml 中调用。
evil.dtd
Payload
">
%dtd; ]>
&all; s
在python中能否行得通取决于解析用的python库选择
Minidom、etree默认情况下不易受到 XXE 的攻击。pulldom与sax则较容易被攻击
使用Minidom解析的情况如下图,代码由xxe-lab中提取主要逻辑获得
换成pulldom解析,可以读取文件(python2.7中可行,但在python3.10中无法成功)
在pulldom中使用php那的外带payload,可以收到请求但无法把内容带出来,而且遇到内容复杂点的文件会报错
使用lxml模块、xml.sax模块的问题参考:https://rules.sonarsource.com/python/RSPEC-2755
通过加载外部实体的响应时间的长短判断该ip、端口是否存活、开放(响应快的就是开放的),php的话也可以通过伪协议看是否返回内容
Java支持jar协议能从远程获取 jar 文件,然后将其中的内容进行解压
jar:{url}!{path}
path是jar包中要解压的文件路径,jar包其实就是个zip压缩包
jar 协议处理文件的过程:
- 下载 jar/zip 文件到临时文件中
- 提取出我们指定的文件
- 删除临时文件
通过延长服务器传递文件的时间使临时文件能存活到完成利用,也就相当于完成了文件上传
server.py传输最后一个字符时延迟30秒,为了文件完整性,需要在文件后面添加垃圾字符
import sys
import time
import threading
import socketserver
from urllib.parse import quote
import http.client as httpc
listen_host = 'localhost'
listen_port = 9999
jar_file = sys.argv[1]
class JarRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
http_req = b''
print('New connection:',self.client_address)
while b'\r\n\r\n' not in http_req:
try:
http_req += self.request.recv(4096)
print('Client req:\r\n',http_req.decode())
jf = open(jar_file, 'rb')
contents = jf.read()
headers = ('''HTTP/1.0 200 OK\r\n'''
'''Content-Type: application/java-archive\r\n\r\n''')
self.request.sendall(headers.encode('ascii'))
self.request.sendall(contents[:-1])
time.sleep(30)
print(30)
self.request.sendall(contents[-1:])
except Exception as e:
print ("get error at:"+str(e))
if __name__ == '__main__':
jarserver = socketserver.TCPServer((listen_host,listen_port), JarRequestHandler)
print ('waiting for connection...')
server_thread = threading.Thread(target=jarserver.serve_forever)
server_thread.daemon = True
server_thread.start()
server_thread.join()
再使用file或者netdoc 协议列目录获得临时文件名,一般就是利用文件包含去触发反序列化
安装expect 扩展可直接利用 XXE 进行 RCE
]>
&cmd;
DOS Billion Laughs 攻击
]>
&lol9;
参数实体允许嵌套定义,内层的定义的参数实体% 需要进行HTML转义
'>
]>
但无法在内部 DTD 子集中引用参数实体,如:
外部 DTD 允许我们将一个实体包含在另一个实体中,但在内部 DTD 语法中是禁止的。这就需要引入外部dtd文件。
也有文章提到三层嵌套后,部分xml解析器无法检查到该语法
https://www.freebuf.com/vuls/207639.html
但是对于三层嵌套参数实体构造的payload有些XML解析器是无法检测出来的,比如我本次测试的两种组合php7.2 + libxml2 2.9.4版本和php5.4 + libxml2 2.9.1都是可以有效利用的
"> %para2; '> %para; ]>
10
上面文件读取那提到的外带方式就是引入服务器DTD文件
Evil.dtd
">
Payload
%remote;%int;%send; ]>
还有一种情况是防火墙规则严格,无法引用远程dtd。这时就得考虑寻找本地dtd文件:
%local_dtd;
Your DTD code
%local_dtd;
利用方式为重写dtd当中定义的参数实体。
">
%eval;
%send;
'>
%remote;
]>
evil.dtd
">
%start;
payload
%remote;
%send;
]>
通过错误的url使得xml解析报错,从而回显数据
https://www.cnblogs.com/backlion/p/9302528.html
https://docs.python.org/zh-cn/3/library/xml.dom.pulldom.html
https://www.acunetix.com/blog/articles/xml-external-entity-xxe-vulnerabilities/
https://www.acunetix.com/blog/web-security-zone/how-to-mitigate-xxe-vulnerabilities-in-python/
https://www.runoob.com/dtd/dtd-tutorial.html
https://www.runoob.com/xml/xml-tutorial.html
https://mohemiv.com/all/exploiting-xxe-with-local-dtd-files/
https://www.w3.org/TR/xml/#wfc-PEinInternalSubset
https://security.stackexchange.com/questions/196889/why-do-we-need-external-dtd-in-blind-xxe