この大会は2021/6/1 3:00(JST)~2021/6/3 2:00(JST)に開催されました。
今回もチームで参戦。結果は900点で80チーム中20位でした。
自分で解けた問題をWriteupとして書いておきます。
Pikatchu's Fault (Fault Injection 100)
from Crypto.Util.number import * import re N = 14428106165822100311240179104702593030922229606910261061860621459798477042443217675708638511393783602097391544907229222395222465303690975922805833664159517707002845392996861764206707180372733989400577838887924912395532925065643238637812097531657082837158436848594708309238918390308191361234059534178077141595873018313112575974600539522798755895311426362091301356794727864093165846318233065617022292712839240223002241134094765005135404901074586741323378531189871102865921045940469715763216120732916020528427859371641401521785824628585853094123501351577248220567930583676467206786442597142305655569749177107891502253803 e = 65537 M = 0x4c6f766520616e64206b69737365732c206d697373696e6720796f7521000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2 M2 = 0x1a36d7ecad4b18bc765ff0e3d3ecde7cae84bf1cc445a6fdcb48c335d9d3a043817fc014d55bfad94b51eb522b4ff719d33f012afe1498efafe660b97d1cb806d4ff7985a40a14f9bec9d93e2eb20f3820423ba0e34302adc82156673fa164822779174e9b7a84a417873358d1d774ccdfdb1f36de6aa8249b00184aac2feb003782f16fb3f5d7b113387989f662062452793bbaad87014b1ebd4a020342a11a19d95f4d3b29dd5449c57ea0fd3b881542a7b8b0bdbc9dcb7b19337f40e723439365dc29625cb775e91aef886d79d1403725c5aefa21b3d69f8cacbbbafb8b35c86822113e71bd272ca7cdf68da0ab397c658cb6cc14225732bc07726155ecd1 C = pow(M, e, N) C2 = pow(M2, e, N) p = GCD(pow(C2 - C, e, N), N) q = N / p p_str = long_to_bytes(p) q_str = long_to_bytes(q) pattern = '(ICHSA_CTF{.+})' m = re.search(pattern, p_str) if m is not None: flag = m.group(1) print flag m = re.search(pattern, q_str) if m is not None: flag = m.group(1) print flag
ICHSA_CTF{who_needs_more_than_1_fault_with_crt}
Crime Cloud (Crypto 150)
同じ部分文字列が複数あると圧縮後のサイズが小さくなる性質を使って、一文字ずつ特定するCRIMEの問題。何回か試し、圧縮できると文字列長が95になることがわかったので、そのことを使った。
import socket import string import base64 def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def send_req(s, inp): data = recvuntil(s, ': ') print data + inp s.sendall(inp + '\n') data = recvuntil(s, '\n').rstrip() print data result = base64.b64decode(data) return len(result) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('crime.ichsa.ctf.today', 8008)) flag = 'ICHSA_CTF{' while True: found = False while True: for c in string.lowercase + '_}': length = send_req(s, flag + c) if length == 95: found = True flag += c break if found: break if '}' in flag: break print flag
実行結果は以下の通り。
Wellcome! Input an empty line to exit. Your input: ICHSA_CTF{a hYdfGqhcCslKVKOZs2IqD2a6Pkk5VY5xoXKL2+3xTtBphMYy32GlD+tqhwnC1bRsPq7Rmn+wI33WDXnmRdQytCBtdppS1PpHD6eFeM+eiS+r8jvGGNePa3/O2eZ9aC4Gmg== Your input: ICHSA_CTF{b 4bMWx8xuv1WFm9L3CEYMVisIzRINZ+vQ6Des5vxVGwxQ5ejMyuS7qgnr9wULW0U+ZkK3pAEYIjAF4Og+eoegDvn8KTuveTen/PLh+noJqsBGL3F7Km8cZVhJK2DHobG7 Your input: ICHSA_CTF{c XWOWujVqSDPHFD1iZbin3i6cjqc4TVyUI/RzzwjH9O5iJFTCNyYBPjbLwR+suKAKC2v6/FGoR5RS2FZGsdX9G4AZqCoC4HV3WsrCXPWe+WET2g6wz1zQBRC75b4D0yFB : Your input: ICHSA_CTF{compressing_secret_with_input_is_a_crimex 5T8szXe3sbLs7QMqhPuLNvgMndeLdSNMTKVph61YSMZ0vFepclTdZLNBOpy3+3DiJK5+vZmomj0ZvUQMDGQNqu1xT1Q11Qz4+o94dEb6cYouIai48YtxZhxTRDElfPnnTQ== Your input: ICHSA_CTF{compressing_secret_with_input_is_a_crimey ipc/khikilO0Nol0u7WJzzq9SzzlgUukLXZywjDShyxVh3ccG66lWf9psRWlrLGOInPYnWWVZ+Htckvv9p5hlhDCN+KosGvxIomyw8dot7uJ1wc5dafI+fjK/Ec7fgwd Your input: ICHSA_CTF{compressing_secret_with_input_is_a_crimez 7yEVcxMNh6VI9I59S5YwRycJo2KeKDuMtl3KeIupONCQPkSk72rEnXbwUoRuGHHk7S/fSYd7NrqSnveIfwTdvvBJAuo9GBmOV5gdhyY/22ZdEiNl0tjKwFy7uO0Zvb+ONw== Your input: ICHSA_CTF{compressing_secret_with_input_is_a_crime_ +4kUG+Kr99nzEscVKE59NOXyAbKaNe1Lwm5y6cEkYxlw0OX/F9nX5liOTOAfedA5nUSynzBhqGuZA2y3e5I3pbChrUKLAwLFtQyfv3meZDvesXmL7fHo/EuTseD4tmJQ Your input: ICHSA_CTF{compressing_secret_with_input_is_a_crime} JGVzIGu6kyAvKFhETysHb39JpWUb/zhPNZ7E3cqCf9qhVZjC1vYivT2HTNgkJH4ZdfwkHR2UH9rDW75pv2+vrw5Z0e0ZxVsRxat2NDtaJoGL9pqmJCSaz5FBvgLbQdg= ICHSA_CTF{compressing_secret_with_input_is_a_crime}
ICHSA_CTF{compressing_secret_with_input_is_a_crime}
Baby Homework (Crypto150)
入力+FLAGがAES暗号化される。1文字ずつはみ出させて該当する暗号ブロックを比較しながら、1文字ずつフラグを割り出す。
$ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? a 88d8390cee4a0b215d1b94e76240f9b07c35ee865d8de8676072d9b798c6e848 $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aa c8b86dfb1d472aab7fc76e7840923af08fedccacc986bf96ad39a7218107079e $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aaa 56bea9a8e003b77d540c509f89094fa3c1a68e6228cf6dcf80d3585f41a66f8d $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aaaa 8d61319569762f580a991128cb825a795a8c7ec693f69900b71da9e365ee72f4 $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aaaaa a93071ba224344256e4e6b461a7cb9e47929019756343aa26e8c2e15fc7ca914 $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aaaaaa db3c7f3f162f1c478cc7acbb54357e5ff6ef6ff296c93f4f8d23a5a276489dad $ nc baby_homework.ichsa.ctf.today 8010 Hello! What do you want to encrypt today? aaaaaaa 8547849b18d7e0fca1aa795d52d956896a5121b13076b6afa043b02b0b930c1c3cf284e29ab154b48e8c11fb9e65cc04
以下のようなイメージになる。
0123456789abcdef XXXXXXXFFFFFFFFF FFFFFFFFFFFFFFFF PPPPPPPPPPPPPPPP
以下のようなイメージで1文字ずつ割り出す。
0123456789abcdef XXXXXXXXXXXXXXX? XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXF FFFFFFFFFFFFFFFF FFFFFFFFPPPPPPPP
比較するブロックは1ブロック目と3ブロック目。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) flag = '' for i in range(25): for code in range(32, 127): if i < 16: try_inp = 'X' * (15 - i) + flag + chr(code) + 'X' * (31 - i) else: try_inp = flag[-15:] + chr(code) + 'X' * (31 - i) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('baby_homework.ichsa.ctf.today', 8010)) data = recvuntil(s, '?\n').rstrip() print data print try_inp s.sendall(try_inp + '\n') data = recvuntil(s, '\n').rstrip() print data b0 = data[:32] b2 = data[64:96] if b0 == b2: flag += chr(code) break print flag
実行結果は以下の通り。
: Hello! What do you want to encrypt today? t_DeF4uL7_V4lu3vXXXXXXX f7e2482f4ded017ed447307088a68c9a1964750ffb854741e05b71deb6afc0ef6a5121b13076b6afa043b02b0b930c1cf25ad892a7e0256af842d1c6a709e777 Hello! What do you want to encrypt today? t_DeF4uL7_V4lu3wXXXXXXX 5d1c67ccc026686bea22df5f4c92020d1964750ffb854741e05b71deb6afc0ef6a5121b13076b6afa043b02b0b930c1c6134fd11f55e338884ec6bf1c50dfe3c Hello! What do you want to encrypt today? t_DeF4uL7_V4lu3xXXXXXXX e9e27e8ec5f4cffd0e2936d4843789811964750ffb854741e05b71deb6afc0ef6a5121b13076b6afa043b02b0b930c1c1b646dd9b79f3f5352a8b8bde6ae65c2 Hello! What do you want to encrypt today? t_DeF4uL7_V4lu3yXXXXXXX 7fd0994e1e4d361a39ad3647bee39d621964750ffb854741e05b71deb6afc0ef6a5121b13076b6afa043b02b0b930c1cc8b1fa40240694818015d8be8fd3b640 Hello! What do you want to encrypt today? t_DeF4uL7_V4lu3zXXXXXXX 6a5121b13076b6afa043b02b0b930c1c1964750ffb854741e05b71deb6afc0ef6a5121b13076b6afa043b02b0b930c1c6cd49de5bb815da7f6f6dea44abb6efc d0n7_7ruzt_DeF4uL7_V4lu3z
ICHSA_CTF{d0n7_7ruzt_DeF4uL7_V4lu3z}
Poodle1 (Crypto 200)
サーバの処理概要は以下の通り。
・flagを特定のkeyとランダムなivでAES-CBC暗号化して表示 ・msg入力(hex) ・hexデコードし、AES-CBC復号 ・パディングエラーになったら、メッセージ表示
CBC Padding Oracle Attackで復号する。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) def unpad(s): return s[:-ord(s[-1])] def is_valid(s, enc): data = recvuntil(s, '>> ') print data + enc s.sendall(enc + '\n') data = recvuntil(s, '\n').rstrip() print data if data != 'invalid padding >:(': return True else: return False s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('poodle1.ichsa.ctf.today', 8003)) data = recvuntil(s, 'poodle?\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data enc = data.decode('hex') enc_blocks = [] for i in range(0, len(enc), 16): enc_blocks.append(enc[i:i+16]) xor_blocks = [] for i in range(len(enc_blocks)-1, 0, -1): xor_block = '' for j in range(16): for code in range(256): print '%d - %d - %d: %s' % (i, j, code, xor_block.encode('hex')) print '****', str_xor(xor_block, enc_blocks[i-1][-j:]), '****' try_pre_block = '\x00' * (16 - j - 1) + chr(code) + str_xor(xor_block, chr(j+1)*j) try_cipher = (try_pre_block + enc_blocks[i]).encode('hex') if is_valid(s, try_cipher): xor_code = (j+1) ^ code xor_block = chr(xor_code) + xor_block break xor_blocks.append(xor_block) xor_blocks.reverse() flag = '' for i in range(len(xor_blocks)): flag += str_xor(enc_blocks[i], xor_blocks[i]) flag = unpad(flag) print flag
実行結果は以下の通り。
hi there! I am the oracle :D searching for a poodle, huh? what about that cute and encrypted poodle? 14bfcca6528034a7bb309e7a8fbd4299fbb3cecff54817a54bc1080965d4011b71fac255e72e39fa6d42baa6462c2531 2 - 0 - 0: **** **** BTW, if you want to tell me something don't forget to encrypt it (with my secret key, of course ;)) What do you want to tell me? ('nothing' to exit) >> 0000000000000000000000000000000071fac255e72e39fa6d42baa6462c2531 invalid padding >:( 2 - 0 - 1: **** **** What do you want to tell me? ('nothing' to exit) >> 0000000000000000000000000000000171fac255e72e39fa6d42baa6462c2531 invalid padding >:( : What do you want to tell me? ('nothing' to exit) >> 4aec94e503cf67e3ed5bc735fb9d3cfdfbb3cecff54817a54bc1080965d4011b invalid padding >:( 1 - 15 - 75: fc84f513df77f3fd4bd725eb8d2ced **** CHSA_CTF{I_d0nt **** What do you want to tell me? ('nothing' to exit) >> 4bec94e503cf67e3ed5bc735fb9d3cfdfbb3cecff54817a54bc1080965d4011b invalid padding >:( 1 - 15 - 76: fc84f513df77f3fd4bd725eb8d2ced **** CHSA_CTF{I_d0nt **** What do you want to tell me? ('nothing' to exit) >> 4cec94e503cf67e3ed5bc735fb9d3cfdfbb3cecff54817a54bc1080965d4011b invalid padding >:( 1 - 15 - 77: fc84f513df77f3fd4bd725eb8d2ced **** CHSA_CTF{I_d0nt **** What do you want to tell me? ('nothing' to exit) >> 4dec94e503cf67e3ed5bc735fb9d3cfdfbb3cecff54817a54bc1080965d4011b mmm... very interesting... thank you! ICHSA_CTF{I_d0nt_like_oracl3s}
ICHSA_CTF{I_d0nt_like_oracl3s}
Poodle2 (Crypto 300)
考え方はPoodle1と同じ。"givemetheflag"は16バイト未満なので、適当な暗号1ブロックに対して、復号したものを求め、それとXORをとればよい。
import socket from Crypto.Cipher import AES from Crypto.Util.Padding import pad def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) def is_valid(s, enc): data = recvuntil(s, '>> ') print data + enc s.sendall(enc + '\n') data = recvuntil(s, '\n').rstrip() print data if data != 'invalid padding >:(': return True else: return False s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('poodle2.ichsa.ctf.today', 8004)) data = recvuntil(s, 'it)\n').rstrip() print data enc_blocks = ['X' * 16, 'X' * 16] xor_blocks = [] for i in range(len(enc_blocks)-1, 0, -1): xor_block = '' for j in range(16): for code in range(256): print '%d - %d - %d: %s' % (i, j, code, xor_block.encode('hex')) try_pre_block = '\x00' * (16 - j - 1) + chr(code) + str_xor(xor_block, chr(j+1)*j) try_cipher = (try_pre_block + enc_blocks[i]).encode('hex') if is_valid(s, try_cipher): xor_code = (j+1) ^ code xor_block = chr(xor_code) + xor_block break xor_blocks.append(xor_block) xor_blocks.reverse() msg = pad('givemetheflag', AES.block_size) iv = str_xor(msg, xor_blocks[0]) cipher = (iv + enc_blocks[1]).encode('hex') data = recvuntil(s, '>> ') print data + cipher s.sendall(cipher + '\n') data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
hi there! it's the oracle again :) ok ok, I promise to give you your poodle this time - not encrypted just send me the word: givemetheflag encrypted with my secret key that only I know >:) (add padding if needed of course... I know you can do it) 1 - 0 - 0: What do you want to send me? ('nothing' to exit) >> 0000000000000000000000000000000058585858585858585858585858585858 invalid padding >:( 1 - 0 - 1: What do you want to send me? ('nothing' to exit) >> 0000000000000000000000000000000158585858585858585858585858585858 invalid padding >:( : What do you want to send me? ('nothing' to exit) >> 178d7ef913f2d24c87e9194c0f03f25c58585858585858585858585858585858 invalid padding >:( 1 - 15 - 24: 9d6ee903e2c25c97f9095c1f13e24c What do you want to send me? ('nothing' to exit) >> 188d7ef913f2d24c87e9194c0f03f25c58585858585858585858585858585858 invalid padding >:( 1 - 15 - 25: 9d6ee903e2c25c97f9095c1f13e24c What do you want to send me? ('nothing' to exit) >> 198d7ef913f2d24c87e9194c0f03f25c58585858585858585858585858585858 mmm... very interesting... thank you! What do you want to send me? ('nothing' to exit) >> 6ef4188c6e87b634f29f653d7810e14f58585858585858585858585858585858 nice work! b'ICHSA_CTF{y3s_y0u_c4n_als0_encrypt_in_tha7_w4y_a660a2}'
ICHSA_CTF{y3s_y0u_c4n_als0_encrypt_in_tha7_w4y_a660a2}