コード例 #1
0
ファイル: query_oracle.py プロジェクト: b4stet/py_cryptochall
def get_response(url, data: bytes, verb='GET'):
    data_hex = encoder.bytes_to_hex(data)

    response = None
    if verb == 'GET':
        response = requests.get(url, params={'data': data_hex})

    if verb == 'POST':
        response = requests.post(url, data={'data': data_hex})
    response.encoding = 'utf-8'

    result = b''
    if response.status_code == '200':
        result = encoder.hex_to_bytes(response.text)
    else:
        result = encoder.utf8_to_bytes(response.reason)

    return encoder.hex_to_bytes(response.text)
コード例 #2
0
        def do_GET(self):
            data = self._parse_query_string().get('data', None)
            data_hex = ''
            if data is not None:
                data_hex = data[0]
            data_utf8 = encoder.hex_to_utf8(data_hex)

            # create comment, encode then encrypt
            comment = self.__create_comment(data_utf8)
            encoded = self.__encode_comment(comment)
            encoded_bytes = encoder.utf8_to_bytes(encoded)

            iv = randomness.get_bytes(16)
            cipher_bytes = aes.encrypt(encoded_bytes, self.__KEY, 'cbc', iv)

            # remove iv
            cipher_hex = encoder.bytes_to_hex(cipher_bytes)
            self._send_response(cipher_hex)
コード例 #3
0
ファイル: main_set2.py プロジェクト: b4stet/py_cryptochall
import sys
import threading
from datetime import datetime
from utils import encoder, bitwise, padding
from crypter import aes
from attack import cpa_guess_mode, cpa_retrieve_secret, cca2_forge_data
from oracle.oracle_server import OracleServer

# Chall 9
print('[+] Chall 9 (implement pkcs7 padding) ...', end='')
data_utf8 = 'YELLOW SUBMARINE'
padded_expected = b'YELLOW SUBMARINE\x04\x04\x04\x04'
data_bytes = encoder.utf8_to_bytes(data_utf8)
padded_bytes = padding.pad_pkcs7(data_bytes, 20)
assert padded_bytes == padded_expected, 'Failed 9: Expected {}, got {}'.format(
    padded_expected, padded_bytes)
print(' ok')

# Chall 10
print('\n[+] Chall 10 (implement AES-CBC) ...', end='')
cipher_b64 = ''
with open('./data/set2_chall10.txt', mode='r') as f:
    lines = f.read().splitlines()
    cipher_b64 = ''.join(lines)
cipher_bytes = encoder.b64_to_bytes(cipher_b64)
iv = bytes([0]) * 16
key_utf8 = 'YELLOW SUBMARINE'
key_bytes = encoder.utf8_to_bytes(key_utf8)
plain_bytes = aes.decrypt_cbc_homemade(cipher_bytes, key_bytes, iv)
encrypted_bytes = aes.encrypt_cbc_homemade(plain_bytes, key_bytes, iv)
assert encrypted_bytes[
コード例 #4
0
ファイル: main_set1.py プロジェクト: b4stet/py_cryptochall
            'score': best_score
        })
        index += 1
        line = f.readline()
plains.sort(key=lambda elt: elt['score'], reverse=True)
print(' ok: found xor cipher at index={}'.format(plains[0]['index']))
print(' | where cipher={}, frequency attack gave key={}, plain=0x{}, score={}'.
      format(encoder.bytes_to_hex(plains[0]['cipher']),
             encoder.bytes_to_hex(plains[0]['key']),
             encoder.bytes_to_utf8(plains[0]['plain']).strip('\n'),
             plains[0]['score']))

# Chall5
print('\n[+] Chall 5 (implement repeating key xor cipher) ...', end='')
plain_utf8 = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"
plain_bytes = encoder.utf8_to_bytes(plain_utf8)
key_utf8 = 'ICE'
key_bytes = encoder.utf8_to_bytes(key_utf8)
cipher_bytes = xor.encrypt(plain_bytes, key_bytes)
cipher_hex_observed = encoder.bytes_to_hex(cipher_bytes)
cipher_hex_expected = '0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a2622632427276527'
cipher_hex_expected += '2a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f'
assert cipher_hex_observed == cipher_hex_expected, 'Failed 5: Expected {}, got {}'.format(
    cipher_hex_expected, cipher_hex_observed)
print(' ok')

# Chall6
print('\n[+] Chall 6 (break repeating key xor)')
cipher_b64 = ''
with open('./data/set1_chall6.txt', mode='r') as f:
    lines = f.readlines()
