2017年9月19日,Apache Tomcat官方确认并修复了两个高危漏洞,漏洞CVE编号:CVE-2017-12615和CVE-2017-12616,该漏洞受影响版本为7.0-7.80之间,官方评级为高危,在一定条件下,攻击者可以利用这两个漏洞,获取用户服务器上 JSP 文件的源代码,或是通过精心构造的攻击请求,向用户服务器上传恶意JSP文件,通过上传的 JSP 文件 ,可在用户服务器上执行任意代码,从而导致数据泄露或获取服务器权限,存在高安全风险。
漏洞编号:
CVE-2017-12616
CVE-2017-12615
漏洞名称:
CVE-2017-12615-远程代码执行漏洞
CVE-2017-12616-信息泄露漏洞
官方评级:
漏洞描述:
当Tomcat中启用了 VirtualDirContext时,攻击者将能通过发送精心构造的恶意请求,绕过设置的相关安全限制,或是获取到由VirtualDirContext提供支持资源服务的JSP源代码,从而造成代码信息泄露。
当 Tomcat运行在Windows操作系统时,且启用了HTTP PUT请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的 JSP 文件,JSP文件中的恶意代码将能被服务器执行。导致服务器上的数据泄露或获取服务器权限。
在一定的条件下,通过以上两个漏洞可在用户服务器上执行任意代码,从而导致数据泄露或获取服务器权限,存在高安全风险。
CVE-2017-12615漏洞利用需要在Windows环境,且需要将 readonly 初始化参数由默认值设置为 false,经过实际测试,Tomcat 7.x版本内web.xml配置文件内默认配置无readonly参数,需要手工添加,默认配置条件下不受此漏洞影响。
CVE-2017-12616漏洞需要在server.xml文件配置VirtualDirContext参数,经过实际测试,Tomcat 7.x版本内默认配置无VirtualDirContext参数,需要手工添加,默认配置条件下不受此漏洞影响。
影响范围:
CVE-2017-12615影响范围: Apache Tomcat 7.0.0 - 7.0.79 (windows环境)
CVE-2017-12616影响范围:Apache Tomcat 7.0.0 - 7.0.80
Apache Tomcat默认开启PUT方法,org.apache.catalina.servlets.DefaultServlet的readonly默认为true,而且默认没有在conf/web.xml里写,需要手工添加并且改为false,才可以测试。
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
本次Apache Tomcat两个CVE漏洞涉及到 DefaultServlet和 JspServlet,DefaultServlet的作用是处理静态文件 ,JspServlet 的作用是处理jsp 与jspx 文件的请求,同时DefaultServlet 可以处理 PUT 或 DELETE请求,以下是默认配置情况:
除了jsp和jspx默认是由org.apache.jasper.servlet.JspServlet处理,其他默认都是由org.apache.catalina.servlets.DefaultServlet来处理。
可以看出即使设置readonly为false,默认tomcat也不允许PUT上传jsp和jspx文件的,因为后端都用org.apache.jasper.servlet.JspServlet来处理jsp或是jspx后缀的请求了,而JspServlet中没有PUT上传的逻辑,PUT的代码实现只存在于DefaultServlet中。
这个漏洞的根本是通过构造特殊后缀名,绕过了tomcat检测,让它用DefaultServlet的逻辑去处理请求,从而上传jsp文件。
目前主要三种方法:
evil.jsp%20
evil.jsp::$DATA
evil.jsp/
利用这两种姿势PUT请求tomcat的时候,骗过tomcat而进入DefaultServlet处理的逻辑,如图:
调试DefaultServlet.java代码流程,如下:
设置readOnly为false
先调用栈
重点看doPut,这里tomcat开始处理PUT请求,可以看到这里如果readonly是true就直接进入error了,所以需要设置成false。
真正写入文件在FileDirContext.java的rebind函数里。
FileOutputStream特性
上面遗留了一个问题就是当请求jsp%20或是jsp::$DATA后缀的时候,为什么最终却写入.jsp后缀的文件,这些其实是java.io. FileOutputStream的问题了,具体需要分析jdk的C代码才能得到解答,如图
跟到open是native的,已经不是java层面的问题了,这个open实际上是一个jni接口,然后调用windowsAPI CreateFileW创建文件,这里下载openjdk6的jdk代码分析,如图:
这里Java_java_io_FileOutputStream_open便是上边java代码里open函数的C代码实现,其中参数path对应open函数的name变量,继续跟踪,如图:
继续跟入winFileHandleOpen,这里最终是调用windows的CreateFileW实现文件创建,如图:
而在windows下,创建文件是对后缀名称有要求的,例如:如果后缀末尾是空格,会被去掉,a.txt::$DATA传入CreateFileW也会被处理成a.txt
java.io.File的特性
前面没有说evil.jsp/,这种方法也可以PUT上传,但是不同于上面两种,这种方法是利用了File类的特性,先看代码,如图:
这里测试发现java.io.File会过滤掉子文件名末尾的斜杠,写一个测试用例确实是这样的,如图:
具体跟踪一下代码,如图:
继续跟入,如图:
在这里这个normalize(path, n, (prev == slash) ? i - 1 : i)会将文件名末尾的/过滤掉,所以可以导致后面文件写入jsp文件。
Apache Tomcat 7默认值是 true,在默认条件下,无法成功利用这两个漏洞。为了触发漏洞,需要在conf/web.xml 中 defaultservlet 的配置中手工添加如下配置:
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
我们通过构造的请求上传b.jsp,执行该请求请求就会由DefaultServlet 处理,从而PUT操作就会顺利执行,成功上传文件,以下通过构造请求,触发并复现该漏洞:
在分析的时候,我们也发现File类存在一个利用点,normalize函数过滤掉了末尾斜杠。我们通过构造请求测试,发现可以被利用,通过对全版本测试,发现Windows、Linux、Unix下的Apache Tomcat 5.X、6.X、7.x、8.x、9.x版本均受到影响,从这点可以看出官方给出的补丁存在绕过,目前该问题已经得到Apache官方的确认。
从以上分析可以得出,该漏洞利用的前提条件需要手动开启readOnly功能,以支持上传操作,在Apache tomcat 7.X版本默认配置的情况下是无法成功利用漏洞,从实际测试来看,漏洞危害性并没有那么高。
但是如果用户一旦启用了readOnly功能,黑客可利用漏洞成功入侵。
从以上分析可以得出,该漏洞利用的前提条件需要手动开启readOnly功能,开发或运维人员可以排查是否启用了PUT方法并开启了readOnly功能。
注意: 如果禁用PUT方法,对于依赖PUT方法的应用,可能导致业务失效。
目前官方已经发布了7.0.81版本修复了两个漏洞,建议用户尽快升级到最新版本;
对于最新版本绕过的问题,建议用户持续关注官方信息,及时更新到最新版本;
可以选用阿里云云盾WAF产品进行防御。
未使用WAF前:
Figure 1成功上传文件
Figure 2上传文件时报405,上传文件失败
为什么PUT方法不安全?
除标准常用的GET和POST方法外,HTTP请求还使用其他各种方法,PUT方法是HTTP请求方法中的一种。此方法用于请求服务器把请求中的实体储存在请求资源下,如果请求资源已经在服务器中存在,那么将会用此请求中的数据替换原先的数据,作为指定资源的最新修改版。如果请求指定的资源不存在,将会创建这个资源,且数据位请求正文。
在以往的实际安全事件案例中,我们可以看到,由于PUT方法自身不带验证机制,有很多利用PUT方法很快捷简单的成功入侵服务器,上传Webshell或其他恶意文件,从而获取敏感数据或服务器权限,从web安全年最佳实践来看,我们不推荐使用这些不安全的http OPTIONS方法。
从安全最佳实践来说,我们应该遵循“最小化”原则,不要随意开启不必要的服务或方法,仅开启必要的功能,减小风险暴露面,从而降低安全风险,保障业务的安全性。
最佳实践:如何知道自己网站使用了哪些http方法?
查看响应的 Allow: GET, HEAD, POST, OPTIONS,TRACE
注:该测试仅限于自身业务。
禁用不必要的http方法
IIS默认拒绝PUT和DELETE请求,如果使用了不安全的方法,建议禁用webDAV模块。
<Location />
仅允许GET和POST方法,修改后重启服务。
<LimitExcept GET POST >
Order Allow,Deny
Deny from all
</LimitExcept>
</Location>
修改web.xml配置,增加以下内容,并重启tomcat服务:
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
最后感谢阿里巴巴集团安全部柏通的技术支持。
[1]. https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
[2]. http://stackoverflow.com/questions/630453/put-vs-post-in-rest
[3]. https://tomcat.apache.org/security-7.html
[4]. http://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.81