この大会は2019/4/25 11:00(JST)~2019/5/2 11:00(JST)に開催されました。
今回もチームで参戦。結果は5076点で363チーム中3位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity check(0x00)
問題にフラグが書いてあった。
CSACTF{w3lc0m3_t0_csa_ctf_2019}
Zippy (Forensics)
zipのヘッダ4バイトが壊れているので、修正する。
(誤) 00 00 00 00 (正) 50 4b 03 04
展開すると、flag.txtにフラグが書いてあった。
CSACTF{z1ppy_z1p_z1p}
Down to basic (Crypto)
フラグの先頭がCSACTF{となることからXOR鍵を算出し、さらに推測して、復号する。
c = '\x13\x13eg#v\t\x05\x0f#HE\x04CC\x07\x0f0V\x14\x15\\\x17\t\x0f2AU\x02\x01\x00\x01#\x1fE{\x14\\\x13\x17#qG{\x04\x00\x1e\x11$q\x14J\n' pre_flag = 'CSACTF{' key = '' for i in range(len(pre_flag)): code = ord(c[i]) ^ ord(pre_flag[i]) key += chr(code) print key ## guess key ## key += 'd' print key flag = '' for i in range(len(c)): code = ord(c[i]) ^ ord(key[i%len(key)]) flag += chr(code) print flag
これでXOR鍵はP@$$w0rdであることがわかり、復号できた。
CSACTF{a_class1c_pr0blem_requ1res_a_class1c_s0lut10n}
Flag server (Crypto)
サーバ処理の概要は以下の通り。
・入力データはBase64 ・Base64デコードして以下の文字列にする。 rowdy123 + <入力> ++ ・AES暗号化してBase64エンコードして表示
文字列の長さを変えて試すと、10文字にしたとき、80バイトになったので、フラグの長さは46バイトであることもわかり、以下のようなイメージになる。
0123456789abcdef rowdy123XXXXXXXX XXFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF PPPPPPPPPPPPPPPP
0123456789abcdef rowdy123XXXXXXXX XXXXXXXXXXXXXXXC XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFPPP
上記のようなイメージで、1文字ずつフラグをはみ出させ、2ブロック目と5ブロック目が一致するものを探すことを繰り返し、フラグを割り出す。
import socket from base64 import * def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) flag = '' for i in range(46): for c in range(32, 127): print '*** flag: %s ***' % flag print c, chr(c) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('34.74.132.34', 1337)) data = recvuntil(s, '\n').rstrip() #print data if i < 16: text = 'X' * 8 + 'X' * (15 - (i % 16)) + flag + chr(c) \ + 'X' * (47 - len(flag)) print text else: text = 'X' * 8 + flag[i-15:i] + chr(c) \ + 'X' * (47 - len(flag)) print text text = b64encode(text) #print text s.sendall(text + '\n') size = len('rowdy123') + 71 - i + 46 size = size + 16 - size % 16 data = recvuntil(s, '\n').rstrip() #print data b64 = '' for j in range(size * 4 / (3 * 76) + 1): data = recvuntil(s, '\n').rstrip() #print data b64 += data enc = b64decode(b64) block1 = enc[16*1:16*2] block4 = enc[16*4:16*5] if block1 == block4: flag += chr(c) break print flag
CSACTF{ser10usly_1_d0nt_kn0w_what_t0_put_here}
Shakespeare (Crypto)
eの値が小さく、平文の上位bitの大半がわかっている。このことから、以下のようなスクリプトで復号することができる。
# solve.sage n = 1006836100850250538339995467613886290568442651407025704748377068277336663840322807255631910003671238333129747821401677091458273063019582521876226866878065342813920204554381690287329117261946553416320027957955605766622398073392496765842697469834794925876050925562024075006141634323253929247109497487662672405697258396584137509321741864516634651999799054786449300271622909227600883634147669010834991795161523697131426220251208433781244773539764161842505052231971382919967824210675662840663441620651420266255868914927411521784353964212033587445690357323252291991393014956932340980364269027698921907294758481600334348209162447060613941156368610501127037691522939020923578832525761118629677247463329559736428451094014067976185200082383567782696893487896242261214414477694719976590065452677776148568775926581762818443660878148364426201543151986222827020715235149143660874147096198623408316970919847284217769846103787437353010406070576416824522568438694050123611781945190356349091784143598551629255439066014442622323403606886940586598393546395396681244923344136771626115764972416530481118005853215560607832082875977841855163209032040736208394910518736188520737990966314657947406636362327107362260153133199038686467983874712019134795287956681 e = 5 c = 86369436556821828495369673854162831950166653329442831115191625341047835876798749659300641169348489814397657938758507056245452483528302648820602222051650089244062216320589929210206763500165600876025118953025309329553936210852805389539140958630003420335312979086383557665369163943903938569533466433527007050293325014896256907213847510652960137337747717702046541551623527661916693185081633247755853469488660176161381955671005765896319088865538114135666529015146105463393845863266076934785270480138522718706320412272484373838264459071202162126712065303703239606240172920462289657145390815683884595752646574944533803755632028037260059034716445191212182713792979655078091574530502495357406706909373119165806528380721574497499485062395281364120812874772053936952075952742036541816610742884797177390325939200184871385346082698051189479559236698554746173964482593646082680562341702105913029374921266420784671717205191676584949585227373190247242357902713051455202286347791244123645109240863246131183709864782892090906846140511095936549733130467184197878019539182792149521284353941031177248258779486351865884845751206163429977363088579292444338819912920250044064118502755440223212479004336304705210529515747947096857605944524561963438120052349 beta = 1 epsilon = beta^2/7 nbits = n.nbits() kbits = floor(nbits*(beta^2/e-epsilon)) with open('msg.txt', 'r') as f: m0 = int(f.read().rstrip().replace('X', '\x00').encode('hex'), 16) def_string = ' Message ends.' def_size = len(def_string) PR.<x> = PolynomialRing(Zmod(n)) f = (m0 + x * (256**def_size))^e - c f = f.monic() x0 = f.small_roots(X=2^kbits, beta=1)[0] m = m0 + x0 * (256**def_size) msg = ('%x' % m).decode('hex') print msg
実行結果は以下の通り。
Message begins. To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer. The slings and arrows of outrageous fortune. Or to take arms against a sea of troubles. And by opposing end them. To die: to sleep; - Hamlet contemplating suicide in his famous soliloquy. (Hamlet) - CSACTF{w1ll14m-sh4k3sp34r3} Message ends.
CSACTF{w1ll14m-sh4k3sp34r3}
A game of apples (Misc)
90個以上あるリンゴから、サーバプレーヤーと交互に取っていき、最後のリンゴを取れれば、フラグを得ることができる。
残り10個目を取ると負けるので、そうならないようスクリプトを組み実行する。
import socket from base64 import * def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('34.74.132.34', 1338)) data = recvuntil(s, '?\n').rstrip() print data print '\n' s.sendall('\n') data = recvuntil(s, '\n').rstrip() print data if data.split(' ')[1] == 'I': data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data remain = 999 count = 9 last = False while True: if remain > 10 and remain < 20: count = remain - 10 elif remain < 10: count = remain last = True data = recvuntil(s, '?\n').rstrip() print data print count s.sendall(str(count) + '\n') if last: break data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data remain = int(data.split(' ')[3]) data = recvuntil(s, '\n').rstrip() print data
CSACTF{0n3_4ppl3_tw0_4ppl3_thr33_4ppl3}
Linux 1 (Misc)
$ ssh user@35.231.176.102 -p1773 The authenticity of host '[35.231.176.102]:1773 ([35.231.176.102]:1773)' can't be established. ECDSA key fingerprint is SHA256:jnrxrLz++wBMSIAGbBB4KOK2Qpr9LW8kQ5tNvFN5LKc. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '[35.231.176.102]:1773' (ECDSA) to the list of known hosts. user@35.231.176.102's password: _________ _________ _____ _______________________________ \_ ___ \ / _____/ / _ \ \_ ___ \__ ___/\_ _____/ / \ \/ \_____ \ / /_\ \/ \ \/ | | | __) \ \____/ \/ | \ \____| | | \ \______ /_______ /\____|__ /\______ /|____| \___ / \/ \/ \/ \/ \/ Welcome to CSACTF 2019! If you find any problems, please report to admin. -[ Rule ]- A few rules before you get started: + don't leave orphan processes running + don't leave exploit-files laying around + don't annoy other players + don't share passwords/solutions + last but not least, don't spoil the fun! Have fun! - Blue user@23ee3cd1ea3b:~$ ls -la total 44 drwxr-xr-x 1 user user 4096 Apr 25 03:04 . drwxr-xr-x 1 root root 4096 Apr 22 20:12 .. -rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout -rw-r--r-- 1 user user 3796 Apr 25 03:03 .bashrc drwx------ 2 user user 4096 Apr 25 03:04 .cache -rw-r--r-- 1 user user 655 May 16 2017 .profile -rw-rw-r-- 1 user user 0 Apr 25 03:04 ahahaha -rwxrwxr-x 1 root root 267 Apr 22 20:09 flag_reader.py -rw-rw-r-- 1 root root 40 Apr 22 19:25 readme.txt -rw-r--r-- 1 user user 19 Apr 25 03:03 temp.txt user@23ee3cd1ea3b:~$ cat flag_reader.py #!/usr/bin/python import time import os f = open('flag.txt', 'r') # ==== Reading the flag flag = f.read() with open('temp.txt','w') as tmp: tmp.write('Reading the flag...') #print flag time.sleep(99999) # ==== Done, Cleaning up os.remove('temp.txt') f.close() user@abb60ff15525:~$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 11:44 ? 00:00:00 /bin/bash /root/init.sh user 13 1 0 11:44 ? 00:00:00 python /home/user/flag_reader.py root 16 1 0 11:44 ? 00:00:00 /usr/sbin/sshd -D root 18 16 0 11:44 ? 00:00:00 sshd: user [priv] user 22 18 0 11:44 ? 00:00:00 sshd: user@pts/0 user 23 22 0 11:44 pts/0 00:00:00 -bash user 38 23 0 11:48 pts/0 00:00:00 ps -ef
python /home/user/flag_reader.pyが実行できて、処理中のようだ。実行中のプロセスの標準出力を見る。
user@abb60ff15525:~$ ls -l /proc/13/fd/ total 0 lr-x------ 1 user user 64 Apr 25 11:49 0 -> /dev/null l-wx------ 1 user user 64 Apr 25 11:49 1 -> pipe:[2690442] l-wx------ 1 user user 64 Apr 25 11:49 2 -> pipe:[2690443] lr-x------ 1 user user 64 Apr 25 11:49 3 -> /home/user/flag.txt (deleted) user@abb60ff15525:~$ cat /proc/13/fd/3 CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}
CSACTF{f34r_cuts_d33p3r_th4n_sw0rds}
stephanography (Misc)
ステガノグラフィーの問題だが、使用しているツールに関することが問題文に書かれている。https://github.com/stncal/appaで該当するツールを見つけた。
$ python3 appa.py -d secret_new.png : Appa found an extremely large string in the image. To save your console, results are saved to file: secret_new.results String length: 187142
secret_new.resultsはhexデータだが、jpgになりそう。hexデコードすると、jpg画像にフラグが書いてあった。
CSACTF{y1p_y1p!}
We are CSA (Reconnaissance)
https://ctf.utsacyber.com/challenges#We%20are%20CSAのResponceを見る。
{"data": {"files": [], "description": "There's nothing here.\r\n", "tags": [], "minimum": 50, "id": 6, "type_data": {"templates": {"create": "/plugins/dynamic_challenges/assets/create.html", "update": "/plugins/dynamic_challenges/assets/update.html", "view": "/plugins/dynamic_challenges/assets/view.html"}, "scripts": {"create": "/plugins/dynamic_challenges/assets/create.js", "update": "/plugins/dynamic_challenges/assets/update.js", "view": "/plugins/dynamic_challenges/assets/view.js"}, "id": "dynamic", "name": "dynamic"}, "hints": [], "category": "Reconnaissance", "name": "We are CSA", "solves": 28, "decay": 50, "initial": 500, "value": 369, "state": "visible", "type": "dynamic", "max_attempts": 0}, "success": true}
Responceにはこんなコメントが入っている。
<!-- Oops, you found me! Part 1: Q1NBQ1RGe1VORDNSU1Q Check out: http://utsacyber.com/resources.html. We have tons of resources for beginners, especially about RE. -->
Base64デコードしてみる。
$ echo Q1NBQ1RGe1VORDNSU1Q | base64 -d CSACTF{UND3RSTbase64: 無効な入力
まだ他の場所にもBase64文字列が隠れていそう。http://utsacyber.com/resources.htmlにアクセスしてHTMLソースを見る。
今度はこんなコメントが入っている。
<!-- Part 2: 0TkQxTkdfQ1lCM1JfUz One of our great members, Missing, has an awesome tutorial on Bug Hunting (and MIPS), you might want to check it out.-->
先ほどのBase64文字列と結合して、Base64デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUz | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_Sbase64: 無効な入力
まだ情報が足りない。このページにMissing's Bug Hunting Cookbookというリンクがある。そのページ https://bh-cookbook.github.io/ にアクセスする。
コメントにはこう書かれている。
<!-- Part 3: NDVVIxVFlfMU5fNExMX Thank you Eli for designing the fancy logo on the CTF Homepage. -->
先ほどのBase64文字列とさらに結合して、Base64デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMX | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LLbase64: 無効な入力
次はhttps://ctf.utsacyber.com/のページのロゴ画像をダウンロードすると、PNGファイルの後ろにデータがある。
Part 4: zFUU19EME00MU5TfQo=
先ほどのBase64文字列とさらに結合して、デコードする。
$ echo Q1NBQ1RGe1VORDNSU1Q0TkQxTkdfQ1lCM1JfUzNDVVIxVFlfMU5fNExMXzFUU19EME00MU5TfQo= | base64 -d CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}
CSACTF{UND3RST4ND1NG_CYB3R_S3CUR1TY_1N_4LL_1TS_D0M41NS}