Exemple #1
0
def get_prefix_size(oracle):
    """
    Determines the size of the prefix for the encryption oracle.

    Args:
        oracle: The encryption oracle

    Returns:
        The size of the prefix and the length of the secret
    """
    controlled_bytes = b''
    original = oracle(controlled_bytes)
    controlled_bytes += b'A'
    test = oracle(controlled_bytes)
    # Find the block where the prefix ends
    prefix_block = find_prefix_block(original, test)
    start_len = (prefix_block * 16)
    # Loop through the block, looking for the end
    for i in range(15):
        controlled_bytes += b'A'
        new_test = oracle(controlled_bytes)
        if c6.get_block(new_test,
                        prefix_block) == c6.get_block(test, prefix_block):
            break
        test = new_test
    # BUG: when prefix size mod block size is 0, this is off by one. I don't care.
    return start_len + 16 - (len(controlled_bytes) -
                             1), (len(controlled_bytes) - 1) % 16
Exemple #2
0
def attack_cbc():
    """
    Breaks CBC mode when the IV is key, as described in the challenge.

    Returns:
        True if the attack worked
    """
    ct        = encrypt_userdata(b'blahblahblah')
    bad_ct    = ct[:16] + (b'\x00' * 16) + ct[:16]
    valid, pt = verify_url(bad_ct)
    k         = c2.xorstrs(c6.get_block(pt, 0), c6.get_block(pt, 2))
    return k == key
Exemple #3
0
def is_ecb(oracle, blocksize):
    """
    Detects whether or not the given encryption oracle is using ECB mode

    Args:
        oracle: The encryption oracle
        blocksize: The block size of the oracle

    Returns:
        True if oracle is using ECB mode
    """
    ct = oracle(b'A' * blocksize * 3)
    return c6.get_block(ct, 0, blocksize) == c6.get_block(ct, 1, blocksize)
Exemple #4
0
def aes_128_cbc_decrypt(txt, key, IV=b'\x00' * 16):
    """
    Decrypts the given bytestring using AES-128 in CBC mode

    Args:
        txt: The text to be decrypted
        key: The encryption key
        iv: The initialization vector

    Returns:
        The decrypted bytestring.
    """
    # Assert all size constrains
    if len(txt) % 16 != 0:
        raise ValueError('Input length must be a multiple of 16, got ' +
                         str(len(txt)))
    if len(key) != 16:
        raise ValueError('Key must be length 16, got ' + str(len(key)))
    if len(IV) != 16:
        raise ValueError('IV must be length 16, got ' + str(len(IV)))
    num_blocks = len(txt) // 16
    prev_block = IV
    result = []
    # Loop through each block, XORing with the previous
    for i in range(num_blocks):
        cur_block = c6.get_block(txt, i, 16)
        temp = cur_block
        cur_block = c7.aes_128_ecb_decrypt(cur_block, key)
        cur_block = c2.xorstrs(cur_block, prev_block)
        prev_block = temp
        result.append(cur_block)
    return b''.join(result)
Exemple #5
0
def aes_128_cbc_encrypt(txt, key, IV=b'\x00' * 16):
    """
    Encrypts a bytestring under AES-128 in CBC mode

    Args:
        txt: The plaintext to be encrypted
        key: The key to encrypt under
        iv: The initialization vector

    Returns:
        The encrypted text under AES-128 in CBC mode.
    """
    # Assert all size constraints
    if len(txt) % 16 != 0:
        raise ValueError('Input length must be a multiple of 16, got ' +
                         str(len(txt)))
    if len(key) != 16:
        raise ValueError('Key must be length 16, got ' + str(len(key)))
    if len(IV) != 16:
        raise ValueError('IV must be length 16, got ' + str(len(IV)))
    num_blocks = len(txt) // 16
    prev_block = IV
    result = []
    # Loop through each block, XORing with the previous
    for i in range(num_blocks):
        cur_block = c6.get_block(txt, i, 16)
        cur_block = c2.xorstrs(prev_block, cur_block)
        cur_block = c7.aes_128_ecb_encrypt(cur_block, key)
        prev_block = cur_block
        result.append(prev_block)
    return b''.join(result)
Exemple #6
0
def attack_block(txt, block_num):
    """
    Attacks a single block of the ciphertext.

    Args:
        txt: The full ciphertext
        block_num: The block number to attack

    Returns:
        The plaintext of this block.
    """
    plaintext  = b''
    block      = c6.get_block(txt, block_num)
    prev_b     = c6.get_block(txt, block_num - 1)
    for i in reversed(range(16)):
        p = attack_byte(block, prev_b, i, plaintext)
        plaintext = p + plaintext
    return plaintext
Exemple #7
0
def find_prefix_block(oracle, test):
    """
    Finds the block that contains the prefix

    Args:
        original: The result of encryption_oracle(b'')
        test: The result of encryption_oracle(b'A')

    Returns:
        The block number where the prefix ends

    Raises:
        RuntimeError if the block is not found
    """
    for i in range(len(oracle) // 16):
        if c6.get_block(oracle, i) != c6.get_block(test, i):
            return i
    raise RuntimeError('could not find prefix block')
Exemple #8
0
def fake_admin():
    """
    Creates a fake admin account using ECB cut-and-paste attack

    Returns:
        The decoded profile as a dictionary with dict[role] == admin
    """
    # This attack involves block alignment.
    #
    # 0123456789ABCDEF 0123456789ABCDEF 0123456789ABCDEF
    # email=sponge@bob .com&uid=2&role= user
    # email=blahblahbl adminBBBBBBBBBBB &uid=3&role=user
    # Cut and paste the blocks you want
    #      email=spongebobsquar&uid=2&role=admin0000000000B
    first_entry  = encode_profile("*****@*****.**")
    second_entry = encode_profile("blahblahbladmin" + '\x0B' * 11)
    bad_cookie   = first_entry[:32] + c6.get_block(second_entry, 1, 16)
    return decode_profile(bad_cookie)
Exemple #9
0
def is_ecb(txt, maxBlocks=1):
    """
    Determines if a given ciphertext was encrypted in ECB mode looking for
    repeated blocks.

    Args:
        txt: The ciphertext in question
        maxBlocks: The maximum number of repeated blocks allowed before it is
        considered to be ECB.

    Returns:
        True if the txt was encrypted with ECB
    """
    num_blocks = len(txt) // 16
    maxCount   = 1
    for i in range(num_blocks):
        block = c6.get_block(txt, i, 16)
        count = txt.count(block)
        if count > maxCount:
            maxCount = count
    return maxCount > maxBlocks