
JavaScript マルウェアを学習するためのツール。
悪意のあるJavaScriptを解析するためのユーティリティ。
npmからbox-jsをインストールするだけです:
npm install box-js --global
box-js は以下の形式でも利用可能です:
- Cuckooモジュールとして(
integrationsディレクトリおよび Nwinternights/Cuckoo_Boxjs を参照);- Dockerfileとして(
integrations/README.mdを参照);- セキュリティ専門家向けのディストリビューションのパッケージとして(REMnux、BlackArch);
- オープンソースアプリケーションの一部として(Intel Owl);
- 商用のサードパーティサービスの一部として(any.run)。
sample.js というサンプルがあるとします。解析するには、単に以下を実行します:
box-js sample.js
ペイロードをダウンロードしたい場合も多いでしょう。ダウンロードを有効にするには --download フラグを使用します。それ以外の場合、エンジンは404エラーをシミュレートし、スクリプトは配布サイトがダウンしていると思い込んで、フォールバックサイトに問い合わせるようになります。
Box.js は Windows JScript 環境をエミュレートし、エミュレーションの概要をコンソールに出力し、sample.js.results というフォルダを作成します(すでに存在する場合は sample.js.1.results などを作成します)。このフォルダには以下のものが含まれます:
analysis.log - 画面上に出力された解析のログ;snippets.json - サンプルによって実行されたコード片(JavaScript、シェルコマンドなど)のリスト;urls.json - コンタクトされたURLのリスト;active_urls.json - アクティブなマルウェアをドロップすると思われるURLのリスト;resources.json - ファイルタイプとハッシュを含む ADODB ストリーム(スクリプトがディスクに書き込んだファイル);IOC.json - IOC(侵害の指標)として特定された動作のリスト。これには、レジストリアクセス、書き込まれたファイル、HTTPリクエストなどが含まれます。これらは自分で分析するか、Malwr、VirusTotal、またはCuckooサンドボックスに自動的に送信することができます。詳細については、box-export --help を実行してください。
さらなる分離のために、一時的なDockerコンテナ内で解析を実行することをお勧めします。詳細については
integrations/README.mdを参照してください。
解析を自動化したい場合は、戻りコード(
integrations/README.mdに記載)を使用して、異なる種類のエラーを区別できます。
git から取得した box-js リポジトリには boilerplate.js ファイルが含まれています。このファイルは、document などの一般的なブラウザオブジェクトのスタブバージョンを定義しています。--prepended-code=DIR/boilerplate.js オプション(DIR はクローンした box-js リポジトリのディレクトリ)または --prepended-code=default を指定して解析を再実行してみてください。--prepended-code オプションは、box-js に指定されたファイル内の JavaScript を解析対象のサンプルの先頭に追加するよう指示します。
boilerplate.js をコピーし、必要に応じて独自のスタブクラスやオブジェクトなどを追加できることに注意してください。--prepended-code=show-default コマンドラインオプションを使用すると、デフォルトの box-js の boilerplate.js ファイルの完全なパスが表示されます。
box.js は通常単一ファイルに使用されますが、バッチ解析も実行できます。解析するファイルやフォルダのリストを渡すだけです:
box-js sample1.js sample2.js /var/data/mySamples ...
デフォルトでは、box.js はサンプルを並列処理し、コアごとに1つの解析を実行します。--threads に値を指定することで異なる設定を使用できます。特に、0 は制限を解除し、box-js が可能な限り多くの解析スレッドを生成するようにし、非常に高速な解析が可能になりますが、システムに過負荷がかかる可能性があります(解析は通常 CPU バウンドであり、RAM バウンドではありません)。
--loglevel=warn を使用すると、解析関連のメッセージを抑制し、進行状況情報のみを表示できます。
解析が完了したら、アクティブなURLを次のように抽出できます:
cat ./*.results/active_urls.json | sort | uniq
NAME 説明
-h, --help ヘルプテキストを表示して終了
-v, --version パッケージバージョンを表示して終了
--license ライセンスを表示して終了
--debug エミュレーションエラーが発生した場合でも(「バッチモード」でも)終了し、終了コードを
渡す。
--loglevel ログレベル(debug、verbose、info、warning、error - デフォルト "info")
--threads バッチモードで実行する場合、同時に実行する解析の数(0 =
無制限、デフォルト: CPUコア数と同じ)
--download ペイロードを実際にダウンロードする
--encoding 入力サンプルのエンコーディング(デフォルトでは自動検出されます)
--timeout スクリプトはこの秒数後にタイムアウトします(デフォルト 10)
--output-dir 結果ファイルとフォルダを書き込むディスク上の場所(デフォルトは
カレントディレクトリ)
--preprocess 元のソースコードを前処理します(リバースエンジニアリングが容易になりますが、
数秒かかります)
--unsafe-preprocess より積極的な前処理。多くの場合、より良いコードになりますが、
一部のエッジケース(例: プロトタイプの再定義)で壊れる可能性があります
--prepended-code 解析対象のJSファイルの前に追加するコードを含む入力ファイルまたはディレクトリ。
ディレクトリが指定された場合、そのディレクトリ内のすべてのファイルの内容を
先頭に追加します。'default' が指定された場合、box-js に付属のデフォルトの
boilerplate.js を使用します。'show-default' が指定された場合、boilerplate.js の
パスを表示して終了します(デフォルトのボイラープレートコードをコピーして
修正する場合に便利です)。
--fake-script-engine WScript.FullName および WScript.Name で報告するスクリプトエンジン(例:
'cscript.exe'、'wscript.exe'、'node')。デフォルトは wscript.exe。
--fake-cl-args 偽のスクリプトコマンドライン引数。文字列内ではカンマ区切りで指定します。
--fake-sample-name 解析中のサンプルに使用する偽のファイル名。完全なパスまたは
ファイル名のみを指定できます。パスに '\' がある場合は、このコマンドライン引数値では
'\\' としてエスケープしてください(例: --fake-sample-name=C:\\foo\\bar.js)。
--fake-language Win32_OperatingSystem.OSLanguage に対して返す言語コードを指定します。
サポートされている値は 'spanish'、'english'、'portuguese' です。
--fake-domain WScript.Network.UserDomain に対して返すユーザードメインを指定します。
--fake-download HTTPリクエストが成功したふりをして、偽のペイロードを返すようにします
--no-kill ランタイムエラーが発生してもアプリケーションを強制終了しない
--no-echo スクリプトがデータを出力するとき、コンソールに表示しない
--no-rewrite `@cc_on` サポート以外のソースコードの書き換えを一切行わない
--no-catch-rewrite try..catch 句を書き換えて例外をグローバルスコープにしない
--no-cc_on-rewrite `/*@cc_on <...>@*/` を `<...>` に書き換えない
--no-eval-rewrite `eval` を書き換えてその引数が書き換えられるようにしない
--no-file-exists Scripting.FileSystemObject.FileExists(x) に対して `false` を返す
--limit-file-checks 多数のチェックが実行された場合、フォルダ/ファイルの存在チェックのデフォルト値を
切り替えます(無限のファイルチェックループを防ぐ試み)。
--no-folder-exists Scripting.FileSystemObject.FileExists(x) に対して `false` を返す
--function-rewrite eval呼び出しをキャッチするために関数呼び出しを書き換える
--no-rewrite-prototype `function A.prototype.B()` のような式を `A.prototype.B =
function()` のように書き換えない
--no-hoist-prototype `function A.prototype.B()` のような式をホイストしない(no-rewrite-prototype
によって暗示される)
--no-shell-error `WScriptShell.Run` を実行するときに偽のエラーをスローしない(デフォルトでは
配布サイトがダウンしているふりをするために偽のエラーをスローし、スクリプトが
すべてのサイトをポーリングしようとするようにする)
--no-typeof-rewrite `typeof` を書き換えない(例: `typeof ActiveXObject`、これは JScript 標準では
'unknown' を返す必要があり、'object' ではない)
--proxy [実験的] ダウンロードに指定されたプロキシを使用します。これは
--download フラグが存在しない場合は関係ありません。
--windows-xp Windows XP をエミュレートします(環境変数の値に影響します)
--dangerous-vm `vm2` ではなく `vm` モジュールを使用します。このサンドボックスは突破可能なので、**使用しないでください**
何をしているか100%確信がない限り。正しいスタックトレースを提供することでデバッグに役立ちます。
--rewrite-loops 一部のタイプのループを書き換えて解析を高速化します
--throttle-writes 大量のデータを書き込むファイル書き込みのレポートとデータ追跡を抑制します
--throttle-commands 同じコマンドが多数実行された場合に解析を停止します
--extract-conditional-code JScript の条件付きコメント(/*@if(...))から実際の解析コードを抽出します。
--loose-script-name == チェックを書き換えて、現在のスクリプト名とハードコードされた
スクリプト名の比較が常に true を返すようにします。
--real-script-name 偽の名前ではなく、現在解析中のスクリプトの実際のファイル名を返します。
--activex-as-ioc すべての ActiveX 呼び出しを IOC として記録し、その呼び出しが JS ソース内で
難読化されているかどうかを判断しようとします。
--ignore-wscript-quit WSCript.Quit() への呼び出しを無視して実行を継続します。
--ignore-rewrite-errors サンプルの書き換えに失敗した場合、元のサンプルを解析します。
最初の情報源はコンソール出力です。解析が成功すると、通常次のような内容が出力されます:
Using a 10 seconds timeout, pass --timeout to specify another timeout in seconds
Analyzing sample.js
Header set for http://foo.bar/baz: User-Agent Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Emulating a GET request to http://foo.bar/baz
Downloaded 301054 bytes.
Saved sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a (301054 bytes)
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a has been detected as data.
Saved sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 (303128 bytes)
sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 has been detected as PE32 executable (GUI) Intel 80386, for MS Windows.
http://foo.bar/baz is an active URL.
Executing sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c in the WScript shell
Header set for http://somethingelse.com/: User-Agent Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Emulating a GET request to http://somethingelse.com/
...
この場合、http://foo.bar/baz からファイルをダウンロードし、HTTP ヘッダー User-Agent を Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0) に設定するドロッパーが見られます。次に、それをデコードし、結果をディスク(PE32 実行可能ファイル)に書き込みます。最後に、Windows シェルでいくつかのコマンドを実行します。
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a には、http://foo.bar/baz からダウンロードされたペイロードが含まれます;sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 には、実際のペイロード(PE 実行可能ファイル)が含まれます;sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c には、Windows シェルで実行されたコマンドが含まれます。すべての HTTP リクエストは端末に出力されるとともに、urls.json に記録されます。重複した URL は挿入されません(つまり、同じ URL を2回要求しても、urls.json には1行しか含まれません)。
active_urls.json には、最終的に実行可能なペイロードをもたらした URL のリストが含まれます。このファイルは、配布サイトを停止させたい場合に最も重要です。
snippets.json には、box-js が遭遇したすべてのコード片(JavaScript、cmd.exe コマンド、PowerShell スクリプト)が含まれます。
resources.json には、サンプルによってディスクに書き込まれたすべてのファイルが含まれます。たとえば、アプリケーションが Hello world! を $PATH/foo.txt に保存しようとした場合、resources.json の内容は次のようになります:
{
"9a24...": {
"path": "(path)\\foo.txt",
"type": "ASCII text, with no line terminators",
"md5": "86fb269d190d2c85f6e0468ceca42a20",
"sha1": "d3486ae9136e7856bc42212385ea797094475802",
"sha256": "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a"
}
}
resources.json ファイルも重要です。実行可能なリソース(例: "type": "PE32 executable (GUI) Intel 80386, for MS Windows")に注意してください。
一部のスクリプトでは、new Date().getFullYear() の代わりに new Date().getYear() を使用していることが観察されています。サンプルに怪しい動作が見られない場合は、Date チェックに注意してください。
.JSE ファイルに遭遇した場合は、デコーダをコンパイルして次のように実行します:
cc decoder.c -o decoder
./decoder foo.jse bar.js
node run bar.js
サポートされていないコンポーネントに遭遇することがあります。その場合は、GitHub で Issue を報告するか、JavaScript が分かれば自分でコンポーネントをエミュレートできます。
エラーは通常次のようになります(行番号は異なる場合があります):
1 Jan 00:00:00 - Unknown ActiveXObject WinHttp.WinHttpRequest.5.1
Trace
at kill (/home/CapacitorSet/box-js/run.js:24:10)
at Proxy.ActiveXObject (/home/CapacitorSet/box-js/run.js:75:4)
at evalmachine.<anonymous>:1:6471
at ContextifyScript.Script.runInNewContext (vm.js:18:15)
at ...
例外が Proxy.ActiveXObject で発生していることがわかります。これは次のようになっています:
function ActiveXObject(name) {
name = name.toLowerCase();
/* ... */
switch (name) {
case "wscript.shell":
return require("./emulator/WScriptShell");
/* ... */
default:
kill(`Unknown ActiveXObject ${name}`);
break;
}
}
新しい case "winhttp.winhttprequest.5.1"(小文字に注意!)を追加し、ES6 の Proxy オブジェクト(例: ProxiedWinHttpRequest)を返すようにします。これは、悪意のあるサンプルから要求された時点で、未実装の機能をキャッチするために使用されます:
/* emulator/WinHttpRequest.exe */
const lib = require("../lib");
module.exports = function ProxiedWinHttpRequest() {
return new Proxy(new WinHttpRequest(), {
get: function(target, name, receiver) {
switch (name) {
/* ここに case 文で「特別な」トラップを追加 */
default:
if (name in target) return target[name];
else lib.kill(`WinHttpRequest.${name} not implemented!`)
}
}
})
}
function WinHttpRequest() {
}
解析を再実行すると、何が正確に実装されていないかを示して再び失敗します。
1 Jan 00:00:00 - WinHttpRequest.open not implemented!
Trace
at kill (/home/CapacitorSet/box-js/run.js:24:10)
at Object.ProxiedWinHttpRequest.Proxy.get (/home/CapacitorSet/box-js/run.js:89:7)
必要に応じて WinHttpRequest.open をエミュレートします:
function WinHttpRequest() {
this.open = function(method, url) {
URLLogger(method, url);
this.url = url;
}
}
コードがエラーなくエミュレートされるまで繰り返します。
@CapacitorSet: オリジナル開発者
@kirk-sayre-work: メンテナー
--output-directory