Nashorn執(zhí)行js的安全策略

java中使用javax.script 執(zhí)行js的安全防御

0x01 背景

在某次滲透測(cè)試中,發(fā)現(xiàn)系統(tǒng)后臺(tái)有一處服務(wù)器接收瀏覽器的js代碼,使用javax.script這個(gè)組件去執(zhí)行js代碼。在這里可以使用以下poc去執(zhí)行系統(tǒng)命令

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;
}

問(wèn)了一下RD,這里本來(lái)是系統(tǒng)底層命令,在此處使用js去更靈活地完成數(shù)據(jù)篩選任務(wù)。在這里,javax.script使用Nashorn引擎去執(zhí)行js代碼,如果不加以限制,則會(huì)通過(guò)調(diào)用runtime類(lèi)等方法去執(zhí)行系統(tǒng)命令。開(kāi)發(fā)那邊也不可能改,所以只要想一下限制的方法了。谷歌搜索了一下,發(fā)現(xiàn)starkoverflow和openjdk都給出了解決方案,這里說(shuō)一下。

0x02 解決

1.openjdk的解決方案

在這里openjdk解釋?zhuān)琷avax執(zhí)行js代碼,默認(rèn)是沒(méi)有安全限制的??梢栽L(fǎng)問(wèn)任意的java對(duì)象。但是我們可以使用java.security.policy編寫(xiě)policy文件,去限制相應(yīng)java相應(yīng)的行為,例如不能執(zhí)行系統(tǒng)命令,不能訪(fǎng)問(wèn)系統(tǒng)某些文件。實(shí)例代碼如下:

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()));
  }
}

在代碼中我們首先創(chuàng)建了一個(gè)nashorn的js引擎,讀取文件,并通過(guò)eval執(zhí)行文件中的代碼。可以看出,如果我們不限制的話(huà),則造成了任意代碼執(zhí)行漏洞,這里假設(shè)js文件的內(nèi)容如下

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

意思是通過(guò)java的File對(duì)象,獲取當(dāng)前文件夾的所有文件夾和文件并打印出來(lái)。下面我們可以通過(guò)編寫(xiě)policy文件去限制這個(gè)行為

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;
};

使用如下命令加載并運(yùn)行
java -Djava.security.manager -Djava.security.policy=./test.policy Main test.js

這時(shí)如果再執(zhí)行的話(huà),因?yàn)榘踩呗缘南拗?,則會(huì)報(bào)錯(cuò)

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

在這里安全策略的權(quán)限是指允許代碼執(zhí)行的操作。包含三部分:權(quán)限類(lèi)型、權(quán)限名和允許的操作。權(quán)限類(lèi)型是實(shí)現(xiàn)了權(quán)限的Java類(lèi)名,是必需的。權(quán)限名一般就是對(duì)哪類(lèi)資源進(jìn)行操作的資源定位(比如一個(gè)文件名或者通配符、網(wǎng)絡(luò)主機(jī)等),一般基于權(quán)限類(lèi)型來(lái)設(shè)置,有的比如java.security.AllPermission不需要權(quán)限名。允許的操作也和權(quán)限類(lèi)型對(duì)應(yīng),指定了對(duì)目標(biāo)可以執(zhí)行的操作行為,比如讀、寫(xiě)等。如下面的例子:

