コード例 #1
0
def challenge_14() -> object:
    """
    Take your oracle function from #12. Now generate a random count of random bytes and prepend this string to every
    plaintext. You are now doing:

    AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)

    :return:
    """

    base64_encoded = (
        "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGll"
        "cyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK"
    )
    base64_decoded = binascii.a2b_base64(base64_encoded)
    random_aes_key = cryptopals.generate_random_bytes(16)
    random_prepend = cryptopals.generate_random_bytes(random.randint(1, 15))
    encrypted_blocks = cryptopals.encrypt_ecb_oracle(
        b"", base64_decoded, random_aes_key, prepend=random_prepend
    )

    # print("Padding: {}".format(obtain_ecb_pkcs7_count(base64_decoded, random_aes_key, prepend=random_prepend)))
    cryptopals.obtain_ecb_prepend_padding_count(
        base64_decoded, random_aes_key, prepend=random_prepend
    )
    # print(decrypt_aes(b''.join(encrypted_blocks), random_aes_key))
    # print("Original Encrypted Blocks: {}".format(len(encrypted_blocks)))

    result = cryptopals.decrypt_ecb_message_without_key(
        encrypted_blocks, base64_decoded, random_aes_key, prepend=random_prepend
    )
    assert base64_decoded == result.strip(
        b"\x01"
    ), "Decryption failed! {} != {}".format(base64_decoded, result)
コード例 #2
0
def challenge_14() -> object:
    """
    Take your oracle function from #12. Now generate a random count of random bytes and prepend this string to every
    plaintext. You are now doing:

    AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)

    :return:
    """

    base64_encoded = (
        "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGll"
        "cyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK"
    )
    base64_decoded = binascii.a2b_base64(base64_encoded)
    random_aes_key = cryptopals.generate_random_bytes(16)
    random_prepend = cryptopals.generate_random_bytes(random.randint(1, 15))
    encrypted_blocks = cryptopals.encrypt_ecb_oracle(b"",
                                                     base64_decoded,
                                                     random_aes_key,
                                                     prepend=random_prepend)

    # print("Padding: {}".format(obtain_ecb_pkcs7_count(base64_decoded, random_aes_key, prepend=random_prepend)))
    cryptopals.obtain_ecb_prepend_padding_count(base64_decoded,
                                                random_aes_key,
                                                prepend=random_prepend)
    # print(decrypt_aes(b''.join(encrypted_blocks), random_aes_key))
    # print("Original Encrypted Blocks: {}".format(len(encrypted_blocks)))

    result = cryptopals.decrypt_ecb_message_without_key(encrypted_blocks,
                                                        base64_decoded,
                                                        random_aes_key,
                                                        prepend=random_prepend)
    assert base64_decoded == result.strip(
        b"\x01"), "Decryption failed! {} != {}".format(base64_decoded, result)
コード例 #3
0
    def encrypt_oracle(text: bytes):
        random_aes_key = cryptopals.generate_random_bytes(16)
        prefix = cryptopals.generate_random_bytes(random.randint(5, 10))
        postfix = cryptopals.generate_random_bytes(random.randint(5, 10))

        message = b"".join([prefix, text, postfix])

        if random.randint(1, 2) == 2:
            encoding_type = "ECB"
            # encrypt ECB
            keysize = len(random_aes_key)
            text = cryptopals.pkcs_7_padding(text, keysize)
            blocks = [text[n : n + keysize] for n in range(0, len(text), keysize)]
            encrypted = []
            for block in blocks:
                text = cryptopals.encrypt_aes(block, random_aes_key)
                # print(binascii.hexlify(text))
                encrypted.append(text)
        else:
            # encrypt_CBC
            encoding_type = "CBC"
            random_iv = cryptopals.generate_random_bytes(16)
            encrypted = cryptopals.encrypt_aes_with_custom_cbc(
                message, random_aes_key, random_iv
            )

        return encrypted, encoding_type
コード例 #4
0
    def encrypt_oracle(text: bytes):
        random_aes_key = cryptopals.generate_random_bytes(16)
        prefix = cryptopals.generate_random_bytes(random.randint(5, 10))
        postfix = cryptopals.generate_random_bytes(random.randint(5, 10))

        message = b"".join([prefix, text, postfix])

        if random.randint(1, 2) == 2:
            encoding_type = "ECB"
            # encrypt ECB
            keysize = len(random_aes_key)
            text = cryptopals.pkcs_7_padding(text, keysize)
            blocks = [
                text[n:n + keysize] for n in range(0, len(text), keysize)
            ]
            encrypted = []
            for block in blocks:
                text = cryptopals.encrypt_aes(block, random_aes_key)
                # print(binascii.hexlify(text))
                encrypted.append(text)
        else:
            # encrypt_CBC
            encoding_type = "CBC"
            random_iv = cryptopals.generate_random_bytes(16)
            encrypted = cryptopals.encrypt_aes_with_custom_cbc(
                message, random_aes_key, random_iv)

        return encrypted, encoding_type
