Deserialization Twice
Deserial Twice
最近遇到了很多java题目,大都弄了个类继承ObjectInputStream,重写其resolveClass方法,在里面添加对反序列化类黑名单的校验。比如下面这个
public class MyObjectInputStream extends ObjectInputStream {
private static final String[] blacklist = new String[]{
"java\\.security.*", "java\\.rmi.*", "com\\.fasterxml.*", "com\\.ctf\\.*",
"org\\.springframework.*", "org\\.yaml.*", "javax\\.management\\.remote.*"
};
public MyObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
protected Class resolveClass(ObjectStreamClass cls) throws IOException, ClassNotFoundException {
if(!contains(cls.getName())) {
return super.resolveClass(cls);
} else {
throw new InvalidClassException("Unexpected serialized class", cls.getName());
}
}
public static boolean contains(String targetValue) {
for (String forbiddenPackage : blacklist) {
if (targetValue.matches(forbiddenPackage))
return true;
}
return false;
}
}或是这样子
当然平时没事的时候可以研究一下这些黑名单中的类在反序列化中的关键用途
但是在比赛做题的时候就很恼火了,若没有积累充足的Java反序列化利用链经验,很难绕过;比赛时临时去找触发类也挺难的。
Java题就变成一道类的排列组合题了🤯,拼出一条可以打通的在黑名单之外的利用链。
这时候就可以考虑一下二次反序列化了,不用你定义的检测黑名单的ObjectInputStream去加载序列化对象,而是找到一条可以触发readObject的链子,用原生的ObjectInputStream去resolveClass
SignedObject
java.security.SignedObject#getObject
这个类在Hessian反序列化中用过,由于Hessian反序列化的特殊性,不会执行类的readObject来反序列化,而是通过反射获取field再填充进一个空的实例化对象,_tfactory又是transient修饰,writeObject不会写进去,导致TemplatesImpl不能利用。
🚩触发方式:能够执行类的getter方法,比如配合ROME或FastJson打
SerializationUtils
org.springframework.util.SerializationUtils.deserialize
RMIConnector
javax.management.remote.rmi.RMIConnector#findRMIServerJRMP
若能控制base64参数的内容就可以任意反序列化。
往上回溯
path以/stub/开头就能进到findRMIServerJRMP,path中/stub/为序列化字节的base64编码
path由directoryURL#getURLPath得到
在往上发现connect和doStart调用了findRMIServer
利用CC链的InvokerTransformer来触发connect(doStart被protected修饰,不能用InvokerTransformer触发)
下面以CC6为例
可以看到findRMIServerJRMP支持jndi、stub、iiop
跟进path以/jndi/开头的分支:findRMIServerJNDI
熟悉的InitialContext#lookup,改一下path就可以jndi注入了
WrapperConnectionPoolDataSource
com.mchange.v2.c3p0.WrapperConnectionPoolDataSource#setuserOverridesAsString可以跟进到
C3P0ImplUtils#parseUserOverridesAsString
注意这里字符截取是从HASM_HEADER.length() + 1到userOverridesAsString.length() - 1,最后一位会吃掉
SerializableUtils#fromByteArray
配合fastjson或ROME
Reference
https://www.anquanke.com/post/id/256986#h3-9
Last updated
Was this helpful?