JavaScriptマルウェアの解析テクニック

2019年12月08日 日曜日


【この記事を書いた人】
ちひろ

2018年新卒入社し、SOCにてインフラ管理を担当。その後、マルウェア解析や検証業務などに従事。2022年度からは、社内のSREチームにて兼務を開始。主な保持資格は、CISSP, OSCP, GREM, GXPN, RISS, CKA, CKSなど。バイナリを読むのが好きで、一番好きな命令はx86の0x90(NOP命令)。

「JavaScriptマルウェアの解析テクニック」のイメージ
IIJ Engineers blog読者プレゼントキャンペーン

Twitterフォロー&条件付きツイートで「バリーくんぬいぐるみ」を抽選で20名にプレゼント!
応募期間は2019/11/29~2019/12/31まで。詳細はこちらをご覧ください。
今すぐツイートするならこちら→ フォローもお忘れなく!

IIJ 2019 TECHアドベントカレンダー 12/8(日)の記事です】

はじめに

JavaScriptは、Webページに対して動的な処理を実行するプログラミング言語ですが、近年マルウェアのダウンローダなど悪意ある様々な用途に利用されるケースが多く見受けられます。PE(Portable Executable)のマルウェアに関する解析記事はインターネット上に多く存在しますが、特に日本語で書かれたJavaScriptに焦点を当てた記事はとても少ないのが現状です。そこで本記事では、JavaScriptマルウェアを解析する際に便利なテクニック(暗黙の内に使っているようなテクニックもあるかとは思いますが)などをご紹介します。

解析環境

JavaScriptは、主にブラウザで動作するものであるため、GUIのブラウザが利用できる仮想環境があると解析がしやすいです。また、PEのマルウェアなどと同様に仮想環境で解析するのが常識なので、何かしらの仮想環境を用意します。後述するツールなどが入れやすいという性質上、Linuxのホストにしておくのが良いと思います。具体的な解析環境としては、REMnuxTsurugi Linuxなどが代表的です。

JavaScriptを整形

近年のJavaScriptマルウェアは難読化されているケースが殆どでパッと見ただけでは、どのような挙動をしているか判断することは難しい場合が多いです。そこで、JavaScriptを整形するツールを使って読みやすくするのがおすすめです。今回紹介するツールは、js-beautifyです。js-beautifyは前述したTsurugi Linuxにデフォルトでインストールされています。これ以外にも様々なツールがありますが、オンラインでJavaScriptを整形するツールなどは企業などで社外秘情報などを扱う際に利用できない可能性があるので、こういったCUIツールは非常に便利です(もちろん本ツールが改ざんなどされていた場合は別ですが今回はそこは触れないでおきます)。使い方は至って簡単で、以下のように引数に対象のJavaScriptファイルを渡すだけです。そして標準出力に整形されたJavaScriptが出力されるので、それをリダイレクトで別ファイルに書き出します。

$ js-beautify obfuscated.js > formatted.js

例えば以下のような1行に圧縮されているJavaScriptファイルに対してjs-beautifyを適用してみましょう。

var _0x5ec0=['log'];(function(_0x1a2628,_0x528615){var _0x4025a2=function(_0x2a0bf2){while(--_0x2a0bf2){_0x1a2628['push'](_0x1a2628['shift']());}};_0x4025a2(++_0x528615);}(_0x5ec0,0x123));var _0x551f=function(_0x22b1cf,_0x42446e){_0x22b1cf=_0x22b1cf-0x0;var _0x1623d6=_0x5ec0[_0x22b1cf];return _0x1623d6;};function hi(){console[_0x551f('0x0')]('Hello\x20World!');}hi();

今回は整形して見たいだけなのでファイルにリダイレクトしていませんが以下のような結果が得られます。

$ js-beautify obfuscated.js
var _0x5ec0 = ['log'];
(function(_0x1a2628, _0x528615) {
    var _0x4025a2 = function(_0x2a0bf2) {
        while (--_0x2a0bf2) {
            _0x1a2628['push'](_0x1a2628['shift']());
        }
    };
    _0x4025a2(++_0x528615);
}(_0x5ec0, 0x123));
var _0x551f = function(_0x22b1cf, _0x42446e) {
    _0x22b1cf = _0x22b1cf - 0x0;
    var _0x1623d6 = _0x5ec0[_0x22b1cf];
    return _0x1623d6;
};

