この大会は2019/10/10 15:30(JST)~2019/10/12 15:30(JST)に開催されました。
今回もチームで参戦。結果は6193点で450チーム中9位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Misc)
Discordの入ると、#announcementsチャネルのメッセージにフラグが書いてある。
rooters{W3lc0m3_t0_RootersCTF}ctf
Programming (Misc)
pngファイルをStegsolveで開き、Red plane 0を見ると、フラグの後半が書いてある。
_T0_d1g_deep3R}ctf
pngファイルの後ろにzipファイルがあるので取り出す。zipを展開すると、input.txtとreadme.txtのファイルが出てくる。readme.txtにはこう書いてある。
there is a file input.txt which contains 2 strings. and the longest common subsequence of those 2 strings will give you the flag
どうやらinput.txtの2行に対して、LCSを求めればよいらしい。
https://www.sanfoundry.com/python-program-find-longest-common-subsequence-using-dynamic-programming-memoization/を参考にスクリプトを組む。
def lcs(u, v): c = [[-1]*(len(v) + 1) for _ in range(len(u) + 1)] lcs_helper(u, v, c, 0, 0) return c def lcs_helper(u, v, c, i, j): if c[i][j] >= 0: return c[i][j] if i == len(u) or j == len(v): q = 0 else: if u[i] == v[j]: q = 1 + lcs_helper(u, v, c, i + 1, j + 1) else: q = max(lcs_helper(u, v, c, i + 1, j), lcs_helper(u, v, c, i, j + 1)) c[i][j] = q return q def get_lcs(u, v, c): s = '' i = j = 0 while not (i == len(u) or j == len(v)): if u[i] == v[j]: s += u[i] i += 1 j += 1 elif c[i][j + 1] > c[i + 1][j]: j += 1 else: i += 1 return s with open('input.txt', 'r') as f: s1 = f.readline() s2 = f.readline() c = lcs(s1, s2) flag1 = get_lcs(s1, s2, c) print flag1
LCSの結果は以下の通り。
rooters{s0m3times_U_h@v3
結合するとフラグになる。
rooters{s0m3times_U_h@v3_T0_d1g_deep3R}ctf
You Can't See Me (Forensics)
pdfを開くと、真っ白なページが93ページまである。
テキストを全選択し、コピーする。
: CUR PRAEMIA CRESCIT HAUSTAM VIM IGNOTAS. AC II QUATENUS RELIQUAS S23432EQ{rooters{Ja1_US1CT}ctfINC ERTAS EO LECTIONE ODORATUM. HA INFINITI EARUMDEM AC FUNDITUS RCTF CURANDUM EJUSMODI CONVERTO.DEI QUID QUAM HUIC SAE FORE NISI. OLIM DERCtUS FOCO AGI SINE DURA ULLO TAM. SU}O DISSIMILEM incrementi duo_98UHB praevidere. v RCTF Meo nullo ens talem dubio age novum aucta eam. In saepius infus}um :
最後の方にフラグらしきものが混ざっている。
rooters{Ja1_US1CT}ctf
Find The Pass (Forensics)
802.11の通信ばかりがあるcapファイルが添付されている。キーをクラックしてみる。
$ aircrack-ng inc0gnito.cap Opening inc0gnito.cap Read 2275250 packets. # BSSID ESSID Encryption 1 E8:DE:27:57:4F:32 BLUE_CORP WEP (20006 IVs) Choosing first network as target. Opening inc0gnito.cap Attack will be restarted every 5000 captured ivs. Starting PTW attack with 20006 ivs. Aircrack-ng 1.2 rc2 [00:00:02] Tested 50922 keys (got 20006 IVs) KB depth byte(vote) 0 0/ 1 FF(32256) CE(25856) 0F(25600) 07(24832) 59(24832) 1 41/ 54 00(22272) 11(22016) 1E(22016) 25(22016) 33(22016) 2 11/ 19 AD(23808) EB(23808) F4(23808) 06(23808) 5F(23552) 3 2/ 5 BE(26112) 58(25600) A7(25600) EA(25088) BF(24320) 4 0/ 10 EF(28928) 8F(27392) 30(27392) BF(26112) AD(25856) KEY FOUND! [ FF:DE:AD:BE:EF ] Decrypted correctly: 100%
Wiresharkで開き、[編集]-[設定]から[Protocols]-[IEEE 802.11]の設定で、wepを選択し、キーをFF:DE:AD:BE:EFと設定し、復号する。
httpでフィルタリングして通信を見てみると、No.7076パケットにBasic認証のデータが入っている。
admin:blu3_c0rp_p4ss
rooters{blu3_c0rp_p4ss}ctf
Frames per Story (Forensics)
たくさんjpgが入っている。EXIFに文章が含まれていることがわかったので、全ファイルのEXIFを取得してみる。
$ exiftool *.jpeg | grep Comment Comment : MICHAEL: All Comment : right Jim, Comment : your Comment : quarterlies Comment : look very Comment : good. How Comment : the thing is Comment : going at the Comment : library?JIM: Comment : Oh I told Comment : you couldn't Comment : close it Comment : soMICHAEL: Comment : So you've Comment : come to the Comment : master for Comment : guidance? Comment : (imitating) Comment : Is http://ti Comment : ny.cc/rosvdz Comment : this what Comment : you're Comment : saying grass Comment : hopper?JIM: Comment : Actually you Comment : called me in Comment : here, but ye Comment : ah.MICHAEL: Comment : All right, Comment : well let me Comment : show you how Comment : it's done. h Comment : ttp://tiny.c Comment : c/rosvdz Comment : (gets on Comment : phone) Yes, Comment : I liked to Comment : speak to Comment : your office Comment : manager Comment : please. Yes Comment : hello this Comment : is Michael Comment : Scott, I am Comment : the regional Comment : manager of Comment : Dunder Comment : Mifflin Comment : paper Comment : products. Comment : Just wanted Comment : to talk to Comment : you, Comment : manager-on- Comment : manager. Comment : (cut to the Comment : office and Comment : cut back) Comment : All right Comment : done deal, Comment : thank you Comment : very much Comment : sir, you're Comment : a gentleman Comment : and a Comment : scholar. Oh Comment : I'm sorry, Comment : ok, I'm Comment : sorry, my Comment : mistake. Comment : (hangs up) Comment : That was a Comment : woman I was Comment : talking to Comment : she had a Comment : very low Comment : voice. Comment : Probably a Comment : smoker. So, Comment : so that's Comment : the way it's Comment : done. Comment : Michael: (to Comment : the camera) Comment : I've been in Comment : Dundler Comment : Mifflin for Comment : twelve Comment : years, the Comment : last four as Comment : regional Comment : manager. If Comment : you want to Comment : come through Comment : here, (opens Comment : the door to Comment : the main Comment : office) so Comment : we have the Comment : entire Comment : floor, so Comment : this is my Comment : kingdom, as Comment : far as the Comment : eye can see, Comment : ah this is Comment : our Comment : receptionist Comment : Pam. (goes Comment : to the recep Comment : tionist) Comment : Pam, Pam Comment : Pam! http:// Comment : tiny.cc/rosv Comment : dz Pam Comment : Beesly. Pam Comment : has been Comment : with us for' Comment : for ever, Comment : Lavc57.70.100
URLが含まれている。http://tiny.cc/rosvdzにアクセスしてみる。実体はGoogleドライブでfinal.pngがダウンロードできる。
ダウンロードした画像をよく見ると二人の間に不自然な線がある。この線の部分のRGBをASCIIコードとして文字にする。
from PIL import Image img = Image.open('final.png').convert('RGB') w, h = img.size flag = '' for y in range(6, 20): r, g, b = img.getpixel((374, y)) flag += chr(r) + chr(g) + chr(b) print flag
実行結果は以下の通り。
rooters{WHY_4R3_TH3_W4Y_TH4T_Y0U_4R3!}c tf
\x00がcとtfの間に入るので、詰める。
rooters{WHY_4R3_TH3_W4Y_TH4T_Y0U_4R3!}ctf
babyRSA (Cryptography)
p = 244117596642286059282649228191796982671 q = 312461564294690980358123469116445272347
あとはそのまま復号する。
from Crypto.Util.number import * c = int('a0d8e768fa9d7238a6974b4a54073165fede084494d52b2ed600e6d0e77c1113', 16) n = 76277366118709104496037524736448350894293924248428417186334555697457434498837 e = 65537 p = 244117596642286059282649228191796982671 q = 312461564294690980358123469116445272347 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print flag
rooters{L34RNING_N3W_TH1NGS}ctf
Really Silly Algorithm LIBrary (Cryptography)
スクリプトの変数名から、RSA鍵生成脆弱性ROCAの問題と推定し、necaで素因数分解する。
$ ./neca 11127212863544389237262565582342312793444654886136310133705565382574067475157051952050355000097126157942244686899397157941082263117851 NECA - Not Even Coppersmith's Attack ROCA weak RSA key attack by Jannis Harder (me@jix.one) *** Currently only 512-bit keys are supported *** N = 11127212863544389237262565582342312793444654886136310133705565382574067475157051952050355000097126157942244686899397157941082263117851 Factoring... Factorization found: N = 3851789689222186911734129440311249236982321127122393533115947118361 * 2888842268486202984677183224410114807785901996516180457699983627091
素因数分解できたので、あとはそのまま復号する。
from Crypto.Util.number import * c = int('014107b18849e23cc0494a1f32d2176a0c0a497e2ad35d054941716d6a60c5be5656b369e0e4bba72fbcaba4586bc0cd352d4da34023b5a8', 16) n = 11127212863544389237262565582342312793444654886136310133705565382574067475157051952050355000097126157942244686899397157941082263117851 e = 0x10001 p = 3851789689222186911734129440311249236982321127122393533115947118361 q = 2888842268486202984677183224410114807785901996516180457699983627091 phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print flag
rooter{H0W_0DD_W45_17}ctf
Digene (Cryptography)
1行目と3行目にURL SafeなBase64文字列がある。いろいろと調べたところ、Fernet暗号である可能性が高い。1行目を暗号データ、3行目を鍵として復号する。
from cryptography.fernet import Fernet token = 'gAAAAABdnF6L7Gb1WxFUr4AVSs3Lg0lHetYDwxJvo84WN9DZd2_UASlX26XhPuFgIFs5v54yzxAbmQaZet9tOP-__y46eLqW5OeyLNlKlRpX_UfMm-aDLAM5p-DrBEBK_IzH_2kXJxc3' key = 'eH9Yta38cisI5gPZiNA3C6yKRRqqEy-K9Q9ZwfF5r1k=' f = Fernet(key) flag = f.decrypt(token) print flag
rooters{d!g3st!f_th4t_d!g3sts_y0ur_m3ss4g3s}ctf