Skip to main content

minimistのプロトタイプ汚染脆弱性を解説

著者
Headshot of Kirill Efimov

Kirill Efimov

2020年3月26日

0 分で読めます

2020年3月11日、Snykは中程度の深刻度と評価したプロトタイプ汚染の脆弱性(CVE-2020-7598)を公開しました。この脆弱性はminimist npm packageに影響します。これは、Snykのセキュリティリサーチチームによる継続的な調査の一環です。同チームは以前にも、lodashやjQueryなど、広く使われているJavaScriptライブラリで同様の脆弱性を発見しています。

プロトタイプ汚染の脆弱性とは何ですか?

プロトタイプ汚染として現れるこのセキュリティ脆弱性を悪用すると、攻撃者はJavaScriptアプリケーションのオブジェクトプロトタイプを上書きできます。そうなると、攻撃者が制御するプロパティがオブジェクトに注入され、JavaScript例外を発生させてサービス拒否を引き起こしたり、アプリケーションのソースコードを改ざんして、攻撃者が注入したコードパスを実行させたりする可能性があります。

Snykチームの今回の調査では、ほかのnpm packagesでも同様の脆弱性が見つかりました。その中には、yargs-parserと同じくらい広く使われているものもあります。

「minimist」のSnyk Vulnerability DB検索結果。npmのコマンドインジェクションとプロトタイプ汚染の脆弱性を表示。
Snykのセキュリティリサーチチームによって発見されたプロトタイプ汚染の脆弱性:https://snyk.io/vuln?type=npm

Snykのセキュリティリサーチチームは責任ある情報開示のガイドラインに従い、minimist、yargs-parserなどのメンテナーと連携して調査結果を共有・検証し、脆弱な攻撃対象領域についてメンテナーの確認と合意を得ました。

迅速に対応し、修正のリリースや旧バージョンへのセキュリティ修正のバックポートを短期間で実施してくださったメンテナーの皆さまに感謝します。たとえば、1.0.0より前のバージョンに対するminimistのセキュリティ修正や、13.1.2より前のバージョンに対するyargs-parserのセキュリティ修正が挙げられます。

CLIパーサーにおけるプロトタイプ汚染

この記事では、プロトタイプ汚染の基本については取り上げません。すでに多くの記事で解説されているためです。Snykのブログでも、プロトタイプ汚染の脆弱性の基礎を解説する記事を公開しています。

この記事では、代わりに次の内容を取り上げます。

  1. コマンドライン(CLI)引数パーサーの仕組みと、現在のJavaScriptエコシステムでの一般的な使われ方を説明します。

  2. こうしたパーサーに依存するアプリケーションが、どのようにプロトタイプ汚染攻撃の標的になり得るのかを、脆弱なパッケージの実例とともに紹介します。

  3. 深刻度の分類根拠と、これらの問題がアプリケーションにもたらす脆弱性について説明します。

コマンドライン引数用のライブラリなら、結局は自分自身を攻撃するだけでは?

minimistとyargs-parserは、Node.jsアプリケーションのコマンドライン引数を解析するためのJavaScriptライブラリです。Node.jsのCLIが不適切な入力検証(CWE-20)によってローカル権限昇格につながる仕組みを見てみましょう。不適切な入力検証は一般的な脆弱性であり、数千件の記録済み事例やCVEがあります。

次の例では、root権限のないユーザーがサーバーを再起動できるシステムユーティリティを作成します。この例でminimistを使う目的は、コマンドラインフラグ--helpの簡単なヘルプを表示することだけです。

次のコードは、u-rebootという小さなNode.js CLIを示しています。

const argv = require('minimist')(process.argv.slice(2));
const cp = require('child_process');
if (argv.help) {
    console.log("My awesome reboot utility with no root access required");
} else {
    cp.execSync('reboot', {uid: 0});
}

この便利なツールを配布しやすくするため、スタンドアロンのバイナリとしてビルドします。そのためにpkgを使います。

npm install -g pkg
pkg u-reboot.js --target node10-linux-x64

最後に、動作させるためにバイナリへ適切な権限を設定します。

chown root u-reboot
chmod 4555 u-reboot

4555は、すべてのユーザーに読み取り・実行を許可し、setuidフラグを設定するものです。setuid についてはこちらをご覧ください。簡単に言うと、バイナリを所有者(この例ではrootユーザー)の権限で実行できるようにします。

これで、すべてのユーザーが実行してサーバーやワークステーションを再起動できるコマンドラインツールu-rebootができました。ただし、サーバー上でそれ以外の操作はできないようにする必要があります。

このようなコマンドラインアプリケーションは、どのように悪用されるのでしょうか。ユーザーはサーバー上で低い権限しか持たず、rootとしてほかのコマンドを実行できないはずである点に注意してください。

Node.js CLIアプリケーションの悪用

minimistのセキュリティ脆弱性を悪用すると、Objectのプロトタイプを汚染できます。その結果、u-rebootは典型的な権限昇格の脆弱性を抱えることになります。

printf '#!/bin/shn id' > /tmp/exploit
chmod +x /tmp/exploit
u-reboot --__proto__.shell /tmp/exploit

出力にuid=0(root)と表示されていますね。minimistのプロトタイプ汚染の脆弱性を悪用すれば、u-reboot CLIを通じてroot権限で任意のコマンドを実行できます。

