Secretive

The app uses AES to encrypt messages, with keys generated from a Linear Congruential Generator (LCG). If we are able to find previously-generated values from the LCG, then we could find the keys used to encrypt the flag.

class Secretizer:
    def __init__(self):
        self._lcg = None

    def init_app(self, app):
        self._lcg = LCG(app.config["LCG_SEED"], app.config["LCG_A"], 
                       app.config["LCG_C"], app.config["LCG_M"])
    
    ...

    def _gen_new_key(self):
        return map(lambda _: self._lcg.random(), range(4))

    def secretize_msg(self, msg):
        key = self._gen_new_key()
        key_str = self._key_to_keystr(key)
        cipher = AESCipher(key_str)
        encrypted_msg = cipher.encrypt(msg)
        return (encrypted_msg, key)

The LCG implementation is as follows.

LCGs can be quite easily broken, allowing us to find the values of a, c and m. We need to submit two messages, so that we get a pair of keys and their corresponding LCG-generated values. This is sufficient for us to find the LCG parameters.

Now, we need to reverse the LCG to find previously-generated values.

To do this, note that we can reorder the LCG next-state equation and apply the modular inverse of a to find the previous value of x.

Starting from the most recently-generated value, we can work backwards to the first 4 generated values, which would be the key used to encrypt the flag.

With the key found, we can obtain the flag!

Last updated

Was this helpful?