この大会は2016/5/14 7:00(JST)~2016/5/16 7:00(JST)に開催されました。
今回もチームで参戦。結果は2110点で、社会人(Overall)ランキング435チーム中31位でした。
自分で解けた問題をWriteupとして書いておきます。
I'm playing! (Misc 10)
IRCの問題ということで、freenodeで#TUCTFに入ると、こんなメッセージが...。
07:43 *topic : vm_decryption_key: DescendIntoDarkness A wild string appeared: "TUCTF{Just_A_Test_" + Flag from logo (in caps) + "}"
TUCTFのロゴにモールス信号が書いてあり、復号するとFLAGになる。
TUCTF{Just_A_Test_FLAG}
The Neverending Crypto Level 1 (Crypto 10)
モールス信号を復号する。50回復号するとフラグが表示された。
TUCTF{i_wi11_n0t_5teal}
The Neverending Crypto Level 2 (Crypto 10)
Level 1の続き。ASCIIコードで13引いて、復号する。50回復号するとフラグが表示された。
TUCTF{c4n_s0me0ne_turn_a_1ight_0n}
The Neverending Crypto Level 3 (Crypto 10)
Level 2の続き。2パターンの換字暗号。どちらのパターンかを判定して復号する。50回復号するとフラグが表示された。
TUCTF{5omething_is_b3tt3r_th4n_n0thing}
The Neverending Crypto Level 4 (Crypto 10)
Level 3の続き。シフト暗号。テストした暗号からシフト数を算出して復号する。50回復号するとフラグが表示された。
TUCTF{7urn_th4t_fr0wn_up5ide_d0wn}
The Neverending Crypto Level 5 (Crypto 10)
Level 4の続き。ASCIIコードで159から引いて、復号する。50回復号するとフラグが表示された。
TUCTF{gn0mes_4re_p3ople_t00}
The Neverending Crypto Level 6 (Crypto 50)
Level 5の続き。テストした暗号からASCIIコードが一定幅で割り当てが変わったものを算出して復号する。50回復号するとフラグが表示された。
TUCTF{mirr0r_mirr0r_0n_th3_w4ll}
The Neverending Crypto Level 7 (Crypto 50)
Level 6の続き。暗号時にASCIIコードの差が一定の変化をする。
abcdefghijklm encrypted is \i~_l"bo%er(h a(97) -> \(92) b(98) -> i(105) +13 c(99) -> ~(126) +21 d(100) -> _(95) -31 e(101) -> l(108) +13 f(102) -> "(34) +21 g(103) -> b(98) -31 h(104) -> o(111) +13 i(105) -> %(37) +21 j(106) -> e(101) -31 k(107) -> r(114) +13 abcdefghijklm encrypted is '/!*2$-5'08*3 a(97) -> '(39) b(98) -> /(47) +8 c(99) -> !(33) -14 d(100) -> *(42) +9 e(101) -> 2(50) +8 f(102) -> $(36) +-14 g(103) -> -(45) +9 h(104) -> 5(53) +8 i(105) -> '(39) -14 j(106) -> 0(48) +9 k(107) -> 8(56) +8
このことを利用してコードを書き、50回復号するとフラグが表示された。
TUCTF{c4ll_m3_ishmae1}
The Neverending Crypto Level 8 (Crypto 150)
Level 7の続き。スキュタレー暗号。必ず6周の暗号になっていることから6で割って、スキップ数を算出し復号する。50回復号するとフラグが表示された。
Level 9は解けなかったので、このレベル以下のコードを記載しておく。
#!/usr/bin/env python import socket import re morse = {'.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-':'X' , '-.--': 'Y', '--..': 'Z', '-----': '0', '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9' } lv3_1 = {'a': 'a', 'b': 'b','c': 'c', 's': 'd', 'f': 'e', 't': 'f', 'd': 'g', 'h': 'h', 'u': 'i', 'n': 'j', 'e': 'k', 'i': 'l', 'm': 'm', 'k': 'n', 'y': 'o', ';': 'p', 'q': 'q', 'p': 'r', 'r': 's', 'g': 't', 'l': 'u', 'v': 'v', 'w': 'w', 'x': 'x', 'j': 'y', 'z': 'z' } lv3_2 = {'a': 'a', 'x': 'b','j': 'c', 'e': 'd', '.': 'e', 'u': 'f', 'i': 'g', 'd': 'h', 'c': 'i', 'h': 'j', 't': 'k', 'n': 'l', 'm': 'm', 'b': 'n', 'r': 'o', 'l': 'p', '\'': 'q', 'p': 'r', 'o': 's', 'y': 't', 'g': 'u', 'k': 'v', ',': 'w', 'q': 'x', 'f': 'y', ';': 'z' } s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('146.148.102.236', 24069)) ######## Level 1 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('a\n') data = s.recv(256) print data pattern = 'What is (.+) decrypted' m = re.search(pattern, data) codes = m.group(1).strip().split(' ') print codes ans = '' sp = 0 for code in codes: if code == '' and sp == 0: ans += ' ' sp = 1 elif code == '' and sp ==1: sp = 0 else: ans += morse[code] print ans s.sendall(ans + '\n') ######## Level 2 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('012ABCxyz\n') data = s.recv(256) print data pattern = 'What is (.+) decrypted' m = re.search(pattern, data) enc = m.group(1) ans = '' for j in range(len(enc)): code = ord(enc[j:j+1]) - 13 if code < 32: code = code + 95 ans += chr(code) print ans s.sendall(ans + '\n') ######## Level 3 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('b\n') data = s.recv(256) print data pattern = 'b encrypted is (.+)\nWhat is (.+) decrypted' m = re.search(pattern, data) sample = m.group(1) enc = m.group(2) ans = '' if sample == lv3_1['b']: for j in range(len(enc)): if enc[j:j+1] == ' ': ans += ' ' else: ans += lv3_1[enc[j:j+1]] else: for j in range(len(enc)): if enc[j:j+1] == ' ': ans += ' ' else: ans += lv3_2[enc[j:j+1]] print ans s.sendall(ans + '\n') ######## Level 4 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('giant\n') data = s.recv(256) print data pattern = 'giant encrypted is (.+)\nWhat is (.+) decrypted' m = re.search(pattern, data) sample = m.group(1) enc = m.group(2) shift = ord(sample[0:1]) - ord('g') ans = '' for j in range(len(enc)): code = ord(enc[j:j+1]) - shift if code < 32: code = code + 95 elif code > 126: code = code - 95 ans += chr(code) print ans s.sendall(ans + '\n') ######## Level 5 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('giant\n') data = s.recv(256) print data pattern = 'What is (.+) decrypted' m = re.search(pattern, data) enc = m.group(1) ans = '' for j in range(len(enc)): code = 159 - ord(enc[j:j+1]) ans += chr(code) print ans s.sendall(ans + '\n') ######## Level 6 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall(' !\"\n') data = s.recv(256) print data pattern = ' !\" encrypted is (.+)\nWhat is (.+) decrypted' m = re.search(pattern, data) sample = m.group(1) enc = m.group(2) diff = ord(sample[1:2]) - ord(sample[0:1]) a_code = ord(sample[0:1]) % diff ans = '' for j in range(len(enc)): for k in range(diff + 1): e_code = ord(enc[j:j+1]) code = e_code + k * 95 if code % diff == a_code: break if code < ord(sample[0:1]): code = ord(enc[j:j+1]) + diff * 95 code = (code - ord(sample[0:1])) / diff + 32 else: code = (code - ord(sample[0:1])) / diff + 32 ans += chr(code) print ans s.sendall(ans + '\n') ######## Level 7 ######## for i in range(1, 51): data = s.recv(256) print data s.sendall('abcd\n') data = s.recv(256) print data pattern = 'abcd encrypted is (.+)\nWhat is (.+) decrypted' m = re.search(pattern, data) sample = m.group(1) enc = m.group(2) diff_head = ord('a') - ord(sample[0:1]) diff = [] for j in range(3): diff.append(ord(sample[j+1:j+2]) - ord(sample[j:j+1]) - 1) ans = '' integ = 0 for j in range(len(enc)): if j == 0: code = ord(enc[0:1]) + diff_head else: code = ord(enc[j:j+1]) - diff[(j-1) % 3] + diff_head - integ integ = integ + diff[(j-1) % 3] if code < 32: code = code + 95 elif code > 126: while True: if code > 126: code = code - 95 else: break ans += chr(code) print ans s.sendall(ans + '\n') ######## Level 8 ######## for i in range(1, 100): data = s.recv(256) print data s.sendall('abcdefghijklmnop\n') data = s.recv(256) print data pattern = 'What is (.+) decrypted' m = re.search(pattern, data) enc = m.group(1) skip = len(enc) / 6 ans = '' for j in range(skip): for k in range(6): letter = enc[k*skip+j:k*skip+j+1] if ord(letter) == 0: break ans += letter print ans s.sendall(ans + '\n')
TUCTF{it5_4ll_in_y0ur_mind}
Magic Image (Crypto 100)
暗号スクリプトとPNGファイルの暗号化ファイルが与えれている。
PNGファイル最初の12バイトは決まっているので、それを前提にkeyを求めた後に、そのkeyで元に戻す。
#!/usr/bin/env python def xor(s1, s2): res = [chr(0)]*12 for i in range(len(res)): q = ord(s1[i]) d = ord(s2[i]) k = q ^ d res[i] = chr(k) res = ''.join(res) return res plain = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D] with open('encrypted.png', 'rb') as f: data = f.read() png_head = '' for i in plain: png_head += chr(i) key = xor(png_head, data[0:12]) flag = '' for i in range(0, len(data), 12): flag += xor(data[i:i+12], key) if ord(flag[-1:]) != 0x82: pad_cnt = ord(flag[-1:]) flag = flag[:-pad_cnt] with open('flag.png', 'wb') as f: f.write(flag)
復号したPNG画像にフラグが書かれている。
TUCTF{st@llowning_xOR_5ince_Apollo}