Java反序列化绕WAF tricks及一个GUI工具
字数 1996 2025-08-20 18:17:02
Java反序列化绕WAF技巧及工具使用指南
一、Java与PHP反序列化差异
-
序列化流格式差异:
- Java序列化流格式比PHP复杂,包含大量二进制数据和复杂结构
- Java序列化性能优于PHP,特别是在传输大数据类型对象时
-
漏洞点差异:
- PHP反序列化漏洞多发生在处理反序列化的方法上(如
__wakeup绕过、fast destruct) - Java反序列化漏洞多由复杂数据结构导致,常通过修改数据流绕过防御机制
- PHP反序列化漏洞多发生在处理反序列化的方法上(如
二、Java反序列化绕WAF技巧
Trick 1: 插入脏数据绕WAF
原理:利用WAF为性能考虑可能限制检测数据长度的特点,通过增加载荷长度绕过检测
1.1 利用可序列化类包裹脏数据和恶意类
实现方式:
class A {
public String var1 = "aaaaaaaaaaaaaa..."; // 垃圾数据
public Object var2 = evil_object; // 恶意对象
}
常用集合类:
- ArrayList
- LinkedList
- HashMap
- LinkedHashMap
- TreeMap
示例代码:
List<Object> arrayList = new ArrayList<Object>();
arrayList.add(dirtyData);
arrayList.add(gadget);
new ObjectOutputStream(new FileOutputStream("bypass.ser")).writeObject(arrayList);
优点:可插入任意字符
缺点:需要多反序列化两个对象,不够"优雅"
1.2 利用序列化流结构-填充TC_RESET
序列化流结构:
stream: magic version contents
contents: content contents content
content: object blockdata
object: newObject newClass newArray newString newEnum newClassDesc prevObject nullReference exception TC_RESET
TC_RESET:
- 定义为一个byte
- 作用:标识byte,用于反序列化时重置handle表
- handle表存储序列化流中"newHandle"结构
Java处理逻辑:
private Object readObject0(boolean unshared) throws IOException {
// ...
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
// ...
}
工具使用(SerializeJava):
- 输入序列化流base64编码或文件路径
- 进入"Modify STREAM Data"模块
- 勾选第一个功能,输入要插入的TC_RESET数量
- 点击"change"生成新的序列化流base64编码
优点:简单有效
缺点:只能插入TC_RESET,可能被针对性防御
1.3 利用序列化结构-array包裹
实现原理:
- 读取classDesc中的长度,根据长度读取相应数据作为数组
- 添加TC_ARRAY头,恶意对象放在数组最后,前面填充脏数据
优点:插入数据任意,特征性不强
工具支持:SerializeJava也支持这种填充方式
1.4 其他序列化结构处理
参考文章:Java序列化中的脏数据策略:绕过WAF的战术分析-CSDN博客
Trick 2: 反序列化UTF解码导致的OverLong Encoding绕过
前置知识
-
UTF编码:
- UTF是Unicode的编码形式
- Java内部使用UTF-16存储字符
- 序列化/反序列化使用修改版UTF-8
-
修改版UTF-8特点:
- 空字符(U+0000):标准UTF-8为1字节,修改版为2字节(0xC0 0x80)
- 补充字符(U+10000及以上):标准UTF-8用4字节,修改版用两个3字节编码(代理对)
Java UTF解码实现
private long readUTFSpan(StringBuilder sbuf, long utflen) throws IOException {
try {
while (pos < stop) {
int b1, b2, b3;
b1 = buf[pos++] & 0xFF;
switch (b1 >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 1 byte format: 0xxxxxxx
cbuf[cpos++] = (char) b1;
break;
case 12: case 13:
// 2 byte format: 110xxxxx 10xxxxxx
b2 = buf[pos++];
if ((b2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) | ((b2 & 0x3F) << 0));
break;
case 14:
// 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
b3 = buf[pos + 1];
b2 = buf[pos + 0];
pos += 2;
if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
}
cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) |
((b2 & 0x3F) << 6) |
((b3 & 0x3F) << 0));
break;
default:
throw new UTFDataFormatException();
}
}
}
// ...
}
实现方式
- 生成字符组合表:
// 两个byte组合
for (int ch = 0x20; ch <= 0x7E; ch++) {
for (int b1 = 0xC0; b1 <= 0xCF; b1++) {
for (int b2 = 0x80; b2 <= 0xFF; b2++) {
char generatedChar = (char) (((b1 & 0x1F) << 6) | ((b2 & 0x3F) << 0));
if (generatedChar == ch) {
System.out.printf("\"%c\": {%#x, %#x},%n", ch, b1, b2);
}
}
}
}
// 三个byte组合
for (int ch = 0x20; ch <= 0x7E; ch++) {
for (int b1 = 0xE0; b1 <= 0xEF; b1++) {
for (int b2 = 0x80; b2 <= 0xFF; b2++) {
for (int b3 = 0x80; b3 <= 0xFF; b3++) {
char generatedChar = (char) (((b1 & 0x0F) << 12) |
((b2 & 0x3F) << 6) |
((b3 & 0x3F) << 0));
if (generatedChar == ch) {
System.out.printf("\"%c\": {%#x, %#x, %#x},%n", ch, b1, b2, b3);
}
}
}
}
}
- 工具使用(SerializeJava):
- 在"UTF OverLong Encoding"模块勾选
- 选择用2字符或3字符OverLong Encoding模式
- 点击"change"生成
效果:将可读字符转变为不可读的字符流,有效绕过过滤可读字符的WAF
RFC 3629标准问题:
- 标准中不允许某些byte出现
- Java的2 byte编码以C0或C1开头,不符合标准
- 但即使最新JDK(23.0.1)仍支持2 byte和3 byte的过长编码
Trick 3: 修改serialVersionUID
原理:
- Java序列化要求类的serialVersionUID一致
- 服务端必须使用同一版本对象的序列化流才能成功反序列化
工具使用(SerializeJava):
- 输入序列化流
- 点击"Change Class SerialVerionUID"的check按钮
- 工具自动解析数据流结构,展示类名及其SerialVerionUID
- 修改值后点击"change"生成新数据流
示例:
将BeanComparator的serialVersionUID从-3490850999041592962改为-2044202215314119608,使其兼容commons-beanutils1.9.2
三、SerializeJava工具介绍
功能概述
- 集成展示JAVA序列化流结构
- 一键插入脏数据
- UTF过长编码绕WAF(Utf OverLoad Encoding)
- 修改类SerializeVersionUID功能
项目信息
- 项目地址:https://github.com/byname66/SerializeJava
- 开发语言:Go
- 借鉴项目:P神的Zkar
- 特点:自写底层代码,增加功能并图形化
四、总结
- 本文总结了Java语言"通用性"的绕WAF技巧
- 针对特定组件、框架、CMS的Trick需要进一步探索
- 工具SerializeJava持续维护,欢迎提出问题和建议
注意:这些技术仅用于安全研究和授权测试,未经授权使用可能违反法律。