文章目录
在《 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。
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
)。
Runtime
类的实例化:
transformer.transform(Runtime.getRuntime());
运行程序后,会执行外部命令,打开电脑上的计算机程序:
至此,我们知道可以利用InvokerTransformer来调用外部命令了,那是不是只需要把一个我们自定义的InvokerTransformer序列化成字符串,然后再反序列化,接口实现远程命令执行:
先将transformer对象序列化到文件中,再从文件中读取出来,并且执行其transform方法,就实现了攻击。
但是,如果事情只有这么简单的话,那这个漏洞应该早就被发现了。想要真的实现攻击,那么还有几件事要做。
因为, newTransformer.transform(Runtime.getRuntime());
这样的代码,不会有人真的在代码中写的。
如果没有了这行代码,还能实现执行外部命令么?
这就要利用到Commons Collections中提供了另一个工具那就是ChainedTransformer,这个类是Transformer的实现类。
ChainedTransformer类提供了一个transform方法,他的功能遍历他的iTransformers数组,然后依次调用其transform方法,并且每次都返回一个对象,并且这个对象可以作为下一次调用的参数。