CSAW CTF Qualification Round 2016 に少しだけ参加したのでwrite-up.自分の都合が合わずチームでの参加ができませんでした.というか他メンバーも都合が合わない人が多かったためチーム自体参加していないです.しかし、全く解かずにwrite-upを見るよりも少しは問題を見た方が良いと思い,一人でちょっとだけ解きました.
(2016/09/29) miscの2問のプログラムちょっと直しました.
手を付けた問題(チラ見含む)
- Notesy 2.0(test submit)
- Sleeping guard (crypto 50)
- Coinslot (misc 25)
- Regexpire (misc 100)
- Neo (crypto 200)
- Gametime(rev 50)
解けた問題
- Notesy 2.0 (test submit)
- Coinslot (misc 25)
- Regexpire (misc 100)
- Gametime (rev 50)
cryptoの問題が解けず落ち込んでmiscやってました…
Write-up
Notesy 2.0 (test submit)
フラグ送信テストです.
The flag is abcdefghijklmnopqrstuvwxyz
Gametime (rev 50)
チート系かと思ったが,普通にゲームにクリアすればフラグが貰えた.
The flag is no5c30416d6cf52638460377995c6a8cf5
Coinslot (misc 25)
nc misc.chal.csaw.io 8000
プログラミング初級的な授業でやりそうなやつ.金額が指定されて,その金額を満たす最小の紙幣と硬貨の数を求める(28400円は,10000円札2枚,5000円札1枚,1000円札3枚,500円玉0枚,100円玉4枚,以下略).
1行目に金額が送られてきて,2行目以降に金額の大きい紙幣から順に枚数を入力.pennies (1c)まで入力し終えると正誤判定され,間違っていれば以下のようにincorrectとなる(この場合は,5cの時点でincorrectとなり,答えは1なのに0になってるよと言われる).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ nc misc.chal.csaw.io 8000 $0.05 $10,000 bills: 0 $5,000 bills: 0 $1,000 bills: 0 $500 bills: 0 $100 bills: 0 $50 bills: 0 $20 bills: 0 $10 bills: 0 $5 bills: 0 $1 bills: 0 half-dollars (50c): 0 0quarters (25c): 0 dimes (10c): 0nickels (5c): pennies (1c): 0 Incorrect number of nickels (5c): expected 1, found 0 |
これを計算して送るだけのプログラムを書けば良いのです.書けばよいのですが,400ラウンドあり中々終わらなかった.(先日勉強会で解き直したら300ラウンドでした.400という数字はどこから出てきたのか…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
from m1z0r3.crypro import * def main(): # ==== template ==== remoteip = "misc.chal.csaw.io" remoteport = 8000 s,f = sock(remoteip,remoteport) # ==== local variable ==== dollars_list = [1000000,500000,100000,50000,10000,5000,2000,1000,500,100,50,25,10,5,1] stage = 0 # ==== Start game! ==== while True: stage += 1 print "[+] STAGE",stage price = read_until(f).strip() print price price = int(round(float(price.strip("$"))*100)) print "[+]",price for x in dollars_list: data = read_until(f,":").strip() print data, ans = str(int(price / x)) price = price % x s.send(ans+"\n") print ans print read_until(f,"!") print read_until(f) # [+] STAGE 401 # flag{started-from-the-bottom-now-my-whole-team-fucking-here} f.close() s.close() if __name__ == '__main__': main() |
100ラウンドごとに金額の桁が上がっていき,300ラウンド目くらいから10,000ドル紙幣を使うくらいになる.
The flag is flag{started-from-the-bottom-now-my-whole-team-fucking-here}
(追記)
直観的には上のプログラムが分かりやすいですが,答えのsendと$10.000: $5.000…の部分のrecvをまとめて行った方が高速に処理ができるのでそうした方が良さそうです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from m1z0r3.crypro import * def main(): # ==== template ==== remoteip = "misc.chal.csaw.io" remoteport = 8000 s,f = sock(remoteip,remoteport) # ==== local variable ==== dollars_list = [1000000,500000,100000,50000,10000,5000,2000,1000,500,100,50,25,10,5,1] stage = 0 # ==== Start game! ==== while True: stage += 1 print "[+] STAGE",stage price = read_until(f).strip() print price price = int(round(float(price.strip("$"))*100)) print "[+]",price ans = "" for x in dollars_list: ans += str(int(price / x))+"\n" price = price % x print ans s.send(ans) print read_until(f,"!") print read_until(f) # [+] STAGE 401 # flag{started-from-the-bottom-now-my-whole-team-fucking-here} f.close() s.close() if __name__ == '__main__': main() |
Regexpire (misc 100)
nc misc.chal.csaw.io 8001
Regexpireは正規表現のこと.繋ぐと”Can you match these regexes?”という文章の後,正規表現が送られてくる.
1 2 3 |
$ nc misc.chal.csaw.io 8001 Can you match these regexes? (bernie|giraffe)(table|phone)[a-z]+[PAku]+9*(apple|dolphin)*[b1al1zt]+[CjwWw2]n+[SD]{2}\we+ |
送られてきた正規表現にマッチする文字列を送れば良い.マッチすれば何でも良い.正規表現弱者だったので調べて,ごり押しで解けそうなコードを書きました.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
from m1z0r3.crypro import * def regex(mystr): index = 0 ans = "" while True: # print ans if index >= len(mystr): break if mystr[index]=="(": tmp = "" for i in xrange(index,len(mystr)): if mystr[i]==")": ans += "---" ans += tmp.split("|")[-1] index = i+1 break tmp += mystr[i] elif mystr[index]=="[": while mystr[index+1]=="\\": index+=2 ans += "---" ans += mystr[index+1] for i in xrange(index,len(mystr)): if mystr[i]=="]": index = i+1 break elif mystr[index]=="\\": index += 1 if mystr[index]=="w": # [a-zA-Z0-9] ans += "---" ans += "a" elif mystr[index]=="W": # [^\w] ans += "---" ans += ":" elif mystr[index]=="d": #[0-9] ans += "---" ans += "1" elif mystr[index]=="D": #[^\d] ans += "---" ans += "a" elif mystr[index]=="s": pass elif mystr[index]=="S": pass index += 1 elif mystr[index] == "*" or mystr[index]=="+": index += 1 elif mystr[index] == "{": tmp = "" for i in xrange(index+1,len(mystr)): if mystr[i]=="}": break tmp += mystr[i] index = i iterate = int(tmp)-1 tmp = ans.split("---")[-1]*iterate ans += "---" ans += tmp index+=1 else: ans += "---" ans += mystr[index] index += 1 return "".join(ans.split("---")) def main(): remoteip = "misc.chal.csaw.io" rempteport = 8001 s,f = sock(remoteip,rempteport) stage = 0 # ==== Connection Start ==== print read_until(f).strip() # Can you match these regexes? # ==== Searching string to match Regexpire ==== while True: stage +=1 print "[+] STAGE",stage data = read_until(f).strip() print data ans = regex(data) s.send(ans+"\n") print "[+] ans is [",ans,"]" s.close() f.close() if __name__ == '__main__': main() |
以下のようにひたすら正規表現にマッチする文字列を送る.
1000ラウンドありましたが,一つあたりの時間が短いので,比較的すぐにフラグが出ました.
The Flag is flag{^regularly_express_yourself$}
(追記)
メンバーの先輩が既存のモジュールを発見いたしましたことをご報告いたします...orz
rstrというモジュールのxeger関数は,引数に正規表現を与えて,それにマッチする文字列を返してくれます(この問題やんけ…).
ただ,戻り値には途中に改行を含む場合があります.本問題にて,改行はsendする際に邪魔になりますので,改行が出なくなるまで繰り返し実行するという処理が必要になるそうです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# -*-coding:utf-8-*- from m1z0r3.crypro import * import rstr # It's shooort!! def regex(mystr): while True: ans = rstr.xeger(mystr) if "\n" not in ans: return ans def main(): remoteip = "misc.chal.csaw.io" rempteport = 8001 s,f = sock(remoteip,rempteport) stage = 0 # ==== Connection Start ==== print read_until(f).strip() # Can you match these regexes? # ==== Searching string to match Regexpire ==== while True: stage +=1 print "\n[+] STAGE",stage data = read_until(f).strip() print data ans = regex(data) s.send(ans+"\n") print "[+] ans is [",ans,"]" s.close() f.close() if __name__ == '__main__': main() |
感想
miscのようなプログラミング問題は好きですが,セキュリティとはほとんど関係がないのでもっと別の問題解けるようにしたいと感じました.
一応チームではCrypto担当なのに解けず….問題は見たので強者様のWrite-upを拝見させていただこうと思います.
余談
miscで書いたプログラム,最後はエラーで止まります←