12

バッファ オーバーフローを実験しており、fgets の特定の入力でスタックのリターン アドレスを上書きしようとしています。

これはコードです:

void foo()
{
    fprintf(stderr, "You did it.\n");
}

void bar()
{
    char buf[20];
    puts("Input:");
    fgets(buf, 24, stdin);
    printf("Your input:.\n", strlen(buf));
}


int main(int argc, char **argv)
{
    bar();
    return 0;
}

通常の実行では、プログラムは入力を返すだけです。コードを変更せずに foo() を出力したい。

buf私のアイデアは、 20秒を入力してのバッファをオーバーフローさせることでした'A'。これは機能し、セグメンテーション違反を引き起こします。私の次のアイデアは、のアドレスを見つけて、foo()これ\x4006cdを 20 に追加すること'A'でした。

私の理解では、これはスタックの戻りアドレスを上書きし、にジャンプするはずfooです。しかし、それはセグメンテーション違反を引き起こすだけです。

私は何を間違っていますか?

更新: アセンブラーは main をダンプします

    Dump of assembler code for function main:
   0x000000000040073b <+0>: push   %rbp
   0x000000000040073c <+1>: mov    %rsp,%rbp
   0x000000000040073f <+4>: sub    $0x10,%rsp
   0x0000000000400743 <+8>: mov    %edi,-0x4(%rbp)
   0x0000000000400746 <+11>:    mov    %rsi,-0x10(%rbp)
   0x000000000040074a <+15>:    mov    $0x0,%eax
   0x000000000040074f <+20>:    callq  0x4006f1 <bar>
   0x0000000000400754 <+25>:    mov    $0x0,%eax
   0x0000000000400759 <+30>:    leaveq 
   0x000000000040075a <+31>:    retq   
   End of assembler dump.

ふー

Dump of assembler code for function foo:
   0x00000000004006cd <+0>: push   %rbp
   0x00000000004006ce <+1>: mov    %rsp,%rbp
   0x00000000004006d1 <+4>: mov    0x200990(%rip),%rax        # 0x601068 <stderr@@GLIBC_2.2.5>
   0x00000000004006d8 <+11>:    mov    %rax,%rcx
   0x00000000004006db <+14>:    mov    $0x15,%edx
   0x00000000004006e0 <+19>:    mov    $0x1,%esi
   0x00000000004006e5 <+24>:    mov    $0x400804,%edi
   0x00000000004006ea <+29>:    callq  0x4005d0 <fwrite@plt>
   0x00000000004006ef <+34>:    pop    %rbp
   0x00000000004006f0 <+35>:    retq   
End of assembler dump.

バー:

Dump of assembler code for function bar:
   0x00000000004006f1 <+0>: push   %rbp
   0x00000000004006f2 <+1>: mov    %rsp,%rbp
   0x00000000004006f5 <+4>: sub    $0x20,%rsp
   0x00000000004006f9 <+8>: mov    $0x40081a,%edi
   0x00000000004006fe <+13>:    callq  0x400570 <puts@plt>
   0x0000000000400703 <+18>:    mov    0x200956(%rip),%rdx        # 0x601060 <stdin@@GLIBC_2.2.5>
   0x000000000040070a <+25>:    lea    -0x20(%rbp),%rax
   0x000000000040070e <+29>:    mov    $0x18,%esi
   0x0000000000400713 <+34>:    mov    %rax,%rdi
   0x0000000000400716 <+37>:    callq  0x4005b0 <fgets@plt>
   0x000000000040071b <+42>:    lea    -0x20(%rbp),%rax
   0x000000000040071f <+46>:    mov    %rax,%rdi
   0x0000000000400722 <+49>:    callq  0x400580 <strlen@plt>
   0x0000000000400727 <+54>:    mov    %rax,%rsi
   0x000000000040072a <+57>:    mov    $0x400821,%edi
   0x000000000040072f <+62>:    mov    $0x0,%eax
   0x0000000000400734 <+67>:    callq  0x400590 <printf@plt>
   0x0000000000400739 <+72>:    leaveq 
   0x000000000040073a <+73>:    retq   
End of assembler dump.
4

2 に答える 2

3

あなたはメモリアラインメントで数えませんでした。正しい場所を見つけやすくするために、コードを少し変更しました。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int **x;
int z;

void foo()
{
    fprintf(stderr, "You did it.\n");
}

void bar()
{
    char buf[2];
    //puts("Input:");
    //fgets(buf, 70, stdin);
    x = (int**) buf;
    for(z=0;z<8;z++)
            printf("%d X=%x\n", z, *(x+z));
    *(x+3) = foo;
    printf("Your input: %d %s\n", strlen(buf), buf);
}


int main(int argc, char **argv)
{
        printf("Foo: %x\n", foo);
        printf("Main: %x\n", main);
        bar();
        return 0;
}

私の例では2という小さなバッファを使用すると、リターンアドレスがバッファの先頭から24バイト離れていることがわかりました(x + 3、8バイトポインタの場合、64ビット、デバッグなし、最適化なし...)。この位置は、バッファ サイズ、アーキテクチャなどによって変わる可能性があります。この例では、bar のリターン アドレスを foo に変更することができます。とにかく、メインに戻るように適切に設定されていないため、foo の戻り時にセグメンテーション違反が発生します。

バーのスタックサイズを変更しないように、x と z をグローバル変数として追加しました。このコードは、buf[0] から始まるポインターのような値の配列を表示します。私の場合、アドレスは main の 3 の位置にありました。そのため、最終的なコードには *(x+3) = foo が含まれています。前述したように、この位置はコンパイル オプションやマシンなどによって変わる可能性があります。正しい位置を見つけるには、アドレス リストで main (bar を呼び出す前に表示される) のアドレスを見つけます。

メインのアドレスではなく、メインのアドレスと言ったことに注意することが重要です。これは、リターンアドレスがメインの先頭ではなく、bar の呼び出しの後の行に設定されているためです。したがって、私の場合は、0x400668 ではなく 0x4006af でした。

あなたの例では、20 バイトのバッファーを使用して、私の知る限り、32 バイト (0x20) に配置されていました。

fgets で同じことをしたい場合は、foo のアドレスを入力する方法を理解する必要がありますが、x86/x64 マシンを実行している場合は、リトルエンディアンで追加することを忘れないでください。バイトごとに値を表示するようにコードを変更すると、値を正しい順序で取得し、ALT + 数字を使用して入力できます。ALT を押しながら入力する数字は 10 進数であることに注意してください。一部の端末は、0x00 を扱いにくい場合があります。

私の出力は次のようになります。

$ gcc test.c -o test
test.c: In function ‘bar’:
test.c:21: warning: assignment from incompatible pointer type
$ ./test
Foo: 400594
Main: 400668
0 X=9560e9f0
1 X=95821188
2 X=889350f0
3 X=4006af
4 X=889351d8
5 X=0
6 X=0
7 X=95a1ed1d
Your input: 5 ▒▒`▒9
You did it.
Segmentation fault
于 2013-12-19T08:59:24.903 に答える