web-dev-qa-db-ja.com

EIPレジスタを上書きできません

ポストの最後にある両方のプログラムのソースコード

それで、私は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;
}
8
user3006498

あなたのコードは間違っています:

#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;
}

IDEoneで実行


実際、これがうまくいかなかった理由はかなりあります。アセンブリをたどってみましょう:

   0x8048416 <main+43>: mov    ecx,DWORD PTR [ebp-0x4]

ebp-0x4は入力バッファー内の場所なので、ecxには0x41414141がロードされます。

   0x8048419 <main+46>: leave  

これにより、現在のスタックフレームがクリアされ、基本的にespebpに設定されます。次に、スタックから最上位の値をebpにポップして、前のフレームポインターを復元します。スタックの最上位のアイテムがまだバッファの一部であったため、ebp0x41414141に設定されたことが再びわかります。

   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を部分的に制御し、スタックをさらに上に向けることができます。完全に上書きするのではなく、.

7
Polynomial