なぜこれが起こっているのかの鍵は、がメモリ内の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:要するに、バッファをオーバーフローさせ、保存されたポインタとリターン アドレスをガベージで上書きしています。