この大会は2024/12/13 21:00(JST)~2024/12/15 21:00(JST)に開催されました。
今回もチームで参戦。結果は110点で1174チーム中251位でした。
自分で解けた問題をWriteupとして書いておきます。
Warmup (Miscellaneous)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
nite{n1t3_1n_sh1n1ng_arm0ur}
RSAabc (Cryptography)
暗号処理の概要は以下の通り
・language: 'A'~'Z', 'a'~'z'に対する文字を辞書型で定義 ・ct=[] ・nlist=[] ・flagの各文字chに対して、以下を実行 ・chのASCIIコードが素数以外の場合 ・transformed_char = reverse_alphabet(ch) ・transformed_charをout.txtに書き込み ・pre: chのASCIIコードを26で割った余り ・pre: preが5より小さい場合、5プラス ・p: preビット素数 ・q: 1024ビット素数 ・n = p * q ・phi_n = (p - 1) * (q - 1) ・e = 65537 ・d = sp.mod_inverse(e, phi_n) ・pubkey = (n, e) ・privkey = (n, d) ・message = ch ・ciphertext = rsa_encrypt(message, pubkey) ・n, e = public_key ・message_as_int: messageのASCIIコード ・ciphertext = pow(message_as_int, e, n) ・ciphertextを返却 ・chのASCIIコードが素数の場合 ・chが英大文字の場合 ・eng=chr(65+ciphertext%26) ・lan=language.get(eng) ・lanをout.txtに書き込み ・ciphertext=googly(ciphertext,ciphertext.bit_length()-ord(eng)) ・chが英小文字の場合 ・eng=chr(97+ciphertext%26) ・lan=language.get(eng) ・lanをout.txtに書き込み ・ciphertext=googly(ciphertext,ciphertext.bit_length()-ord(eng)) ・ctにciphertextを追加 ・nlistにnを追加 ・ctの各要素を","区切りで出力 ・nlistの各要素を","区切りで出力
1文字ずつブルートフォースで条件を満たすものを探す。
#!/usr/bin/env python3 import sympy as sp from Crypto.Util.number import * language = { 'A': 'Α', 'a': 'α', 'B': 'Β', 'b': 'β', 'C': 'Σ', 'c': 'σ', 'D': 'Δ', 'd': 'δ', 'E': 'Ε', 'e': 'ε', 'F': 'Φ', 'f': 'φ', 'G': 'Γ', 'g': 'γ', 'H': 'Η', 'h': 'η', 'I': 'Ι', 'i': 'ι', 'J': 'Ξ', 'j': 'ξ', 'K': 'Κ', 'k': 'κ', 'L': 'Λ', 'l': 'λ', 'M': 'Μ', 'm': 'μ', 'N': 'Ν', 'n': 'ν', 'O': 'Ο', 'o': 'ο', 'P': 'Π', 'p': 'π', 'Q': 'Θ', 'q': 'θ', 'R': 'Ρ', 'r': 'ρ', 'S': 'Σ', 's': 'ς', 'T': 'Τ', 't': 'τ', 'U': 'Υ', 'u': 'υ', 'V': 'Ω', 'v': 'ω', 'W': 'Ψ', 'w': 'ψ', 'X': 'Χ', 'x': 'χ', 'Y': 'Υ', 'y': 'υ', 'Z': 'Ζ', 'z': 'ζ' } def googly(number, position): mask = 1 << position return number ^ mask def string_to_int(message): return int.from_bytes(message.encode('utf-8'), byteorder='big') def rsa_encrypt(message, public_key): n, e = public_key message_as_int = string_to_int(message) ciphertext = pow(message_as_int, e, n) return ciphertext def reverse_alphabet(char): if char.isupper(): return chr(155 - ord(char)) elif char.islower(): return chr(219 - ord(char)) elif char == '_' or '{' or '}': return 'e' with open('out.txt', 'r', encoding='utf-8') as f: params = f.read().splitlines() out = params[0] ct = list(map(int, params[2][:-1].split(', '))) nlist = list(map(int, params[4].split(', ')[:-1])) flag = '' for i in range(len(out)): for code in range(32, 127): if not sp.isprime(code): transformed_char = reverse_alphabet(chr(code)) if transformed_char != out[i]: continue n = nlist[i] e = 65537 pubkey = (n, e) message = chr(code) ciphertext = rsa_encrypt(message, pubkey) if sp.isprime(code): if chr(code).isupper(): eng = chr(65 + ciphertext % 26) else: eng = chr(97 + ciphertext % 26) lan = language.get(eng) if lan != out[i]: continue ciphertext = googly(ciphertext, ciphertext.bit_length() - ord(eng)) if ciphertext == ct[i]: flag += chr(code) break print(flag)
nite{quICklY_grab_the_codE5_sgOqkA}
La Casa de Papel (Cryptography)
サーバの処理概要は以下の通り。
・secret_key = secret().encode() ※長さ21の文字列 ・menu(secret_key) ・以下繰り返し ・choice: 入力 ・choiceが"1"の場合 ・practice_convo(secret) ・message: 入力 ・hash = md5(secret, message) ・hash: secret + messageのmd5ダイジェストの16進数表記 ・hashのbase64エンコード文字列を返却 ・hashを表示 ・choiceが"2"の場合 ・fool_alice(secret) ・user_name: 入力 ・user_hmac: 入力 ・user_nameに"Bob"が含まれている場合 ・hash: md5(secret, user_name)のbase64デコード文字列 ・user_hmacがhashと一致する場合 ・secret.txtの内容を表示 ・choiceが"3"の場合 ・crack_the_vault() ・passs: 入力 ・secret_content: secret.txtの内容 ・passsとsecret_contentが一致する場合、flag.txtの内容を表示
1で取得したhashをbase64デコードし、2のHMACで指定すれば、secret.txtの内容が取得できる。あとは3でその内容を指定すればフラグを取得できる。
$ ncat --ssl la-casa-de-papel.chals.nitectf2024.live 1337 _ ____ _ ____ _ | | __ _ / ___|__ _ ___ __ _ __| | ___ | _ \ __ _ _ __ ___| | | | / _` | | | / _` / __|/ _` | / _` |/ _ \ | |_) / _` | '_ \ / _ \ | | |__| (_| | | |__| (_| \__ \ (_| | | (_| | __/ | __/ (_| | |_) | __/ | |_____\__,_| \____\__,_|___/\__,_| \__,_|\___| |_| \__,_| .__/ \___|_| |_| 1. Practice Convo 2. Let's Fool Alice! 3. Crack the Vault 4. Exit Choose an option: 1 Send a message: Bob Here is your encrypted message: YjRlMGE4MDI0MjhjYjM1ZjY5YzBlOTUyZDk2MTcyZDY=
ここでbase64デコードする。
>>> from base64 import *
>>> b64decode('YjRlMGE4MDI0MjhjYjM1ZjY5YzBlOTUyZDk2MTcyZDY=')
b'b4e0a802428cb35f69c0e952d96172d6'1. Practice Convo 2. Let's Fool Alice! 3. Crack the Vault 4. Exit Choose an option: 2 Bot: Okay, let's see if you're the real deal. What's your name? Your name: Bob Bot: Please provide your HMAC Your HMAC: b4e0a802428cb35f69c0e952d96172d6 Alice: Oh hey Bob! Here is the vault code you wanted: G0t_Th3_G0ld_B3rl1nale 1. Practice Convo 2. Let's Fool Alice! 3. Crack the Vault 4. Exit Choose an option: 3 Vault Person: Enter password Password: G0t_Th3_G0ld_B3rl1nale Vault Unlocked! The flag is: nite{El_Pr0f3_0f_Prec1s10n_Pl4ns}
nite{El_Pr0f3_0f_Prec1s10n_Pl4ns}