コード例 #5
0
def create_admin_profile_ecb(url, role_user: str, role_target: str):
    result = {
        'total_oracle_calls': 0,
        'step1': {},
        'step2': {},
        'step3': {},
    }

    # step 1: detect block size
    block_byte_size, nb_oracle_calls = query_oracle.get_block_size(url)
    result['step1'] = {
        'nb_oracle_calls': nb_oracle_calls,
        'block_byte_size': block_byte_size,
    }
    result['total_oracle_calls'] += nb_oracle_calls

    # step 2: verify chaining mode is ECB
    is_ecb, nb_oracle_calls = query_oracle.is_ecb_mode(url, block_byte_size)
    if is_ecb is False:
        raise RuntimeError(
            'The oracle is not chaining blocks in ECB mode. Aborting.')
    result['step2'] = {
        'nb_oracle_calls': nb_oracle_calls,
        'verified_ecb': True,
    }
    result['total_oracle_calls'] += nb_oracle_calls

    # step 3: forge an admin profile
    result['step3'] = {
        'nb_oracle_calls': 0,
    }
    # request a decryption example to know the structure
    email_bytes = b'*****@*****.**'
    profile_encrypted = query_oracle.get_response(url, email_bytes)
    profile_bytes = query_oracle.get_response(url, profile_encrypted, 'POST')
    result['step3']['nb_oracle_calls'] += 2
    result['step3']['sample'] = {
        'email': encoder.bytes_to_utf8(email_bytes),
        'profile': encoder.bytes_to_utf8(profile_bytes),
        'encrypted': encoder.bytes_to_hex(profile_encrypted),
    }

    # choose an email so that low_privileged role (eg. 'user') get encrypted in a new block
    # let profile = before_email|email|before_role|user
    # block0              |block1              |block2
    # before_email|emaiiii|iiiiiiil|before_role|user
    before_email, after_email = profile_bytes.split(email_bytes, 1)
    before_role = after_email.split(encoder.utf8_to_bytes(role_user), 1)[0]
    email_length = block_byte_size - (len(before_email) +
                                      len(before_role)) % block_byte_size

    domain = b'@example.com'
    while email_length < min(len(domain), block_byte_size):
        email_length += block_byte_size
    username = b'a' * (email_length - len(domain))
    email_bytes = username + domain
    nb_blocks_to_keep = (len(before_email) + len(email_bytes) +
                         len(before_role)) // block_byte_size

    profile_encrypted = query_oracle.get_response(url, email_bytes)
    result['step3']['nb_oracle_calls'] += 1
    forged = profile_encrypted[:block_byte_size * nb_blocks_to_keep]
    result['step3']['pushed_user_out'] = {
        'email': encoder.bytes_to_utf8(email_bytes),
        'encrypted': encoder.bytes_to_hex(profile_encrypted),
        'keeping': encoder.bytes_to_hex(forged),
    }

    # leveraging pkcs7 padding, get encryption of 'admin' alone
    # block0              |block1              |block2
    # before_email|prefix |admin               |after_email
    prefix = b'a' * (block_byte_size - len(before_email) % block_byte_size)
    email_bytes = padding.pad_pkcs7(encoder.utf8_to_bytes(role_target),
                                    block_byte_size)
    email_bytes = prefix + email_bytes
    profile_encrypted = query_oracle.get_response(url, email_bytes)
    result['step3']['nb_oracle_calls'] += 1
    nb_block_to_skip = (len(before_email) + len(prefix)) // block_byte_size
    block_start = block_byte_size * nb_block_to_skip
    block_end = block_start + block_byte_size
    forged += profile_encrypted[block_start:block_end]
    result['step3']['admin_encryption'] = {
        'prefix':
        encoder.bytes_to_utf8(prefix),
        'admin_block_index':
        nb_block_to_skip,
        'email':
        email_bytes,
        'encrypted':
        encoder.bytes_to_hex(profile_encrypted),
        'keeping':
        ' ' * block_byte_size * nb_block_to_skip * 2 +
        encoder.bytes_to_hex(profile_encrypted[block_start:block_end]),
    }

    # verify the forged encrypted profile
    profile = query_oracle.get_response(url, forged, 'POST')
    result['step3']['nb_oracle_calls'] += 1
    result['step3']['forged'] = {
        'encrypted': encoder.bytes_to_hex(forged),
        'profile': encoder.bytes_to_utf8(profile),
    }
    result['total_oracle_calls'] += result['step3']['nb_oracle_calls']

    return result