コード例 #5
0
    class Server:
        iv = cryptopals.generate_random_bytes(16)
        key = cryptopals.generate_random_bytes(16)
        test_data = [
            b"MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc =",
            b"MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIHB1bXBpbic =",
            b"MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw ==",
            b"MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg ==",
            b"MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl",
            b"MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA ==",
            b"MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw ==",
            b"MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8 =",
            b"MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g =",
            b"MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93",
        ]

        def get_encrypted_blocks(self):
            # The first function should
            # - select at random one of ten strings
            # - generate a random AES key (which it should save for all future encryptions),
            # - pad the string out to the 16-byte AES block size and
            # - CBC-encrypt it under that key,
            # - providing the caller the ciphertext and IV.

            # grab a random string
            random_string = self.test_data[random.randrange(
                0, len(self.test_data))]

            print("-" * 128)
            print("PRIOR TO ENCRYPTION: {}".format(random_string))
            print("PRIOR TO ENCRBASE64: {}".format(
                binascii.a2b_base64(random_string)))

            # encrypt using CBC
            encrypted_blocks = cryptopals.encrypt_aes_with_custom_cbc(
                random_string, self.key, self.iv)

            return encrypted_blocks

        def decrypt_cookie(self, ciphertext):
            # Consume the ciphertext
            # decrypt it,
            # check its padding, and
            # return true or false depending on whether the padding is valid.
            blocks = cryptopals.decrypt_aes_with_custom_cbc(
                ciphertext, self.key, self.iv)
            message = b"".join(blocks)
            # print("{} (Decrypted)".format(message))

            try:
                cryptopals.pkcs_7_padding_verification(message)
            except ValueError:
                return False

            return True
コード例 #6
0
def challenge_20() -> None:
    """
    Using 20.txt, find a similar set of Base64'd plaintext. Do with them exactly what you did with the first, but
    solve the problem differently.  Instead of making spot guesses at to known plaintext, treat the collection of
    ciphertexts the same way you would repeating-key XOR.

    Obviously, CTR encryption appears different from repeated-ke XOR, but with a fixed nonce they are effectively
    the same thing.

    To exploit this:
    1. take your collection of ciphertexts and
    2. truncate them to a common length (the length of the smallest ciphertext will work).

    Solve the resulting concatenation of ciphertexts as if for repeating- key XOR, with a key size of the length of
    the ciphertext you XOR'd.
    """
    key = cryptopals.generate_random_bytes(16)
    nonce = 0

    with open("data/20.txt", "r") as handle:
        lines = [
            binascii.a2b_base64(line.strip()) for line in handle.readlines()
        ]
        crypts = [
            cryptopals.aes_with_custom_ctr(line, key, nonce) for line in lines
        ]

    # min_length = len(max(crypts, key=len))
    blocks = [crypt for crypt in crypts]

    transposed = cryptopals.transpose(blocks)

    # check each index for possible hits
    # if more than one hit -- check for score.

    keys = {}
    for index, block in enumerate(transposed):
        keys[index] = []
        for guess in range(255):
            items = [(item ^ guess) for item in block]
            count = [
                item for item in items
                if chr(item) in (string.ascii_letters + " ,.'?:-;")
            ]
            # if index == 10:
            #    print(len(items), len(count), [chr(item) for item in items])

            keys[index].append([len(count), bytes([guess])])

    key = []
    for index in keys:
        key.append(max(keys[index], key=lambda x: x[0])[1])

    result = b"".join(key)
    print(result)

    for block in blocks:
        print(cryptopals.decrypt_xor(block, result))
