この大会は2017/10/7 7:00(JST)~2017/10/9 5:00(JST)に開催されました。
今回もチームで参戦。結果は2191点で132チーム中2位でした。
自分で解けた問題をWriteupとして書いておきます。
Beautiful Blue (Steg 25)
高さ10pxごとに色が微妙に変わっている。青の色情報をASCIIコードとして文字にする。
from PIL import Image img = Image.open('beautiful_blue.png').convert('RGB') w, h = img.size flag = '' for y in range(0, h, 10): r, g, b = img.getpixel((0, y)) flag += chr(b) print flag
FLAG{Such_a_beautiful_color}
sudo (Steg 25)
解凍しようとすると、同じファイル名のファイルが2つ入っていることがわかる。1つを別名で展開し、ファイルサイズの小さい方を見てみると、フラグが書いてある。
FLAG{archives_are_waaaaayyy_easier_than_sudokus}
Coffee Stain (Steg 50)
ピクロスパズルのJPG画像。一部見えにくくなっている。
JPGの終端の後に、以下のデータあり。
h14:6,1,1,4,1,4,1 h15:7,2,2,3,1,5,2 h16:1,3,6,7,2 v29:2,5,2,7,10,2 v30:2,5,2,7,10,2
見えにくい部分はこれでわかる。この情報と合わせて、パズルを解いていくと、QRコードのようになる。
このままだとうまく読み取れないので、3角の部分を少し調整すると、読み取ることができた。
FLAG{NQNQR_C0D3}
Always Backup (Forensic 30)
http://159.203.38.169:5676/.git/にアクセスすると、flagファイルがある。
http://159.203.38.169:5676/.git/flagにアクセスしたら、フラグが書いてあった。
FLAG{ALWAYS-BACKUP!!!-and-use-git-to-backup...}
All is lost (Forensic 34)
http://159.203.38.169:5676/.git/objects/9d/dbc445f674f2891526db63b7bc5b5faea1e748をダウンロード
$ python -c 'import zlib; print zlib.decompress(open("dbc445f674f2891526db63b7bc5b5faea1e748").read())' blob 77F L A G { n o t h i n g - i s - l o s t - f o r e v e r - w i t h - g i t }
FLAG{nothing-is-lost-forever-with-git}
Tri it 0 (Prog 60)
$ nc 159.203.38.169 5672
Read https://en.wikipedia.org/wiki/Balanced_ternary
TT00T + TT010
a
Wrong answer.
https://en.wikipedia.org/wiki/Balanced_ternaryを参考に数値にして計算して、元の形式に戻すというプログラムを作成する。
import socket bal3_letter = '01T' def recvuntil(s, tails): data = "" while True: for tail in tails: if tail in data: return data data += s.recv(1) def get_elem(s): elems = [] elem = '' for i in range(len(s)): if s[i] in bal3_letter: elem += s[i] else: if elem != '': elems.append(elem) elem = '' if i == len(s) - 1: if elem != '': elems.append(elem) return elems def get_formula(s, elems): formula = '' elem = '' index = 0 for i in range(len(s)): if s[i] in bal3_letter: elem += s[i] if elem == elems[index]: formula += str(dec_bal3(elem)) elem = '' index += 1 else: formula += s[i] return formula def dec_bal3(s): val = 0 for i in range(len(s)): bit = len(s) - i - 1 if s[i] == '0': bit_val = 0 elif s[i] == '1': bit_val = 3 ** bit else: bit_val = - 3 ** bit val += bit_val return val def enc_bal3(n): s = '' while n: s = bal3_letter[n % 3] + s n = -~n/3 return s or 0 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('159.203.38.169', 5672)) data = recvuntil(s, '\n') print data # Try it 0 for i in range(2): print 'Round %d' % (i+1) data = recvuntil(s, '\n') print data data = data.replace('\n', '') elems = get_elem(data) formula = get_formula(data, elems) ans = enc_bal3(eval(formula)) print ans s.sendall(ans + '\n') data = recvuntil(s, '\n') print data
2回答えると、フラグが表示された。
FLAG{I_CAN_MATH_LIKE_A_10RD_GRADER!}
Tri it 1 (Prog 150)
Tri it 0 の続き。足し算だけでなく、引き算、掛け算、かっこなども式に含まれる。
import socket bal3_letter = '01T' def recvuntil(s, tails): data = "" while True: for tail in tails: if tail in data: return data data += s.recv(1) def get_elem(s): elems = [] elem = '' for i in range(len(s)): if s[i] in bal3_letter: elem += s[i] else: if elem != '': elems.append(elem) elem = '' if i == len(s) - 1: if elem != '': elems.append(elem) return elems def get_formula(s, elems): formula = '' elem = '' index = 0 for i in range(len(s)): if s[i] in bal3_letter: elem += s[i] if elem == elems[index]: formula += str(dec_bal3(elem)) elem = '' index += 1 else: formula += s[i] return formula def dec_bal3(s): val = 0 for i in range(len(s)): bit = len(s) - i - 1 if s[i] == '0': bit_val = 0 elif s[i] == '1': bit_val = 3 ** bit else: bit_val = - 3 ** bit val += bit_val return val def enc_bal3(n): s = '' while n: s = bal3_letter[n % 3] + s n = -~n/3 return s or 0 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('159.203.38.169', 5672)) data = recvuntil(s, '\n') print data # Try it 0 for i in range(2): print 'Round %d' % (i+1) data = recvuntil(s, '\n') print data data = data.replace('\n', '') elems = get_elem(data) formula = get_formula(data, elems) ans = enc_bal3(eval(formula)) print ans s.sendall(ans + '\n') data = recvuntil(s, '\n') print data # Try it 1 for i in range(17): print 'Round %d' % (i+1) data = recvuntil(s, '\n') print data data = data.replace('\n', '') elems = get_elem(data) formula = get_formula(data, elems) ans = enc_bal3(eval(formula)) print ans s.sendall(ans + '\n') data = recvuntil(s, '\n') print data
17回答えると、フラグが表示された。
FLAG{ONE_PLUS_ONE_EQ_ONE_TON}
Rich 2 (Crypto 200)
タイトル的な順番では逆だが、結果的にはRich 1よりこちらの方が先に解けた。
$ nc 159.203.38.169 5682 Please give me your wallet and I'll determine if you are rich enough to access out VIP area. Here is an example wallet: 56e3fc849a2c06c2f858615a2a17b0525a1a5020874b2ceebaf411f2753f91864dced78d862c46daf45a204e3c18b35e40360675d70c7db1f9a417e5337ec4cf01de87d8d67a5ae0e4471a5c7854d613033d2b6ad5117780eee964a36471f6cd15d8ac9c9e330ebfb3521a5c7846de1f04362b7acd0b66939b9209c43d41d1dd028987ecc97d4df5c66c3e5f5c46ee1511351b19ef1052a4fad30ff5103792d237e1a88eec2254d3f9057c155351a262 56e3fc849a2c06c2f858615a2a17b0525a1a5020874b2ceebaf411f2753f91864dced78d862c46daf45a204e3c18b35e40360675d70c7db1f9a417e5337ec4cf01de87d8d67a5ae0e4471a5c7854d613033d2b6ad5117780eee964a36471f6cd15d8ac9c9e330ebfb3521a5c7846de1f04362b7acd0b66939b9209c43d41d1dd028987ecc97d4df5c66c3e5f5c46ee1511351b19ef1052a4fad30ff5103792d237e1a88eec2254d3f9057c155351a262 Plebs be plebs. No money, no access. Guessing you cant math... 1137 is way too low.
提示された暗号文字列をそのまま入れると、amountの値が1137になっていることがわかる。
コードを読むと、暗号の概要は以下の通りとわかる。
plaintextは以下の形式 {"CCCCCC": "TTTTTT", "EEEEEE": "FLAG{DDDDDD}", "RRRRRR": "EEEEEE", "DDDDDD": "AAAAAA"} enc_xor_cbc(plaintext, key, iv):暗号化16進数文字列を表示 plaintextを16の倍数になるようパディング(16の倍数の場合は16バイトパディング) 16バイトごとに以下の処理 1ブロック目暗号:1ブロック目平文 ^ key ^ IV 2ブロック目暗号:2ブロック目平文 ^ key ^ 1ブロック目暗号 3ブロック目暗号:3ブロック目平文 ^ key ^ 2ブロック目暗号 :
このことから以下のようにして平文がわかるはず。
3ブロック目平文:3ブロック目暗号 ^ key ^ 2ブロック目暗号 2ブロック目平文:2ブロック目暗号 ^ key ^ 1ブロック目暗号 1ブロック目平文:1ブロック目暗号 ^ key ^ IV 11ブロック目暗号 ^ key ^ 10ブロック目暗号 = 11ブロック目平文
パディングの長さを1~16で総当たり、さらに部分的に復号された値と平文の一部を頭に入れながら、復号する。最終的には以下のコードでフラグが得られた。
import socket def recvuntil(s, tails): data = "" while True: for tail in tails: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('159.203.38.169', 5682)) data = recvuntil(s, "\n") print data data = recvuntil(s, "\n") print data data = recvuntil(s, "\n") print data c_hex = data.strip() c_list = [] for i in range(0, len(c_hex), 32): c_list.append(c_hex[i:i+32].decode('hex')) for pad_length in range(16, 0, -1): p10_tail = '"}' + chr(pad_length) * pad_length block_chk_size = len(p10_tail) if block_chk_size > 16: block_chk_size = 16 c09_tail = c_list[9][-block_chk_size:] c10_tail = c_list[10][-block_chk_size:] key = '' for i in range(block_chk_size): c09_char = c09_tail[-block_chk_size+i] c10_char = c10_tail[-block_chk_size+i] p10_char = p10_tail[-block_chk_size+i] code = ord(c09_char) ^ ord(c10_char) ^ ord(p10_char) key += chr(code) plain_list = [] error = False for i in range(9): c_pre_tail = c_list[8-i][-block_chk_size:] c_cur_tail = c_list[9-i][-block_chk_size:] plain_tail = '' for j in range(block_chk_size): c_pre_char = c_pre_tail[-block_chk_size+j] c_cur_char = c_cur_tail[-block_chk_size+j] key_char = key[-block_chk_size+j] code = ord(c_pre_char) ^ ord(c_cur_char) ^ ord(key_char) if code < 32 or code > 126: error = True break else: plain_tail += chr(code) if error: break plain_list.append(plain_tail) if error == False: print plain_list # 6th block c_pre_char = c_list[4][12] c_cur_char = c_list[5][12] plain_char = '"' key1_char = chr(ord(c_pre_char) ^ ord(c_cur_char) ^ ord(plain_char)) # 7th block c_pre_char = c_list[5][:12] c_cur_char = c_list[6][:12] plain_char = 'unt": 1137, ' key2_char = '' for i in range(len(c_cur_char)): key2_char += chr(ord(c_pre_char[i]) ^ ord(c_cur_char[i]) ^ ord(plain_char[i])) key = key2_char + key1_char + key # Decrypt the Later Parts plain_tail = '' for i in range(10): plain_part = '' c_pre = c_list[9 - i] c_cur = c_list[10 - i] for j in range(16): code = ord(c_pre[j]) ^ ord(c_cur[j]) ^ ord(key[j]) plain_part += chr(code) plain_tail = plain_part + plain_tail print plain_tail
FLAG{CBse4_Butch3reD_Cryp7o!WoW_R3cur51vE_@Cr0NymZ!?}
Rich 1 (Crypto 150)
Rich 2の続き。amountを1337にして、そのブロック以降を暗号化して投入する。
import socket def recvuntil(s, tails): data = "" while True: for tail in tails: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('159.203.38.169', 5682)) data = recvuntil(s, "\n") print data data = recvuntil(s, "\n") print data data = recvuntil(s, "\n") print data c_hex = data.strip() c_list = [] for i in range(0, len(c_hex), 32): c_list.append(c_hex[i:i+32].decode('hex')) for pad_length in range(16, 0, -1): p10_tail = '"}' + chr(pad_length) * pad_length block_chk_size = len(p10_tail) if block_chk_size > 16: block_chk_size = 16 c09_tail = c_list[9][-block_chk_size:] c10_tail = c_list[10][-block_chk_size:] key = '' for i in range(block_chk_size): c09_char = c09_tail[-block_chk_size+i] c10_char = c10_tail[-block_chk_size+i] p10_char = p10_tail[-block_chk_size+i] code = ord(c09_char) ^ ord(c10_char) ^ ord(p10_char) key += chr(code) plain_list = [] error = False for i in range(9): c_pre_tail = c_list[8-i][-block_chk_size:] c_cur_tail = c_list[9-i][-block_chk_size:] plain_tail = '' for j in range(block_chk_size): c_pre_char = c_pre_tail[-block_chk_size+j] c_cur_char = c_cur_tail[-block_chk_size+j] key_char = key[-block_chk_size+j] code = ord(c_pre_char) ^ ord(c_cur_char) ^ ord(key_char) if code < 32 or code > 126: error = True break else: plain_tail += chr(code) if error: break plain_list.append(plain_tail) if error == False: print plain_list # 6th block c_pre_char = c_list[4][12] c_cur_char = c_list[5][12] plain_char = '"' key1_char = chr(ord(c_pre_char) ^ ord(c_cur_char) ^ ord(plain_char)) # 7th block c_pre_char = c_list[5][:12] c_cur_char = c_list[6][:12] plain_char = 'unt": 1137, ' key2_char = '' for i in range(len(c_cur_char)): key2_char += chr(ord(c_pre_char[i]) ^ ord(c_cur_char[i]) ^ ord(plain_char[i])) key = key2_char + key1_char + key # Decrypt the Later Parts plain_tail = [] for i in range(10): plain_part = '' c_pre = c_list[9 - i] c_cur = c_list[10 - i] for j in range(16): code = ord(c_pre[j]) ^ ord(c_cur[j]) ^ ord(key[j]) plain_part += chr(code) plain_tail.append(plain_part) print plain_tail # Encrypt "amount": 1337 mod_c_list = [] for i in range(5): p_part = plain_tail[4-i] if i == 0: p_part = plain_tail[4-i][:7] + '3' + plain_tail[4-i][8:] c_pre = c_list[5+i] else: c_pre = mod_c_list[-1] c_part = '' for j in range(16): code = ord(p_part[j]) ^ ord(c_pre[j]) ^ ord(key[j]) c_part += chr(code) mod_c_list.append(c_part) new_c_hex = '' for i in range(6): new_c_hex += c_list[i].encode('hex') for i in range(5): new_c_hex += mod_c_list[i].encode('hex') print new_c_hex s.sendall(new_c_hex + '\n') data = recvuntil(s, "\n") print data data = recvuntil(s, "\n") print data
FLAG{nVm_74BLeZ_FL1P_B!Ts=O}