web-dev-qa-db-ja.com

単純なバッファオーバーフローに挿入される前に、シェルコードを「逆転」する必要がありますか?

バッファオーバーフローは初めてで、既成の簡単に実行できるスクリプトを使用する前に、自分が何をしているかを正確に理解しようとしています。私の目標はシェルをスポーンすることなので、execve("/bin/sh")を実行するasmコードを見つけました。シェルコードは Shell-storm から取得されます。 :

_char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
          "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
_

チュートリアルとスタックのドキュメントを調べたところ、スタックが下がっていくことがわかりました(データを追加すると、アドレスが上位から下位に)。

とりあえず、このシェルコードが_0x00_で始まるローカルバッファーに格納されているとしましょう。

  1. シェルコードをそのように保存しますか?

    _0x00 31 c0 50 68
    0x04 2f 2f 73 68 
    0x08 .....
    _

    スタックでは次のようになります。

    _0x08 .....
    0x04 2f 2f 73 68 
    0x00 31 c0 50 68
    _
  2. scanf("%d",myInt)が実行されると、_"9"_と_&myInt = 0x04_を指定すると、_0x04 : 09 00 00 00_になることを理解しています。 _"09"_がアドレス_0x04_にあることを意味します。アドレスが4の倍数で、これがリトルエンディアンである場合にのみアクセス可能であっても。

  3. scanfは_%s_とどのように連携しますか? charを4つずつ読み取って、最初の文字を(リトルエンディアン)の最も低い0x04アドレスに格納し、4番目を_0x07_に格納しますか?

だから本当の質問は:

通常、格納されたIPの代わりに表示したいアドレスを逆にすることを考慮してください。シェルコードについても同じようにする必要がありますか?スタックのIPレジスタを_0x01020304_で上書きする場合、通常はスタックに上向きに書き込むため、ペイロードに_\x04\03\02\01_を書き込みます。つまり、最初にアドレスの最下位ビットにヒットします。

2
Jiro chan
  1. いいえ、今日のほとんどのオペレーティングシステムはリトルエンディアンであり、文字列はメモリ内で逆になります。これを確認するには、32ビットで次のようなコードをコンパイルします。
    // test.c
    #include <stdio.h>

    int main(){
        char buf[8] = "ABCDEFGH";
        int n = 9;
        printf("%s\n", buf);
        printf("%d\n", n);
        return 0;
    }

と:

$ apt install gcc-multilib
$ gcc -g test.c -o test -m32

次に、gdbでバイナリを実行し、printfの実行前に中断して、スタックを確認します。

$ gdb ./test -q 
Reading symbols from ./test...done.
(gdb) disas main
Dump of assembler code for function main:
   [...]
   0x000011db <+50>:    sub    $0xc,%esp
   0x000011de <+53>:    lea    -0x14(%ebp),%eax
   0x000011e1 <+56>:    Push   %eax
   0x000011e2 <+57>:    call   0x1040 <puts@plt>
   [...]
End of assembler dump.
// printf is replaced by puts system call here

(gdb) b *main+57
Breakpoint 1 at 0x11e2: file test.c, line 6.

(gdb) r
Starting program: /root/test 

Breakpoint 1, 0x565561e2 in main () at test.c:6
6       printf("%s\n", buf);

(gdb) x/20x $esp
0xffffd280: 0xffffd294  0x56559000  0xffffd35c  0x565561c0
0xffffd290: 0x00000001  0x44434241  0x48474645  0x00000009
0xffffd2a0: 0xffffd2c0  0x00000000  0x00000000  0xf7deab41
0xffffd2b0: 0xf7faa000  0xf7faa000  0x00000000  0xf7deab41
0xffffd2c0: 0x00000001  0xffffd354  0xffffd35c  0xffffd2e4

'A'のASCIIコードは0x41、 'B' 0x42、...なので、2行目にABCDEFGHが次のように格納されていることがわかります。

0x94: DCBA
0x98: HGFE
0x9B: 0x00000009

文字列の終わりが上位アドレスにあることに注意してください。

  1. Scanfは同じ方法で入力文字列を格納します。
$ cat test.c

#include <stdio.h>

int main(){
    char input[8];
    scanf("%s\n", input);
    printf("%s\n", input);
    return 0;
}

$ gcc -g test.c -o test -m32
$ gdb ./test -q

(gdb) disas main
   [...]
   0x000011d4 <+43>:    call   0x1050 <__isoc99_scanf@plt>
   0x000011d9 <+48>:    add    $0x10,%esp
   0x000011dc <+51>:    sub    $0xc,%esp
   0x000011df <+54>:    lea    -0x10(%ebp),%eax
   0x000011e2 <+57>:    Push   %eax
   0x000011e3 <+58>:    call   0x1030 <puts@plt>
   [...]

(gdb) b * main+58
Breakpoint 1 at 0x11e3: file test.c, line 6.

(gdb) r < <(echo "ABCDEFG")
Starting program: /root/test < <(echo "ABCDEFG")

Breakpoint 1, 0x565561e3 in main () at test.c:6
6       printf("%s\n", input);
(gdb) x/20x $esp
0xffffd280: 0xffffd298  0xffffd298  0xffffd35c  0x565561c0
0xffffd290: 0x00000001  0xffffd354  0x44434241  0x00474645
0xffffd2a0: 0xffffd2c0  0x00000000  0x00000000  0xf7deab41
0xffffd2b0: 0xf7faa000  0xf7faa000  0x00000000  0xf7deab41
0xffffd2c0: 0x00000001  0xffffd354  0xffffd35c  0xffffd2e4
(gdb) 

  1. そのため、メモリに正しいアドレスを設定するには、入力文字列を介してeipに入れたいアドレスを逆にする必要があります(例:0xdeadbeefは "\ xef\xbe\xad\de"になります)。

シェルコードは、メモリに格納する一連のアセンブリ命令であり、次の文字列は入力の準備ができているため、変更する必要はありません。

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

入力後、コードセグメントのように、メモリ内に一連のオペコードがあります。
スタックは大きくなりますが、文字列は「通常どおりに」成長し(「ABCDEFGH」を覚えておいてください)、eipは各命令の後にインクリメントされるため、文字列内の命令の順序に従います。

Disassembly:
0:  31 c0                   xor    eax,eax
2:  50                      Push   eax

String Literal:
"\x31\xC0\x50"

リトルエンディアンについて心配する必要はありません。これは、データの保存方法に過ぎず、アドレスを作成するときに重要です(保存されたeipの上書きなど)。

0
SunSun