類(lèi)型 權(quán)限名 操作 例子
文件權(quán)限 java.io.FilePermission 文件名(平臺(tái)依賴(lài)) 讀、寫(xiě)、刪除、執(zhí)行 允許所有問(wèn)價(jià)的讀寫(xiě)刪除執(zhí)行:permission java.io.FilePermission "<< ALL FILES>>", "read,write,delete,execute";。允許對(duì)用戶(hù)主目錄的讀:permission java.io.FilePermission "${user.home}/-", "read";。
套接字權(quán)限 java.net.SocketPermission 主機(jī)名:端口 接收、監(jiān)聽(tīng)、連接、解析 允許實(shí)現(xiàn)所有套接字操作:permission java.net.SocketPermission "<em>:1-", "accept,listen,connect,resolve";。允許建立到特定網(wǎng)站的連接:permission java.net.SocketPermission "</em>.abc.com:1-", "connect,resolve";。
屬性權(quán)限 java.util.PropertyPermission 需要訪(fǎng)問(wèn)的jvm屬性名 讀、寫(xiě) 讀標(biāo)準(zhǔn)Java屬性:permission java.util.PropertyPermission "java.<em>", "read";。在sdo包中創(chuàng)建屬性:permission java.util.PropertyPermission "sdo.</em>", "read,write";。
運(yùn)行時(shí)權(quán)限 java.lang.RuntimePermission 多種權(quán)限名[見(jiàn)附錄A] 無(wú) 允許代碼初始化打印任務(wù):permission java.lang.RuntimePermission "queuePrintJob"
AWT權(quán)限 java.awt.AWTPermission 6種權(quán)限名[見(jiàn)附錄B] 無(wú) 允許代碼充分使用robot類(lèi):permission java.awt.AWTPermission "createRobot"; permission java.awt.AWTPermission "readDisplayPixels";。
網(wǎng)絡(luò)權(quán)限 java.net.NetPermission 3種權(quán)限名[見(jiàn)附錄C] 無(wú) 允許安裝流處理器:permission java.net.NetPermission "specifyStreamHandler";。
安全權(quán)限 java.security.SecurityPermission 多種權(quán)限名[見(jiàn)附錄D] 無(wú)
序列化權(quán)限 java.io.SerializablePermission 2種權(quán)限名[見(jiàn)附錄E] 無(wú)
反射權(quán)限 java.lang.reflect.ReflectPermission uppressAccessChecks(允許利用反射檢查任意類(lèi)的私有變量) 無(wú)
完全權(quán)限 java.security.AllPermission 無(wú)(擁有執(zhí)行任何操作的權(quán)限) 無(wú)

</tbody></table>

策略文件的編寫(xiě)

策略文件是控制沙箱的管理要素,一個(gè)策略文件包含一個(gè)或多個(gè)保護(hù)域的項(xiàng)。策略文件完成了代碼權(quán)限的指定任務(wù),策略文件包括全局和用戶(hù)專(zhuān)屬兩種。

為了管理沙箱,策略文件我認(rèn)為是最重要的內(nèi)容。JVM可以使用多個(gè)策略文件,不過(guò)一般兩個(gè)最常用。一個(gè)是全局的:$JREHOME/lib/security/java.policy,作用于JVM的所有實(shí)例。另一個(gè)是用戶(hù)自己的,可以存儲(chǔ)到用戶(hù)的主目錄下。策略文件可以使用jdk自帶的policytool工具編輯。

2. stackoverflow給出的解決方案

在jdk 1.8u40中,可以使用ClassFilter去限制js引擎可以訪(fǎng)問(wèn)的類(lèi)。代碼如下:

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();
  }
}

執(zhí)行的話(huà),則會(huì)報(bào)錯(cuò),如下所示

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.

通過(guò)下面的代碼,即可控制nashorn引擎可以訪(fǎng)問(wèn)/拒絕某些類(lèi)了,簡(jiǎn)單易用

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

并且這個(gè)沙箱的作用很大,不光可以限制java類(lèi)的訪(fǎng)問(wèn),還可以限制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';};");
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 【譯者按】作為軟件研發(fā)項(xiàng)目的項(xiàng)目經(jīng)理,只懂項(xiàng)目管理知識(shí)是不夠的,需要對(duì)軟件技術(shù)本身有基本的了解。Java 是一種主...
    黃軍雷閱讀 3,160評(píng)論 1 15
  • 當(dāng)時(shí)可能是學(xué)了講雷鋒的這篇文章,我也想做一個(gè)大家眼中做好事不留名的人。當(dāng)時(shí)不知道為什么知道一個(gè)小女孩家里很困難,沒(méi)...
    藤木同學(xué)閱讀 170評(píng)論 0 0
  • ## --- 劃過(guò)天空的青鳥(niǎo) 孩子, 謝謝你讓我懂得了幸福。 得到你的擁抱, 從此不再孤獨(dú)。 你的一個(gè)個(gè)新奇的問(wèn)題...
    劃過(guò)天空的青鳥(niǎo)閱讀 733評(píng)論 2 9
  • 今晚的天空好清透月亮也好亮呀,如果你也在這個(gè)城市,不信你抬起頭看看. --------------------...
    45度向陽(yáng)閱讀 211評(píng)論 0 0

友情鏈接更多精彩內(nèi)容