バッファ オーバーフローについて聞いたことがありますが、その原因を知りたいです。
誰かが小さなバッファ オーバーフローの例を見せてもらえますか? 新しい(そして、それらは何に使用されますか?)
バッファ オーバーフローについて聞いたことがありますが、その原因を知りたいです。
誰かが小さなバッファ オーバーフローの例を見せてもらえますか? 新しい(そして、それらは何に使用されますか?)
バッファ オーバーフローの古典的な例:
// noone will ever have the time to type more than 64 characters...
char buf[64];
gets(buf); // let user put his name
ほとんどの場合、バッファ オーバーフローだけが意図的に発生することはありません。これは、いわゆる「off-by-one」エラーが原因で最も頻繁に発生します。配列サイズの計算を 1 つ間違えたことを意味します。おそらく、終端の null 文字を考慮するのを忘れたか、その他の理由が原因です。
しかし、それはいくつかの邪悪なものにも使用できます。実際、ユーザーはこの穴を長い間知っていて、たとえば 70 文字を挿入します。最後の文字には、スタックスロットを上書きする特別なバイトが含まれています。ユーザーが入力したのは名前ではなく、以前にコンパイルしてダンプしたシェルコードだったからです。それが実行されます。いくつかの問題があります。たとえば、そのバイナリ コードに "\n" を含めないようにする必要があります (gets がそこで読み取りを停止するため)。危険な文字列関数を混乱させる他の方法では、文字列関数がバッファへのコピーを停止するため、バイナリ ゼロが問題になります。人々は使用しましたxor
ゼロバイトを明示的に書き込まずに、同じ値を2回使用してゼロも生成します。
それが古典的なやり方です。しかし、そのようなことが起こったことを伝えることができるいくつかのセキュリティ ブロックや、スタックを実行不可能にするその他のものがあります。しかし、今説明したよりもはるかに優れたトリックがあると思います。一部のアセンブラーの男は、おそらくそれについて長い話をすることができます:)
バッファが実際に十分な大きさであるかどうか100%確信が持てない場合は、最大長の引数を取る関数も常に使用してください。「ああ、数が5文字を超えない」などのゲームをプレイしないでください。いつか失敗します。科学者がその数がある程度の大きさを超えることはないと述べたあるロケットを思い出してください。しかし、ある日、それは実際にはより速くなり、その結果、整数オーバーフローが発生し、ロケットがクラッシュしました (これは、歴史上最も高価なコンピューター バグの 1 つであるAriane 5のバグに関するものです)。
たとえば、 gets の代わりに use を使用しますfgets
。sprintf
そして、適切で利用可能な場所で使用する代わりにsnprintf
(または istream などの C++ スタイルのものだけを使用する)
バッファ オーバーフローは、基本的に、メモリの細工されたセクション (またはバッファ) が意図した範囲外に書き込まれた場合です。攻撃者がプログラムの外部からこれを行うことができた場合、任意のメモリ位置を操作できる可能性があるため、セキュリティ上の問題が発生する可能性がありますが、最新のオペレーティング システムの多くはこのような最悪のケースから保護しています。
意図した範囲外での読み取りと書き込みは一般に悪い考えと見なされますが、「バッファ オーバーフロー」という用語は、一般に、境界外での書き込みに対して予約されています。これは、攻撃者がコードの実行方法を簡単に変更できるようにするためです。ウィキペディアには、バッファー オーバーフローと、それらを悪用するさまざまな方法に関する優れた記事があります。
自分でプログラムする方法に関しては、次の簡単な問題になります。
char a[4];
strcpy(a,"a string longer than 4 characters"); // write past end of buffer (buffer overflow)
printf("%s\n",a[6]); // read past end of buffer (also not a good idea)
それがコンパイルされるかどうか、および実行時に何が起こるかは、おそらくオペレーティング システムとコンパイラに依存します。
最新の Linux OS では、余分な実験を行わないと、バッファ オーバーフローを悪用することはできません。なぜ ?この最新の GNU C コンパイラでは、 ASLR (Address Stack Layer Randomization) とスタック プロテクターによってブロックされるためです。ASLRによってメモリがランダム メモリに分類されるため、メモリを簡単に見つけることはできません。プログラムをオーバーフローさせようとすると、スタック プロテクターによってブロックされます。
最初に、ASLR を 0 にする必要があります。デフォルト値は 2 です。
root@bt:~# cat /proc/sys/kernel/randomize_va_space
2
root@bt:~# echo 0 > /proc/sys/kernel/randomize_va_space
root@bt:~# cat /proc/sys/kernel/randomize_va_space
0
root@bt:~#
これは、インターネットから入手した OLD STYLE バッファ オーバーフロー チュートリアルに関するものではありません。または aleph one チュートリアルは、システムで機能しなくなりました。
バッファ オーバーフロー シナリオに対するプログラムの脆弱性を作成できるようになりました
---------------------bof.c--------------------------
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char buffer[400];
strcpy(buffer, argv[1]);
return 0;
}
---------------------EOF-----------------------------
strcpy 関数を見ると、入力するバイト数をチェックせずに機能するため、スタックプロテクターなしでは危険です。C プログラムでスタック プロテクターをオフにするための追加オプション-fno-stack-protector dan -mpreferred-stack-boundary=2を使用してコンパイルします。
root@bt:~# gcc -g -o bof -fno-stack-protector -mpreferred-stack-boundary=2 bof.c
root@bt:~# chown root:root bof
root@bt:~# chmod 4755 bof
バッファー オーバーフロー SUID ルート アクセス シナリオを使用した C プログラムを作成しました。次に、プログラムのセグメンテーション違反を発生させるためにバッファに入れる必要があるバイト数を検索してみましょう
root@bt:~# ./bof `perl -e 'print "A" x 400'`
root@bt:~# ./bof `perl -e 'print "A" x 403'`
root@bt:~# ./bof `perl -e 'print "A" x 404'`
Segmentation fault
root@bt:~#
プログラムのセグメンテーション違反 (クラッシュ) を発生させるには 404 バイトが必要であることがわかります。EIPを上書きするには何バイト必要ですか? EIP 命令は後に実行されます。そのため、ハッカーは、プログラムのバイナリ SUID で必要なEIPを悪意のある命令に上書きします。プログラムが SUID ルートにある場合、命令はルート アクセスで実行されます。
root@bt:~# gdb -q bof
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main(int argc, char** argv)
5 {
6 char buffer[400];
7 strcpy(buffer, argv[1]);
8
9 return 0;
10 }
(gdb) run `perl -e 'print "A" x 404'`
Starting program: /root/bof `perl -e 'print "A" x 404'`
Program received signal SIGSEGV, Segmentation fault.
0xb7e86606 in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6
(gdb) run `perl -e 'print "A" x 405'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 405'`
Program received signal SIGSEGV, Segmentation fault.
0xb7e800a9 in ?? () from /lib/tls/i686/cmov/libc.so.6
(gdb)
プログラム GOT セグメンテーション違反の戻りコード。さらにバイトを入力して、EIP レジスターを見てみましょう。
(gdb) run `perl -e 'print "A" x 406'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 406'`
Program received signal SIGSEGV, Segmentation fault.
0xb7004141 in ?? ()
(gdb)
(gdb) run `perl -e 'print "A" x 407'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 407'`
Program received signal SIGSEGV, Segmentation fault.
0x00414141 in ?? ()
(gdb)
もう少し
(gdb) run `perl -e 'print "A" x 408'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/bof `perl -e 'print "A" x 408'`
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)
(gdb) i r
eax 0x0 0
ecx 0xbffff0b7 -1073745737
edx 0x199 409
ebx 0xb7fc9ff4 -1208180748
esp 0xbffff250 0xbffff250
ebp 0x41414141 0x41414141
esi 0x8048400 134513664
edi 0x8048310 134513424
eip 0x41414141 0x41414141 <-- overwriten !!
eflags 0x210246 [ PF ZF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb)
これで次のステップに進むことができます...
バッファ オーバーフローは、バッファの末尾を超えて書き込むだけです。
int main(int argc, const char* argv[])
{
char buf[10];
memset(buf, 0, 11);
return 0;
}
既に述べたことに加えて、バッファ オーバーフローが発生したときにプログラムが「クラッシュ」する場合としない場合があることに注意してください。クラッシュするはずです。クラッシュすることを願っていますが、バッファ オーバーフローがアプリケーションが割り当てた別のアドレスに「オーバーフロー」した場合、アプリケーションは長時間正常に動作しているように見える場合があります。
Microsoft Visual Studio の新しいエディションを使用している場合は、sprintf の代わりに sprintf_s など、stdlib で新しい安全な対応物を使用することをお勧めします...
プログラムのバッファオーバーフローをチェックしたい場合は、Valgrindなどのツールを使用して実行できます。彼らはあなたのためにいくつかのメモリ管理のバグを見つけるでしょう。
これはそれを再現するのに十分なはずです:
void buffer_overflow()
{
char * foo = "foo";
char buffer[10];
for(int it = 0; it < 1000; it++) {
buffer[it] = '*';
}
char accessViolation = foo[0];
}
これは、受け取った回答に関する一般的なコメントです。例えば:
int main(int argc, char *argv[]) { char buffer[10]; strcpy(buffer, argv[1]); }
と:
int main(int argc, const char* argv[]) { char buf[10]; memset(buf, 0, 11); return 0; }
最新の Linux プラットフォームでは、これは期待または意図したとおりに機能しない場合があります。FORTIFY_SOURCE セキュリティ機能のため、動作しない場合があります。
memcpy
FORTIFY_SOURCE は、 や などのリスクの高い関数の「より安全な」バリアントを使用しstrcpy
ます。コンパイラは、宛先バッファー サイズを推測できる場合、より安全なバリアントを使用します。コピーがコピー先のバッファ サイズを超える場合、プログラムは を呼び出しますabort()
。
テストのために FORTIFY_SOURCE を無効にするには、-U_FORTIFY_SOURCE
またはを使用してプログラムをコンパイルする必要があります-D_FORTIFY_SOURCE=0
。
「古典的な」バッファオーバーフローの例は次のとおりです。
int main(int argc, char *argv[])
{
char buffer[10];
strcpy(buffer, argv[1]);
}
これにより、バッファ オーバーフロー パラメータをいじって、思いのままに微調整できます。本 " Hacking - The Art of Exploitation " (リンクは Amazon に移動します) は、バッファ オーバーフローをいじる方法について非常に詳細に説明しています (純粋に知的な演習として)。
このコンテキストでは、バッファは特定の目的のために取っておかれるメモリの一部であり、バッファ オーバーフローは、バッファへの書き込み操作が最後を超え続けたときに発生するものです (別の目的を持つメモリへの書き込み)。これは常にバグです。
バッファ オーバーフロー攻撃は、このバグを利用して、プログラムの作成者が意図していないことを実行する攻撃です。
正解は次のとおりです。このトピックについて詳しく知りたい場合は、Podcast Security Now を聞くことをお勧めします。エピソード 39 (少し前) で、彼らはこれについて深く議論しました。これは、本全体を消化する必要なく、より深い理解を得るための迅速な方法です。
(リンクには、複数のサイズのバージョンとトランスクリプトを含むアーカイブがあります。視覚的に重視している場合)。オーディオはこのトピックに最適な媒体ではありませんが、Steve はこれに対処するために驚異的な作業を行っています。
バッファ オーバーフローは、割り当てられたメモリが保持できる範囲を超えて文字が挿入されることです。