コード例 #7
0
def challenge_12() -> None:
    """
    Byte - at - a - time

    ECB decryption(Simple)
    Copy your oracle function to a new function that encrypts buffers under ECB mode using a
    consistent but unknown key (for instance, assign a single random key, once, to a global variable).

    Now take that same function and have it append to the plaintext, BEFORE ENCRYPTING, the
    following string:

    Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK

    Spoiler alert. Do not decode this string now. Don't do it.

    Base64 decode the string before appending it. Do not base64
    decode the string by hand; make your code do it.The point is that you don't know its contents.

    What you have now is a function that produces:

    AES - 128 - ECB(your - string | | unknown - string, random - key)

    It turns out: you can decrypt "unknown-string" with repeated calls to the oracle function!

    Here's roughly how:

    Feed identical bytes of your-string to the function 1 at a time
    --- start with 1 byte ("A"), then "AA", then "AAA" and so on.

    1. Discover the block size of the cipher. You know it, but do this step anyway
    2. Detect that the function is using ECB. You already know, but do this step anyways.
    3. Knowing the block size, craft an input block that is exactly 1 byte short (for instance, if the block size is
       8 bytes, make "AAAAAAA"). Think about what the oracle function is going to put in that last byte position.
    4. Make a dictionary of every possible last byte by feeding different strings to the oracle; for instance,
       "AAAAAAAA", "AAAAAAAB", "AAAAAAAC", remembering the first block of each invocation.
    5. Match the output of the one-byte-short input to one of the entries in your dictionary. You've now discovered
       the first byte of unknown-string.
    6. Repeat for the next byte.
    """
    base64_encoded = (
        "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGll"
        "cyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK"
    )
    base64_decoded = binascii.a2b_base64(
        base64_encoded)  #  base64.b64decode(base64_encoded)
    random_aes_key = cryptopals.generate_random_bytes(16)

    encrypted_blocks = cryptopals.encrypt_ecb_oracle(b"", base64_decoded,
                                                     random_aes_key)

    result = cryptopals.decrypt_ecb_message_without_key(
        encrypted_blocks, base64_decoded, random_aes_key)

    assert (
        result ==
        b"Rollin' in my 5.0\nWith my rag-top down so my hair can blow\nThe girlies on standby"
        b" waving just to say hi\nDid you stop? No, I just drove by\n\x01"
    ), "Decryption Failed!"
コード例 #8
0
def challenge_20() -> None:
    """
    Using 20.txt, find a similar set of Base64'd plaintext. Do with them exactly what you did with the first, but
    solve the problem differently.  Instead of making spot guesses at to known plaintext, treat the collection of
    ciphertexts the same way you would repeating-key XOR.

    Obviously, CTR encryption appears different from repeated-ke XOR, but with a fixed nonce they are effectively
    the same thing.

    To exploit this:
    1. take your collection of ciphertexts and
    2. truncate them to a common length (the length of the smallest ciphertext will work).

    Solve the resulting concatenation of ciphertexts as if for repeating- key XOR, with a key size of the length of
    the ciphertext you XOR'd.
    """
    key = cryptopals.generate_random_bytes(16)
    nonce = 0

    with open("data/20.txt", "r") as handle:
        lines = [binascii.a2b_base64(line.strip()) for line in handle.readlines()]
        crypts = [cryptopals.aes_with_custom_ctr(line, key, nonce) for line in lines]

    # min_length = len(max(crypts, key=len))
    blocks = [crypt for crypt in crypts]

    transposed = cryptopals.transpose(blocks)

    # check each index for possible hits
    # if more than one hit -- check for score.

    keys = {}
    for index, block in enumerate(transposed):
        keys[index] = []
        for guess in range(255):
            items = [(item ^ guess) for item in block]
            count = [
                item
                for item in items
                if chr(item) in (string.ascii_letters + " ,.'?:-;")
            ]
            # if index == 10:
            #    print(len(items), len(count), [chr(item) for item in items])

            keys[index].append([len(count), bytes([guess])])

    key = []
    for index in keys:
        key.append(max(keys[index], key=lambda x: x[0])[1])

    result = b"".join(key)
    print(result)

    for block in blocks:
        print(cryptopals.decrypt_xor(block, result))
