def hmac_sha1(key, message): """Returns the HMAC-SHA1 for the given key and message. Written following Wikipedia pseudo-code.""" if len(key) > 64: key = unhexlify(sha1(key)) if len(key) < 64: key += b'\x00' * (64 - len(key)) o_key_pad = xor_data(b'\x5c' * 64, key) i_key_pad = xor_data(b'\x36' * 64, key) return sha1(o_key_pad + unhexlify(sha1(i_key_pad + message)))
def parameter_injection_attack(alice, bob): """Simulates a MITM key-fixing attack on Diffie-Hellman with parameter injection.""" # Step 1: Alice computes A and sends it to the MITM (thinking of Bob) A = alice.get_public_key() # Step 2: the MITM changes A with p and sends it to Bob A = alice.p # Step 3: Bob computes B and sends it to the MITM (thinking of Alice) B = bob.get_public_key() # Step 4: the MITM changes B with p and sends it to Alice B = bob.p # Step 5: Alice finally sends her encrypted message to Bob (without knowledge of MITM) _msg = b'Hello, how are you?' _a_key = unhexlify(sha1(str(alice.get_shared_secret_key(B)).encode()))[:16] _a_iv = Random.new().read(AES.block_size) a_question = aes_cbc_encrypt(_msg, _a_key, _a_iv) + _a_iv # Step 6: the MITM relays that to Bob # Step 7: Bob decrypts the message sent by Alice (without knowing of the attack), encrypts it and sends it again _b_key = unhexlify(sha1(str(bob.get_shared_secret_key(A)).encode()))[:16] _a_iv = a_question[-AES.block_size:] _a_message = aes_cbc_decrypt(a_question[:-AES.block_size], _b_key, _a_iv) _b_iv = Random.new().read(AES.block_size) b_answer = aes_cbc_encrypt(_a_message, _b_key, _b_iv) + _b_iv # Step 8: the MITM relays that to Alice # Step 9: the MITM decrypts the message (either from a_question or from b_answer, it's the same). # # Finding the key after replacing A and B with p is, in fact, very easy. # Instead of (B^a % p) or (A^b % p), the shared secret key of the exercise became (p^a % p) # and (p^b % p), both equal to zero! mitm_hacked_key = unhexlify(sha1(b'0').encode())[:16] # Hack Alice's question mitm_a_iv = a_question[-AES.block_size:] mitm_hacked_message_a = aes_cbc_decrypt(a_question[:-AES.block_size], mitm_hacked_key, mitm_a_iv) # Hack Bob's answer (which here is the same) mitm_b_iv = b_answer[-AES.block_size:] mitm_hacked_message_b = aes_cbc_decrypt(b_answer[:-AES.block_size], mitm_hacked_key, mitm_b_iv) # Check if the attack worked assert _msg == mitm_hacked_message_a == mitm_hacked_message_b
def length_extension_attack(message, original_digest, oracle): """Performs a length extension attack on the SHA1 keyed MAC, forging a variant of the given message that ends with ";admin=true". Returns the new message and its valid MAC digest. """ extra_payload = b';admin=true' # Try multiple key lengths for key_length in range(100): # Get the forged message (original-message || glue-padding || new-message) # The bytes of the key are not relevant in getting the glue padding, since we only # care about its length. Therefore we can use any key for the padding purposes. forged_message = md_pad(b'A' * key_length + message)[key_length:] + extra_payload # Get the SHA1 internal state (h1, h2, h3, h4, h5) by reversing the last step of the hash h = struct.unpack('>5I', unhexlify(original_digest)) # Compute the SHA1 hash of the extra payload, by setting the state of the SHA1 function to the # cloned one that we deduced from the original digest. # We also set the message length ml to be the total length of the message. forged_digest = sha1(extra_payload, (key_length + len(forged_message)) * 8, h[0], h[1], h[2], h[3], h[4]) # If the forged digest is valid, return it together with the forged message if oracle.validate(forged_message, forged_digest): return forged_message, forged_digest # Otherwise it means that we didn't guess correctly the key length raise Exception( "It was not possible to forge the message: maybe the key was longer than 100 characters." )
def forge_signature(message, key_length): """Forges a valid RSA signature for the given message using the Bleichenbacher's e=3 RSA Attack.""" # Prepare the block which will look like PKCS1.5 standard format to the vulnerable server block = b'\x00\x01\xff\x00' + ASN1_SHA1 + unhexlify(sha1(message)) garbage = (((key_length + 7) // 8) - len(block)) * b'\x00' block += garbage # Get the int version of the block and find its cube root (emulating the signing process) pre_encryption = int.from_bytes(block, byteorder='big') forged_sig = find_cube_root(pre_encryption) # Convert the signature to bytes and return it return int_to_bytes(forged_sig)
def verify(self, encrypted_signature, message): # Decrypt the given encrypted signature signature = b'\x00' + int_to_bytes(self.encrypt(encrypted_signature)) # Verify that the signature contains a block in PKCS1.5 standard format (vulnerable implementation) r = re.compile(b'\x00\x01\xff+?\x00.{15}(.{20})', re.DOTALL) m = r.match(signature) if not m: return False # Take the hash part of the signature and compare with the server-computed hash hashed = m.group(1) return hashed == unhexlify(sha1(message))
def main(): # Check that the implementation of DSA is correct dsa = DSA() r, s = dsa.sign(b"hello") assert dsa.verify(b"hello", r, s) # Given the following values, we can recover the private key x, when k is chosen among a small range message = b"For those that envy a MC it can be hazardous to your health\n" \ b"So be friendly, a matter of life and death, just like a etch-a-sketch\n" r = 548099063082341131477253921760299949438196259240 s = 857042759984254168557880549501802188789837994940 y = int( "84ad4719d044495496a3201c8ff484feb45b962e7302e56a392aee4abab3e4bdebf2955b4736012f21a0808" "4056b19bcd7fee56048e004e44984e2f411788efdc837a0d2e5abb7b555039fd243ac01f0fb2ed1dec56828" "0ce678e931868d23eb095fde9d3779191b8c0299d6e07bbb283e6633451e535c45513b2d33c99ea17", 16) # Verify that the attack works and that the hacked private key produces the correct fingerprint hacked_x = key_recovery_from_nonce(r, s, DSA.H(message), y) c = hex(hacked_x)[2:].encode() assert sha1(c) == "0954edd5e0afe5542a4adf012611a91912a3ec16"
def H(message): return int(sha1(message), 16)
def malicious_g_attack(): """Simulates the break of Diffie-Hellman with negotiated groups by using malicious 'g' parameters.""" p = DiffieHellman.DEFAULT_P for g in [1, p, p - 1]: # Step 1: the MITM changes the default g sent by Alice to Bob with a forced value alice = DiffieHellman() bob = DiffieHellman(g=g) # Step 2: Bob receives this forced g and sends an ACK to Alice # Step 3: Alice computes A and sends it to the MITM (thinking of Bob) A = alice.get_public_key() # Step 4: Bob computes B and sends it to the MITM (thinking of Alice) B = bob.get_public_key() # Step 5: Alice sends her encrypted message to Bob (without knowledge of MITM) _msg = b'Hello, how are you?' _a_key = unhexlify(sha1(str( alice.get_shared_secret_key(B)).encode()))[:16] _a_iv = Random.new().read(AES.block_size) a_question = aes_cbc_encrypt(_msg, _a_key, _a_iv) + _a_iv # Step 6: Bob receives the message sent by Alice (without knowing of the attack) # However, this time Bob will not be able to decrypt it, because (if I understood the # challenge task correctly) Alice and Bob now use different values of g. # Step 7: the MITM decrypts the Alice's question mitm_a_iv = a_question[-AES.block_size:] # When g is 1, the secret key is also 1 if g == 1: mitm_hacked_key = unhexlify(sha1(b'1').encode())[:16] mitm_hacked_message = aes_cbc_decrypt(a_question[:-AES.block_size], mitm_hacked_key, mitm_a_iv) # When g is equal to p, it works the same as in the S5C34 attack (the secret key is 0) elif g == p: mitm_hacked_key = unhexlify(sha1(b'0').encode())[:16] mitm_hacked_message = aes_cbc_decrypt(a_question[:-AES.block_size], mitm_hacked_key, mitm_a_iv) # When g is equal to p - 1, the secret key is (-1)^(ab), which is either (+1 % p) or (-1 % p). # We can try both and later check the padding to see which one is correct. else: for candidate in [str(1).encode(), str(p - 1).encode()]: mitm_hacked_key = unhexlify(sha1(candidate).encode())[:16] mitm_hacked_message = aes_cbc_decrypt( a_question[:-AES.block_size], mitm_hacked_key, mitm_a_iv, unpad=False) if is_pkcs7_padded(mitm_hacked_message): mitm_hacked_message = pkcs7_unpad(mitm_hacked_message) break # Check if the attack worked assert _msg == mitm_hacked_message