天天看点

禁止JVM执行外部命令Runtime.exec -- 由Apache Commons Collections漏洞引发的思考

最近出来一个比较严重的漏洞,在使用了apache commons collections的java应用,可以远程代码执行。包括最新版的weblogic、websphere、jboss、jenkins、opennms这些大名鼎鼎的java应用。

这个漏洞的严重的地方在于,即使你的代码里没有使用到apache commons collections里的类,只要java应用的classpath里有apache commons collections的jar包,并且开放了允许java序列化协议的端口,都可以远程代码执行。

参考:

https://github.com/frohoff/ysoserial

http://blog.chaitin.com/2015-11-11_java_unserialize_rce/

这个漏洞的演示很简单,只要在maven依赖里增加

再执行下面的java代码:

这个漏洞的根本问题并不是java序列化的问题,而是apache commons collections允许链式的任意的类函数反射调用。攻击者通过允许java序列化协议的端口,把攻击代码上传到服务器上,再由apache commons collections里的transformedmap来执行。

这里不对这个漏洞多做展开,可以看上面的参考文章。

从这个漏洞,引发了很久之前的一个念头:**如何简单的防止java程序调用外部命令?**

java相对来说安全性问题比较少。出现的一些问题大部分是利用反射,最终用runtime.exec(string cmd)函数来执行外部命令的。**如果可以禁止jvm执行外部命令,未知漏洞的危害性会大大降低,可以大大提高jvm的安全性。**

换而言之,就是如何禁止java执行runtime.exec(string cmd)之类的函数。

在java里有一套security policy,但是实际上用的人比较少。因为配置起来太麻烦了。

http://docs.oracle.com/javase/8/docs/technotes/guides/security/policyfiles.html

http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html

http://docs.gigaspaces.com/xap102sec/java-security-policy-file.html 详细的权限列表可以参考这个文档

从文档里可以知道,java里并没有直接禁止rumtine.exec 函数执行的权限。

禁止文件执行的权限在java.io.filepermission里。如果想禁止所有外部文件执行,可以在下面的配置文件中把<code>execute</code>删除:

但是**java权限机制是白名单的**,还有一大堆的权限要配置上去,非常复杂。

从tomcat的配置就知道了。http://tomcat.apache.org/tomcat-7.0-doc/security-manager-howto.html

所以tomcat默认是没有启用security policy的,可以通过在命令加上-security参数来启用。

那么有没有简单的办法可以在代码里禁止java执行外部命令?

研究了下,通过扩展securitymanager可以简单实现:

只要在java代码里简单加上面那一段,就可以禁止执行外部程序了。

其实java自身的序列化机制就比较蛋疼,可以参考effective java里的。

http://allenlsy.com/notes-of-effective-java-10/

要注意的是,如果可以任意执行java代码,还可以做很多事情,比如写入ssh密钥,从而可以远程登陆,参考最近的redis未授权访问漏洞:https://www.sebug.net/vuldb/ssvid-89715

禁止jvm执行外部命令,是一个简单有效的提高jvm安全性的办法。但是以前没有见到有相关的内容,有点奇怪。

可以考虑在代码安全扫描时,加强对runtime.exec相关代码的检测。在jvm层面对所有runtime.exec打日志。

有些开源库喜欢用runtime.exec来执行命令获取网卡mac等操作,个人表示相当的蛋疼,不会使用这样子的代码。