コード例 #9
0
def challenge_19() -> None:
    """
    Attack this cryptosystem piecemeal: guess letters, use expected English language frequence to validate guesses,
    catch common English trigrams, and so on.
    :return:
    """
    lines = [
        "SSBoYXZlIG1ldCB0aGVtIGF0IGNsb3NlIG9mIGRheQ==",
        "Q29taW5nIHdpdGggdml2aWQgZmFjZXM=",
        "RnJvbSBjb3VudGVyIG9yIGRlc2sgYW1vbmcgZ3JleQ==",
        "RWlnaHRlZW50aC1jZW50dXJ5IGhvdXNlcy4=",
        "SSBoYXZlIHBhc3NlZCB3aXRoIGEgbm9kIG9mIHRoZSBoZWFk",
        "T3IgcG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==",
        "T3IgaGF2ZSBsaW5nZXJlZCBhd2hpbGUgYW5kIHNhaWQ=",
        "UG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==",
        "QW5kIHRob3VnaHQgYmVmb3JlIEkgaGFkIGRvbmU=",
        "T2YgYSBtb2NraW5nIHRhbGUgb3IgYSBnaWJl",
        "VG8gcGxlYXNlIGEgY29tcGFuaW9u",
        "QXJvdW5kIHRoZSBmaXJlIGF0IHRoZSBjbHViLA==",
        "QmVpbmcgY2VydGFpbiB0aGF0IHRoZXkgYW5kIEk=",
        "QnV0IGxpdmVkIHdoZXJlIG1vdGxleSBpcyB3b3JuOg==",
        "QWxsIGNoYW5nZWQsIGNoYW5nZWQgdXR0ZXJseTo=",
        "QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=",
        "VGhhdCB3b21hbidzIGRheXMgd2VyZSBzcGVudA==",
        "SW4gaWdub3JhbnQgZ29vZCB3aWxsLA==",
        "SGVyIG5pZ2h0cyBpbiBhcmd1bWVudA==",
        "VW50aWwgaGVyIHZvaWNlIGdyZXcgc2hyaWxsLg==",
        "V2hhdCB2b2ljZSBtb3JlIHN3ZWV0IHRoYW4gaGVycw==",
        "V2hlbiB5b3VuZyBhbmQgYmVhdXRpZnVsLA==",
        "U2hlIHJvZGUgdG8gaGFycmllcnM/",
        "VGhpcyBtYW4gaGFkIGtlcHQgYSBzY2hvb2w=",
        "QW5kIHJvZGUgb3VyIHdpbmdlZCBob3JzZS4=",
        "VGhpcyBvdGhlciBoaXMgaGVscGVyIGFuZCBmcmllbmQ=",
        "V2FzIGNvbWluZyBpbnRvIGhpcyBmb3JjZTs=",
        "SGUgbWlnaHQgaGF2ZSB3b24gZmFtZSBpbiB0aGUgZW5kLA==",
        "U28gc2Vuc2l0aXZlIGhpcyBuYXR1cmUgc2VlbWVkLA==",
        "U28gZGFyaW5nIGFuZCBzd2VldCBoaXMgdGhvdWdodC4=",
        "VGhpcyBvdGhlciBtYW4gSSBoYWQgZHJlYW1lZA==",
        "QSBkcnVua2VuLCB2YWluLWdsb3Jpb3VzIGxvdXQu",
        "SGUgaGFkIGRvbmUgbW9zdCBiaXR0ZXIgd3Jvbmc=",
        "VG8gc29tZSB3aG8gYXJlIG5lYXIgbXkgaGVhcnQs",
        "WWV0IEkgbnVtYmVyIGhpbSBpbiB0aGUgc29uZzs=",
        "SGUsIHRvbywgaGFzIHJlc2lnbmVkIGhpcyBwYXJ0",
        "SW4gdGhlIGNhc3VhbCBjb21lZHk7",
        "SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=",
        "VHJhbnNmb3JtZWQgdXR0ZXJseTo=",
        "QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=",
    ]

    # Generate a random key and encrypt the above base64 text
    random_key = cryptopals.generate_random_bytes(16)
    encrypted_messages = []
    for item in lines:
        message = binascii.a2b_base64(item)
        encrypted = cryptopals.aes_with_custom_ctr(message, random_key, nonce=0)
        encrypted_messages.append(encrypted)

    """
    foo_BYTE XOR bar_BYTE = KEYSTREAM-BYTE
    And since the keystream is the same for every ciphertext:
    foo-BYTE XOR KEYSTREAM-BYTE = bar-BYTE
    """
    key = []
    max_line_length = len(max(encrypted_messages, key=len))
    for index in range(0, max_line_length):
        scores = {}
        for guess in range(256):

            items = [
                chr(message[index] ^ guess)
                for message in encrypted_messages
                if len(message) > index
            ]
            score = cryptopals.english.score_text("".join(items).encode())
            scores[guess] = score

        high_score = max(scores, key=lambda x: scores[x])
        if high_score > 0:
            key.append(bytes([high_score]))

    test_total_decrypt = []
    final_key = b"".join(key)
    keysize = len(final_key)
    for line, message in zip(lines, encrypted_messages):
        blocks = [message[n : n + keysize] for n in range(0, len(message), keysize)]
        decrypt = [cryptopals.decrypt_xor(block, final_key) for block in blocks]

        test_total_decrypt.append(
            cryptopals.compute_hamming_distance(
                b"".join(decrypt), binascii.a2b_base64(line)
            )
        )

    normalized_distance = sum(test_total_decrypt) / len(test_total_decrypt)
    assert (
        normalized_distance < 5
    ), "Hamming Distance of {} suggests decryption failed.".format(normalized_distance)
