#!/usr/local/bin/python from Crypto.Cipher import AES from Crypto.Util.number import long_to_bytes from random import randint from binascii import hexlify with open('flag.txt','r') as f: flag = f.read().strip() with open('keyfile','rb') as f: key = f.read() assert len(key)==32 ''' Pseudorandom number generators are weak! True randomness comes from phyisical objects, like dice! ''' class TrueRNG: @staticmethod def die(): return randint(1, 6) @staticmethod def yahtzee(N): dice = [TrueRNG.die() for n in range(N)] return sum(dice) def __init__(self, num_dice): self.rolls = num_dice def next(self): return TrueRNG.yahtzee(self.rolls) def encrypt(message, key, true_rng): nonce = true_rng.next() cipher = AES.new(key, AES.MODE_CTR, nonce=long_to_bytes(nonce)) return cipher.encrypt(message) ''' Stick the flag in a random quote! ''' def random_message(): NUM_QUOTES = 25 quote_idx = randint(0,NUM_QUOTES-1) with open('quotes.txt','r') as f: for idx, line in enumerate(f): if idx == quote_idx: quote = line.strip().split() break quote.insert(randint(0, len(quote)), flag) return ' '.join(quote) banner = ''' ============================================================================ = Welcome to the yahtzee message encryption service. = = We use top-of-the-line TRUE random number generators... dice in a cup! = ============================================================================ Would you like some samples? ''' prompt = "Would you like some more samples, or are you ready to 'quit'?\n" if __name__ == '__main__': NUM_DICE = 2 true_rng = TrueRNG(NUM_DICE) inp = input(banner) while 'quit' not in inp.lower(): message = random_message().encode() encrypted = encrypt(message, key, true_rng) print('Ciphertext:', hexlify(encrypted).decode()) inp = input(prompt)
RNGのエントロピーが小さいので同じキーストリームが再生成されるのでmultiple times pad
1 - Connect to server and collect a huge amount of ciphered messages.
2 - Create buckets with messages of the same size.
3 - AES-CTR should not use the same nonce. Since the nonce is the sum of two dices, it will repeat sometimes and because of that AES(m1) ^ AES(m2) == m1 ^ m2
4 - Each message has the flag, so I XOR(m1 ^ m2, 'flag{') for all pair of messages in the same bucket and got some partial clear messages.
5 - List of messages where the flag was put at the begining:
178 - Life
180 - Every
184 - I did
190 - Life
192 - I att
194 - Defin
196 - We mu
200 - Build
204 - The q
210 - Too m
212 - The o
216 - The b
224 - Remem
6 - Since the messages are from quotes.txt, I tried to find some quote (from "Albert Einstein") that fits to what I had.
178 - Life is what we make it, always has ben, always will be