def recover_key(ciphertext: bytes, ascii_oracle: Callable[[bytes], None], blksize: int = 16) \ -> bytes: """ params: ciphertext: encrypted using `encryption_method()` must be at least three blocks long ascii_oracle: `validate_ascii()` blksize: blocksize used by encryption returns: key used by encryption, or None if less than three bytes were given """ ciphertext_blocks = [block for block in blocks(ciphertext, blksize)] if len(ciphertext_blocks) < 3: return None # append last two blocks to maintain valid padding attack_ciphertext = ciphertext_blocks[0] + bytes(blksize) + \ ciphertext_blocks[0] + ciphertext_blocks[-2] + ciphertext_blocks[-1] decrypted = ascii_oracle(attack_ciphertext) if decrypted: return xor(get_block_n(decrypted, blksize, 0), get_block_n(decrypted, blksize, 2)) return None
def test_get_block_n_get_first_block(self): b = b"ABCDEF" expected_block = b"AB" actual_block = get_block_n(b, 2, 0) self.assertEqual(expected_block, actual_block)
def test_get_block_n_negative_n_returns_empty(self): b = b"ABCDEF" expected_block = b"" actual_block = get_block_n(b, 2, -1) self.assertEqual(expected_block, actual_block)
def test_get_block_n_n_larger_than_numblks_returns_empty(self): b = b"ABCDEF" expected_block = b"" actual_block = get_block_n(b, 2, 3) self.assertEqual(expected_block, actual_block)
def test_get_block_n_get_last_block_smaller_than_blksize(self): b = b"ABCDE" expected_block = b"E" actual_block = get_block_n(b, 2, 2) self.assertEqual(expected_block, actual_block)
def test_get_block_n_get_last_block_of_size_blksize(self): b = b"ABCDEF" expected_block = b"EF" actual_block = get_block_n(b, 2, 2) self.assertEqual(expected_block, actual_block)
def test_get_block_n_get_middle_block(self): b = b"ABCDEF" expected_block = b"CD" actual_block = get_block_n(b, 2, 1) self.assertEqual(expected_block, actual_block)
def main(): """ build an admin profile this must be done only by using `encrypt_profile(profile_for(input))` i.e. we may only control `input` """ trap_email = "10_PADDINGadmin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" encrypted = encrypt_profile(profile_for(trap_email)) admin_block = get_block_n(encrypted, 16, 1) valid_email = "[email protected]" encrypted = encrypt_profile(profile_for(valid_email)) admin_profile_encrypted = get_block_n(encrypted, 16, 0) + \ get_block_n(encrypted, 16, 1) + get_block_n(encrypted, 16, 2) + \ admin_block print(decode_cookie(decrypt_profile(admin_profile_encrypted)))
def break_ecb(encrypt: Callable[[bytes], bytes]) -> bytes: """ params: encrypt: encryption oracle generated from `gen_encryption_oracle` returns: `unknownstr` from the `encryption_oracle` None if `encrypt` does not use ECB mode """ input_bytes = bytes(1) prev_cipher_size = len(encrypt(input_bytes)) curr_cipher_size = prev_cipher_size while curr_cipher_size == prev_cipher_size: input_bytes += b'\x00' curr_cipher_size = len(encrypt(input_bytes)) blksize = (curr_cipher_size - prev_cipher_size) # copied determine_blksize() to also calculate expected message size decrypted_size = prev_cipher_size - len(input_bytes) if not is_ecb(encrypt, blksize): return None numblks = len(encrypt(b'')) // blksize decrypted = b'' for i in range(numblks): input_bytes = bytes(blksize - 1) for j in range(blksize): enc_blk = get_block_n(encrypt(input_bytes), blksize, i) block_to_byte = {} for k in range(256): b = k.to_bytes(1, byteorder=sys.byteorder) last_blk = get_block_n(encrypt(input_bytes + decrypted + b), blksize, i) block_to_byte[last_blk] = b decrypted += block_to_byte.get(enc_blk, b'') if len(decrypted) == decrypted_size: return decrypted input_bytes = input_bytes[:-1] return decrypted
def encrypt(self: CTRMode, plaintext: bytes) -> bytes: """ Use cipher to encrypt combination of nonce and block counter, then XORs the result with the block """ ciphertext = b'' i = 0 for blk_count in self.counter(): curr_blk = get_block_n(plaintext, self.blksize, i) if curr_blk == b'': break counter_blk = self.combine(self.nonce, blk_count, self.blksize) ciphertext += xor(curr_blk, self.encrypt_blk(counter_blk)) i += 1 return ciphertext
def main(): """ insert ";admin=true;" string inside encrypted message using only `encryption_oracle()` """ encryption_oracle = gen_encryption_oracle() desired_plaintext = b";admin=true;" # `encryption_oracle()` prepends exactly two blocks # feed some zeroes to figure out the key-cipher for the third block encrypted = encryption_oracle(bytes(len(desired_plaintext))) keycipher_block3 = get_block_n(encrypted, 16, 2) # knowing the key cipher it's easy to figure out what input we should feed # to get the decryption we desire attack_block = xor(desired_plaintext, keycipher_block3) attack_encrypted = encrypted[:32] + attack_block + \ encrypted[32+len(desired_plaintext):] print(is_admin(attack_encrypted))