function hi() {
    console[_0x551f('0x0')]('Hello\x20World!');
}

上記の出力結果のとおり、1行に圧縮されているよりもはるかに読みやすいJavaScriptを得ることができます。

JavaScriptエンジンによるテンプレートファイルを用いた解析

解析環境ではGUIのブラウザを用意するべきだという話をしましたが、実際のところJavaScriptマルウェアを解析する際には、ブラウザではなく生のJavaScriptエンジンなどで動的解析するのがおすすめです。ブラウザ上で行うと意図しない副作用が発生したりする場合があり、解析効率が下がるからです。そこで本記事では、JavaScriptエンジンを利用した解析方法をご紹介します。ここで紹介するのはFirefoxで利用されているJavaScriptエンジンのSpidermonkeyです。Ubuntu の場合は以下のようにしてインストールできます。

$ sudo apt install libmozjs-52-0 libmozjs-52-dev

JavaScriptエンジンは、通常ブラウザが提供しているwindowオブジェクトやdocumentオブジェクトを解釈することができません。そこでテンプレートファイルをかませて対象のJavaScriptを実行します。この際にテンプレートファイルの中で、evalなどの関数を上書きすることで、evalで実行しようとしているコードを表示することなどが可能になります。以下にテンプレートの例を示します。また、前述したREMnux では/usr/share/remnux/object.js というテンプレートファイルが用意されており便利です。

// Filename: template.js
document = {
    write: print,
    createElement: function(s) {
        print("[debug] document.CreateElement(" + s + ")");
    }
    "location": {
        "href": "http://example.com"
    }
};
window = {
    "location": {
        "href": "http://example.com"
    }
};
eval_org = eval;
function eval(s) {
    console.log(s); // eval内容を出力
    eval_org(s);
}

上記のテンプレートファイル(template.js)を用いてJavaScriptマルウェア(malware.js)を以下のように実行することができます。

$ js52 -f template.js -f malware.js

JavaScript 難読化テクニックの理解

ここまで紹介してきた2つの手法などを駆使することで読みやすくデバッグしやすい環境は作れるのですが、最終的には難読化された部分を読む必要がでてきます。そのため、JavaScriptの難読化の理論を知ることで、解析力がアップすると考えられます。また、JavaScript自体を勉強することも解析力をつけるためには良いと思います。ここでは、複数ある難読化テクニックの中から、記号を用いたJavaScript難読化をご紹介します。本手法自体は古くから存在する有名な手法なので目新しくはないですが、初見の方にとっては面白いかなと思ったのでご紹介します。JavaScriptは、他のプログラミング言語に比べると不思議な挙動をすることが多く存在します。特に暗黙の型変換などにより意図しない形の値を生成します。例えば、[]は空配列を示しますが、[]を2つ+で繋げてみたらどうなるでしょうか。以下は、Node.jsで実行した結果です。

> []+[]
''

上記の結果からどうやら空文字列を生成していることがわかります。では、今度は+の代わりに*に置き換えてみましょう。

> []*[]
0

上記の結果より数値の0が返されていることがわかります。これにより0が得られたため"false"[[]*[]]のように文字列の1文字目を抽出する際などに利用することができます。また、falseという文字列自体も以下のような命令を利用することで生成することができます。

> [![]]+[]+[]
'false'

上記の例では、![]falseを生成し、[false]と最初に見た[]+[]を足すことで、'false'という文字列を生成しています。このように、JavaScriptは記号のみで様々な値を生成し繋げていくことで、プログラミングすることが可能となっています。この原理を用いた難読化手法を行うオンラインツールも存在します。

おわりに

本記事では、いくつかのJavaScriptマルウェア解析するためのテクニックを紹介してきました。JavaScriptマルウェアは、特にダウンローダとしての役割を担うことも多く攻撃の中で非常に重要な解析対象です。本記事を通して、学んだ知識などが解析の際の手助けになれば幸いです。

ちひろ

2019年12月08日 日曜日

2018年新卒入社し、SOCにてインフラ管理を担当。その後、マルウェア解析や検証業務などに従事。2022年度からは、社内のSREチームにて兼務を開始。主な保持資格は、CISSP, OSCP, GREM, GXPN, RISS, CKA, CKSなど。バイナリを読むのが好きで、一番好きな命令はx86の0x90(NOP命令)。

Related
関連記事