コード例 #10
0
def challenge_16() -> None:
    """
    CBC bitflipping attacks
    Generate a random AES key.

    Combine your padding code and CBC code to write two functions.

    The first function should take an arbitrary input string, prepend the string:

    "comment1=cooking%20MCs;userdata="
    .. and append the string:

    ";comment2=%20like%20a%20pound%20of%20bacon"
    The function should quote out the ";" and "=" characters.

    The function should then pad out the input to the 16-byte AES block length and encrypt it under the random AES key.

    The second function should decrypt the string and look for the characters ";admin=true;" (or, equivalently, decrypt,
    split the string on ";", convert each resulting string into 2-tuples, and look for the "admin" tuple).

    Return true or false based on whether the string exists.

    If you've written the first function properly, it should not be possible to provide user input to it that will
    generate the string the second function is looking for. We'll have to break the crypto to do that.

    Instead, modify the ciphertext (without knowledge of the AES key) to accomplish this.

    You're relying on the fact that in CBC mode, a 1-bit error in a ciphertext block:

    Completely scrambles the block the error occurs in
    Produces the identical 1-bit error(/edit) in the next ciphertext block.
    Stop and think for a second.
    Before you implement this attack, answer this question: why does CBC mode have this property?
    :return:
    """

    def encrypt_using_cbc(message, key):
        prepend = r"comment1=cooking%20MCs;userdata="
        cleaned = message.replace(";", "").replace("=", "")
        append = r";comment2=%20like%20a%20pound%20of%20bacon"

        full_message = "{}{}{}".format(prepend, cleaned, append).encode()

        encrypted = cryptopals.encrypt_aes_with_custom_cbc(
            full_message, key, b"YELLOW SUBMARINE"
        )

        return encrypted

    def is_admin(encrypted, random_aes_key):

        decrypted = cryptopals.decrypt_aes_with_custom_cbc(
            b"".join(encrypted), random_aes_key, b"YELLOW SUBMARINE"
        )
        # print('FUN: {}'.format(decrypted[2]))
        return False if b"".join(decrypted).find(b";admin=true;") == -1 else True

    random_aes_key = cryptopals.generate_random_bytes(16)
    encrypted = encrypt_using_cbc("?admin?true", random_aes_key)

    # for debugging info
    # decrypted = decrypt_aes_with_custom_cbc(b''.join(encrypted), random_aes_key, b'YELLOW SUBMARINE')

    # do something here to make ;admin=true exist in encrypted.
    success = False
    for index in range(0, 255):
        first_array = bytearray(encrypted[1])
        first_array[0] = index
        for index2 in range(0, 255):
            first_array[6] = index2
            encrypted[1] = bytes(first_array)
            result = is_admin(encrypted, random_aes_key)
            if result:
                # print("Hacked Using the following byte Manipulations: {}, {}".format(index, index2))
                success = True
                break
        if success:
            break

    assert success is True, "Unable to hack admin gate!"
コード例 #11
0
def challenge_13() -> None:
    """
    ECB cut-and-paste

    :return:
    """

    def profile_for(user_input: str):
        """
        Now write a function that encodes a user profile in that format, given an email address.

        You should have something like:

        profile_for("*****@*****.**")

        ... and it should produce:

        {
          email: '*****@*****.**',
          uid: 10,
          role: 'user'
        }
        ... encoded as:

        [email protected]&uid=10&role=user
        Your "profile_for" function should not allow encoding metacharacters (& and =).
        Eat them, quote them, whatever you want to do,
        but don't let people set their email address to "[email protected]&role=admin".
        :return:
        """

        # Eat illegals
        illegals = "&="
        for illegal in illegals:
            user_input.replace(illegal, "")

        user_profile = collections.OrderedDict()
        user_profile["email"] = user_input
        user_profile["uid"] = 10
        user_profile["role"] = "user"

        items = ["{0}={1}".format(k, v) for k, v in user_profile.items()]
        user_text = "&".join(items)

        return user_text

    email = (
        "theadminisfake.test@gmail." + "admin{}".format("\x11" * 11) + "com"
    )  # necessary to push 'user' to its own line
    profile = profile_for(email)
    cookie = cryptopals.create_structured_cookie(profile)

    # print(cookie)

    """
    Now, two more easy functions. Generate a random AES key, then:
        A.	Encrypt the encoded user profile under the key; "provide" that to the "attacker".
        B.	Decrypt the encoded user profile and parse it.

    Using only the user input to profile_for() (as an oracle to generate "valid" ciphertexts)
    and the ciphertexts themselves, make a role=admin profile.
    """
    random_aes_key = cryptopals.generate_random_bytes(16)
    keysize = len(random_aes_key)
    message = cryptopals.pkcs_7_padding(profile.encode(), keysize)
    for_attacker = cryptopals.encrypt_aes(message, random_aes_key)

    # print("For Attacker: {}".format(for_attacker))

    # to_be_swizzled = pkcs_7_padding(for_attacker, len(random_aes_key))
    to_be_swizzled = [
        for_attacker[n : n + keysize] for n in range(0, len(for_attacker), keysize)
    ]
    # Reorder the ECB Blocks and throw away the regular user account :)
    final = list()
    final.append(to_be_swizzled[0])
    final.append(to_be_swizzled[1])
    final.append(to_be_swizzled[3])
    final.append(to_be_swizzled[2])

    for_me = cryptopals.decrypt_aes(b"".join(final), random_aes_key)

    assert (
        for_me == b"[email protected]&uid=10&"
        b"role=admin\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11"
    ), "Admin account could not be hacked!"
