{
char buf[8];
sprintf(buf,"AAAA%3s","XXXXXXXX");
printf("%s\n",buf);
}
何が起こるか?
バッファーには 8 文字分のスペースがあり、空き文字は 3 文字しか残っていませんが、「XXXXXXXX」は 8 文字です。
Windows 7 上の Visual Studio 2008 でテストを行いました。その結果、プログラムは AAAAXXXXXXX を出力し、実行時エラーが発生しました。
{
char buf[8];
sprintf(buf,"AAAA%3s","XXXXXXXX");
printf("%s\n",buf);
}
何が起こるか?
バッファーには 8 文字分のスペースがあり、空き文字は 3 文字しか残っていませんが、「XXXXXXXX」は 8 文字です。
Windows 7 上の Visual Studio 2008 でテストを行いました。その結果、プログラムは AAAAXXXXXXX を出力し、実行時エラーが発生しました。
自分のケース、さらに重要なことに、同様のケースで何が起こるかを検討することは非常に理にかなっています。他のポスターが指摘したように、UB を呼び出します。それはおそらく本当です。しかし、誰かが次に何が起こるべきかを正確に定義しなかったという理由だけで、世界が止まるわけではありません。次に物理的に起こることは、おそらく重大なセキュリティ ホールです。
文字列XXX...が制御されていないソースからのものである場合、バッファ オーバーフローの脆弱性が発生する寸前です。
(1) 通常、スタックは逆方向に「成長」します。つまり、アドレスが小さいほど、スタックがいっぱいになります。
(2) 文字列は、文字 n+1 が文字 n の後に格納されるように、その文字列に属する文字が格納されることを期待します。
(3) 関数を呼び出すと、戻りアドレス、つまり関数が戻った後に実行される命令のアドレスがスタックにプッシュされます (とりわけ、通常)。
次に、関数のスタック フレームを考えてみましょう。
|----------------|
| buf [size 8] |
|----------------|
| (func args) |
|----------------|
| (other stuff) |
|----------------|
| return address |
|----------------|
悪意のあるユーザーは、スタック上のリターン アドレスとの間のオフセットが正確に何であるかを調べることによって、制御されていない関数がスタック上のアドレスを上書きする時点で、攻撃者が選択したアドレスが文字列に含まれるbufように、アプリケーションへの入力を操作する可能性があります。スタック上の戻りアドレス。(注:利用可能な場合は、より適切に使用してください)。これにより、攻撃者はバッファ オーバーフロー攻撃を仕掛けました。彼はNOP スレッド テクニックのようなものを使用して、アプリケーションにシェルを開始させるかもしれません。特権ユーザー アカウントで実行されるアプリケーションを作成していた場合、攻撃者に貸衣装のシステムへの第 1 級のエントリであるACEを提供したことになります。XXX...sprintfsnprintfあなたが望むなら、穴。
発生する実行時エラーは、上書きされたリターン アドレスが原因である可能性があります。基本的にガーガベで埋めたので、CPUがジャンプしたアドレスには、プログラムテキストとして解釈され、無効なメモリアクセスを引き起こすバイトシーケンスが含まれている可能性があります(またはアドレス自体がすでに不良でした)。
一部のコンパイラは、これらの種類のエラーに対して役立つことに注意してください。たとえば、GCCには-fstack-protector. それらの機能がどれほど優れているかはよくわかりません。
この関数sprintf()は、文字列に書き込むときに配列を超えて書き込むため、未定義の動作が発生します。あなたのコードを見ると、スタック上で次に発生するものの最初の数バイトを上書きするか、実行時エラーを引き起こす可能性がありますが、その動作は保証されていません。
未定義の動作とは、文字通り、何でも起こり得ることを意味します。つまり、あなたのコードが何の問題もなく実行時エラーを引き起こしたり、コンピューターを爆発させたり、宝くじに当選したり、裏庭にユニコーンを出現させたり、ヒトラーを復活させたり、米国大統領を暗殺したりする可能性があります。これをしないでください。
文字バッファーには、何を入れても十分なスペースがsprintf()あり、null ターミネーター用の余分な文字が含まれていることを常に確認してください。一般に、自分のものではないメモリ空間をいじろうとしないでください。
このメソッドを使用する代わりに、ここで説明されている snprintf() メソッドを使用してみてください。このメソッドは本質的に同じ機能を実行しますが、文字数を明示的に制御して、未定義の動作を防ぐことができます (これは良いことです)。
snprintf は str に size バイトを超える書き込みをしないことが保証されているため、これを使用するとバッファ オーバーフローのリスクを回避できます Wiki
フォーマット文字列にバグ/タイプミスがあります。代わり"AAAA%3s"に"AAAA%.3s". フィールド [最小] 幅とフィールド精度は大きく異なります。前者は、フィールドを拡張して埋める最小バイト数を設定します。後者 (文字列の場合) は、出力される最大バイト数を設定します。文字列の追加のバイトは、検査も出力へのコピーもされません。
sprintf()関数は、テキストの無制限のコピーを容易にし、バッファをオーバーフロー攻撃の影響を受けやすくします。プロセスがfixe-lengthバッファに境界で許可されているよりも多くのデータを格納しようとすると、バッファオーバーフローが発生します。
攻撃者はオーバーフローの脆弱性を発見した後、呼び出しがユーザー入力を取得し、関数呼び出しを介してルーティングされる方法を観察します。その後、攻撃者はエクスプロイトを作成できます。これにより、ソフトウェアは通常では実行できないことを実行します。これは、単にマシンをクラッシュさせることから、攻撃者がマシンにリモートアクセスできるようにコードを挿入することまでさまざまです。
Cの多くのファンクチンは、適切に使用しないとエラーを引き起こします。一部の機能は代替ソリューションを提供します。
Avoid prefer
sprintf snprintf
vsprintf vsnprintf
strcat strlcat
strcpy strlcpy
strncat strlcat
strncpy strlcpy
出典:ECSP-セキュアプログラマー。
「インシリコ」は非常に正しいですが、おそらくコンピューターのカーネルが以前よりもはるかに賢くなっているため、その後に続くものに書き込むことができずchar buf[4];、プログラムを強制終了し、セグメンテーション フォールト シグナルを発行します。
メモリの次の部分が本当に重要なものである場合、コンピュータをクラッシュさせる代わりに安全に保管されるため、これは素晴らしいことです。
また、彼が言ったように、決してこれをしないでください。
何が起こるか?...
{ char buf[8]; sprintf(buf,"AAAA%3s","XXXXXXXX"); printf("%s\n",buf); }
Windows では、 を使用することになっていますsprintf_s。コードは監査に失敗する必要があるため、本番環境に移行することはできません。参考として、Microsoft のWriting Secure Code (Developer Best Practices)を参照してください。特に、第 5 章を参照してください。
Linux では、コンパイラとプラットフォームが FORTIFY_SOURCE を提供している場合、上記のコードはabort(). 多くの最新の Linux プラットフォームがサポートしているので、私はそれを期待しています。
memcpyFORTIFY_SOURCE は、 、 、strcpyなどのリスクの高い関数の「より安全な」バリアントを使用しsprintfます。コンパイラは、宛先バッファー サイズを推測できる場合、より安全なバリアントを使用します。コピーがコピー先のバッファ サイズを超える場合、プログラムは を呼び出しますabort()。
テストのために FORTIFY_SOURCE を無効にするには、-U_FORTIFY_SOURCEまたはを使用してプログラムをコンパイルする必要があります-D_FORTIFY_SOURCE=0。
移植性に関する @prng のコメントに対応するには、strcpy_s、printf_s、sprintf_sおよびフレンズが標準 Cです。 ISO/IEC TR 24731-1を参照してください。
Linux と glibc で欠落している機能が問題である場合は、プリプロセッサ マクロを使用して機能を失った glibc による違いを抽象化できます。Linux と glibc の機能に関係なく、コードは Windows プラットフォームの最低基準を満たしていません。