This is a challenge about length extension attack on hashes like MD5 and SHA1 that uses the Merkle-Damgard construction. More on that can be found in this link, and a good Youtube video about this topic, if you can understand Vietnamese, is this series by CyberJutsu.
The following is written under the assumption that you have some experience with the attack. In MD5 hash extension attack, it is often the case that we are allowed to extend the secret with some arbitrary data that we decide.
The code is vulnerable to hash length extension attack. The hash for a message with a new block appended can be computed from the hash of the old message, and xor with the result of encrypting the old hash with the key as the new block. Denote $H$ as the old hash, $H’$ as the new hash, the newly appended block as $B$, and encryption as $E$, we have the relation:
There are two possible solutions to this problem. One involves the method of differential cryptanalysis (normally done on the substitution box SBOX like these), and another brute-force, or more specifically birthday attack solution.
I attempt this using the birthday attack solution. The idea is that the space of state is too small. Indeed, the shuffling of the first stage of the hash:
state = [16, 32, 48, 80, 80, 96, 112, 128] for i in range(0, len(data), 4): block = data[i:i+4] state[4] ^= block[0] state[5] ^= block[1] state[6] ^= block[2] state[7] ^= block[3] state = permute(state) state = substitute(state) Only state[4:] are modified from the block’s content.
In the two keys that we have to “insert”, one has to start with the prefix of “CryptoHack Secure Safe”, and the other must not have this prefix. The two keys have to pass this check:
h1 = hashes[0] h2 = hashes[1] for i in range(2, 2**(random.randint(2, 10))): h1 = xor(self.magic1, xor(h2, xor(xor(h2, xor(h1, h2)), h2))) h2 = xor(xor(xor(h1, xor(xor(h2, h1), h1)), h1), self.magic2) ... if h1 == h2: return {"msg": f"The safe clicks and the door opens.
Thanks to JosePisco for the very useful hint of “a property md5 has on collisions”. Completely shifted my approach.
The server is signing the hash of the prime sent using RSA, and there is no information to figure out the private keys so we have to forge two numbers $n_1$ and $n_2$ such that $MD5(n_1) = MD5(n_2)$.
Initially my approach is to use fastcoll to generate MD5 collisions with a given prefix, but this approach is just too unreliable, as the probability of finding a prime with some reasonably chosen prefix is too low - fastcoll always generate 1024-bit messages anyways.