ポストの最後にある両方のプログラムのソースコード
それで、私はHacking:The Art Of Exploitationに取り組んできました。脆弱なプログラムnotesearch.cでEIPを制御できました。
gdb-peda$ run $(Perl -e 'print "a"x112 . "bbbb"')
Starting program: /root/hacking/booksrc/notesearch $(Perl -e 'print "a"x112 . "bbbb"')
[DEBUG] found a 5 byte note for user id 0
[DEBUG] found a 7 byte note for user id 0
-------[ end of note data ]-------
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers---------------------------------- -]
EAX: 0x0
EBX: 0x0
ECX: 0xbffff300 ('a' <repeats 36 times>, "\003")
EDX: 0x0
ESI: 0x2
EDI: 0xb7fb5000 --> 0x1b3db0
EBP: 0x0
ESP: 0xbffff300 ('a' <repeats 36 times>, "\003")
EIP: 0x61616161 ('aaaa')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code------------------------------------ -]
Invalid $PC address: 0x61616161
[------------------------------------stack------------------------------------ -]
0000| 0xbffff300 ('a' <repeats 36 times>, "\003")
0004| 0xbffff304 ('a' <repeats 32 times>, "\003")
0008| 0xbffff308 ('a' <repeats 28 times>, "\003")
0012| 0xbffff30c ('a' <repeats 24 times>, "\003")
0016| 0xbffff310 ('a' <repeats 20 times>, "\003")
0020| 0xbffff314 ('a' <repeats 16 times>, "\003")
0024| 0xbffff318 ('a' <repeats 12 times>, "\003")
0028| 0xbffff31c ("aaaaaaaa\003")
[----------------------------------------------------------------------------- -]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x61616161 in ?? ()
gdb-peda$
しかし、私が自分の非常に単純なバグのあるコードを書いてEIPを制御しようとすると、これは起こります
gdb-peda$ run
Starting program: /root/vulnerable
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbbbb
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0x41414141 ('AAAA')
EDX: 0xb7fb687c --> 0x0
ESI: 0x1
EDI: 0xb7fb5000 --> 0x1b3db0
EBP: 0x41414141 ('AAAA')
ESP: 0x4141413d ('=AAA')
EIP: 0x804841d (<main+50>: ret)
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048416 <main+43>: mov ecx,DWORD PTR [ebp-0x4]
0x8048419 <main+46>: leave
0x804841a <main+47>: lea esp,[ecx-0x4]
=> 0x804841d <main+50>: ret
0x804841e: xchg ax,ax
0x8048420 <__libc_csu_init>: Push ebp
0x8048421 <__libc_csu_init+1>: Push edi
0x8048422 <__libc_csu_init+2>: Push esi
[------------------------------------stack-------------------------------------]
Invalid $SP address: 0x4141413d
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0804841d in main ()
gdb-peda$ p/x $eip
$1 = 0x804841d
gdb-peda$
何も取得しません。ESPは変更されないまま(スタックに格納されていないため)で、EIPは上書きされませんか?
あなたはnotesearch.c @ https://github.com/intere/hacking を見つけることができます以下は私の疑わしい「悪用可能な」プログラムです。
言うまでもなく、ASLRを無効にしており、プログラムは-fno-stack-protectorフラグと-zexecstackフラグを使用してコンパイルされています。さらに情報が必要な場合は、コメントをドロップしてください。
#include <stdio.h>
int main(){
char *buffer[64];
gets(buffer);
return 0;
}
あなたのコードは間違っています:
#include <stdio.h>
int main(){
char *buffer[64];
gets(buffer);
return 0;
}
char *buffer[64]
は、charsへのポインタの配列を作成します。
これでもう一度試してください:
#include <stdio.h>
int main(){
char buffer[64];
gets(buffer);
return 0;
}
実際、これがうまくいかなかった理由はかなりあります。アセンブリをたどってみましょう:
0x8048416 <main+43>: mov ecx,DWORD PTR [ebp-0x4]
ebp-0x4
は入力バッファー内の場所なので、ecx
には0x41414141
がロードされます。
0x8048419 <main+46>: leave
これにより、現在のスタックフレームがクリアされ、基本的にesp
がebp
に設定されます。次に、スタックから最上位の値をebp
にポップして、前のフレームポインターを復元します。スタックの最上位のアイテムがまだバッファの一部であったため、ebp
が0x41414141
に設定されたことが再びわかります。
0x804841a <main+47>: lea esp,[ecx-0x4]
これにより、ecx-0x4
の実効アドレスがesp
に読み込まれます。 lea
は、便利なポインタ演算命令のように考えてください。この場合、文字どおりesp = ecx - 0x4
を実行しています。これがesp
値が0x4141413d
である理由です。
=> 0x804841d <main+50>: ret
ret
命令は、eip
をスタックの最上位の値に設定します。ここでの問題は、スタックポインターが無効なメモリに設定されていることです。 0x4141413d
で上書きしました。
なぜこれが起こったのですか?ペイロードは最初はほぼ確実に長すぎますが、コードの動作とサンプルコードの動作にはアセンブリに違いがあります。ただし、銀色の裏地があります。ret
命令の直前にスタックポインターを制御できます。つまり、esp
を制御するメモリをポイントするだけで、eip
を制御できます。
やりたいことは、<main+43>
に到達するまでコードを実行し、ペイロードのワークアウトオフセットがebp+0x4
に配置されていることです。 Metasploitのpattern_create.rbなどのツールを使用すると、これを少し簡単にすることができますが、スタック内の値を監視するだけで、手動で計算できます。
したがって、ペイロードの80バイトが、アプリケーションがその値を読み取る場所であると想定しましょう。ペイロードが'A' * 80
で始まるようになりました。この後、スタックのアドレスから8バイトを引いた値をペイロードにエンコードします。したがって、その時点でのスタックポインターが0x4120EC40の場合、8を減算して0x4120EC38を取得し、\x41\x20\xEC\x38
をすべてのA
値の後にペイロードに配置します。
次にプログラムを実行すると、次のようになります。
0x8048416 <main+43>: mov ecx,DWORD PTR [ebp-0x4]
; [ebp-0x4] contains an address slightly further up the stack (0x4120EC38)
; all the memory around this address contains 0x41414141
0x8048419 <main+46>: leave
; leave the stack frame, setting esp and popping ebp
; both registers are trashed but it doesn't matter
0x804841a <main+47>: lea esp,[ecx-0x4]
; subtract 4 from 0x4120EC38, giving us 0x4120EC34
; then set esp to this value - [esp] is now 0x41414141
0x804841d <main+50>: ret
; pop the top value off the stack and set eip to it
; eip is now 0x41414141
あなたが勝ちます。
補足として、スタックアドレスをペイロードにエンコードできないという問題が発生する可能性があります。これは、ヌルバイトが含まれるためです。ペイロードを短くしてスタックフレームポインターを部分的に上書きすることでこれを回避できます。したがって、最下位バイト(または2バイト)を変更して、esp
を部分的に制御し、スタックをさらに上に向けることができます。完全に上書きするのではなく、.