コード例 #12
0
def challenge_12() -> None:
    """
    Byte - at - a - time

    ECB decryption(Simple)
    Copy your oracle function to a new function that encrypts buffers under ECB mode using a
    consistent but unknown key (for instance, assign a single random key, once, to a global variable).

    Now take that same function and have it append to the plaintext, BEFORE ENCRYPTING, the
    following string:

    Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK

    Spoiler alert. Do not decode this string now. Don't do it.

    Base64 decode the string before appending it. Do not base64
    decode the string by hand; make your code do it.The point is that you don't know its contents.

    What you have now is a function that produces:

    AES - 128 - ECB(your - string | | unknown - string, random - key)

    It turns out: you can decrypt "unknown-string" with repeated calls to the oracle function!

    Here's roughly how:

    Feed identical bytes of your-string to the function 1 at a time
    --- start with 1 byte ("A"), then "AA", then "AAA" and so on.

    1. Discover the block size of the cipher. You know it, but do this step anyway
    2. Detect that the function is using ECB. You already know, but do this step anyways.
    3. Knowing the block size, craft an input block that is exactly 1 byte short (for instance, if the block size is
       8 bytes, make "AAAAAAA"). Think about what the oracle function is going to put in that last byte position.
    4. Make a dictionary of every possible last byte by feeding different strings to the oracle; for instance,
       "AAAAAAAA", "AAAAAAAB", "AAAAAAAC", remembering the first block of each invocation.
    5. Match the output of the one-byte-short input to one of the entries in your dictionary. You've now discovered
       the first byte of unknown-string.
    6. Repeat for the next byte.
    """
    base64_encoded = (
        "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGll"
        "cyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK"
    )
    base64_decoded = binascii.a2b_base64(
        base64_encoded
    )  #  base64.b64decode(base64_encoded)
    random_aes_key = cryptopals.generate_random_bytes(16)

    encrypted_blocks = cryptopals.encrypt_ecb_oracle(
        b"", base64_decoded, random_aes_key
    )

    result = cryptopals.decrypt_ecb_message_without_key(
        encrypted_blocks, base64_decoded, random_aes_key
    )

    assert (
        result
        == b"Rollin' in my 5.0\nWith my rag-top down so my hair can blow\nThe girlies on standby"
        b" waving just to say hi\nDid you stop? No, I just drove by\n\x01"
    ), "Decryption Failed!"
