Debian i386 VMで単純なバッファオーバーフローの例をコンパイルしようとしています。実行可能ファイルは正常にビルドされますが、バッファーをオーバーフローさせ、スタックにプッシュされたEIPを上書きするのに十分な大きさの入力が提供されても、EIPは正しく上書きされません。これがルートとしての私のコマンドです:
sysctl -w kernel.randomize_va_space=0
gcc test.c -z execstack -z norelro -fno-stack-protector -D_FORTIFY_SOURCE=0 -ggdb -o test
あなたが見ることができるように私は持っています:
ここにtest.cがあります:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[128];
if(argc < 2) return 1;
strcpy(buf, argv[1]);
printf("%s\n", buf);
return 0;
}
私は、128〜256の長さのクラッシュ文字列(すべてAsのみ)を試しました。 gdbでは、プッシュされたEIPが上書きされることはありません。他のローカルスタック変数はないため、EIPを上書きするには、実際には132バイトの文字列にする必要があります。
コンパイル時に無効にする必要がある他の保護機能はありますか?または、sysctlで無効にする何か他のものはありますか?
あなたの例はここでsegfaultを実行します(または、スタックプロテクターがアクティブな場合はプログラムを終了させます)。
おそらく、バッファオーバーフローを別の関数に移動する必要があります。おそらくあなたのgccバージョンは_return 0;
_をmainからexit(0);
に変換しています。
受け入れられた答えは、すべてを別の関数に移動することを提案することで正しいですが、理由は説明されません。
この質問と回答 を見るとわかるように、main()
関数は、多くの場合、スタックが適切に設定されていることを確認するためにスタックアライメントを実行します。次の指示が実行されます。
_83 e4 f0 and esp, 0xfffffff0
_
これにより、スタックが8バイトにアラインされます。これは、減算演算がesp
で実行されたことを意味します。したがって、戻りアドレスをオーバーフローさせるには、_128 bytes + <variable stack alignment> + <optional saved ebp> + <new_return_address>
_が必要です。
プログラムをコンパイルして実行しました(_-m32
_を追加する必要がありました)。
_Starting program: /home/user/Private/misc/overflow_test
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFaaaabbbbcccc
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFaaaabbbbcccc
Program received signal SIGSEGV, Segmentation fault.
0x63636363 in ?? ()
_
私は128 Asを使用し、次に新しい文字を4つ使用しました。
プログラムを逆コンパイルすると、次の行が表示されます。
_; .//overflow_test.c:4
;-- main:
;-- sym.main:
0x0804842d 55 Push ebp
0x0804842e 89e5 mov ebp, esp
0x08048430 83e4f0 and esp, 0xfffffff0
0x08048433 81ec90000000 sub esp, 0x90
_
ここでは、0x90(144)がスタックから差し引かれていることがわかります。つまり、128バイトを超えるスペースがローカル変数に割り当てられます。 このSO answer は、これが発生する理由をいくつか示します。128バイトを考慮すると、16ローカル変数用に追加されたバイト、および8バイトのスタックアライメントを想定:次に、EBPを_0x62626262
_で上書きし、戻りアドレスを_0x63636363
_で上書きします。
通常、通常の関数は、スタックを調整するこのステップを実行しません。そのため、別の関数に移動すると、すべてが期待どおりに機能しました。実際の悪用では、main()
に入るパラメーターを悪用する可能性はほとんどありません。別の関数のスコープで実行する方が現実的です。