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

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


OSコマンドインジェクションとは、WebサイトやWebアプリケーションにおいて、ユーザーからサーバーへ送信されるHTTPリクエストに特定の文字列を挿入することによって、想定していないOSコマンドをサーバー上で実行させられる脆弱性のことです。この脆弱性を攻撃に利用されると、システム全体に甚大な被害が及びます。
簡単な例として、ユーザーが指定したE-Mailアドレスにメールを送信するホームページ上のフォームを作る事を考えます。多くのWebアプリケーションの開発言語ではOSコマンドの呼び出しが可能なので、この開発者は「sendmail」というメールを送信するOSコマンドを呼び出すことでこのフォームを実装しようと考えました。ユーザーがE-Mailアドレスとして「user@example.com」をフォームに入力した際にサーバーは以下のようなOSコマンドを呼び出すとします。

cat message.txt | sendmail –i –f sender@example.com user@example.com;

「|(パイプライン)」や「;(セミコロン)」はOSコマンドの区切り文字になっており、これらの文字の後には別のOSコマンドを繋げることができます。

これらの攻撃の目的は、サービスの停止や嫌がらせ、金銭などの要求、組織に対する抗議などさまざまです。またDoS攻撃/DDoS攻撃を隠れみのにして、他のサーバ攻撃が行われる場合もあります。
これに対し、悪意を持ったユーザー(攻撃者)はアドレスの代わりに「user@example.com; <OSコマンド>」の入力を試みます。このアドレスが適切な文字のみで構成されているかの確認(バリデーションチェック)を行わず、そのまま用いると、サーバー上でのコマンドは以下の様になります。

cat message.txt | sendmail -i -f sender@example.com user@example.com; <OSコマンド>;

「;」はOSコマンドの区切り文字なので、これによりsendmailに続いて指定された任意のOSコマンド(cat, rm等のシェルコマンドや、その他サーバーにインストールされているプログラム)を不正に実行してしまいます。このようにシェルに引き渡されるコマンド文字列にユーザーがOSコマンドの区切り文字などを挿入して、想定していないOSコマンドが実行してしまう脆弱性を「OSコマンドインジェクション」と呼びます。
攻撃者はこの脆弱性を利用することで、データベース内にあるパスワード・クレジットカード番号等といった個人情報や、その他システム内にある秘匿情報を取得したり、データを改ざん・消去したり、マルウェアをダウンロードさせたりと、非常に広範囲の操作をサーバー上で行うことができます。多くのWebアプリケーションで用いられているフレームワークのStruts2でも、OGNL式を挿入して任意のコードやOSコマンドを実行させる脆弱性が過去に知られており、Webアプリケーションを運用する上で対策の欠かせないリスクになっています。

OSコマンドインジェクションの対策法とは


OSコマンドインジェクションを防ぐためには、以下の対策1,2のいずれかを実施することが必要です。

対策1. OSコマンド(シェル)を呼び出す言語機能の利用を避ける
可能であればこの対策を選択するべきです。アプリケーションのコード内でOSコマンドやシェルを呼び出すと、OSコマンドインジェクションのリスクを少なからずはらみます。内部でシェルを呼び出すAPIは避け、代替となるライブラリ・フレームワーク内のAPIを探して利用するほうが安全です。ただし、Struts2の例のように、既知の脆弱性が残存するバージョンは言うまでもなく使用すべきではありません。

対策2. OSコマンドの呼び出しが必要な場合は、引数を厳密にチェックし、予め許可した処理のみを実行する
プログラムがAPIを提供しない等で、OSコマンドをアプリケーション内から呼び出す必要がある場合は、許可する処理をあらかじめ想定して決めましょう。その上で、コマンドの引数を厳密にチェックし、妥当である場合に限って呼び出すようにします。チェックの際は、コマンド終端( | , &, ; )や、引用符(", ')、改行区切り(スペースとタブ)、ハイフン、改行文字等の特殊記号といった、多くの注意すべき文字があります。
多くの言語系では、意図しない文字によるOSコマンドインジェクションを防ぐために、コマンド呼び出しを単一の文字列では無く、コマンドとその引数からなる文字列配列で与えることができます。たとえばPythonでは「ls -l」コマンドを「subprocess.run(["ls", "-l"])」として実行します。先ほどのsendmailの例では、コマンド実行は以下の様になり攻撃者のパラメータはクォーティングとエスケープによってsendmailの引数として処理されます。

subprocess.run(["sendmail", "-i", "-f", "sender@example.com", "user@example.com; <OSコマンド>"], input= subprocess.run(["cat", "message.txt"], stdout=subprocess.PIPE).stdout)

OSコマンドの呼び出しではこのような対策がされた言語機能を利用するとともに、ユーザー由来の文字列に対しては、記号を含まない英数字のみを許可するといったように、ホワイトリスト方式によってバリデーションチェックを行うことをお勧めします。上記のように単に引数として与えていても、パラメータの文字列がハイフンで始まる場合など、誤動作を引き起こすリスクは残るため、ユーザーのパラメータに対するバリデーションチェックは必須です。