Beispiel #1
0
def challenge_50():
    # Create hash function
    original_key = b'YELLOW SUBMARINE'
    original_iv = b'\x00' * 16
    generate_hash = lambda javascript: set2.encrypt_aes_cbc(
        javascript, original_key, original_iv, False)[-16:]

    # Recreate original hash (the one we will forge)
    original_message = set2.pkcs7_add_padding(b"alert('MZA who was that?');\n",
                                              16)
    original_hash = generate_hash(original_message)
    assert original_hash == bytes.fromhex('296b8d7cb78a243dda4d0a61d33bbdd1')

    # New piece of JavaScript
    new_message = b"alert('Ayo, the Wu is back!');//"
    # Find the last cipher block of our new JavaScript (will be the IV of the following block)
    ciphertext_block_1_2 = set2.encrypt_aes_cbc(new_message, original_key,
                                                original_iv, False)[-16:]
    # Generate a third plaintext block for our new JavaScript, by XORing the found IV with the first plaintext block of the original JavaScript
    plaintext_block_3 = xor_string(ciphertext_block_1_2[-16:],
                                   original_message[:16])
    # Now, append the found block plus the remaining blocks of the original JavaScript
    new_message += plaintext_block_3 + original_message[16:]
    # Hashing this will result in the original hash
    assert_true(generate_hash(new_message) == original_hash)
Beispiel #2
0
def challenge_13():
    # Get the encryption and decryption oracles
    enc_oracle, dec_oracle = get_oracles()

    # Using the encryption oracle, generate two valid blocks containing email, uid in full and role header
    profile = bytes(profile_for('x' * 13), 'utf-8')
    ciphertext = enc_oracle(profile)
    part_1 = ciphertext[:32]  # represents: email=xxxxxxxxxxxxx&uid=10&role=

    # Using the encryption oracle, generate a block that only contains 'admin' and padding characters
    profile = bytes(profile_for('x' * 10 + "admin" + (chr(11) * 11)), 'utf-8')
    ciphertext = enc_oracle(profile)
    part_2 = ciphertext[16:32]  # represents: admin\x0B*11

    # Concatenate the two parts to get ciphertext for:
    # email=xxxxxxxxxxxxx&uid=10&role=admin\x0B*11
    constructed_ciphertext = part_1 + part_2

    # Using the decryption oracle and the constructed ciphertext, get the parsed profile
    plaintext = str(dec_oracle(constructed_ciphertext), 'utf-8')
    print(plaintext)
    created_profile = query_parser(plaintext)

    # Make sure the role was spoofed successfully
    assert_true(created_profile['role'] == 'admin')
Beispiel #3
0
def challenge_43():
    # Set parameters p, q, g
    p = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1
    q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b
    g = 0x5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291

    message = b'Who is the king of the jungle?'

    # Set up new DSA instance and keypair
    dsa = DSA(p, q, g)
    x, y = dsa.generate_keypair()

    # Part 1: try to verify a valid signature
    sig, k = dsa.sign(message, x)
    assert dsa.verify(message, sig, y)

    # Part 2: try and obtain x
    assert dsa.crack_x(k, message, sig) == x

    # Part 3: try and brute force private key based on signature and message#
    message = b'For those that envy a MC it can be hazardous to your health\nSo be friendly, a matter of life and death, just like a etch-a-sketch\n'
    sig = 548099063082341131477253921760299949438196259240, 857042759984254168557880549501802188789837994940

    # Try all `k`s in given range
    for k in range(0, 2 ** 16):
        # Try k, get private key `x`
        x = dsa.crack_x(k, message, sig)
        # Verify hash of found private key `x`
        if hashlib.sha1(hex(x)[2:].encode()).hexdigest() == '0954edd5e0afe5542a4adf012611a91912a3ec16':
            print("The private key is x={}".format(x))
            assert_true(True)
            break
    else:
        print('Unable to find x')
        assert_true(False)
