Nashorn执行js的安全策略

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';};");
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 【译者按】作为软件研发项目的项目经理,只懂项目管理知识是不够的,需要对软件技术本身有基本的了解。Java 是一种主...
    黄军雷阅读 3,052评论 1 15
  • 当时可能是学了讲雷锋的这篇文章,我也想做一个大家眼中做好事不留名的人。当时不知道为什么知道一个小女孩家里很困难,没...
    藤木同学阅读 139评论 0 0
  • ## --- 划过天空的青鸟 孩子, 谢谢你让我懂得了幸福。 得到你的拥抱, 从此不再孤独。 你的一个个新奇的问题...
    划过天空的青鸟阅读 647评论 2 9
  • 今晚的天空好清透月亮也好亮呀,如果你也在这个城市,不信你抬起头看看. --------------------...
    45度向阳阅读 184评论 0 0