Tomcat 远程代码执行漏洞分析(CVE-2017-12615)


文章目录
  1. 1. 复现环境
  2. 2. 复现过程
  3. 3. 原理分析
  4. 4. Bypass 分析
  5. 5. 影响

复现环境

环境是Windows 7 64位 Apache Tomcat 7.0.70
img

复现过程

根据描述,在 Windows 服务器下,将 readonly 参数设置为 false 时,即可通过 PUT 方式创建一个 JSP 文件,并可以执行任意代码。
通过阅读 conf/web.xml 文件,可以发现:
img
修改 Tomcat 7.0/conf/web.xml 文件。添加 readonly 属性,设置为false。
img
重启tomcat
启动 Tomcat,利用 PUT 请求创建文件:
img
提示 404。通过所描述的 Windows 受影响,可以结合 Windows 的特性。一是 NTFS 文件流,二是文件名的相关限制(比如 Windows 中文件名不能以空格结尾)来绕过限制:
img
img
访问发现可以正常输出:
img

原理分析

本次的tomcat漏洞涉及到DefaultServlet和 JspServlet。
Tomcat的Servlet 是在 conf/web.xml 配置的,通过配置文件可知,当后缀名为 .jsp 和 .jspx 的时候,是通过JspServlet处理请求的,下面的默认的配置情况:
img
而其他的静态文件时通过DefaultServlet处理的,同时DefaultServlet 可以处理 PUT 或 DELETE请求:
img
也就是说,除了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文件。
目前主要三种方法:

1
2
3
test.jsp%20
test.jsp::$DATA
test.jsp/ (bypass)

可以得知,“test.jsp ”(末尾有一个和空格即“test.jsp%20”)和“test.jsp::$DATA
”并不能匹配到 JspServlet,而是会交由DefaultServlet去处理。当处理 PUT 请求时:
img
主要是doPut,这里tomcat开始处理PUT请求,可以看到这里如果readonly是true就直接进入error了,所以在前面的步骤中需要设置成false。
img
会调用resources.bind:
img
img
而继续调用dirContext.bind
img
真正写入文件在FileDirContext.java的rebind函数里
img
又由于 Windows 不允许“ ”(此处为一个空格)作为文件名结尾,所以会创建一个 .jsp 文件,导致代码执行。
FileOutputStream特性
到这里是否会想,当请求jsp%20或是jsp::$DATA后缀的时候,为什么最终却写入.jsp后缀的文件?
这些其实是java.io. FileOutputStream的问题了,需要进一步分析jdk的C代码才能得到解答,如图
img
跟进去FileOutputStream
img
跟进去open
img
跟到open是native的,不是java层面的问题了,这里open实际上是一个jni接口,然后调用windows API CreateFileW创建文件,这里下载openjdk6的jdk代码进行分析,如图:
img
FileOutputStream_md.c
这里Java_java_io_FileOutputStream_open便是上面java代码里open函数的C代码实现,其中参数path对应open函数的name变量,继续跟踪,如图:
img
io_util_md.c
继续跟进去winFileHandleOpen,这里最终调用windows的CreateFileW实现文件创建,如图:
img
io_util_md.c
而在windows下,创建文件是对后缀名称进行处理的,例如:如果后缀末尾是空格,会被去掉,a.txt::$DATA传入CreateFileW也会被处理成a.txt

Bypass 分析

然而,当 PUT 地址为/test.jsp/时,仍然会创建 JSP,会影响 Linux 和 Windows 服务器,并且 Bypass 了之前的补丁,分析如下。
在进入 bind 函数时,会声明一个 File 变量:
img
进入 File 后,会对 name 进行 normalize (在file.class)
img
继续跟入
img
在这里这个normalize(path, n, (prev == slash) ? i - 1 : i)会将文件名末尾的/过滤掉,所以可以导致后面文件写入jsp文件。

影响

由于存在去掉最后的 / 的特性,那么这个漏洞自然影响 Linux 以及 Windows 版本。而且经过测试,这个漏洞影响全部的 Tomcat 版本,从 5.x 到 9.x 都会受到影响。目前来说,最好的解决方式是将 conf/web.xml 中对于 DefaultServlet 的 readonly 设置为 true,(默认设置是false)。