9

大学のコンピュータ セキュリティのコースの一環として、バッファ オーバーフローと、それをエクスプロイトとして使用する方法について学ぶ予定です。次のコードで単純なバッファ オーバーフローを実行しようとしています。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buffer_one[4], buffer_two[16];

    strcpy(buffer_one, "one");
    strcpy(buffer_two, "two");

    strcpy(buffer_one, argv[1]);

    printf("buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
    printf("buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
}

実行すると、 buffer_one の内容を null ターミネータで上書きできます

$./overflow 1234567890123456
 buffer_two is at 0x7fff5fbff8d0 and contains '1234567890123456'
 buffer_one is at 0x7fff5fbff8e0 and contains ''

しかし、引数として 16 文字を超える文字を送信すると、プログラムは Abort トラップを送信します。これは、Snow Leopard での何らかのバッファ保護だと思います (ASLR かな?)。buffer_two のサイズを 16 未満にすると、アドレスはまだ 16 ビット離れています。

gcc -o overflow overflow.c -fno-stack-protectorスタック保護を削除するために実行しています

Linux ディストリビューションを実行している VM をインストールする以外に、この問題の解決策はありますか?

4

4 に答える 4

3

なぜこれが起こっているのかの鍵は、がメモリ内のbuffer_one後に配置されているという事実です。buffer_twoこれは、 がオーバーフローしても、 にオーバーフローしbuffer_oneいないbuffer_twoことを意味します。ebp代わりに、保存されたポインターや最も重要なリターンアドレスなど、他のものを保持するために使用されているスタックメモリにオーバーフローしています。

これはまさに、バッファ オーバーフロー エクスプロイトを試みるときに発生させたいことです! プログラムがstrcpy(buffer_one, argv[1]);から最初の 4 バイトを実行すると、argv[1]に割り当てられたメモリに移動しbuffer_oneます。しかし、次の 12 回は、他の用途に使用されているメモリのオーバーフローを開始し、最終的に戻りアドレスを上書きします。マシン コードを見ないと、正確にどのバイトがリターン アドレスをオーバーフローしているかはわかりません。しかし、SIGABRT の時点での EIP の値は 0x31323334 または同様のもの ('1234' の 16 進表現) であると推測しています。重要なのは、返信アドレスを上書きできるようにすることで、EIP を制御できることを認識することです。そして、EIP を 制御するときは、システムを制御します. (やや誇張されていますが、ほとんどの場合、そう遠くはありません) EIP を制御するときは、プロセッサが次に実行する命令を制御します (実際に OS/カーネルが間に立っているという事実はさておき)。

ここで、戻りアドレスを上書きする 8 バイトを正確に見つけた場合、それらのバイトをバッファーのアドレスに置き換えることができ ( 0x00007fff5fbff8e0)、元の呼び出し元 (この場合は libc) に戻る代わりに、プログラムは指定された命令の実行を開始します ( AKA シェルコード)。最も重要な場所に暗黙の 0 を入力し、0x00 0x00 0x7f 0xff 0x5f実際の数字/文字7ff5などではなく、実際の印刷不可能な ASCII 文字 (など) としてアドレスを指定する必要があることに注意してください。x86-64 アーキテクチャを使用している場合は、また、リトル エンディアンを考慮して、逆方向に指定する必要があり0xe0 0xf8 0xbfます。これらの印刷不可能な文字の指定は、簡単な Python または Perl スクリプトを使用してバッククォートとコマンド置換を使用することで最も簡単に実現できます。

./overflow `python -c 'print "AAAAAAAAAAAAAAAA\xe0\xf8\xbf\x5f\xff\x7f"'`

\x00(A はバッファをオーバーフローさせるためにパディングされています。) 残念ながら、アドレスに必要な2 つの追加を提供することはできません。これらの NULL の 1 つが によってそこに配置されますstrcpyが、最後の NULL で幸運をつかみ、上書きするアドレスが既に開始されていることを祈る必要があります0x00(実際には非常に可能性が高い)。適切な数の A でこれを実行すると、大文字の A にジャンプして実際のマシン命令 ( 0x41=> inc ecx) として実行するため、セグメンテーション違反や不正な命令が発生する可能性があります。

そして最後に、実際のシェルコードを挿入します。限られたバッファ サイズを考えると、わずか 12 バイト程度で有用なものを提供することは非常に困難です。この場合のコードを書いているので、おそらく最も簡単な方法はバッファを大きくすることでしょう。これがオプションではない場合は、A)buffer_two前に来るのでさらに 16 バイトも使用するbuffer_oneか、B) 環境変数にシェルコードを指定して、代わりにそれにジャンプすることができます。

