9

現在、この関数を使用するターゲット プログラムを悪用するシェルコードを作成していputsます。プログラムは次のようになります。

#include <stdio.h>
main() {
    char buf[123];
    puts(gets(buf));
}

私がやりたいことは、このバッファをオーバーフローさせexecve、いくつかの引数で呼び出すことです。execveいくつかの引数を指定して呼び出すことができる c/inline アセンブリで記述されたテスト プログラムがありgdb、このプログラムからシェルコードを取得するために使用します。私の理解では、スタック レイアウトは次のようになります。

|------buffer(+padding)---------|---------sfp--------|-------戻る-------------|

gcc によって生成されたターゲット プログラムのアセンブリ コードの一部を見ると、次のようになります。

.cfi_startproc                  
pushq   %rbp                    
.cfi_def_cfa_offset 16          
.cfi_offset 6, -16              
movq    %rsp, %rbp              
.cfi_def_cfa_register 6         
addq    $-128, %rsp             
leaq    -128(%rbp), %rax        
movq    %rax, %rdi              
call    gets                    
movq    %rax, %rdi              
call    puts                    
leave                           
.cfi_def_cfa 7, 8               
ret                             
.cfi_endproc       

バッファとパディングは128バイト、sfp と戻りアドレスはそれぞれ8バイトかかるので、合計で144バイトになると思います。これに基づいて、私の nop sled 、ペイロード、および新しい戻りアドレス (バッファーのアドレスに等しい) を組み合わせた (つまり、私のシェルコード) も144バイトになるはずです。たとえば、ペイロードが 36 バイトの場合、戻りアドレスが 8 バイトを占めるため、nop スレッドは 100 バイトになります。しかし、私がそのようにしたとき、それはうまくいきませんでした。スタック レイアウトの理解の仕方が間違っていたのではないかと思います。違いますか?

私の場合、バッファアドレスがわかっていることに注意してexecstackくださいsetarch。したがって、戻りアドレスがバッファーのアドレスによって上書きされた場合、そのバッファーに書き込まれたコードが実行されます。

そして、私はx86 64ビットマシンで作業しています。

スタック レイアウトに関する私の理解が正しければ、シェルコードに関する詳細情報を掲載します。

4

2 に答える 2

1

ルーチンが開始されると、スタック上に戻りアドレスがあります。の値を想定してrsp = 0x428、実際の (ただし任意に選択された) 数値を確認できるようにします。

0x428 return addr

次に、プッシュしますrbp

0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)

次に、123 バイト + パディング = のバッファーを割り当てます。0x80

0x428 return addr (8 bytes)
0x420 original rbp (8 bytes)
0x418 last eight bytes of buffer+padding 
  ...
0x3a0 buffer (128 bytes)

したがって、0x428 でリターン アドレスを破壊することを期待している場合、応答はgets()合計 144 バイトの長さである必要があり、最後の 8 バイトはシェルコードによって別の場所 (通常はバッファー領域自体) を指すように変更されます。

スタック ダイアグラムのsfpが「保存されたフレーム ポインター」(または として知られているrbp) を意味すると仮定すると、はい、あなたの理解は正しいです。 うまくいかない場合は、シェルコードに問題がある可能性があります。(そして、それはおそらく別の質問になるでしょう。)

于 2014-01-31T18:15:31.297 に答える
1

1) 脆弱なコードを悪用しているのは関数があるからではなく、関数puts()を使用しているため悪用しているのでありgets()、ここではスタック オーバーフローに対して脆弱です。

2) char があるbuf[123]場合、122 文字を入力してから null ターミネータを 1 つ入力すると、スタックは問題ありません。しかし、それ以上入力すると、次のようになります。

gets() を実行するとき、それが buf[4] であると想像してみましょう。

input AAAA
EBP - 4 => will be AAAA

input AAAAAAAA (8 bytes)
EBP -4 => AAAA
EBP also => AAAA

if you enter 12x A
function return address will be 0x41414141

関数の戻りアドレスも上書きするので、これも AAAA 0x41414141 になります。そこから、シェルコードを実行するために、戻りアドレスをシェルコード アドレスにポイントする必要があります。

したがって、関数を呼び出してオーバーフローする場合、レイアウトは次のようになります。

Buffer for temporary storage
local variables
The saved EBP
Function return address
Function's arguments
Stack frame

なので下から上です。実際、大きな変数の場合は metasploit を使用することをお勧めしpattern_offset.rbます。これは大きな文字列を生成し、EIP 値を見つけたら、patter_offset.rbの出力を使用して、シェルコードを実行するために EIP を上書きするために必要な正確なパディングを検出できます。

したがって、実際に関数の戻りアドレスを上書きするには、ほとんどの場合 [変数サイズ] + 8 が必要です。ただし、ローカル変数、それらのサイズ、それらの順序などに依存します。また、コンパイラ、アーキテクチャなどにも依存します。ほとんどの場合、試してみることで行われます。および pattern_offset.rb など。

于 2014-01-31T17:51:25.487 に答える