この攻撃が可能なのは、child_peorccess.execSyncが、任意のoptionsプロパティを持つshellオブジェクトを受け取るためです。shellが空の場合、ドキュメントによるとexecSyncは/bin/shを使います。しかし、すべてのオブジェクトのshellプロパティを/tmp/exploitに汚染すると、execSyncはそのexploitをシェルとして使います。

これはローカルの脆弱性に見えます。なぜネットワーク経由の攻撃ベクトルがあるとされるのでしょうか。

これは、2014年に公開されたShellshockの脆弱性を思い起こさせます。Shellshockでは、環境変数を介して注入された任意のコードを実行するよう、Bashシェルをだますことができます。一見、完全なローカル脆弱性に思えます(環境変数を操作できるのは誰でしょうか)。しかし、Bashシェルは非常に広く使われており、多くのWebサービスがリクエスト処理に利用しているため、攻撃者が任意のコマンドを実行できる可能性があります。この脆弱性について詳しく知らない方は、こちらの簡単な解説をお読みください。

この脆弱性は権限昇格だけに関係するのでしょうか。答えは「いいえ」です。調査チームがminimistとyargs-parserのさまざまなユースケースを調べたところ、興味深い例がいくつか見つかりました。各ケースを詳しく掘り下げることはしません。CLI引数パーサーが、必ずしも想定どおりに使われているとは限らないことを示す例です。

  1. Reactで動くターミナル — Webブラウザー上のターミナルをエミュレートするReactコンポーネントです。minimistを使ってコマンドのCLI引数を解析します。ターミナルエミュレーターの例はほかにもあります。

  2. チャットボットの引数はCLI引数によく似ていますよね。実際にその目的でCLI引数パーサーを使っている例が多数見つかりました。1、2、3、4、5をご覧ください。

  3. apibone — クエリ可能なサービス向けのインターフェースを提供するライブラリです。定義された関数のリクエストオブジェクトとレスポンスオブジェクトを抽象化します。yargs-parserを使ってHTTPクエリの一部を解析します。

オープンソースのGitHubリポジトリを詳しく調べると、一般的ではないさまざまな使い方の例がさらに見つかります。

こうしたアプリケーションでは、CLI引数の解析に使われるminimistのようなライブラリを避け、Webやネットワーク関連のアプリケーションに転用しないほうがよいのでしょうか。そうかもしれません。しかしSnykでは、パーサーは特に高い責任が求められるコードだと考えています。通常、パーサーはデータ処理の最初の段階にあり、ユーザー入力を直接扱うからです。

さらに、minimistは汎用の引数解析ライブラリです。Node.jsのprocess.argvのようなものに直接結び付いているわけではなく、文字列の配列を渡せば、そのデータがCLI引数であるかのように解析できます。

考えてみてください。XMLパーサーはいくつの問題を引き起こしたでしょうか。Javaのデシリアライゼーション機構はどうでしょう。よく知られた例として、Eran Hammerがhapiとjoiでのプロトタイプ汚染に対処した後に作成したbourne JSON parserがあります。このパーサーは、JSONペイロード内の__proto__プロパティから保護するという、ただ一つの目的のために作られました。詳しくはこちらの記事をご覧ください。

この脆弱性の深刻度はどのように分類されるのでしょうか。

この脆弱性を説明するため、次のCVSSベクトルを割り当てました。CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L。

  1. 攻撃元区分 — ネットワーク。複数のチャットボットやWebアプリケーションでminimistとyargs-parserが使われていることを確認しました。こうした用途でこれらのライブラリを使うのがベストプラクティスでないとしても、実際に使われている以上、その事実を無視することはできません。

  2. 攻撃条件の複雑さ — 高。プロトタイプ汚染の脆弱性が現実の脅威となるのは、攻撃者がリモートコード実行など、攻撃を続けるために必要な操作を実行できる適切なガジェットを見つけた場合に限られます。この例では、「execSync」呼び出しがそのようなガジェットの役割を果たします。

  3. 必要な特権 — なし。攻撃者は、CLI引数として扱われる文字列を送信できればよく、特別な権限は必要ありません。

  4. ユーザー関与 — なし。

  5. スコープ — 変更なし。この2つの特性について、これ以上の説明は不要だと考えます。

  6. 機密性 — 低。

  7. 完全性 — 低。

  8. 可用性 — 低。これら3つの特性がいずれも低いのは、#2で説明した理由によるものです。プロトタイプ汚染の脆弱性は、アプリケーションの文脈で評価する必要があります。

この脆弱性の深刻度は決して高くありません(CVSSスコアは5.6で、中程度です)。しかし、調査チームは多様な攻撃シナリオがあることを確認しています。ここで取り上げた2つのライブラリが広く使われていることを踏まえ、適切な情報開示と修正が必要だと考えています。

次に何をすればよいでしょうか。

  • すでにSnykを使ってアプリケーションを監視し、GitHubまたはBitbucketのリポジトリを接続している場合は、脆弱なライブラリを修正済みバージョンにアップグレードするための自動プルリクエストがSnykから届いているはずです。

  • Snykをまだお使いでない場合は、プロジェクトを追加してみてください。Snykはオープンソース向けに無料で利用でき、コードリポジトリからプロジェクトをSnykダッシュボードにインポートできます。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

カテゴリー: