PR

OSコマンドインジェクションについて

この記事は学習用です。記事の作成には ChatGPTとGitHub Copilotを使用しています。

1. 概要

OSコマンドインジェクション(以下、コマンドインジェクション)は、アプリケーションが外部から渡された入力をそのままOSコマンドに渡すことで、攻撃者が任意のシェルコマンドを実行できてしまう脆弱性です。

2. 定義・重要用語

  • OSコマンドインジェクション: アプリケーションを経由して外部入力がOSのシェルコマンドとして実行される脆弱性。攻撃者は悪意ある入力によりシェルでの任意コマンド実行を誘発できます。
  • サニタイズ: 入力の無害化処理。例: 不正な文字列を除去・エスケープする処理。
  • エスケープ: シェルメタ文字(|, &, ;, >, <, $, ` など)を無効化する処理。
  • ホワイトリスト: 許可する値のみを受け入れる方式。ブラックリスト(禁止文字列の列挙)より安全性が高い。

3. 発生する背景と脅威の深刻度

背景

  • シェルコマンド呼び出しは便利で、ファイル操作(圧縮、変換)、外部ツール実行(ImageMagick、ffmpeg)、システム管理(ユーザー作成)などで多用されます。
  • 例えば、ユーザー入力のファイル名をそのまま system() / exec() / subprocess に渡すケースで発生します。

なぜ重要か

  1. 任意コマンド実行(RCE: Remote Code Execution)の一形態であり、サーバーの完全な乗っ取りにつながる可能性があります。
  2. 権限が高いプロセスで実行されると、機密情報漏洩・永続的バックドア設置・他システムへの横展開など被害が大きくなります。
  3. 検出されにくく、ログに痕跡を残さない工夫(標準出力や標準エラー出力を /dev/null にリダイレクトするなど)で隠蔽されやすいです。

4. メカニズムと具体例

4.1 単純な脆弱コード(PHP)

// 脆弱: ユーザー入力をそのままシェルへ渡す
$filename = $_GET['file'];
$output = shell_exec("/usr/bin/cat " . $filename);
echo $output;

攻撃例: ?file=/etc/passwd;rm -rf /tmp/data のように ; を使って追加コマンドを実行可能。

4.2 Pythonの脆弱例(subprocess)

import subprocess
name = input('user> ')
subprocess.call('/bin/ls ' + name, shell=True)

subprocess.call(..., shell=True) は入力に対するシェル解釈を許すため危険です。shell=True を避け、引数をリストで渡します。

4.3 コマンド挿入の変種(パイプ・バッククォート・環境変数)

  • パイプ | を使って他コマンドへ出力を渡す
  • バッククォート `cmd`$(cmd) によるコマンド埋め込み
  • 環境変数展開 $VAR を利用して意図しない動作を誘発する手法

4.4 画像アップロード+ImageMagickを使うWebアプリ

ユーザーがアップロードした画像名を convert に渡すとき、ファイル名に ;$(...) を含めると任意コマンド実行につながる可能性があります。

5. 発見・検出の方法

5.1 手動テスト(ペネトレーションテスト的)

  1. 入力フィールドにシェルメタ文字を注入して挙動を観察: ;, &&, ||, |, >, 2>&1, `id`, $(id)
  2. 出力やサーバー応答、HTTPステータス、エラーメッセージ、遅延(長い sleep で応答が遅れるか)を確認。例: ; sleep 5 # でレスポンス遅延があれば実行の可能性。

5.2 自動検査ツール

  • Burp Suite: 手動+自動でペイロードを注入・確認可能(Intruder/Scanner)。
  • OWASP ZAP: 無料のスキャン機能。
  • 専用ルールを持つスキャナやCMS向けスキャナ: ルールベースで既知パターンを検出。

5.3 ログ調査

  • システムログ(/var/log/syslog、auth.log)、アプリケーションログ、ウェブサーバーアクセスログを確認。
  • コマンド実行に伴うプロセス生成やファイル削除のタイムスタンプ不整合を確認。

5.4 検出のチェックリスト表

チェック項目判定基準
shell=True の使用Pythonで subprocessshell=True があるか
外部コマンド呼び出しsystem/exec/shell_exec などの使用箇所
ユーザー入力の引数混入文字列連結でユーザー入力を渡していないか
エラーメッセージの露出コマンド出力がレスポンスに含まれていないか
サニタイズ不足ホワイトリストが使われているか

6. 対策

6.1 根本方針: 入力を信用しない(ゼロトラスト/Zero Trust)

  • ホワイトリスト: 許可する値(例: ファイル名一覧、許可拡張子、明示的なコマンド名)のみ受け入れる。
  • エスケープ: シェルメタ文字をエスケープ。ただし取りこぼしがあるため、原則はホワイトリスト優先。

6.2 言語別安全策

  • PHP: escapeshellarg() / escapeshellcmd() を利用。可能なら proc_open() など引数を明示できるAPIを使用。
  • Python: subprocess.run(['cmd', 'arg1', 'arg2'], shell=False) のように引数リストで渡す。実行ファイルは絶対パス指定がより安全。
  • Node.js: child_process.execFile()spawn() を使用し、exec() のようなシェル実行は避ける。

6.3 設計的回避

  • 外部コマンド呼び出しを可能な限り廃止し、ライブラリや言語組み込み機能で代替(例: 画像処理はCLIではなくライブラリ)。

6.4 権限分離(Least Privilege)

  • コマンド実行プロセスは必要最小限の権限で動作。専用ユーザーで実行し、アクセス可能なファイル/ディレクトリを制限。

6.5 コンテナ・サンドボックス化

  • コンテナ内で外部コマンドを実行する場合、Linux Capabilitiesを削減し、不要なツールを含めないイメージを利用。

6.6 サニタイズの実装例

PHP(安全化例)

$allowed = ['file1.txt', 'report.csv'];
$filename = $_GET['file'];
if (!in_array($filename, $allowed, true)) {
  http_response_code(400);
  exit('invalid file');
}
$path = '/path/to/data/' . $filename;
$cmd = '/usr/bin/cat ' . escapeshellarg($path);
exec($cmd, $out, $rc);

Python(安全化例)

import subprocess
allowed = {'summary.txt', 'report.csv'}
name = input()
if name not in allowed:
    raise ValueError('invalid')
subprocess.run(['/usr/bin/cat', '/data/' + name], shell=False, check=True)

補足: 実運用ではパス正規化(ディレクトリトラバーサル防止)や固定ディレクトリの厳格化も併用するとより安全です。

7. インシデント対応と復旧手順

7.1 即時対応(初動)

  1. 該当機能の一時停止(Feature toggle / firewall / WAF ルールでブロック)
  2. 影響範囲の確認(ログ、プロセス、新規ユーザー、公開鍵ファイル、cronの調査)
  3. 重要データのバックアップ取得(現状のログを含む)
  4. 可能なら侵害されたサーバーのネットワーク隔離

7.2 根本原因解析

  • 攻撃ペイロードの収集、脆弱箇所のコードパス特定、実行されたコマンド履歴の推定
  • 侵入後に設置されたファイルやユーザー、ネットワーク通信先の特定

7.3 復旧と再発防止

  • 脆弱箇所を修正し、テスト(ユニット/統合/ペンテスト)を実施
  • ロールアウト前にWAF/IDSルールを追加、レッドチーム演習を計画
  • アクセスキーやパスワードが漏えいした恐れがある場合は再発行

8. よくある誤り・落とし穴

  • ブラックリストに頼る: 禁止文字列の列挙は取りこぼしが多く危険。例: Unicodeや混合バイトで回避され得る。
  • ログだけで安心する: 攻撃者は痕跡を消す可能性がある。ログ改ざんの可能性も考慮する。
  • escapeshell* に万能を期待する: 間違った組み合わせで使うと失敗する。呼び出し方法自体の見直し(シェル非使用・引数の明示)を優先。

9. まとめ

  • 外部コマンド呼び出しの存在をコードベースで列挙する。
  • 各呼び出しについて「なぜ必要か」「代替はないか」を評価する。
  • 入力をホワイトリストで検証し、シェルを使わない呼び出しに置き換える。
  • 実行権限を最小化し、監査ログとアラートを整備する。

A. 参考サイト

B. 関連書籍

コメント