def main():
    ciphertext, iv = get_random_ciphertext()
    target_plaintext = decrypt_aes_128_cbc(ciphertext, KEY, iv)
    print(ciphertext.encode('hex'))
    print(target_plaintext.encode('hex'))

    # Detects byte one by one with the padding oracle leaked information.
    decoded_message = ''
    previous_encrypted_block = iv
    num_blocks = len(ciphertext) / BLOCK_SIZE
    for block_idx in range(num_blocks):
        block = ciphertext[block_idx * BLOCK_SIZE:(block_idx + 1) * BLOCK_SIZE]
        print('Expected Block: %s' %
              target_plaintext[block_idx * BLOCK_SIZE:(block_idx + 1) *
                               BLOCK_SIZE].encode('hex'))
        print('Target Block: %s' % block.encode('hex'))
        intermediate_decoded_last_bytes = last_byte_oracle(
            padding_oracle, block)
        print('Decoded last bytes: %s' %
              intermediate_decoded_last_bytes.encode('hex'))
        intermediate_decoded_block = block_decrypt_oracle(
            padding_oracle, block, intermediate_decoded_last_bytes)
        decoded_block = xor(intermediate_decoded_block,
                            previous_encrypted_block)
        previous_encrypted_block = block
        print('Decoded block: %s\n' % decoded_block.encode('hex'))
        decoded_message += decoded_block

    print('Decoded message: %s' % decoded_message.decode('base64'))
def main():
    keystream_length = max(len(c) for c in ciphertexts)
    keystream_guessed = ['\x00' for i in range(keystream_length)]
    longest_ciphertext = max(ciphertexts, key=lambda x: len(x))
    #longest_ciphertext = ciphertexts[0]
    for keystream_byte_idx in range(keystream_length):
        for c in VALID_LETTERS:
            keystream_byte = single_char_xor(
                c, longest_ciphertext[keystream_byte_idx])
            plaintext_byte_guesses = []
            for ct in ciphertexts[1:]:
                if keystream_byte_idx >= len(ct):
                    # Skip short ciphertexts
                    continue
                plaintext_byte = single_char_xor(keystream_byte,
                                                 ct[keystream_byte_idx])
                plaintext_byte_guesses.append(plaintext_byte)
            if all(b in VALID_LETTERS for b in plaintext_byte_guesses):
                keystream_guessed[keystream_byte_idx] = keystream_byte
                break
        else:
            print('No satisfying letter...')
            raw_input()

        # Decode all of them
        print('\nDECODING')
        for idx, ct in enumerate(ciphertexts):
            decoded_length = min(len(ct), keystream_byte_idx + 1)
            truncated_keystream = ''.join(keystream_guessed)[:decoded_length]
            truncated_ciphertext = ct[:decoded_length]
            plaintext = xor(truncated_keystream, truncated_ciphertext)
            print('#%d: "%s"' % (idx, plaintext))
def block_decrypt_oracle(oracle, ciphertext_block, decoded_last_bytes):
    """
  Implements the "Block Decryption Oracle" from Serge Vaudenay original paper on the exploit.
  Source: https://www.iacr.org/archive/eurocrypt2002/23320530/cbc02_e02d.pdf
  """
    block_size = len(ciphertext_block)
    iv = '\x00' * block_size
    decoded_bytes = decoded_last_bytes
    while len(decoded_bytes) < block_size:
        j = block_size - 1 - len(decoded_bytes)
        pad_byte = chr(len(decoded_bytes) + 1)
        random_bytes_block = Random.new().read(block_size - len(decoded_bytes))
        random_bytes_block += xor(decoded_bytes, pad_byte * len(decoded_bytes))
        for i in range(0, 256):
            forged_block = random_bytes_block[:j]
            forged_block += single_char_xor(random_bytes_block[j], chr(i))
            forged_block += random_bytes_block[j + 1:]
            forged_ciphertext = forged_block + ciphertext_block
            try:
                oracle(forged_ciphertext, iv)
            except ValueError as inst:
                # Wrong padding, go to next byte.
                continue

            # Valid padding found.
            new_decoded_byte = single_char_xor(forged_block[j], pad_byte)
            decoded_bytes = new_decoded_byte + decoded_bytes
    return decoded_bytes
def encrypt_aes_128_cbc(plaintext, key, iv, block_size=16):
    cipher = AES.new(key)
    ciphertexts = []
    plaintext = pkcs_pad(plaintext, block_size=block_size)
    for block_idx in range(len(plaintext) / block_size):
        block = plaintext[block_idx * block_size:(block_idx + 1) * block_size]
        block = xor(block, iv)
        block = cipher.encrypt(block)
        ciphertexts.append(block)
        iv = block
    return ''.join(ciphertexts)