実際のシェルコードを自分で書きたい場合は、syscall の実行方法、呼び出し規約とは何か、それらの使用方法、およびシェルコードで NULL バイトを回避する方法を知る必要があります。もう 1 つの方法は、Metasploit に含まれているようなペイロード ジェネレーターを使用することです。

特にアドレスがどうなるかはよくわかっているので、技術的にはこれらが必要な唯一の要素です。ただし、多くの場合 (特にシェルコード アドレスが不明な場合)、いわゆる NOP スレッドがシェルコードの前に配置されるため、アドレスを正確に取得する必要はありません。NOP スレッド (No Operation の略) は、数百から数千の NOP 命令 (0x90) であり、途中でジャンプして、実行がシェルコードに続くまで効果がありません。

GDB ですべてをトレースし、実行がシェルコードに正しくジャンプしてもアクセス違反が発生する場合は、スタック ページに NX ビットが設定されている可能性があります。これは、プロセッサがスタックからのデータを命令として実行することを拒否することを意味します。が OSX に含まれているかどうかexecstackはわかりませんが、含まれている場合は、NX ビットを無効にするテスト目的で使用できます ( execstack -s overflow)。

テキストの壁で申し訳ありませんが、バッファ オーバーフローをどこまで研究したいのかわかりませんでした。Aleph One の典型的なガイド「Smashing the Stack for Fun and Profit」など、チェックアウトできる他のガイドもあります。Shellcoder's Handbookもチェックアウトするのに適した本であり、他の人が推奨事項を追加できると確信しています。

TL;DR:要するに、バッファをオーバーフローさせ、保存されたポインタとリターン アドレスをガベージで上書きしています。

于 2011-04-14T00:45:45.587 に答える
1

x86のスタック上のデータは4バイトで整列されます。長さが4バイトの倍数でない場合は、間buffer_twoにパディングが配置されます。12以下に変更すると、12バイト離れている必要があります。buffer_onebuffer_two

【更新】アドレスサイズを見落としました。64ビットシステムを使用しており、スタックは8バイトにアラインされています。アドレスの違いは、バッファサイズが少なくとも8バイト変更されるまで変更されません。

この行は正しいですか:

strcpy(buffer_one, argv[1]);

argv[1]出力は、にコピーしているように見えますbuffer_two

その場合、クラッシュしたときにどれだけコピーしますか?17バイト?18?24を超える場合は、アボートにつながるような方法でスタックを壊し始めます。

"1234567890123456"実際には、ヌルターミネータの切り捨てを含む17バイトをコピーしていることに注意してくださいbuffer_one

于 2011-04-13T20:15:37.083 に答える
1

コンパイル時に FORTIFY_SOURCE を無効にしようとしましたか?

-D_FORTIFY_SOURCE=0

于 2014-07-15T15:09:42.267 に答える
1

エクスプロイトについて学習している場合は、詳細を深く掘り下げる必要があります。

さあ、マシンコードを読んでください!Snow Leopard が使用しているチェック方法が何であれ、オーバーフローをすり抜ける方法を見つけることができるかもしれません。

問題もそれよりも簡単かもしれません。コンパイラが特定の順序でスタックに配置する必要がある、またはそれらをスタックに配置する必要buffer_oneがあるという規則はまったくありません。実際にはレジスタに収まることにbuffer_two注意してください。buffer_one

もちろん、ここではそうではありませんが、buffer_two が buffer_one の前に配置されていることがわかります。つまり、オーバーフローを に書き込んでbuffer_oneも には書き込まれないということbuffer_twoです。が含まれてしまう理由を説明することはできませんが''f8d0間違いなくメモリ内 f8e0あります。

于 2011-04-13T20:12:46.867 に答える