Beispiel #4
0
def challenge_40():
    e = 3
    # For this attack to work, it is assumed that:
    # - all `p`s and `q`s are unique;
    # - all `n`s are coprime with each other;
    # - the message is smaller than the smallest `n`.
    # The code below will make sure that these conditions are met.
    while True:
        # Generate a random message
        msg = random.randrange(1, 1000)
        data, pqs = [], set()
        for _ in range(e):
            # Generate new public/private key pair
            public, _ = set_up_rsa(e, pqs)
            # If we end up getting a public key that's too small for our message, start over
            if public[1] <= msg: break
            # Collect encrypted text and public key
            data.append((encrypt_rsa(msg, public), public[1]))
        else:
            # If we succsefully generated `e` ciphertexts and pubkeys, verify the pubkeys are coprime with each other
            if are_coprime([n for _, n in data]):
                #  If not, start over
                break

    # Use CRT and take `e`th root
    r = round(crt(data) ** (1/e))
    print('Original message: {}, found message: {}'.format(msg, r))
    assert_true(r == msg)
Beispiel #5
0
def challenge_34():
    # Simple EchoBot exchange
    alice = EchoAlice()
    bob = EchoBob()

    msg1 = alice.initiate(p=nist_p, g=nist_g)
    msg2 = bob.receive(msg1)
    msg3 = alice.send_msg(msg2, set2.random_bytes(random.randrange(10, 100)))
    msg4 = bob.receive_msg(msg3)
    exchange_1_valid = alice.verify_echo(msg4)

    # EchoBot exchange with MitM
    alice = EchoAlice()
    bob = EchoBob()
    eve = EchoEve()

    secret_msg = set2.random_bytes(random.randrange(10, 100))
    msg1 = alice.initiate(p=nist_p, g=nist_g)
    msg1p = eve.intercept_alice_initiate(msg1)
    msg2 = bob.receive(msg1p)
    msg2p = eve.intercept_bob_receive(msg2)
    msg3 = alice.send_msg(msg2p, secret_msg)
    msg3p = eve.intercept_alice_send_msg(msg3)
    msg4 = bob.receive_msg(msg3p)
    msg4p = eve.intercept_bob_receive_msg(msg4)
    exchange_2_valid = alice.verify_echo(msg4p)
    exchange_2_cracked = secret_msg == eve.decrypt_message()

    # Verify exchanges were valid and Eve was able to obtain the original message
    assert_true(exchange_1_valid and exchange_2_valid and exchange_2_cracked)
Beispiel #6
0
def challenge_44():
    # Set up `p`, `q` and `g` parameters
    p = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1
    q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b
    g = 0x5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291
    # Create new DSA instance
    dsa = DSA(p, q, g)
    # Read inputs for this challenge from file
    messages = get_challenge_44_messages()
    # Iterate over messages
    for i, msg1 in enumerate(messages):
        for msg2 in messages[(i+1):]:
            # Check if the same `k` was used (implies `r` values are equal)
            if msg1['r'] == msg2['r']:
                # Use '9th grade math' to recover `k`
                a = (msg1['m'] - msg2['m']) % q
                b1 = (msg1['s'] - msg2['s']) % q
                b2 = set5.modinv(b1, q)
                k = (a * b2) % q
                print('Recovered k: {}'.format(k))
                # Recover `x`
                x = dsa.crack_x(k, msg=msg1['msg'], sig=(msg1['r'], msg1['s']))
                print('Recovered x: {}'.format(hashlib.sha1(hex(x)[2:].encode()).hexdigest()))
                # Verify the found private key `x` is the one we are looking for
                assert_true(hashlib.sha1(hex(x)[2:].encode()).hexdigest() == 'ca8f6f7c66fa362d40760d135b763eb8527d3d52')
                return
Beispiel #7
0
def challenge_28():
    # Generate random key, message
    key = set2.random_bytes(16)
    message = set2.random_bytes(random.randrange(128, 1024))
    # Compute mac for the generated key and message
    original_mac = simple_mac(key, message)
    # Verify changing a byte will change the mac significantly
    assert_true(mac_tamper(key, message, original_mac))
Beispiel #8
0
def challenge_14():
    # Intialise the encryption function that will be used
    key = random_bytes(16)
    prefix = random_bytes(random.randrange(1, 16))
    encrypt = lambda x: encrypt_aes_ecb(prefix + x + magic_string, key)

    # Brute force the magic string
    assert_true(bruteforce_ecb_key_2(encrypt) == magic_string)