def decrypt_aes_128_cbc(ciphertext, key, iv, block_size=16):
    cipher = AES.new(key)
    plaintexts = []
    for block_idx in range(len(ciphertext) / block_size):
        block = ciphertext[block_idx * block_size:(block_idx + 1) * block_size]
        next_iv = block
        block = cipher.decrypt(block)
        block = xor(block, iv)
        plaintexts.append(block)
        iv = next_iv
    plaintext = ''.join(plaintexts)
    plaintext = pkcs_unpad(plaintext)
    return plaintext
def padding_oracle(ciphertext, iv):
    """Raises exception if the padding is not valid."""
    block_size = BLOCK_SIZE
    cipher = AES.new(KEY)
    plaintexts = []
    for block_idx in range(len(ciphertext) / block_size):
        block = ciphertext[block_idx * block_size:(block_idx + 1) * block_size]
        next_iv = block[:]
        block = cipher.decrypt(block)
        block = xor(block, iv)
        plaintexts.append(block)
        iv = next_iv
    plaintext = ''.join(plaintexts)
    validate_pkcs_7_pad(plaintext)
    plaintext = pkcs_unpad(plaintext)
    return plaintext
def encrypt_decrypt_aes_128_ctr(key, text, nonce):
    """Encrypt/Decrypt the given text."""
    block_size = 16
    nonce = struct.pack('<Q', nonce)
    assert len(nonce) == block_size / 2
    cipher = AES.new(key)
    ciphertexts = []
    counter = 0
    num_blocks = int(math.ceil(float(len(text)) / block_size))
    for block_idx in range(num_blocks):
        block = text[block_idx * block_size:(block_idx + 1) * block_size]
        keystream = cipher.encrypt(nonce + struct.pack('<Q', counter))
        keystream = keystream[:len(block)]
        block = xor(block, keystream)
        ciphertexts.append(block)
        counter += 1
    return ''.join(ciphertexts)
Exemple #8
0
def main():
  injection_input = 'a;admin=true'
  ciphertext, iv = encrypt_input(injection_input)
  print('Successfully authenticated: %s' % decrypt_and_check_for_admin(ciphertext, iv))

  # Corrupts the ciphertext at the place we have control on the plaintext.
  # NB: the prefix has a 32 bytes length.
  prefix_size = 32  # TODO: determine this dynamically
  exploit_input = '\x00' * len(injection_input)
  target_ciphertext, iv = encrypt_input(exploit_input)

  # Selects the block before the one we ant to corrupt.
  target_ciphertext_block = target_ciphertext[(prefix_size - BLOCK_SIZE):prefix_size]

  # Corrupts the ciphertext-block with the desired output after the decrypting XOR operation.
  corrupted_ciphertext_block = xor(target_ciphertext_block, injection_input + target_ciphertext_block[len(injection_input):])

  # Recompiles the complete ciphertext to get admin access.
  corrupted_ciphertext = target_ciphertext[:(prefix_size - BLOCK_SIZE)] + corrupted_ciphertext_block + target_ciphertext[prefix_size:]
  print('Successfully authenticated: %s' % decrypt_and_check_for_admin(corrupted_ciphertext, iv))
def last_byte_oracle(oracle, ciphertext_block):
    """
  Implements the "Last Word Oracle" from Serge Vaudenay original paper on the exploit.
  Source: https://www.iacr.org/archive/eurocrypt2002/23320530/cbc02_e02d.pdf
  """
    block_size = len(ciphertext_block)
    iv = '\x00' * block_size
    random_bytes_block = Random.new().read(block_size)
    target_byte_index = len(random_bytes_block) - 1
    for i in range(0, 256):
        forged_block = random_bytes_block[:target_byte_index]
        forged_block += single_char_xor(random_bytes_block[target_byte_index],
                                        chr(i))
        forged_ciphertext = forged_block + ciphertext_block
        try:
            oracle(forged_ciphertext, iv)
        except ValueError as inst:
            # Wrong padding, go to next byte.
            continue

        # Validates if the correct padding is \x01 or \x02\x02 or \x03\x03\x03, etc.
        for n in range(target_byte_index, 0, -1):
            forged_block_copy = forged_block[:]
            forged_block_copy = forged_block[:(target_byte_index - n)]
            forged_block_copy += single_char_xor(
                forged_block[target_byte_index - n], '\x01')
            forged_block_copy += forged_block[(target_byte_index - n + 1):]
            forged_ciphertext = forged_block_copy + ciphertext_block
            try:
                oracle(forged_ciphertext, iv)
            except ValueError as inst:
                # Wrong padding, meaning we've detected the correct padding
                detected_padding = forged_block_copy[(target_byte_index - n):]
                return xor(detected_padding, '\x00' * len(detected_padding))

        # Nothing found, the valid padding is \x01.
        return single_char_xor(forged_block[target_byte_index], '\x01')