美文网首页我爱编程
Nashorn执行js的安全策略

Nashorn执行js的安全策略

作者: 烤土豆啦 | 来源:发表于2018-06-07 17:24 被阅读0次

    java中使用javax.script 执行js的安全防御

    0x01 背景

    在某次渗透测试中,发现系统后台有一处服务器接收浏览器的js代码,使用javax.script这个组件去执行js代码。在这里可以使用以下poc去执行系统命令

    function getResult(sqlArr) {
        var r = "";
        java.io.BufferedReader
        b = java.lang.Runtime.getRuntime().exec('whoami').getInputStream();
        r = String.fromCharCode(b.read());
        while ((i = b.read()) != -1)
            r = r + String.fromCharCode(i);
        return r;
    }
    
    

    问了一下RD,这里本来是系统底层命令,在此处使用js去更灵活地完成数据筛选任务。在这里,javax.script使用Nashorn引擎去执行js代码,如果不加以限制,则会通过调用runtime类等方法去执行系统命令。开发那边也不可能改,所以只要想一下限制的方法了。谷歌搜索了一下,发现starkoverflow和openjdk都给出了解决方案,这里说一下。

    0x02 解决

    1.openjdk的解决方案

    在这里openjdk解释,javax执行js代码,默认是没有安全限制的。可以访问任意的java对象。但是我们可以使用java.security.policy编写policy文件,去限制相应java相应的行为,例如不能执行系统命令,不能访问系统某些文件。实例代码如下:

    import java.io.*;
    import java.nio.file.*;
    import javax.script.*;
    import jdk.nashorn.api.scripting.*;
     
    public class Main {
      public static void main(String[] args) throws Exception {
        ScriptEngineManager m = new ScriptEngineManager();
        ScriptEngine e = m.getEngineByName("nashorn");
        if (args.length == 0) {
          System.err.println("Usage: java Main <script_file>");
          return;
        }
     
        // args[0] is script file to which permissions are granted
        // in security policy
        File file = new File(args[0]);
     
        // read the file content and pass a String to 'eval'
        // The script is untrusted as nashorn does not know the origin!
        try {
            e.eval(new String(Files.readAllBytes(file.toPath())));
        } catch (SecurityException se) {
            System.out.println(se);
        }
     
        // create a Reader over the file and pass to 'eval'
        // The script is untrusted as nashorn does not know the origin!
        try {
            e.eval(new FileReader(file));
        } catch (SecurityException se) {
            System.out.println(se);
        }
     
        // pass a URLReader on file - script will get permissions
        // configured in security policy!
        e.eval(new URLReader(file.toURL()));
      }
    }
    
    

    在代码中我们首先创建了一个nashorn的js引擎,读取文件,并通过eval执行文件中的代码。可以看出,如果我们不限制的话,则造成了任意代码执行漏洞,这里假设js文件的内容如下

    test.js
    var File = java.io.File;
    // list contents of the current directory!
    for each (var f in new File(".").list())
       print(f)
    

    意思是通过java的File对象,获取当前文件夹的所有文件夹和文件并打印出来。下面我们可以通过编写policy文件去限制这个行为

    test.policy
    / give AllPermission for Main class (or any class in that directory!)
    grant codeBase "file:///d:/test" {
        permission java.security.AllPermission;
    };
     
    // give AllPermission to test.js script
    grant codeBase "file:///d:/test/test.js" {
        permission java.security.AllPermission;
    };
    

    使用如下命令加载并运行
    java -Djava.security.manager -Djava.security.policy=./test.policy Main test.js

    这时如果再执行的话,因为安全策略的限制,则会报错

    java.security.AccessControlException: access denied ("java.io.FilePermission" "." "read")
    java.security.AccessControlException: access denied ("java.io.FilePermission" "." "read")
    Main.class
    Main.java
    test.js
    test.policy
    

    在这里安全策略的权限是指允许代码执行的操作。包含三部分:权限类型、权限名和允许的操作。权限类型是实现了权限的Java类名,是必需的。权限名一般就是对哪类资源进行操作的资源定位(比如一个文件名或者通配符、网络主机等),一般基于权限类型来设置,有的比如java.security.AllPermission不需要权限名。允许的操作也和权限类型对应,指定了对目标可以执行的操作行为,比如读、写等。如下面的例子:

    类型 权限名 操作 例子
    文件权限 java.io.FilePermission 文件名(平台依赖) 读、写、删除、执行 允许所有问价的读写删除执行:permission java.io.FilePermission "<< ALL FILES>>", "read,write,delete,execute";。允许对用户主目录的读:permission java.io.FilePermission "${user.home}/-", "read";。
    套接字权限 java.net.SocketPermission 主机名:端口 接收、监听、连接、解析 允许实现所有套接字操作:permission java.net.SocketPermission "<em>:1-", "accept,listen,connect,resolve";。允许建立到特定网站的连接:permission java.net.SocketPermission "</em>.abc.com:1-", "connect,resolve";。
    属性权限 java.util.PropertyPermission 需要访问的jvm属性名 读、写 读标准Java属性:permission java.util.PropertyPermission "java.<em>", "read";。在sdo包中创建属性:permission java.util.PropertyPermission "sdo.</em>", "read,write";。
    运行时权限 java.lang.RuntimePermission 多种权限名[见附录A] 允许代码初始化打印任务:permission java.lang.RuntimePermission "queuePrintJob"
    AWT权限 java.awt.AWTPermission 6种权限名[见附录B] 允许代码充分使用robot类:permission java.awt.AWTPermission "createRobot"; permission java.awt.AWTPermission "readDisplayPixels";。
    网络权限 java.net.NetPermission 3种权限名[见附录C] 允许安装流处理器:permission java.net.NetPermission "specifyStreamHandler";。
    安全权限 java.security.SecurityPermission 多种权限名[见附录D]
    序列化权限 java.io.SerializablePermission 2种权限名[见附录E]
    反射权限 java.lang.reflect.ReflectPermission uppressAccessChecks(允许利用反射检查任意类的私有变量)
    完全权限 java.security.AllPermission 无(拥有执行任何操作的权限)

    </tbody></table>

    策略文件的编写

    策略文件是控制沙箱的管理要素,一个策略文件包含一个或多个保护域的项。策略文件完成了代码权限的指定任务,策略文件包括全局和用户专属两种。

    为了管理沙箱,策略文件我认为是最重要的内容。JVM可以使用多个策略文件,不过一般两个最常用。一个是全局的:$JREHOME/lib/security/java.policy,作用于JVM的所有实例。另一个是用户自己的,可以存储到用户的主目录下。策略文件可以使用jdk自带的policytool工具编辑。

    2. stackoverflow给出的解决方案

    在jdk 1.8u40中,可以使用ClassFilter去限制js引擎可以访问的类。代码如下:

    import javax.script.ScriptEngine;
    import jdk.nashorn.api.scripting.ClassFilter;
    import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
    
    public class MyClassFilterTest {
    
      class MyCF implements ClassFilter {
        @Override
        public boolean exposeToScripts(String s) {
          if (s.compareTo("java.io.File") == 0) return false;
          return true;
        }
      }
    
      public void testClassFilter() {
    
        final String script =
          "print(java.lang.System.getProperty(\"java.home\"));" +
          "print(\"Create file variable\");" +
          "var File = Java.type(\"java.io.File\");";
    
        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
    
        ScriptEngine engine = factory.getScriptEngine(
          new MyClassFilterTest.MyCF());
        try {
          engine.eval(script);
        } catch (Exception e) {
          System.out.println("Exception caught: " + e.toString());
        }
      }
    
      public static void main(String[] args) {
        MyClassFilterTest myApp = new MyClassFilterTest();
        myApp.testClassFilter();
      }
    }
    

    执行的话,则会报错,如下所示

    C:\Java\jre8
    Create file variable
    Exception caught: java.lang.RuntimeException: java.lang.ClassNotFoundException:
    java.io.File
    

    3.nashornsandbox沙箱方案

    A secure sandbox for executing JavaScript in Java apps using the Nashorn engine.

    通过下面的代码,即可控制nashorn引擎可以访问/拒绝某些类了,简单易用

    NashornSandbox sandbox = NashornSandboxes.create();
         
    sandbox.allow(File.class);
         
    sandbox.eval("var File = Java.type('java.io.File'); File;")
    

    并且这个沙箱的作用很大,不光可以限制java类的访问,还可以限制nashorn引擎的资源使用情况,如下

    NashornSandbox sandbox = NashornSandboxes.create();
         
    sandbox.setMaxCPUTime(100);
    sandbox.setMaxMemory(50*1024);
    sandbox.allowNoBraces(false);
    sandbox.setMaxPreparedStatements(30); // because preparing scripts for execution is expensive
    sandbox.setExecutor(Executors.newSingleThreadExecutor());
         
    sandbox.eval("var o={}, i=0; while (true) {o[i++]='abc';};");
    

    相关文章

      网友评论

        本文标题:Nashorn执行js的安全策略

        本文链接:https://www.haomeiwen.com/subject/diggsftx.html