Exemple #1
0
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
Exemple #2
0
def is_ecb(encrypt: Callable[[bytes], bytes], blksize: int = 16) \
        -> bool:
    """
    params:
        encrypt: encrypts bytes using either ECB or CBC mode
    returns:
        True if `encrypt` used ECB mode, False otherwise
    """
    input_bytes = bytes(blksize*4)   # four blocks of zeroes
    res = encrypt(input_bytes)
    block_count = defaultdict(lambda: 0)
    for block in blocks(res, blksize):
        block_count[block] += 1
        if block_count[block] >= 3:
            return True
    return False
Exemple #3
0
def break_cbc(encrypted: bytes, iv: bytes, padding_oracle: Callable[[bytes, bytes], bool]) \
        -> bytes:
    """
    params:
        encrypted: encrypted message from `encryption_oracle()`
        iv: IV used for encryption from `encryption_oracle()`
        padding_oracle: `valid_padding()`
    returns:
        decrypted bytes for `encrypted`
    """
    decrypted = b''

    prev_blk = iv
    for curr_blk in blocks(encrypted, 16):
        decrypted += break_cbc_single_blk(prev_blk, curr_blk, padding_oracle)
        prev_blk = curr_blk

    return PKCS7Padding.unapply(decrypted)
Exemple #4
0
    def decrypt(self: CBCMode, ciphertext: bytes) -> bytes:
        """
        decrypts each block, then XORs with cipher of the previous block
        (or IV if first block)
        raises:
            ValueError: if size of `plaintext` is not divisible by
                        `self.blksize`
        """
        if len(ciphertext) % self.blksize != 0:
            raise ValueError("ciphertext is not %s-bit padded" % self.blksize)

        plaintext = b''

        prev_cipherblk = self.iv
        for block in blocks(ciphertext, self.blksize):
            plaintext += xor(self.decrypt_blk(block), prev_cipherblk)
            prev_cipherblk = block

        return plaintext
Exemple #5
0
    def encrypt(self: CBCMode, plaintext: bytes) -> bytes:
        """
        XORs each block with the cipher of the previous block (or the IV if
        first block), then encrypts
        raises:
            ValueError: if size of `plaintext` is not divisible by
                        `self.blksize`
        """
        if len(plaintext) % self.blksize != 0:
            raise ValueError("plaintext is not %s-bit padded" % self.blksize)

        ciphertext = b''

        prev_cipherblk = self.iv
        for block in blocks(plaintext, self.blksize):
            prev_cipherblk = self.encrypt_blk(xor(block, prev_cipherblk))
            ciphertext += prev_cipherblk

        return ciphertext
Exemple #6
0
def length_extension(
        message: bytes,
        newtext: bytes,
        validate_mac_oracle: Callable[[bytes], bool],
        max_keysize: int = 128) \
        -> bytes:
    """
    params:
        message: authenticated using SHA-1 keyed MAC
        newtext: text to append to message
        validate_mac_oracle: oracle that returns True if input is authenticated
                             with a valid MAC, False otherwise
        max_keysize: max keysize to try (>= 0)
    returns:
        valid authenticated message (i.e. passes `MAC.validate()`)
        with `newtext` appended to it
    """
    mac = message[:SHA1.digest_size]
    oldtext = message[SHA1.digest_size:]

    start_state = tuple(
        int.from_bytes(block, "big") for block in blocks(mac, 4)
    )
    hasher = SHA1()

    for keysize in range(max_keysize):
        glue_padding = MDPadding.apply(
            bytes(keysize)+oldtext
            )[keysize+len(oldtext):]
        hasher._message_byte_length = keysize + len(oldtext) + \
            len(glue_padding)
        hasher._h = start_state
        hasher._unprocessed = b''

        hasher.update(newtext)
        new_mac = hasher.digest()
        new_message = new_mac + oldtext + glue_padding + newtext

        if validate_mac_oracle(new_message):
            return new_message
Exemple #7
0
def main():
    """
    insert ";admin=true;" string inside encrypted message using only
    `encryption_oracle()`
    """
    encryption_oracle = gen_encryption_oracle()

    # `encryption_oracle()` prepends exactly two blocks
    # add two blocks of our own, the first block will be used to mutate the
    # second one to our desired string
    plain2 = bytes(16)
    plain3 = bytes(16)
    hack_enc = encryption_oracle(plain2 + plain3)
    hack_enc_blocks = [b for b in blocks(hack_enc, 16)]

    # knowing the blocks that are getting encrypted (we just picked them above)
    # we can figure out the AES decryption of the second block we added
    decrypted_block3 = xor(hack_enc_blocks[2], plain3)

    # the AES decryption of our block will get XORed with the previous block
    # (i.e. the first block we added)
    # simply XOR decrypted and desired to figure out what first block we need
    # to use to mutate the second block as desired
    desired_block = b"\x00\x00\x00\x00;admin=true;"
    encrypted2 = xor(decrypted_block3, desired_block)

    # remix the encrypted message by replacing the encrypted block of the first
    # block we added with the block that will give us the required mutation
    # all other blocks can remain the same as changing one block will only
    # mutate the decryption of the next one and won't change the ones further
    # down (i.e. not risk of ruining the padding down the line)
    encrypted = hack_enc_blocks[0] + hack_enc_blocks[1] + encrypted2
    for i in range(3, len(hack_enc_blocks)):
        encrypted += hack_enc_blocks[i]

    print(is_admin(encrypted))
    def test_blocks_bytes_not_padded_ignores_extraneous(self):
        b = b"ABCDEFG"
        expected_blocks = [b"AB", b"CD", b"EF"]

        for i, block in enumerate(blocks(b, 2)):
            self.assertEqual(expected_blocks[i], block)
    def test_blocks_bytes_of_proper_length(self):
        b = b"ABCDEF"
        expected_blocks = [b"AB", b"CD", b"EF"]

        for i, block in enumerate(blocks(b, 2)):
            self.assertEqual(expected_blocks[i], block)
    def test_blocks_negative_blksize_returns_empty_bytes(self):
        b = b"ABCDEF"
        expected_blocks = [b'']

        for i, block in enumerate(blocks(b, -1)):
            self.assertEqual(expected_blocks[i], block)