この大会は2023/6/10 11:00(JST)~2023/6/12 11:00(JST)に開催されました。
今回もチームで参戦。結果は2137点で431チーム中70位でした。
自分で解けた問題をWriteupとして書いておきます。
Join the Discord (Misc)
Discordに入り、#rulesチャネルのメッセージを見ると、フラグの前半があった。
SEE{1_h4v3_r34d_th3_rul3s
#faqチャネルのメッセージを見ると、フラグの後半があった。
_4nd_f4qs_30543ee20a21ececf3962724f7ad5549}
SEE{1_h4v3_r34d_th3_rul3s_4nd_f4qs_30543ee20a21ececf3962724f7ad5549}
1337 Word Search (Misc)
1行1337文字の適当な文字が1337行分テキストファイルに書いてある。縦横斜めで"SEE{"と並ぶ箇所を見つける。
#!/usr/bin/env python3 def get_flag(s): if '}' in s: index = s.index('}') return s[:index + 1] return '' with open('wordsearch1.txt', 'r') as f: lines = f.read().splitlines() tbl = [] for line in lines: tbl.append(list(line)) #### horizontally #### for i in range(len(tbl)): for j in range(len(tbl[0]) - 3): if ''.join(tbl[i][j:j+4]) == 'SEE{': flag = ''.join(tbl[i][j:]) flag = get_flag(flag) if flag != '': print(flag) if ''.join(tbl[i][j:j+4]) == '{EES': flag = ''.join(tbl[i][:j+3])[::-1] flag = get_flag(flag) if flag != '': print(flag) #### vertically #### for i in range(len(tbl[0])): for j in range(len(tbl) - 3): if tbl[j][i] == 'S' and tbl[j+1][i] == 'E' and tbl[j+2][i] == 'E' \ and tbl[j+3][i] == '{': flag = '' for k in range(j, len(tbl)): flag += tbl[k][i] flag = get_flag(flag) if flag != '': print(flag) if tbl[j][i] == '{' and tbl[j+1][i] == 'E' and tbl[j+2][i] == 'E' \ and tbl[j+3][i] == 'S': flag = '' for k in range(j+3, -1. -1): flag += tbl[k][i] flag = get_flag(flag) if flag != '': print(flag) #### diagonally (left top <-> right bottom) for i in range(len(tbl) - 3): for j in range(len(tbl[0]) - 3): if tbl[i][j] == 'S' and tbl[i+1][j+1] == 'E' and tbl[i+2][j+2] == 'E' \ and tbl[i+3][j+3] == '{': l = len(tbl) - max(i, j) flag = '' for k in range(l): flag += tbl[i+k][j+k] flag = get_flag(flag) if flag != '': print(flag) for j in range(len(tbl[0]) - 3): if tbl[i][j] == '{' and tbl[i+1][j+1] == 'E' and tbl[i+2][j+2] == 'E' \ and tbl[i+3][j+3] == 'S': l = min(i, j) + 1 flag = '' for k in range(l): flag += tbl[i+3-k][j+3-k] flag = get_flag(flag) if flag != '': print(flag)
SEE{you_found_me_now_try_the_1337er_one}
NoCode (Misc)
2つ目のコードエリアの箇所をコピペして、code.txtに貼り付けし保存する。スペースの種類が2つ混ざっているので、以下の変換をしてデコードする。
"\xe2\x80\x8b" -> "0" " " -> "1"
#!/usr/bin/env python3 with open('code.txt', 'rb') as f: data = f.read() data = data.replace(b'\xe2\x80\x8b', b'0') data = data.replace(b' ', b'1') data = data.decode() flag = '' for i in range(0, len(data), 8): flag += chr(int(data[i:i+8], 2)) print(flag)
SEE{vanilla.js.org_dfe6a05ccbec9bda49cd1b70b2692b45}
decompile-me (Rev)
pycをデコンパイルする。
$ decompyle3 decompile-me.pyc # decompyle3 version 3.9.0 # Python bytecode version base 3.7.0 (3394) # Decompiled from: Python 3.10.6 (main, May 29 2023, 11:10:38) [GCC 11.3.0] # Embedded file name: decompile-me.py # Compiled at: 2023-04-25 00:58:34 # Size of source mod 2**32: 433 bytes from pwn import xor with open('flag.txt', 'rb') as f: flag = f.read() a = flag[0:len(flag) // 3] b = flag[len(flag) // 3:2 * len(flag) // 3] c = flag[2 * len(flag) // 3:] a = xor(a, int(str(len(flag))[0]) + int(str(len(flag))[1])) b = xor(a, b) c = xor(b, c) a = xor(c, a) b = xor(a, b) c = xor(b, c) c = xor(c, int(str(len(flag))[0]) * int(str(len(flag))[1])) enc = a + b + c with open('output.txt', 'wb') as f: f.write(enc) # okay decompiling decompile-me.pyc
逆算してフラグを得る。
#!/usr/bin/env python3 from pwn import xor with open('output.txt', 'rb') as f: enc = f.read() l = len(enc) l0 = int(str(l)[0]) l1 = int(str(l)[1]) assert l % 3 == 0 a = enc[:l // 3] b = enc[l // 3:l * 2 // 3] c = enc[l * 2 // 3:] c = xor(c, l0 * l1) c = xor(b, c) b = xor(a, b) a = xor(c, a) c = xor(b, c) b = xor(a, b) a = xor(a, l0 + l1) flag = (a + b + c).decode() print(flag)
SEE{s1mP4l_D3c0mp1l3r_XDXD}
BabyRC4 (Crypto)
RC4はkeyが同じ場合、平文と暗号文のXORは同じ。わかっている平文と暗号文のペアからXOR鍵を求め。その鍵でフラグの暗号文をXORして復号する。
#!/usr/bin/env python3 from Crypto.Util.strxor import strxor p1 = b'a' * 36 c0 = bytes.fromhex('b99665ef4329b168cc1d672dd51081b719e640286e1b0fb124403cb59ddb3cc74bda4fd85dfc') c1 = bytes.fromhex('a5c237b6102db668ce467579c702d5af4bec7e7d4c0831e3707438a6a3c818d019d555fc') key = strxor(p1, c1) assert len(c0) - len(key) == 2 flag = 'SE' + strxor(key, c0[:len(key)]).decode()[::-1] print(flag)
SEE{n3vEr_reU53_rC4_k3y5ss5s:cafe2835}
Dumb Chall (Crypto)
サーバの処理概要は以下の通り。
・p: ランダム128ビット素数 ・g: ランダム128ビット素数 ・x: FLAGの数値化したもの ・y = pow(g, x, p) ・p, g, yを表示 ・seen_c: 空集合 ・30回以下繰り返し ・w, r: None ・choice: ランダムTrue or False ・choiceがFalseの場合 ・w: 数値入力 ・C: 数値入力 ・Cがseen_cにあった場合、エラー ・seen_cにCを追加 ・verify = first_verify ・(y * C) % p == pow(g, w, p) ・choinceがTrueの場合 ・r: 数値入力 ・C: 数値入力 ・Cがseen_cにあった場合、エラー ・seen_cにCを追加 ・verify = second_verify ・pow(g, r, p) == C ・verify(g, p, y, C, w, r)がFalseの場合、エラー ・フラグを表示
first_verifyの場合は、wを適当に選べば、Cを算出できる。
second_verifyの場合は、rを適当に選べば、Cを算出できる。
#!/usr/bin/env python3 import socket import random def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('win.the.seetf.sg', 3002)) data = recvuntil(s, b'\n').rstrip() print(data) p = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) g = int(data.split(' ')[-1]) data = recvuntil(s, b'\n').rstrip() print(data) y = int(data.split(' ')[-1]) for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) for round in range(30): data = recvuntil(s, b': ') if 'w: ' in data: w = random.randrange(p) C = pow(g, w, p) * pow(y, -1, p) % p print(data + str(w)) s.sendall(str(w).encode() + b'\n') else: r = random.randrange(p) C = pow(g, r, p) print(data + str(r)) s.sendall(str(r).encode() + b'\n') data = recvuntil(s, b': ') print(data + str(C)) s.sendall(str(C).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
p = 296501284807546731286222674011617639919 g = 95371665021421347944344267760751115144 y = 225243662779361830239958981796122960771 Something something zero-knowledge proofs blah blah... Why not just issue the challenge and the verification at the same time? Saves TCP overhead! Enter r: 133572976341856494726824263203248470720 Enter C: 179588428044400899838332318476058719280 You passed round 1. Enter r: 202973207580200590528110454250281327631 Enter C: 235480426437196502413054566028726346305 You passed round 2. Enter r: 7414755408304036614441511798254244165 Enter C: 54125112289563613715101342247374523629 You passed round 3. Enter w: 260123886617174086643349635478769386459 Enter C: 17219449578745458937954879953842511031 You passed round 4. Enter r: 91889379823064068530969714361335767267 Enter C: 131968360735560353118798543907743310427 You passed round 5. Enter w: 137336816467403325168950719926067898227 Enter C: 60123469497270109417416956907291578135 You passed round 6. Enter r: 145023642770488950169867193724427525785 Enter C: 144185721178483374498394339752661577914 You passed round 7. Enter w: 164294497030901449452042347261545803281 Enter C: 236697805861904489943500865186383061603 You passed round 8. Enter r: 269766404332636621028071094501291774109 Enter C: 203449902044462863689198391512397768065 You passed round 9. Enter r: 83740389473424216611859576229610376232 Enter C: 5097305045013870186650024825216032349 You passed round 10. Enter w: 3870506410528159151889283566532960802 Enter C: 175441168270801461041788133725955717704 You passed round 11. Enter w: 266958709167233109481673204951008904973 Enter C: 233951923491560929521148314504251574134 You passed round 12. Enter w: 103978737507927507127124098590270305201 Enter C: 82973205426509303560237009061738487148 You passed round 13. Enter r: 272960543211065485146103697541658078626 Enter C: 188274484168571980400986889814133975815 You passed round 14. Enter r: 131875022480537854319235450449672949118 Enter C: 212924441625621256291586645770170291573 You passed round 15. Enter w: 224311984960205702179955563713205137203 Enter C: 260949222951303776585333476358645488540 You passed round 16. Enter r: 34953140111251853371285999319997486857 Enter C: 290736585547277197568283160428093874000 You passed round 17. Enter w: 207151185320386504972684171560450260610 Enter C: 203639970643156885061979274509687607624 You passed round 18. Enter w: 36814522119674132218714126600848195317 Enter C: 149056176588752735110431047315769789213 You passed round 19. Enter w: 249272653774376107204407990390443255206 Enter C: 142998004902767948862154654443648018808 You passed round 20. Enter r: 147967477305859324932884197820440616342 Enter C: 291025086042792789992780774686934492305 You passed round 21. Enter r: 228827713399248474102259804963877211978 Enter C: 57583549422604186310417640883288189694 You passed round 22. Enter r: 175144007867686060403800121553758231661 Enter C: 67133237178855319905147393665118054014 You passed round 23. Enter r: 84311493882052458862214241208805711939 Enter C: 290627072170301548507002119797988462323 You passed round 24. Enter r: 154673847729816788029099691905080011589 Enter C: 248517482066109990184661715319967535598 You passed round 25. Enter r: 287812620350746355699538586297835217189 Enter C: 77224864890694517704548610830262991286 You passed round 26. Enter r: 112277248992406389673897555590419227653 Enter C: 268919531425183396107371045940075649237 You passed round 27. Enter w: 16801219959875109241529074924012074495 Enter C: 179922130443790799793669184787258002277 You passed round 28. Enter w: 252059376635180990332396670531906090801 Enter C: 24522676018904173495938438968496760461 You passed round 29. Enter r: 240227875194019421705211875747502302959 Enter C: 89586121949639401073820496191564995588 You passed round 30. You were more likely to get hit by lightning than proof correctly 30 times in a row, you must know the secret right? A flag for your troubles - SEE{1_571ll_h4v3_n0_kn0wl3d63}
SEE{1_571ll_h4v3_n0_kn0wl3d63}
OpenEndedRSA (Crypto)
RSA暗号で、n、e、cの他にsも出力されている。sはpの2乗とppの2乗の和でppは素数、ppのバイト文字列の"?"の長さから16バイトと推測できる。
ppのブルートフォースで条件を満たすpを求める。
あとは通常通り復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * from gmpy2 import iroot from sympy import nextprime n = 102273879596517810990377282423472726027460443064683939304011542123196710774901060989067270532492298567093229128321692329740628450490799826352111218401958040398966213264648582167008910307308861267119229380385416523073063233676439205431787341959762456158735901628476769492808819670332459690695414384805355960329 e = 65537 c = 51295852362773645802164495088356504014656085673555383524516532497310520206771348899894261255951572784181072534252355368923583221684536838148556235818725495078521334113983852688551123368250626610738927980373728679163439512668552165205712876265795806444660262239275273091657848381708848495732343517789776957423 s = 128507372710876266809116441521071993373501360950301439928940005102517141449185048274058750442578112761334152960722557830781512085114879670147631965370048855192288440768620271468214898335819263102540763641617908275932788291551543955368740728922769245855304034817063220790250913667769787523374734049532482184053 pp = 1 while pp < 2 ** (8 * 16): pp = nextprime(pp) p, success = iroot(s - pp ** 2, 2) if success: if n % p == 0: break q = n // p phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m).decode() print(flag)
SEE{0dd_3vEN:deadbeef}