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

update: 2015-11-16

新版apache commons collections 3.2.2修复漏洞

新版本的apache commons collections默认禁止了不安全的一些转换类。可以通过升级来修复漏洞。参考release说明:https://commons.apache.org/proper/commons-collections/release_3_2_2.html

Dubbo rpc远程代码执行的例子

update: 2015-11-13

重新思考了下这个漏洞,给出一个dubbo rpc远程代码执行的例子。

https://github.com/hengyunabc/dubbo-apache-commons-collections-bug

可以说很多公司开放的rpc,只要协议里支持java序列化方式,classpath里有apache commons collections的jar包,都存在被远程代码执行的风险。

至于能不能通过http接口再调用dubbo rpc远程代码,貌似不太可行。因为信息难以传递。


Apache Commons Collections远程代码执行漏洞

最近出来一个比较严重的漏洞,在使用了Apache Commons Collections的Java应用,可以远程代码执行。包括最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS这些大名鼎鼎的Java应用。

这个漏洞的严重的地方在于,即使你的代码里没有使用到Apache Commons Collections里的类,只要Java应用的Classpath里有Apache Commons Collections的jar包,都可以远程代码执行。

参考:

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

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>

再执行下面的java代码:

        Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class },
                        new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class },
                        new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc" }) };

        Transformer transformedChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);

        Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
        onlyElement.setValue("foobar");

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

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

如何简单的防止Java程序调用外部命令?

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

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

换而言之,就是如何禁止Java执行Runtime.exec(String cmd)之类的函数。

在Java里有一套Security Policy,但是实际上用的人比较少。因为配置起来太麻烦了。
参考:

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

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

grant {
  permission java.io.FilePermission "<<ALL FILES>>", "read, write, delete, execute";
};

但是Java权限机制是白名单的,还有一大堆的权限要配置上去,非常复杂。
从tomcat的配置就知道了。http://tomcat.apache.org/tomcat-7.0-doc/security-manager-howto.html
所以Tomcat默认是没有启用Security Policy的,可以通过在命令加上-security参数来启用。

catalina.sh start -security

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

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

        SecurityManager originalSecurityManager = System.getSecurityManager();
        if (originalSecurityManager == null) {
            // 创建自己的SecurityManager
            SecurityManager sm = new SecurityManager() {
                private void check(Permission perm) {
                    // 禁止exec
                    if (perm instanceof java.io.FilePermission) {
                        String actions = perm.getActions();
                        if (actions != null && actions.contains("execute")) {
                            throw new SecurityException("execute denied!");
                        }
                    }
                    // 禁止设置新的SecurityManager,保护自己
                    if (perm instanceof java.lang.RuntimePermission) {
                        String name = perm.getName();
                        if (name != null && name.contains("setSecurityManager")) {
                            throw new SecurityException("System.setSecurityManager denied!");
                        }
                    }
                }

                @Override
                public void checkPermission(Permission perm) {
                    check(perm);
                }

                @Override
                public void checkPermission(Permission perm, Object context) {
                    check(perm);
                }
            };

            System.setSecurityManager(sm);
        }

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

Java序列化的本身的蛋疼之处

其实Java自身的序列化机制就比较蛋疼,可以参考Effective Java里的。
http://allenlsy.com/NOTES-of-Effective-Java-10/

并非禁止外部程序执行,Java程序就安全了

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

总结

禁止JVM执行外部命令,是一个简单有效的提高JVM安全性的办法。但是以前没有见到有相关的内容,有点奇怪。
可以考虑在代码安全扫描时,加强对Runtime.exec相关代码的检测。
有些开源库喜欢用Runtime.exec来执行命令获取网卡mac等操作,个人表示相当的蛋疼,不会使用这样子的代码。

时间: 2024-09-17 04:32:31

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

java 执行外部命令,想要得到按时间顺序正确的完整的打印信息,并且能阻塞,有什么办法呢?