コード例 #13
0
def challenge_19() -> None:
    """
    Attack this cryptosystem piecemeal: guess letters, use expected English language frequence to validate guesses,
    catch common English trigrams, and so on.
    :return:
    """
    lines = [
        "SSBoYXZlIG1ldCB0aGVtIGF0IGNsb3NlIG9mIGRheQ==",
        "Q29taW5nIHdpdGggdml2aWQgZmFjZXM=",
        "RnJvbSBjb3VudGVyIG9yIGRlc2sgYW1vbmcgZ3JleQ==",
        "RWlnaHRlZW50aC1jZW50dXJ5IGhvdXNlcy4=",
        "SSBoYXZlIHBhc3NlZCB3aXRoIGEgbm9kIG9mIHRoZSBoZWFk",
        "T3IgcG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==",
        "T3IgaGF2ZSBsaW5nZXJlZCBhd2hpbGUgYW5kIHNhaWQ=",
        "UG9saXRlIG1lYW5pbmdsZXNzIHdvcmRzLA==",
        "QW5kIHRob3VnaHQgYmVmb3JlIEkgaGFkIGRvbmU=",
        "T2YgYSBtb2NraW5nIHRhbGUgb3IgYSBnaWJl",
        "VG8gcGxlYXNlIGEgY29tcGFuaW9u",
        "QXJvdW5kIHRoZSBmaXJlIGF0IHRoZSBjbHViLA==",
        "QmVpbmcgY2VydGFpbiB0aGF0IHRoZXkgYW5kIEk=",
        "QnV0IGxpdmVkIHdoZXJlIG1vdGxleSBpcyB3b3JuOg==",
        "QWxsIGNoYW5nZWQsIGNoYW5nZWQgdXR0ZXJseTo=",
        "QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=",
        "VGhhdCB3b21hbidzIGRheXMgd2VyZSBzcGVudA==",
        "SW4gaWdub3JhbnQgZ29vZCB3aWxsLA==",
        "SGVyIG5pZ2h0cyBpbiBhcmd1bWVudA==",
        "VW50aWwgaGVyIHZvaWNlIGdyZXcgc2hyaWxsLg==",
        "V2hhdCB2b2ljZSBtb3JlIHN3ZWV0IHRoYW4gaGVycw==",
        "V2hlbiB5b3VuZyBhbmQgYmVhdXRpZnVsLA==",
        "U2hlIHJvZGUgdG8gaGFycmllcnM/",
        "VGhpcyBtYW4gaGFkIGtlcHQgYSBzY2hvb2w=",
        "QW5kIHJvZGUgb3VyIHdpbmdlZCBob3JzZS4=",
        "VGhpcyBvdGhlciBoaXMgaGVscGVyIGFuZCBmcmllbmQ=",
        "V2FzIGNvbWluZyBpbnRvIGhpcyBmb3JjZTs=",
        "SGUgbWlnaHQgaGF2ZSB3b24gZmFtZSBpbiB0aGUgZW5kLA==",
        "U28gc2Vuc2l0aXZlIGhpcyBuYXR1cmUgc2VlbWVkLA==",
        "U28gZGFyaW5nIGFuZCBzd2VldCBoaXMgdGhvdWdodC4=",
        "VGhpcyBvdGhlciBtYW4gSSBoYWQgZHJlYW1lZA==",
        "QSBkcnVua2VuLCB2YWluLWdsb3Jpb3VzIGxvdXQu",
        "SGUgaGFkIGRvbmUgbW9zdCBiaXR0ZXIgd3Jvbmc=",
        "VG8gc29tZSB3aG8gYXJlIG5lYXIgbXkgaGVhcnQs",
        "WWV0IEkgbnVtYmVyIGhpbSBpbiB0aGUgc29uZzs=",
        "SGUsIHRvbywgaGFzIHJlc2lnbmVkIGhpcyBwYXJ0",
        "SW4gdGhlIGNhc3VhbCBjb21lZHk7",
        "SGUsIHRvbywgaGFzIGJlZW4gY2hhbmdlZCBpbiBoaXMgdHVybiw=",
        "VHJhbnNmb3JtZWQgdXR0ZXJseTo=",
        "QSB0ZXJyaWJsZSBiZWF1dHkgaXMgYm9ybi4=",
    ]

    # Generate a random key and encrypt the above base64 text
    random_key = cryptopals.generate_random_bytes(16)
    encrypted_messages = []
    for item in lines:
        message = binascii.a2b_base64(item)
        encrypted = cryptopals.aes_with_custom_ctr(message,
                                                   random_key,
                                                   nonce=0)
        encrypted_messages.append(encrypted)
    """
    foo_BYTE XOR bar_BYTE = KEYSTREAM-BYTE
    And since the keystream is the same for every ciphertext:
    foo-BYTE XOR KEYSTREAM-BYTE = bar-BYTE
    """
    key = []
    max_line_length = len(max(encrypted_messages, key=len))
    for index in range(0, max_line_length):
        scores = {}
        for guess in range(256):

            items = [
                chr(message[index] ^ guess) for message in encrypted_messages
                if len(message) > index
            ]
            score = cryptopals.english.score_text("".join(items).encode())
            scores[guess] = score

        high_score = max(scores, key=lambda x: scores[x])
        if high_score > 0:
            key.append(bytes([high_score]))

    test_total_decrypt = []
    final_key = b"".join(key)
    keysize = len(final_key)
    for line, message in zip(lines, encrypted_messages):
        blocks = [
            message[n:n + keysize] for n in range(0, len(message), keysize)
        ]
        decrypt = [
            cryptopals.decrypt_xor(block, final_key) for block in blocks
        ]

        test_total_decrypt.append(
            cryptopals.compute_hamming_distance(b"".join(decrypt),
                                                binascii.a2b_base64(line)))

    normalized_distance = sum(test_total_decrypt) / len(test_total_decrypt)
    assert (normalized_distance <
            5), "Hamming Distance of {} suggests decryption failed.".format(
                normalized_distance)
