2017年09月21日
漏洞分析
来源:先知安全技术社区
作者:阿里云
一. 漏洞概述
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-信息泄露漏洞
官方评级:
高危,实际测试漏洞危害较低
漏洞描述:
- CVE-2017-12616:信息泄露漏洞
当 Tomcat 中启用了 VirtualDirContext 时,攻击者将能通过发送精心构造的恶意请求,绕过设置的相关安全限制,或是获取到由 VirtualDirContext 提供支持资源服务的 JSP 源代码,从而造成代码信息泄露。
- CVE-2017-12615:远程代码执行漏洞
当 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
三. 漏洞详细分析信息
3.1. 环境搭建
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>
3.2. 原理分析
本次 Apache Tomcat 两个 CVE 漏洞涉及到 DefaultServlet 和 JspServlet,DefaultServlet 的作用是处理静态文件 ,JspServlet 的作用是处理 jsp 与 jspx 文件的请求,同时 DefaultServlet 可以处理 PUT 或 DELETE 请求,以下是默认配置情况:
![](https://images.seebug.org/content/images/2017/09/26972994-af8d-4cb3-afdb-102b31656719.png-w331s)
除了 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 处理的逻辑,如图:
![](https://images.seebug.org/content/images/2017/09/86bf5b30-e8e6-4485-97f2-d47963a571b7.png-w331s)
![](https://images.seebug.org/content/images/2017/09/5027bcec-6b1b-4cc8-8d67-806c51a2dc71.png-w331s)
![](https://images.seebug.org/content/images/2017/09/c2bb5fc6-45cb-4774-9a0c-25fbf17f61c6.png-w331s)
3.3. 调试代码
调试 DefaultServlet.java 代码流程,如下:
![](https://images.seebug.org/content/images/2017/09/0f4a6dad-7363-4bbe-ae76-5ba4cc573d8d.png-w331s)
设置 readOnly 为 false
先调用栈
![](https://images.seebug.org/content/images/2017/09/955cf8bf-b559-4627-bae7-2e9fb41aded8.png-w331s)
重点看 doPut,这里 tomcat 开始处理 PUT 请求,可以看到这里如果 readonly 是 true 就直接进入 error 了,所以需要设置成 false。
![](https://images.seebug.org/content/images/2017/09/f6ea75c6-60ad-4f42-a1f5-ee7c90c4df33.png-w331s)
![](https://images.seebug.org/content/images/2017/09/83f2866f-161c-4e62-bd20-8c8369c5fad9.png-w331s)
真正写入文件在 FileDirContext.java 的 rebind 函数里。
![](https://images.seebug.org/content/images/2017/09/1745ca74-428a-4b09-9233-85faaed1de87.png-w331s)
![](https://images.seebug.org/content/images/2017/09/01c719e9-fa1f-4ce6-b3da-68f0a9d3ac2b.png-w331s)
![](https://images.seebug.org/content/images/2017/09/e68684dc-53a6-4d13-b0ba-9e5690e95a4c.png-w331s)
![](https://images.seebug.org/content/images/2017/09/44367a84-4a04-4e23-9863-b444b679c1fc.png-w331s)
FileOutputStream 特性
上面遗留了一个问题就是当请求 jsp%20
或是 jsp::$DATA
后缀的时候,为什么最终却写入 .jsp
后缀的文件,这些其实是 java.io. FileOutputStream 的问题了,具体需要分析 jdk 的 C 代码才能得到解答,如图
![](https://images.seebug.org/content/images/2017/09/baa97b4d-70fb-4c19-9cb5-d8b5709a6505.png-w331s)
![](https://images.seebug.org/content/images/2017/09/29c3a323-0938-4a86-8264-51635ee491d5.png-w331s)
![](https://images.seebug.org/content/images/2017/09/dc354bdf-9f2b-46c9-b962-a9f610475e99.png-w331s)
跟到 open 是 native 的,已经不是 java 层面的问题了,这个 open 实际上是一个 jni 接口,然后调用 windowsAPI CreateFileW 创建文件,这里下载 openjdk6 的 jdk 代码分析,如图:
![](https://images.seebug.org/content/images/2017/09/e3c6580c-7754-4228-b3fd-0af9bc184e32.png-w331s)
这里 Java_java_io_FileOutputStream_open
便是上边 java 代码里 open 函数的C代码实现,其中参数 path 对应 open 函数的 name 变量,继续跟踪,如图:
![](https://images.seebug.org/content/images/2017/09/dc52a5b4-e969-4ef5-b44a-016206cd2e77.png-w331s)
继续跟入 winFileHandleOpen,这里最终是调用 windows 的 CreateFileW 实现文件创建,如图:
![](https://images.seebug.org/content/images/2017/09/45fbcc41-db75-4f26-b04f-00853ae7c685.png-w331s)
而在 windows 下,创建文件是对后缀名称有要求的,例如:如果后缀末尾是空格,会被去掉,a.txt::$DATA
传入 CreateFileW 也会被处理成 a.txt
java.io.File 的特性
前面没有说evil.jsp/
,这种方法也可以 PUT 上传,但是不同于上面两种,这种方法是利用了 File 类的特性,先看代码,如图:
![](https://images.seebug.org/content/images/2017/09/92a8f30b-6e18-4b45-be6c-3aba3f2ca733.png-w331s)
![](https://images.seebug.org/content/images/2017/09/8e30c16d-793f-434e-979a-05352a2a60f0.png-w331s)
这里测试发现 java.io.File 会过滤掉子文件名末尾的斜杠,写一个测试用例确实是这样的,如图:
![](https://images.seebug.org/content/images/2017/09/5168cf39-b6c2-4f5b-89ea-4d3a2df2c196.png-w331s)
具体跟踪一下代码,如图:
![](https://images.seebug.org/content/images/2017/09/ff96b625-8c2c-47cb-b628-c42d5bcf3b88.png-w331s)
继续跟入,如图:
![](https://images.seebug.org/content/images/2017/09/b2183a3f-4713-4fdc-a49a-a2b8bfa5f592.png-w331s)
在这里这个 normalize(path, n, (prev == slash) ? i - 1 : i)
会将文件名末尾的/过滤掉,所以可以导致后面文件写入 jsp 文件。
3.4. 漏洞复现
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操作就会顺利执行,成功上传文件,以下通过构造请求,触发并复现该漏洞:
![](https://images.seebug.org/content/images/2017/09/dc1973dc-889e-4123-8e64-f95a29e291f1.png-w331s)
在分析的时候,我们也发现 File 类存在一个利用点,normalize 函数过滤掉了末尾斜杠。我们通过构造请求测试,发现可以被利用,通过对全版本测试,发现 Windows、Linux、Unix 下的 Apache Tomcat 5.X、6.X、7.x、8.x、9.x 版本均受到影响,从这点可以看出官方给出的补丁存在绕过,目前该问题已经得到 Apache 官方的确认。
![](https://images.seebug.org/content/images/2017/09/aadd158a-6495-426e-ab2c-55dc0b469c1d.png-w331s)
![](https://images.seebug.org/content/images/2017/09/fe05886e-684c-47d4-85d6-98eb561c5abe.png-w331s)
四. 漏洞分析总结
从以上分析可以得出,该漏洞利用的前提条件需要手动开启 readOnly 功能,以支持上传操作,在 Apache tomcat 7.X 版本默认配置的情况下是无法成功利用漏洞,从实际测试来看,漏洞危害性并没有那么高。
但是如果用户一旦启用了 readOnly 功能,黑客可利用漏洞成功入侵。
五. 如何检测漏洞?
从以上分析可以得出,该漏洞利用的前提条件需要手动开启 readOnly 功能,开发或运维人员可以排查是否启用了 PUT 方法并开启了 readOnly 功能。
六. 如何应对漏洞?
根据业务评估配置 conf/webxml 文件的 readOnly 值为 Ture 或注释参数,禁用 PUT 方法并重启 tomcat 服务,临时规避安全风险; 注意: 如果禁用 PUT 方法,对于依赖PUT方法的应用,可能导致业务失效。
目前官方已经发布了 7.0.81 版本修复了两个漏洞,建议用户尽快升级到最新版本;
对于最新版本绕过的问题,建议用户持续关注官方信息,及时更新到最新版本;
可以选用阿里云云盾WAF产品进行防御。
未使用WAF前:
![](https://images.seebug.org/content/images/2017/09/ac332dd3-f17c-46ce-917f-89b4a2f66c95.png-w331s)
Figure 1成功上传文件
- 使用WAF后的防御效果:
![](https://images.seebug.org/content/images/2017/09/19a4be4d-298d-4b02-b5d0-7b9fbbbc3d5e.png-w331s)
Figure 2上传文件时报405,上传文件失败
七. Web安全最佳实践
为什么PUT方法不安全?
除标准常用的 GET 和 POST 方法外,HTTP 请求还使用其他各种方法,PUT 方法是 HTTP 请求方法中的一种。此方法用于请求服务器把请求中的实体储存在请求资源下,如果请求资源已经在服务器中存在,那么将会用此请求中的数据替换原先的数据,作为指定资源的最新修改版。如果请求指定的资源不存在,将会创建这个资源,且数据位请求正文。
在以往的实际安全事件案例中,我们可以看到,由于 PUT 方法自身不带验证机制,有很多利用PUT方法很快捷简单的成功入侵服务器,上传 Webshell 或其他恶意文件,从而获取敏感数据或服务器权限,从 web 安全年最佳实践来看,我们不推荐使用这些不安全的 http OPTIONS 方法。
从安全最佳实践来说,我们应该遵循“最小化”原则,不要随意开启不必要的服务或方法,仅开启必要的功能,减小风险暴露面,从而降低安全风险,保障业务的安全性。
最佳实践:如何知道自己网站使用了哪些http方法?
![](https://images.seebug.org/content/images/2017/09/0d6bf6c4-30e4-491c-9f88-90423a162337.png-w331s)
查看响应的 Allow: GET, HEAD, POST, OPTIONS,TRACE
注:该测试仅限于自身业务。
禁用不必要的 http 方法
- IIS:
IIS 默认拒绝 PUT 和 DELETE 请求,如果使用了不安全的方法,建议禁用 webDAV 模块。
![](https://images.seebug.org/content/images/2017/09/aa9f0b6d-80ef-4831-9d5e-7b2738948467.png-w331s)
- Apache:
<Location />
仅允许GET和POST方法,修改后重启服务。
<LimitExcept GET POST >
Order Allow,Deny
Deny from all
</LimitExcept>
</Location>
- Tomcat:
修改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
![Paper](api/attachments/jkwTaGByvsd4/image/image.jpg)
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/399/