问题描述 java 执行外部命令,想要得到按时间顺序正确的完整的打印信息,并且能阻塞,有什么办法呢? Process p = Runtime.getRuntime().exec(commands); 打印信息是分开普通信息缓冲区和错误信息缓冲区的,2者分开后,就体现不了顺序关系了,比如第一行错误信息,是在哪个普通信息之后打印的,第二行错误信息是在第几行普通信息之后打印的,如果使用 > xx.log的方式,可以得到顺序正确的打印文件,但是就不能用java.lang.Process阻塞了,会直接往下

PHP 执行系统外部命令 system() exec() passthru()_php技巧

区别: system() 输出并返回最后一行shell结果. exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面. passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上. 相同点:都可以获得命令执行的状态码 demo: 复制代码 代码如下: //system('dir'); // exec ('dir'); // passthru ('dir'); // echo `dir`; PHP作为一种服务器端的脚本语言,象编写简单,或者

PHP用反撇号执行外部命令_php技巧

例如: echo `whoami`; // 导出数据库,要导入的文件夹必须要有可写权限, -u -p之后的内容必须要紧挨着写 复制代码 代码如下: echo `mysqldump -h localhost -u$DbUser -p$DbPwd --default-character-set=utf8 $DbName > /var/$dumpFileName`; 反撇号其他用法 <?php #使用反撇号,暗示作为命令来执行 $result=`date`; echo "<p>

Windows Powershell 执行外部命令_PowerShell

Powershell 能够像CMD一样很好的执行外部命令. 通过netstat查看网络端口状态 PS C:\PS> netstat Active Connections Proto Local Address Foreign Address State TCP 192.168.0.100:3049 192.168.0.88:7575 ESTABLISHED TCP 192.168.0.100:3052 192.168.0.88:7575 ESTABLISHED TCP 192.168.0.100

执行外部命令行-PHP执行如何外部命令行

问题描述 PHP执行如何外部命令行 e: cd E:Program Files (x86)Weeny Free Word to PDF Converter word2pdf.exe /source "d:wampwwwup11437027098.doc" /target "c:My PDF" 这三条命令怎么用exec()执行,能麻烦写一下代码么? 解决方案 http://www.jb51.net/article/19618.htm <?php exec(&qu

Runtime调用shell文件执行外部命令,传10个位置参数进去,在执行shell时,判断出参数总数不是10,执行退出,求解答

问题描述 cm=/usr/local/bea/watool/tact/share/rootdir/system/expendableparts/uploadComm.sh /usr/local/bea/watool/tact/share/rootdir/system/expendableparts/91010_888888_20110822134252_mytest.csv watool tactclient tact /usr/local/bea/watool/tact/share/rootd

PHP用反撇号执行外部命令

 例如: echo `whoami`; // 导出数据库,要导入的文件夹必须要有可写权限, -u -p之后的内容必须要紧挨着写  代码如下: echo `mysqldump -h localhost -u$DbUser -p$DbPwd --default-character-set=utf8 $DbName > /var/$dumpFileName`;   反撇号其他用法 1 2 3 4 5 6 7 8 9 10 <?php #使用反撇号,暗示作为命令来执行 $result=`date`;

PHP启动windows应用程序、执行bat批处理、执行cmd命令的方法(exec、system函数详解)_php实例

exec 或者 system 都可以调用cmd 的命令 直接上代码: 复制代码 代码如下: <?php /** 打开windows的计算器 */ exec('start C:WindowsSystem32calc.exe'); /** php生成windows的批处理文件后,再执行这个批处理文件*/ $filename = 't.bat'; $somecontent = 'C: '; $somecontent .= 'cd "C:/Program Files/MySQL-Front&quo

使用PHP执行系统外部命令的四种方法

在PHP文件函数关于如何删除文件实例教程中,我提到有两种方法,一种使用unlink函数删除文件,还有一种是通过system函数调用系统命令来执行删除文件命令,其实除了system函数,PHP执行命令的方法还有很多种,下面介绍在PHP网站开发中最常用的四种执行命令的方法. 准备工作 出于安全性考虑,在通常情况下虚拟主机.XAMPP.DedeAMPZ等PHP运行环境都是禁止调用系统外部命令的.所以当你需要使用PHP执行命令的函数调用系统外部命令时,你需要确保PHP运行环境支持PHP执行命令的函数.