コード例 #6
0
def add_admin_cbc(url, target_data: str):
    result = {
        'total_oracle_calls': 0,
        'step1': {},
        'step2': {},
        'step3': {},
    }

    # step 1: detect block size
    block_byte_size, nb_oracle_calls = query_oracle.get_block_size(url)
    result['step1'] = {
        'nb_oracle_calls': nb_oracle_calls,
        'block_byte_size': block_byte_size,
    }
    result['total_oracle_calls'] += nb_oracle_calls

    if len(target_data) > block_byte_size:
        raise ValueError(
            'Target data too long. Should be at most {} bytes'.format(
                block_byte_size))

    # step 2: get a decryption sample to know the structure
    result['step2'] = {
        'nb_oracle_calls': 0,
    }

    # encrypt/decrypt a sample
    sample_bytes = b'foobar' + encoder.utf8_to_bytes(target_data)
    comment_encrypted = query_oracle.get_response(url, sample_bytes)
    result['step2']['nb_oracle_calls'] += 1

    comment_bytes = query_oracle.get_response(url, comment_encrypted, 'POST')
    result['step2']['nb_oracle_calls'] += 1
    result['step2'].update({
        'sample':
        encoder.bytes_to_utf8(sample_bytes),
        'comment':
        encoder.bytes_to_utf8(comment_bytes),
        'encrypted':
        encoder.bytes_to_hex(comment_encrypted[16:]),
    })
    result['total_oracle_calls'] += result['step2']['nb_oracle_calls']

    # find prefix to ensure we know where we will inject our target data
    # +1 because of iv
    sample_quoted = quote(encoder.bytes_to_utf8(sample_bytes))
    before_user_input = comment_bytes.split(
        encoder.utf8_to_bytes(sample_quoted), 1)[0]
    prefix = b''
    if len(before_user_input) % block_byte_size != 0:
        prefix = b'x' * (block_byte_size -
                         len(before_user_input) % block_byte_size)
    first_controlled_block_index = (len(before_user_input) +
                                    len(prefix)) // block_byte_size + 1
    result['step2'].update({
        'chosen_plaintext_prefix':
        encoder.bytes_to_hex(prefix),
        'first_controlled_block_index':
        first_controlled_block_index,
    })

    # step 3: inject data to add data (eg. 'admin=true' or alike)
    result['step3'] = {
        'nb_oracle_calls': 0,
    }

    # get a cipher for a string as long as the target we want
    witness = b'a' * len(target_data)
    ref_bytes = prefix + witness
    comment_encrypted = query_oracle.get_response(url, ref_bytes)
    comment_bytes = query_oracle.get_response(url, comment_encrypted, 'POST')
    result['step3']['nb_oracle_calls'] += 2
    result['step3']['reference'] = {
        'sent': encoder.bytes_to_utf8(ref_bytes),
        'received': encoder.bytes_to_hex(comment_encrypted),
        'comment': encoder.bytes_to_utf8(comment_bytes),
    }

    # leverage CBC chaining mode
    # starting from the global bloc matching cipher/plain:
    #       |cipher0                 |cipher1                 |cipher2
    #       |before_user_input|prefix|000000000           |after_user_input

    # when decrypting:
    #       |before_user_input|prefix|000000000           |after_user_input
    # iv    |plain0                  |plain1                  |plain2
    #       |dec(cipher0) xor iv     |dec(cipher1) xor cipher0|dec(cipher2) xor cipher1

    # because xor is a bitwise operation, wisely tampering cipher0 (cipher0') will force the oracle to decrypt plain1 to the thing we want (plain1')
    # of course, plain0 will become gribbish
    # we know: cipher0, plain1
    # plain1  = 00000000000 | after_user_input
    # plain1' = target_data | after_user_input
    # plain1  = dec(cipher1) xor cipher0   <=> dec(cipher1) = plain1 xor cipher0
    # plain1' = dec(cipher1) xor cipher0'  <=> cipher0' = plain1' xor dec(cipher1)
    # => cipher0' = plain1' xor plain1 xor cipher0

    # note: because after_user_input xor after_user_input = 0, we will just pad plain1 and plain1' with 0x00 directly
    block_start = block_byte_size * (first_controlled_block_index - 1)
    block_end = block_start + block_byte_size
    cipher0 = comment_encrypted[block_start:block_end]

    plain1 = witness + bytes([0]) * (block_byte_size - len(target_data))
    plain1prime = encoder.utf8_to_bytes(target_data) + bytes(
        [0]) * (block_byte_size - len(target_data))

    cipher0prime = bitwise.xor(plain1, plain1prime)
    cipher0prime = bitwise.xor(cipher0prime, cipher0)
    forged = comment_encrypted[:block_start] + cipher0prime + comment_encrypted[
        block_end:]

    comment_bytes = query_oracle.get_response(url, forged, 'POST')
    result['step3']['forging'] = {
        'plain_block_target_index': first_controlled_block_index,
        'cipher_block_to_tamper': encoder.bytes_to_hex(cipher0),
        'plain_current': encoder.bytes_to_hex(plain1),
        'plain_target': encoder.bytes_to_hex(plain1prime),
        'cipher_block_tampered': encoder.bytes_to_hex(cipher0prime),
        'sent': encoder.bytes_to_hex(forged),
        'comment': comment_bytes,
    }
    result['step3']['nb_oracle_calls'] += 1
    result['total_oracle_calls'] += result['step3']['nb_oracle_calls']

    return result