示例#1
0
def attack_padding_oracle(ciphertext, oracle):
    """Decrypts the given ciphertext by using the padding oracle CBC encryption attack."""
    plaintext = b''

    # Split the ciphertext in blocks of the AES block_size (which can get it from the IV too)
    ciphertext_blocks = [oracle.iv] + [ciphertext[i:i + block_size] for i in range(0, len(ciphertext), block_size)]

    for c in range(1, len(ciphertext_blocks)):
        plaintext_block = b''   # This is the part of plaintext corresponding to each ciphertext block

        # Take each character of the ciphertext block (starting from the last one)
        # and decrypt it by forcing the previous block as IV.
        for i in range(block_size - 1, -1, -1):

            # The padding len for the current character will depend on how many characters of this
            # block (starting from the right), we have already decrypted.
            padding_len = len(plaintext_block) + 1

            # Find each possible character which gives us a correct padding
            possible_last_bytes = []
            for j in range(256):

                # Create a IV with the guessed character j
                forced_iv = create_forced_previous_block(ciphertext_blocks[c - 1], j, padding_len, plaintext_block)

                # If the guessed character j gave us a working padding, save it as one of the candidates
                if oracle.decrypt_and_check_padding(ciphertext_blocks[c], forced_iv) is True:
                    possible_last_bytes += bytes([j])

            # In case of ambiguity, if we found more than one candidate, we can choose the best by trying
            # to force the next character too.
            #
            # This is useful because, for example, if we were trying to find the last character
            # of this plaintext (which was already padded):
            #
            #     123456789012/x04/x04/x04/x04
            #
            # There would be two possible last characters that form a valid padding (/x01 and /x04).
            # However if we try the next character too, we can easily choose the correct one.
            if len(possible_last_bytes) != 1:
                for byte in possible_last_bytes:
                    for j in range(256):
                        forced_iv = create_forced_previous_block(ciphertext_blocks[c - 1], j, padding_len + 1,
                                                                 bytes([byte]) + plaintext_block)

                        # If we manage to get a valid padding, then it's very likely that this
                        # candidate is the one that we want. So exclude the others and exit the loop.
                        if oracle.decrypt_and_check_padding(ciphertext_blocks[c], forced_iv) is True:
                            possible_last_bytes = [byte]
                            break

            # Got the new byte of the plaintext corresponding to the block we are decrypting,
            # add it on top of the decrypted text.
            plaintext_block = bytes([possible_last_bytes[0]]) + plaintext_block

        # Add the block we have decrypted to the final plaintext
        plaintext += plaintext_block

    # Return the unpadded plaintext bytes (in base 64)
    return pkcs7_unpad(plaintext)
示例#2
0
def aes_ecb_decrypt(data, key):
    """Decrypts the given AES-ECB encrypted data with the given key.
    The un-padding part has been added to support the use that I will make of this
    method on future challenges (for the sake of this challenge it's not needed).
    """
    cipher = AES.new(key, AES.MODE_ECB)
    return pkcs7_unpad(cipher.decrypt(data))
示例#3
0
def main():
    """Approach:
    1) Find the block_length and the encryption mode
    2) Decrypt byte-by-byte the mysterious message
    """
    secret_padding = b64decode("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGF"
                               "pciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IH"
                               "RvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK")
    oracle = ECBOracle(secret_padding)
    discovered_secret_padding = byte_at_a_time_ecb_decryption_simple(oracle)

    # Check if the attack works correctly
    assert pkcs7_unpad(discovered_secret_padding) == secret_padding
示例#4
0
def aes_cbc_decrypt(data, key, iv, unpad=True):
    """Decrypts the given AES-CBC encrypted data with the given key and iv.
    Returns the unpadded decrypted message when unpad is true, or keeps the plaintext
    padded when unpad is false.
    """
    plaintext = b''
    prev = iv

    # Process the decryption block by block
    for i in range(0, len(data), AES.block_size):
        curr_ciphertext_block = data[i:i + AES.block_size]
        decrypted_block = aes_ecb_decrypt(curr_ciphertext_block, key)
        plaintext += xor_data(prev, decrypted_block)
        prev = curr_ciphertext_block

    # Return the plaintext either unpadded or left with the padding depending on the unpad flag
    return pkcs7_unpad(plaintext) if unpad else plaintext
示例#5
0
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