この大会は2016/12/17 11:00(JST)~2016/12/24 11:00(JST)に開催されました。
今回もチームで参戦。結果は6402点で536チーム中12位でした。
自分で解けた問題をWriteupとして書いておきます。
Hot Sun? (CRYPTO 100)
シーザー暗号。http://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipher
でROT14にして復号する。
3DS{th1s_r0t_1s_n0t_s0_H0t}
Results does not matter!!! (PROG 100)
演算の図が与えられているので、FLAGの値を求める。逆算すればよい。
0b10101111101(= 334453 ^ 0b1010001111100001000) :上側のXOR演算 0b10100111001(= 1337) :最終結果
上側のXOR演算とANDで最終結果になるものは次の値。
0b1?1?01110?1(*1)
0b00100111110(= 318) とXORして(*1)になるものは次の値。
0b1?0?00001?1(*2)
0b01000000000(0x200) とORして(*2)になるものは次の値。
0b1?0?00001?1
この条件を満たすもので最小値は次の値。
1029(=0b10000000101)
SHA256('1029')を計算する。
3DS{d9a5223b761c375d1263e6e57ebec42d3e0fe3f6f283488d2eb204fb6ff17ee5}
Excaliflag (STEGO 100)
pngファイルが与えられている。StegSolveで開いてみると、Blueのビットにフラグが隠れていた。
3DS{Gr4b_Only_th1s_B1ts}
base3200 (MISC 100)
Base64エンコードを複数回行っていると思われる。例外が発生するまでデコードする。
import base64 with open('msg.txt', 'r') as f: data = f.read() i = 1 while True: print 'Try %d times' % i try: data = base64.b64decode(data) except: break i+= 1 print data
3DS{B453_64_L00p_50}
Crypt or Encode? (MISC 200)
いくつかの文字をこのシステムでエンコードして、そのデータをBase64デコードして比較してみる。
平文:0123456789 暗号文:YGJkZmhqbG5wcg== 暗号文のBase64デコードデータ:'`bdfhjlnpr' 平文:ABCDEFGHIJKLMNOPQRSTUVWXYZ 暗号文:goSGiIqMjpCSlJaYmpyeoKKkpqiqrK6wsrQ= 暗号文のBase64デコードデータ: '\x82\x84\x86\x88\x8a\x8c\x8e\x90\x92\x94\x96\x98\x9a\x9c\x9e\xa0\xa2\xa4\xa6\xa8\xaa\xac\xae\xb0\xb2\xb4' 平文:abcdefghijklmnopqrstuvwxyz 暗号文:wsTGyMrMztDS1NbY2tze4OLk5ujq7O7w8vQ= 暗号文のBase64デコードデータ: '\xc2\xc4\xc6\xc8\xca\xcc\xce\xd0\xd2\xd4\xd6\xd8\xda\xdc\xde\xe0\xe2\xe4\xe6\xe8\xea\xec\xee\xf0\xf2\xf4'
暗号文をBase64デコードして、ASCIIコードを半分にしたものを文字にしていけば復号できそう。
enc = 'Zoim9tRgkNy+iGBmvrLeqr6MaGLY+g==' b64_plain = enc.decode('base64') flag = '' for i in range(len(b64_plain)): code = ord(b64_plain[i]) / 2 flag += chr(code) print flag
3DS{j0Hn_D03_YoU_F41l}
Different and Notorious Alignment (PROG 200)
最初に聞かれるタイプの数値はそのまま提示されたものと同じ数値を答えればよい。あとは文字の違う箇所の数を答えていく。
#!/usr/bin/env python import socket import re s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('54.175.35.248', 8001)) data = s.recv(2048) print data pattern1 = 'To receive the first pair type (.+):' m = re.search(pattern1, data) s.sendall(m.group(1) + '\n') for i in range(1, 101): data = s.recv(1024) print data, pattern2 = 'Sample 01\: (.+) - Sample 02\: (.+)' m = re.search(pattern2, data) s1 = m.group(1) s2 = m.group(2) count = 0 if len(s1) < len(s2): LEN = len(s1) DIFF = len(s2) - len(s1) else: LEN = len(s2) DIFF = len(s1) - len(s2) for i in range(LEN): if s1[i] != s2[i]: count += 1 count += DIFF print count s.sendall(str(count) + '\n') data = s.recv(256) print data
3DS{Y0u_4ch13v3d_h4mm1ng_D1st4nc3}
We also have memes! (STEGO 300)
画像の中に文字データとしてフラグを隠している。そのロジックはスクリプトとして提示されている。スクリプトからpは0~5、offsetは1~h(=5000)-255の範囲であることまでわかったので、ブルートフォースでフラグが"3DS{"で始まるものを探す。
from PIL import Image pre_flag = '3DS{' def get_pixel(t, x1, y1): if t == 0: r, g, b = img.getpixel((x1, y1)) elif t == 1: r, b, g = img.getpixel((x1, y1)) elif t == 2: g, r, b = img.getpixel((x1, y1)) elif t == 3: g, b, r = img.getpixel((x1, y1)) elif t == 4: b, r, g = img.getpixel((x1, y1)) elif t == 5: b, g, r = img.getpixel((x1, y1)) return r, g, b img = Image.open('output.png').convert('RGB') h, w = img.size found = 0 for p in range(6): for offset in range(1, h - 255 + 1): LEN, x, y = get_pixel(0, 0, 0) flag = '' for i in range(LEN): r, g, b = get_pixel(p, x + offset, y + offset) if i < len(pre_flag): if chr(r) != pre_flag[i]: break else: found = 1 flag += chr(r) x = g y = b if found == 1: break if found == 1: break print flag
3DS{w0w_aw3s0me_scr1pt}
Return of the Notorious Alignment (PROG 300)
部分文字列で共通する文字列の最大長を答える問題。
#!/usr/bin/env python import socket import re s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('209.190.1.131', 9002)) data = s.recv(2048) print data pattern1 = 'To receive the first pair type (.+):' m = re.search(pattern1, data) s.sendall(m.group(1) + '\n') for i in range(1, 101): data = s.recv(2048) print data, pattern2 = 'Sample 01\: (.+) - Sample 02\: (.+)' m = re.search(pattern2, data) s1 = m.group(1) s2 = m.group(2) if len(s1) < len(s2): s_s = s1 s_l = s2 else: s_s = s2 s_l = s1 found = 0 for l in range(len(s_s), 0, -1): for i in range(len(s_s) - l + 1): if s_s[i:i+l] in s_l: found = 1 ans = l break if found == 1: break if found == 0: ans = 0 print ans s.sendall(str(ans) + '\n') data = s.recv(256) print data
3DS{C0mp4ris0n_0f_substr1ngs_1s_c00l}
Fibonacci Calls (PROG 400)
何回関数が呼ばれるかを答える問題。回数はG(N) = G(N-1) + G(N-2) + 2、G(0) = 0、 G(1) = 0の漸化式になるので、それを使って最初に想定するNの範囲で計算結果をメモリ上に保存しておき、聞かれたときに取り出せるようにする。
#!/usr/bin/env python import socket import re dic = {} dic[0] = 0 dic[1] = 0 for i in range(2, 1000): dic[i] = dic[i-1] + dic[i-2] + 2 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('54.175.35.248', 8000)) data = s.recv(2048) print data pattern1 = 'To start the challenge inform the number (.+):' m = re.search(pattern1, data) s.sendall(m.group(1) + '\n') data = s.recv(256) print data for i in range(1, 101): data = s.recv(256) print data, pattern2 = 'Stage (.+) -> N = (.+)\n' m = re.search(pattern2, data) count = int(str(dic[int(m.group(2))])[-3:]) print count s.sendall(str(count) + '\n') data = s.recv(256) print data data = s.recv(256) print data
3DS{g00d4lgorithmsC4nSaveYourTime}