コード例 #14
0
def challenge_16() -> None:
    """
    CBC bitflipping attacks
    Generate a random AES key.

    Combine your padding code and CBC code to write two functions.

    The first function should take an arbitrary input string, prepend the string:

    "comment1=cooking%20MCs;userdata="
    .. and append the string:

    ";comment2=%20like%20a%20pound%20of%20bacon"
    The function should quote out the ";" and "=" characters.

    The function should then pad out the input to the 16-byte AES block length and encrypt it under the random AES key.

    The second function should decrypt the string and look for the characters ";admin=true;" (or, equivalently, decrypt,
    split the string on ";", convert each resulting string into 2-tuples, and look for the "admin" tuple).

    Return true or false based on whether the string exists.

    If you've written the first function properly, it should not be possible to provide user input to it that will
    generate the string the second function is looking for. We'll have to break the crypto to do that.

    Instead, modify the ciphertext (without knowledge of the AES key) to accomplish this.

    You're relying on the fact that in CBC mode, a 1-bit error in a ciphertext block:

    Completely scrambles the block the error occurs in
    Produces the identical 1-bit error(/edit) in the next ciphertext block.
    Stop and think for a second.
    Before you implement this attack, answer this question: why does CBC mode have this property?
    :return:
    """
    def encrypt_using_cbc(message, key):
        prepend = r"comment1=cooking%20MCs;userdata="
        cleaned = message.replace(";", "").replace("=", "")
        append = r";comment2=%20like%20a%20pound%20of%20bacon"

        full_message = "{}{}{}".format(prepend, cleaned, append).encode()

        encrypted = cryptopals.encrypt_aes_with_custom_cbc(
            full_message, key, b"YELLOW SUBMARINE")

        return encrypted

    def is_admin(encrypted, random_aes_key):

        decrypted = cryptopals.decrypt_aes_with_custom_cbc(
            b"".join(encrypted), random_aes_key, b"YELLOW SUBMARINE")
        # print('FUN: {}'.format(decrypted[2]))
        return False if b"".join(decrypted).find(
            b";admin=true;") == -1 else True

    random_aes_key = cryptopals.generate_random_bytes(16)
    encrypted = encrypt_using_cbc("?admin?true", random_aes_key)

    # for debugging info
    # decrypted = decrypt_aes_with_custom_cbc(b''.join(encrypted), random_aes_key, b'YELLOW SUBMARINE')

    # do something here to make ;admin=true exist in encrypted.
    success = False
    for index in range(0, 255):
        first_array = bytearray(encrypted[1])
        first_array[0] = index
        for index2 in range(0, 255):
            first_array[6] = index2
            encrypted[1] = bytes(first_array)
            result = is_admin(encrypted, random_aes_key)
            if result:
                # print("Hacked Using the following byte Manipulations: {}, {}".format(index, index2))
                success = True
                break
        if success:
            break

    assert success is True, "Unable to hack admin gate!"
コード例 #15
0
def challenge_13() -> None:
    """
    ECB cut-and-paste

    :return:
    """
    def profile_for(user_input: str):
        """
        Now write a function that encodes a user profile in that format, given an email address.

        You should have something like:

        profile_for("*****@*****.**")

        ... and it should produce:

        {
          email: '*****@*****.**',
          uid: 10,
          role: 'user'
        }
        ... encoded as:

        [email protected]&uid=10&role=user
        Your "profile_for" function should not allow encoding metacharacters (& and =).
        Eat them, quote them, whatever you want to do,
        but don't let people set their email address to "[email protected]&role=admin".
        :return:
        """

        # Eat illegals
        illegals = "&="
        for illegal in illegals:
            user_input.replace(illegal, "")

        user_profile = collections.OrderedDict()
        user_profile["email"] = user_input
        user_profile["uid"] = 10
        user_profile["role"] = "user"

        items = ["{0}={1}".format(k, v) for k, v in user_profile.items()]
        user_text = "&".join(items)

        return user_text

    email = ("theadminisfake.test@gmail." + "admin{}".format("\x11" * 11) +
             "com")  # necessary to push 'user' to its own line
    profile = profile_for(email)
    cookie = cryptopals.create_structured_cookie(profile)

    # print(cookie)
    """
    Now, two more easy functions. Generate a random AES key, then:
        A.	Encrypt the encoded user profile under the key; "provide" that to the "attacker".
        B.	Decrypt the encoded user profile and parse it.

    Using only the user input to profile_for() (as an oracle to generate "valid" ciphertexts)
    and the ciphertexts themselves, make a role=admin profile.
    """
    random_aes_key = cryptopals.generate_random_bytes(16)
    keysize = len(random_aes_key)
    message = cryptopals.pkcs_7_padding(profile.encode(), keysize)
    for_attacker = cryptopals.encrypt_aes(message, random_aes_key)

    # print("For Attacker: {}".format(for_attacker))

    # to_be_swizzled = pkcs_7_padding(for_attacker, len(random_aes_key))
    to_be_swizzled = [
        for_attacker[n:n + keysize]
        for n in range(0, len(for_attacker), keysize)
    ]
    # Reorder the ECB Blocks and throw away the regular user account :)
    final = list()
    final.append(to_be_swizzled[0])
    final.append(to_be_swizzled[1])
    final.append(to_be_swizzled[3])
    final.append(to_be_swizzled[2])

    for_me = cryptopals.decrypt_aes(b"".join(final), random_aes_key)

    assert (for_me == b"[email protected]&uid=10&"
            b"role=admin\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11"
            ), "Admin account could not be hacked!"