制御されていないフォーマット文字列は、 1989 年頃に発見されたコードインジェクションの脆弱性の一種で、セキュリティ攻撃に悪用される可能性があります。[ 1 ]当初は無害と考えられていたフォーマット文字列の悪用は、プログラムをクラッシュさせたり、悪意のあるコードを実行したりするために使用できます。この問題は、フォーマットを実行する特定のC関数 (など) で、フォーマット文字列パラメータとしてチェックされていないユーザー入力を使用することに起因します。悪意のあるユーザーは、フォーマットトークンなどを使用して、コールスタックまたはメモリ内の他の場所からデータを出力する可能性があります。また、フォーマットトークンを使用して任意の場所に任意のデータを書き込むこともできます。このトークンは、コマンドや同様の関数を使用して、フォーマットされたバイト数をスタックに格納されているアドレスに書き込みます。printf()%s%x%nprintf()
典型的なエクスプロイトでは、これらの手法を組み合わせてプロセスの命令ポインタ(IP)を制御します。 [ 2 ]例えば、プログラムにライブラリ関数のアドレスやスタック上の戻りアドレスを悪意のあるシェルコードへのポインタで上書きさせるなどです。フォーマット指定子のパディングパラメータは、出力されるバイト数を制御するために使用され、%xトークンはフォーマット文字列の先頭に到達するまでスタックからバイトをポップするために使用されます。フォーマット文字列の先頭は、フォーマットトークンが実行する悪意のあるコードのアドレスで上書きできるアドレスを含むように細工されています%n。
これは一般的な脆弱性です。フォーマットのバグは以前は無害だと考えられており、多くの一般的なツールに脆弱性をもたらしました。MITREのCVEプロジェクトには、2007年6月時点で約500の脆弱なプログラムがリストされており、トレンド分析では、2001年から2006年の間に報告された脆弱性の種類の中で9番目に多いとされています。[ 3 ]
書式文字列のバグは、プログラマがユーザーから提供されたデータを含む文字列を出力しようとする場合(ファイル、バッファ、またはユーザーへの出力)、最もよく発生します。プログラマは、printf(buffer)の代わりに と誤って記述してしまうことがありますprintf("%s", buffer)。最初のバージョンはbuffer書式文字列として解釈され、含まれている書式指定子を解析します。2番目のバージョンは、プログラマの意図どおり、単に文字列を画面に出力します。文字列に書式指定子がない場合、どちらのバージョンも同じように動作するため、開発者はこの間違いに気づきにくいのです。
フォーマットバグは、C言語の引数渡し規約が型安全ではないために発生します。具体的には、このvarargsメカニズムでは、関数が任意の数の引数(例:)を受け入れることができます。これは、呼び出しスタックから必要な数の引数をprintf「ポップ」することで実現され、初期の引数によって追加でポップする引数の数と型が示されることを前提としています。
フォーマット文字列のバグは、C 言語以外にも Perl などのプログラミング言語で発生する可能性がありますが、発生頻度は低く、通常は攻撃者が選択したコードを実行するために悪用することはできません。[ 4 ]
フォーマットのバグは、1989年にウィスコンシン大学で行われたファジングテスト作業で初めて指摘されました。このテストでは、 Cシェル(csh)のコマンド履歴メカニズムと、安全な文字列入力を前提とするエラールーチンとの間の「相互作用効果」が発見されました。[ 5 ]
フォーマット文字列のバグを攻撃ベクトルとして使用することは、1999 年 9 月にTymm TwillmanがProFTPDデーモンのセキュリティ監査中に発見しました。 [ 6 ]監査では、フォーマット文字列なしでユーザー生成データを直接渡すことが発覚しました。printf スタイルの関数に人為的に引数を付けて広範囲にテストした結果、これを使用して権限昇格が可能であることがわかりました。これにより、1999 年 9 月にBugtraqメーリングリストにこの種の脆弱性に関する最初の投稿が行われ、基本的なエクスプロイトも含まれていました。[ 6 ]しかし、この方法を使用する他のソフトウェアのエクスプロイトが表面化し始めたため、セキュリティ コミュニティがフォーマット文字列の脆弱性の危険性を完全に認識するまでには、まだ数か月かかりました。この問題を広く知らしめた最初のエクスプロイト (コード実行によるリモート root アクセスの提供) は、2000 年 6 月にPrzemysław Frasunek [ 7 ]とtf8というニックネームを使用する人物によってBugtraqリストに同時に公開されました。[ 8 ]その後すぐに、lamagraというニックネームの人物が説明を投稿した。[ 9 ]「フォーマットのバグ」は、2000 年 7 月に Pascal Bouchareine によってBugtraqリストに投稿された。 [ 10 ] Tim Newshamによる 画期的な論文「フォーマット文字列攻撃」[ 11 ]は 2000 年 9 月に発表され、その他の詳細な技術解説論文は、チームTesoによる「フォーマット文字列の脆弱性の悪用」など、2001 年 9 月に発表された。[ 2 ]snprintf
Java ( を使用String.format())、C# (String.Format()またはその補間文字列を使用)、C++ ( を使用)などの現代の言語では、この種のフォーマット文字列攻撃はもはや不可能ですが、 Log4Shellstd::format()を可能にした脆弱性など、他のフォーマット文字列の脆弱性がまだ存在する可能性があります。
多くのコンパイラはフォーマット文字列を静的にチェックし、危険なフォーマットや疑わしいフォーマットに対して警告を発することができます。GNU Compiler Collectionでは、関連するコンパイラフラグは、、、、、、-Wallおよびです。[ 12 ]-Wformat-Wno-format-extra-args-Wformat-security-Wformat-nonliteral-Wformat=2
これらのほとんどは、コンパイル時に既知の不正なフォーマット文字列を検出する場合にのみ役立ちます。フォーマット文字列がユーザーまたはアプリケーション外部のソースから来る可能性がある場合、アプリケーションはフォーマット文字列を使用する前に検証する必要があります。アプリケーションが実行時にフォーマット文字列を生成または選択する場合にも注意が必要です。GNU Cライブラリを使用する場合は、この-D_FORTIFY_SOURCE=2パラメータを使用して、実行時に発生する特定の種類の攻撃を検出できます。チェック-Wformat-nonliteralはより厳格です。
他の多くのセキュリティ問題とは異なり、フォーマット文字列の脆弱性の根本原因は、x86 コンパイルされた実行ファイルでは比較的簡単に検出できます。printf-family 関数の場合、適切な使用法では、フォーマット文字列とフォーマットする引数を別々の引数として指定します。このような関数の誤った使用は、関数に渡される引数の数を数えるだけで見つけることができます。「引数の不足」[ 2 ]は、関数が誤用されたことを示す強力な指標となります。
x86では、呼び出し規約により、呼び出し元が呼び出し後にスタックポインタに加算することでスタックにプッシュされた引数を削除するため、引数の数を数えるのは容易な場合が多い。したがって、スタック補正を単純に調べれば、printf-family関数に渡された引数の数が得られる。[ 2 ]
printfscanf