Beispiel #9
0
def challenge_39():
    # Get public and private key
    public, private = set_up_rsa(3)
    # Generate message
    msg = random.randrange(2, public[1])
    # Encrypt and decrypt
    ciphertext = encrypt_rsa(msg, public)
    plaintext = encrypt_rsa(ciphertext, private)
    # Verify output
    assert_true(msg == plaintext)
Beispiel #10
0
def challenge_24():
    # Initialise our new stream cipher
    stream_cipher_instance = MT19937Cipher(424242)
    # Generate a random plaintext
    plaintext = set2.random_bytes(1024)
    # Encrypt the plaintext, decrypt is
    ciphertext = stream_cipher_instance.encrypt(plaintext)
    obtained_plaintext = stream_cipher_instance.decrypt(ciphertext)
    # Verify the decrypted ciphertext equals our original plaintext
    assert_true(obtained_plaintext == plaintext)
Beispiel #11
0
def challenge_53():
    # Define our Merkle Damgard hash function
    md_hash = lambda x, y, z=False: MerkleDamgard(x, HASH_LENGTH, y, z)
    # Generate the message we'll attack
    M = set2.random_bytes(16 * 16)
    # Generate the hashmap, i.e. the intermediate hashes per block
    M_hashmap = md_hash(M, b'\x00' * HASH_LENGTH, True)
    # Compute `k`
    k = int(math.log2(len(M) // 16))
    # Generate expandable message
    print('Message to preimage has {} blocks (i.e. k = {})'.format(
        len(M) // 16, k))
    print("Generating expandable message... ", end='')
    expandable_output = expandable_message(k, md_hash)
    print('done')

    # Get hash of our final block in the expandable message
    final_hash = list(expandable_output.values())[-1][0]
    index = -1
    # Find an index that is at equal to or greater than `k`
    print("Generating bridge... ", end='')
    while index < k:
        bridge, bridge_hash = None, None
        # Check if generated message collides with one of our intermediate hashes
        while bridge_hash not in M_hashmap[1].keys():
            bridge = set2.random_bytes(16)
            bridge_hash = md_hash(bridge, final_hash)
        # If it does, that will be our index
        index = M_hashmap[1][bridge_hash]
    print("done\nFound collision against block {} of M".format(index))

    # The next step is to replace all blocks up until our collision (i.e. the bridge)
    # This requires some binary math, as sometimes the short message is required, while
    #  sometimes the long one is required.
    prefix_length = index
    prefix = b''
    for i in range(k, 0, -1):
        # Compute the length of the long message we're considering
        q = 2**(i - 1) + 1
        # If the prefix length minus the long message length is smaller than the remaining blocks:
        if prefix_length - q < i - 1:
            # Use short message
            prefix_length -= 1
            prefix += expandable_output[i][1]
        else:
            # Use long message
            prefix_length -= q
            prefix += expandable_output[i][2]
    # Construct the new message
    constructed_message = prefix + bridge + M[(16 * (index + 1)):]
    # Verify the constructed message has the same length and the same hash value as our original message
    assert_true(
        len(constructed_message) == len(M)
        and md_hash(constructed_message, b'\x00' * HASH_LENGTH) == md_hash(
            M, b'\x00' * HASH_LENGTH))
Beispiel #12
0
def diffie_helman(p, g):
    # Generate private keys in p
    a, b = random.randrange(1, p), random.randrange(1, p)
    # Generate public keys
    A, B = modexp(g, a, p), modexp(g, b, p)
    # Generate shared secret
    s, s2 = modexp(B, a, p), modexp(A, b, p)
    # Verify Bob and Alice have the same shared secret
    assert_true(s == s2)
    # Return SHA256 of shared secret
    return hashlib.sha256(long_to_bytes(s)).digest()
Beispiel #13
0
def challenge_31():
    # Generate random key
    key = set2.random_bytes(16)
    filename = b'filename'
    # Set up web server
    _thread.start_new_thread(webserver, (key, ))
    # Generate the hmac we're looking for
    correct_hmac = hmac_sha1(key, filename).hexdigest()
    # Try to guess the hmac using our timing attack
    guessed_hmac = retrieve_valid_hmac(filename)
    # Verify the guessed hmac is equal to the correct hmac
    assert_true(correct_hmac == guessed_hmac)
Beispiel #14
0
def challenge_32():
    # Generate random key
    key = set2.random_bytes(16)
    filename = b'filename'
    # Set up web server, but now with a very small 'insecure_comparison' sleep time
    _thread.start_new_thread(webserver, (key, 0.01))
    # Generate the hmac we're looking for
    correct_hmac = hmac_sha1(key, filename).hexdigest()
    # Try to guess the hmac using our timing attack, but now with 10 rounds for each guess
    guessed_hmac = retrieve_valid_hmac(filename, 10)
    # Verify the guessed hmac is equal to the correct hmac
    assert_true(correct_hmac == guessed_hmac)
Beispiel #15
0
def challenge_22():
    # Get UNIX timestamp
    timestamp = int(time.time())
    # Generate a pseudo-random number using a given timestamp, with delay
    output, secret_seed = generate_prn_with_delay(int(time.time()))
    # Now bruteforce the obtained output based on the given timestamp
    found_seeds = bruteforce_seed(output, timestamp)
    # Display number of possible seeds found
    print("{} possible seed{} found".format(
        len(found_seeds), 's' if len(found_seeds) != 1 else ''))
    # Verify the used seed is in the list of found (possible) seeds
    assert_true(secret_seed in found_seeds)
Beispiel #16
0
def challenge_18():
    # Test given string
    test_string = "L77na/nrFsKvynd6HzOoG7GHTLXsTVu9qvY/2syLXzhPweyyMTJULu/6/kXX0KSvoOLSFQ=="
    assert_true(
        decrypt_aes_ctr(base64.b64decode(test_string), "YELLOW SUBMARINE", 0)
        == b'Yo, VIP Let\'s kick it Ice, Ice, baby Ice, Ice, baby ')

    # Generate random data, nonce and key, test if decrypting after encrypting results in the generated data again.
    data = set2.random_bytes(64)
    nonce = random.randint(128, 65536)
    key = set2.random_bytes(16)
    assert_true(
        data == decrypt_aes_ctr(encrypt_aes_ctr(data, key, nonce), key, nonce))
Beispiel #17
0
def challenge_26():
    # Intialise the encryption parameters
    nonce = random.randrange(1, 10**10)
    key = set2.random_bytes(16)

    # Generate ciphertext for our string
    ciphertext = bytearray(challenge_26_encryption('-admin-true', key, nonce))

    # Replace the 32th character with the XOR of itself with '-' and ';'
    # Note that AES CTR will XOR this again with the key stream,
    # hence resulting in ';'. Same strategy for the 38th character.
    ciphertext[32] = ciphertext[32] ^ ord('-') ^ ord(';')
    ciphertext[38] = ciphertext[38] ^ ord('-') ^ ord('=')

    assert_true(challenge_26_admin_check(bytes(ciphertext), key, nonce))
Beispiel #18
0
def challenge_17():
    # Initialise key
    key = set2.random_bytes(16)
    # Call function_1 to get random ciphertext with IV used
    ciphertext, iv = function_1(key)
    # For verification purposes, decrypt given ciphertext with key
    expected = set2.decrypt_aes_cbc(ciphertext, key, iv)
    print('Expected: {}'.format(expected))
    # Initialise oracle function
    oracle = lambda x: function_2(x, key, iv)
    # Run oracle attack
    result = aes_oracle_attack(oracle, ciphertext, iv)
    print('Found:    {}'.format(bytes(result)))
    # Verify found answer equals what we're expecting
    assert_true(result == expected)
    print("")
Beispiel #19
0
def challenge_27():
    # Intialise the encryption parameters
    key = set2.random_bytes(16)
    # Generate ciphertext for our string
    ciphertext = challenge_27_encryption('whatevah', key)
    # Modify the ciphertext such that it is equal to c_1 + 0 + c_2
    ciphertext = ciphertext[0:16] + b'\x00' * 16 + ciphertext[0:16]
    try:
        # Verify this modified ciphertext will now parse 'admin':'true'
        assert_true(not challenge_27_check_decrypt(ciphertext, key))
    except ValueError as error:
        # Extract the deciphered text from the exception
        plaintext = error.args[1]
        # XOR p_1 with p_3
        derived_key = bytes(
            [x ^ y for x, y in zip(plaintext[0:16], plaintext[32:48])])
        # Verify the derived key equals the original key
        assert_true(derived_key == key)
Beispiel #20
0
def challenge_48():
    # Set up new RSA instance, this time with bigger key length
    pub, priv = set5.set_up_rsa(e=3, keysize=768)
    # Prepare message
    message = pad_PKCS(b'I don\'t know, Marge. Trying is the first step towards failure - Homer Simpson', k=(pub[1].bit_length() + 7) // 8)
    # Get ciphertext using generated RSA instance
    ciphertext = set5.encrypt_rsa(set5.bytes_to_int(message), pub)
    # Set up our Oracle
    oracle = lambda x: rsa_oracle_02(x, priv)
    assert oracle(ciphertext)

    # Perform the actual attack: set up bleichenbacher98 instance
    bb98 = bleichenbacher98(ciphertext, pub, oracle)
    # Run the attack
    found_message = bb98.solve()
    print("Found message:", found_message)

    # Verify the found message equals our original plaintext
    assert_true(found_message == message)
Beispiel #21
0
def challenge_47():
    # Set up new RSA instance
    pub, priv = set5.set_up_rsa(e=3, keysize=256)
    # Prepare message
    message = pad_PKCS(b'kick it, CC', k=(pub[1].bit_length() + 7) // 8)
    # Get ciphertext using generated RSA instance
    ciphertext = set5.encrypt_rsa(set5.bytes_to_int(message), pub)
    # Set up our Oracle
    oracle = lambda x: rsa_oracle_02(x, priv)
    assert oracle(ciphertext)

    # Perform the actual attack: set up bleichenbacher98 instance
    bb98 = bleichenbacher98(ciphertext, pub, oracle)
    # Run the attack
    found_message = bb98.solve()
    print("Found message:", found_message)

    # Verify the found message equals our original plaintext
    assert_true(found_message == message)
Beispiel #22
0
def challenge_54():
    # Set parameters, define hash function
    k = 4  # 2^4 = 16
    md_hash = lambda x, y: MerkleDamgard(x, 2, y)
    START_IV = b'\x00' * 16

    # Generate `2^k` random messages with their hashes, then add them as leaves to the hash/message tree
    hash_tree, msg_tree = [[]], [[]]
    for _ in range(2**k):
        msg = set2.random_bytes(16)
        msg_tree[0].append(msg)
        hash_tree[0].append(md_hash(msg, START_IV))

    # Generate full hash/message tree
    print('k = {}'.format(k))
    with tqdm(desc="Generating hash tree", total=(2**k) - 1) as progress:
        hash_tree, msg_tree = generate_hash_tree(hash_tree, msg_tree, md_hash,
                                                 progress)
    # Make claim based on the root of the hash tree
    hash_value = hash_tree[-1][0]
    print('> I hereby claim the hash will be equal to {}!'.format(hash_value))

    # Now, _after_ we have made our claim, craft our message with the match results
    msg = b'Ajax-PSV=3-0; Feyenoord-RKC=0-15'
    # Generate hash of our message
    msg_hash = md_hash(msg, START_IV)
    # Our crafted message starts with the original message
    crafted_msg = msg
    # Find a collision between our message's hash and one of the leaves in the hash tree
    glue, glue_hash = None, None
    while glue_hash not in hash_tree[0]:
        glue = set2.random_bytes(16)
        glue_hash = md_hash(glue, msg_hash)
    # Append the glue to our crafted message
    crafted_msg += glue
    # Find the index of the leaf we found a collision against
    index = hash_tree[0].index(glue_hash)
    # Now find the remaining `k` blocks that will eventually hash to `hash_value`
    for level in range(1, k + 1):
        index = index // 2
        crafted_msg += msg_tree[level][index]
    # Verify our crafted message hashes to the hash value we claimed it would have
    assert_true(md_hash(crafted_msg, START_IV) == hash_value)
Beispiel #23
0
def challenge_42():
    # Create new RSA Sign/Verify instance
    m = RsaSignVerify()
    print('> Signed message')
    # Part 1: try to verify a valid signature
    signature = m.sign(b'Hello world')
    assert m.verify(b'Hello world', signature)

    # Part 2: forge a signature
    print('\n> Forged message')
    # Find digest for message to forge
    forged_message = b'hi mom'
    digest = hashlib.sha1(forged_message).digest()
    # Set the contents of the (fake) message to forge
    fake_message = b'\x00\x01\xff\x00' +  digest
    fake_message = set5.bytes_to_int(fake_message + (b'\x00' * (128 - len(fake_message))))
    # The actual trick: find the cube root
    fake_signed_message = cuberoot(fake_message)
    # Check that the forged message passes verification
    assert_true(m.verify(forged_message, fake_signed_message))
Beispiel #24
0
def challenge_23():
    # Create prng instance to clone
    prng_instance = MT19937(13371337)
    generated_values = []
    found_mt_values = []
    # Generate the first N numbers, and try to find the underlying values in the internal MT array using our untemper function
    for _ in range(prng_instance.N):
        generated_values.append(prng_instance.generate())
        found_mt_values.append(untemper(generated_values[-1]))
    # Create a dummy MT19937 instance
    clone_prng_instance = MT19937(0)
    # Set the internal MT array, reset index value to 0
    clone_prng_instance.mt = found_mt_values
    clone_prng_instance.index = 0
    # Using this cloned MT19937 instance, generate the first N values again
    clone_generated_numbers = [
        clone_prng_instance.generate() for _ in range(MT19937.N)
    ]
    # Verify the results
    assert_true(generated_values == clone_generated_numbers)
Beispiel #25
0
def challenge_51():
    # Prepare a Stream Cipher compression oracle and an AES-CBC compression oracle
    stream_oracle = lambda msg: compression_oracle(
        msg, lambda x: set3.encrypt_aes_ctr(
            x, key=set2.random_bytes(16), nonce=random.randint(2**8, 2**16)))
    cbc_oracle = lambda msg: compression_oracle(
        msg, lambda x: set2.encrypt_aes_cbc(
            x, key=set2.random_bytes(16), iv=set2.random_bytes(16)))

    # Define the base text
    base = 'POST / HTTP/1.1\r\nHost: hapless.com\r\nCookie: sessionid='

    # Find the tokens using the Stream Cipher
    print('Stream Cipher: ', end='')
    assert_true(find_token(base, oracle=stream_oracle)[len(base):] == token)

    # AES-CBC is more challenging, as it works with blocks.
    # To detect a successful guess, we'll have to add padding which will make 'wrong' guesses one block longer than the correct one
    print('AES-CBC:       ', end='')
    assert_true(find_token(base, oracle=cbc_oracle)[len(base):] == token)
Beispiel #26
0
def challenge_25():
    key = set2.random_bytes(16)
    nonce = random.randrange(1, 10**10)
    # Get plaintext
    with open('inputs/25.txt') as file:
        contents = base64.b64decode(file.read())
    plaintext = set2.pkcs7_remove_padding(
        set1.decrypt_aes_ecb(contents, 'YELLOW SUBMARINE'))
    # Generate ciphertext
    ciphertext = set3.encrypt_aes_ctr(plaintext, key, nonce)

    # Create vulnerable function
    def vulnerable_ctr_stream_edit_function(ciphertext, offset, newtext):
        return edit_ctr_stream(ciphertext, key, nonce, offset, newtext)

    # Find the plaintext using the above function
    obtained_plaintext = find_ctr_plaintext(
        ciphertext, vulnerable_ctr_stream_edit_function)
    # Verify found plaintext matches with the original plaintext
    assert_true(obtained_plaintext == plaintext)
Beispiel #27
0
def challenge_16():
    # Intialise the encryption parameters
    iv = random_bytes(16)
    key = random_bytes(16)

    # Generate ciphertext for our string
    ciphertext = bytearray(challenge_16_encryption('-admin-true', key, iv))

    # Replace the 16th character with the XOR of itself with '-' and ';'
    # Note that AES CBC will XOR this again with the 16th character and '-',
    # hence resulting in ';'. Same strategy for the 22nd character.
    ciphertext[16] = set1.XOR(set1.XOR(bytes([ciphertext[16]]), b'-'), b';')[0]
    ciphertext[22] = set1.XOR(set1.XOR(bytes([ciphertext[22]]), b'-'), b'=')[0]

    try:
        # Verify this modified ciphertext will now parse 'admin':'true'
        assert_true(challenge_16_admin_check(bytes(ciphertext), key, iv))
    except ValueError as error:
        # This may happen if the second block outputs an '=' or '&' by accident
        print('Invalid string formed, trying again')
        challenge_16()
Beispiel #28
0
def challenge_45():
    # Set up `p` and `q` parameters
    p = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1
    q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b

    # Create new DSA instance with `g` = `p` + 1
    dsa = DSA(p, q, g=p+1)
    x, y = dsa.generate_keypair()

    # Part 1: try to verify a valid signature
    message = b"Hello world!"
    sig, _ = dsa.sign(message, x)
    assert dsa.verify(message, sig, y)

    # Part 2: Try to obtain the magic signature that will verify every message
    # Let's pick z=1 to keep things simple
    r2 = (y % p) % q
    s2 = r2 % q
    sig2 = (r2, s2)

    # Verify two arbitrary strings against the same signature
    assert_true(dsa.verify(b"Hello, world", sig2, y) and dsa.verify(b"Goodbye, world", sig2, y))
Beispiel #29
0
def challenge_29():
    # Generate random key of random number of bytes
    key = set2.random_bytes(random.randrange(4, 40))

    # Prepare validation function
    def validate_mac(message, digest):
        return simple_mac(key, message,
                          False).hexdigest() == digest.hexdigest()

    # Prepare message to sign
    msg = b"comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon"
    # Generate genuine mac of the message
    original_mac = simple_mac(key, msg, False)
    # Obtain a, b, c, d and e
    abcde = original_mac._digest()
    # Prepare string to inject
    string_to_inject = b";admin=true"
    # Create a forged mac:
    # - The forged length is {msg} and it's padding (hence a multiple of 64), plus the length of our injected string
    # - We feed the SHA-1 instance the [a-e] parameters obtained from the original mac
    forged_mac = external.slowsha.SHA1(
        string_to_inject,
        math.ceil(len(msg) / 64) * 64 + len(string_to_inject), abcde)
    # To figure out what message we actually signed, we need to figure out what the padding was of the original mac
    # Because we don't know the key, we have to guess the key length
    for key_length_guess in range(4, 41):
        # Compose the candidate message based on the key length guess
        signed_msg_guess = msg + get_sha1_padding(b'\x00' * key_length_guess +
                                                  msg) + string_to_inject
        # Verify the validity of the guessed message with the forged mac
        if validate_mac(signed_msg_guess, forged_mac):
            print(
                "Successfully forged message - key used had length {}".format(
                    key_length_guess))
            assert_true(True)
            return
    raise Exception("Could not forge MAC")
Beispiel #30
0
def challenge_49():
    # Define User IDs
    Alice, Bob, Carol, Eve = 100, 101, 102, 600
    # Define fixed IV
    fixed_iv = b'\x00' * 16

    # Sample for Alice
    print('Sample for Alice')
    valid_message_alice = generate_transaction(Alice, fixed_iv, [{
        'to_id': Bob,
        'amount': 100
    }, {
        'to_id': Carol,
        'amount': 500
    }])
    assert (validate_transaction(valid_message_alice))

    # Generate transaction based on own account (transferring 1M from own account to own account)
    valid_message_eve = generate_transaction(Eve, fixed_iv, [{
        'to_id': Eve,
        'amount': 1000000
    }])
    print('\nValid message (not sent to server): {}'.format(valid_message_eve))

    # Forge transaction by changing the 'from' account to Alice's
    forged_first_block = 'from={}&tx_list'.format(Alice).encode()
    # As we can control the IV, XOR new string with original string (and the original, fixed IV) to obtain the IV that will make our forged message valid
    custom_iv = xor_string(
        fixed_iv, xor_string(valid_message_eve[:16], forged_first_block))
    # Construct message
    forged_message = forged_first_block + valid_message_eve[
        16:-32] + custom_iv + valid_message_eve[-16:]
    print('Forged message:                     {}'.format(forged_message))

    print('Send to server...')
    # Validate forged message
    assert_true(validate_transaction(forged_message))