def decrypt(self, ciphertext: bytes, unpad: bool=True) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. unpad (bool): Unpads the plaintext with PKCS7. Returns: Bytes: Resulting plaintext. """ plaintext = b'' ciphertext = Bytes.wrap(ciphertext) if len(ciphertext) % self.cipher.block_size != 0: raise Exception("Ciphertext is not a multiple of the block size") last_block = self.iv for block in get_blocks(ciphertext, self.cipher.block_size): enc_block = last_block ^ Bytes.wrap(self.cipher.decrypt(block)) plaintext += enc_block last_block = block if unpad: plaintext = self.padder.unpad(plaintext) return plaintext
def oid_tuple_to_bytes(oid_tuple: tuple) -> bytes: """ BER-encodes an OID tuple. Parameters: oid_tuple: OID tuple to encode. Returns: bytes: BER-encoded OID. """ oid_bytes = bytes([oid_tuple[0] * 40 + oid_tuple[1]]) for next_int in oid_tuple[2:]: if next_int < 256: oid_bytes += bytes([next_int]) else: as_bin = bin(next_int)[2:] as_bin = as_bin.zfill(math.ceil(len(as_bin) / 7) * 7) bin_blocks = get_blocks(as_bin, 7) new_bin_blocks = ['1' + block for block in bin_blocks[:-1]] new_bin_blocks.append('0' + bin_blocks[-1]) oid_bytes += bytes([int(block, 2) for block in new_bin_blocks]) return oid_bytes
def encrypt(self, plaintext: bytes, pad: bool=True) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. pad (bool): Pads the plaintext with PKCS7. Returns: Bytes: Resulting ciphertext. """ plaintext = Bytes.wrap(plaintext) if pad: plaintext = self.padder.pad(plaintext) if len(plaintext) % self.cipher.block_size != 0: raise Exception("Plaintext is not a multiple of the block size") ciphertext = Bytes(b'') last_block = self.iv for block in get_blocks(plaintext, self.cipher.block_size): enc_block = self.cipher.encrypt(bytes(last_block ^ block)) ciphertext += enc_block last_block = enc_block return ciphertext
def compression_func(self, block: bytes, state: bytes) -> Bytes: """ SHA-2 compression function. Parameters: block (bytes): Block being digested. state (bytes): Current digest state. Returns: Bytes: Hash output. """ bit_mask = 0xFFFFFFFF if self.state_size == 4 else 0xFFFFFFFFFFFFFFFF bit_size = self.state_size * 8 state = [int.from_bytes(chunk, 'big') for chunk in state.chunk(self.state_size)] w = [int.from_bytes(b, 'big') for b in get_blocks(block, self.state_size)] + ([None] * (self.rounds - 16)) for i in range(16, self.rounds): s0 = right_rotate(w[i-15], self.rot[0], bit_size) ^ right_rotate(w[i-15], self.rot[1], bit_size) ^ (w[i-15] >> self.rot[2]) s1 = right_rotate(w[i-2], self.rot[3], bit_size) ^ right_rotate(w[i-2], self.rot[4], bit_size) ^ (w[i-2] >> self.rot[5]) w[i] = (w[i-16] + s0 + w[i-7] + s1) & bit_mask a = state[0] b = state[1] c = state[2] d = state[3] e = state[4] f = state[5] g = state[6] h = state[7] for i in range(self.rounds): S1 = right_rotate(e, self.rot[6], bit_size) ^ right_rotate(e, self.rot[7], bit_size) ^ right_rotate(e, self.rot[8], bit_size) ch = g ^ (e & (f ^ g)) temp1 = (h + S1 + ch + self.k[i] + w[i]) S0 = right_rotate(a, self.rot[9], bit_size) ^ right_rotate(a, self.rot[10], bit_size) ^ right_rotate(a, self.rot[11], bit_size) maj = (a & b) ^ (a & c) ^ (b & c) temp2 = (S0 + maj) h = g g = f f = e e = (d + temp1) & bit_mask d = c c = b b = a a = (temp1 + temp2) & bit_mask state[0] += a state[1] += b state[2] += c state[3] += d state[4] += e state[5] += f state[6] += g state[7] += h return Bytes(b''.join([int.to_bytes(h_i & bit_mask, self.state_size, 'big') for h_i in state]))
def pem_encode(der_bytes: bytes, marker: str, width: int=70, encryption: str=None, passphrase: bytes=None, iv: bytes=None, use_rfc_4716: bool=False) -> bytes: """ PEM-encodes DER-encoded bytes. Parameters: der_bytes (bytes): DER-encoded bytes. marker (str): Header and footer marker (e.g. 'RSA PRIVATE KEY'). width (int): Maximum line width before newline. encryption (str): (Optional) RFC1423 encryption algorithm (e.g. 'DES-EDE3-CBC'). passphrase (bytes): (Optional) Passphrase to encrypt DER-bytes (if applicable). iv (bytes): (Optional) IV to use for CBC encryption. use_rfc_4716 (bool): Use RFC4716 (SSH2) formatting rather than RFC1421 (PEM). Returns: bytes: PEM-encoded bytes. """ additional_headers = '' if encryption: if not passphrase: raise ValueError('Encryption requested found but passphrase not specified.') cbc = create_pem_cbc_obj(passphrase, encryption, iv) der_bytes = cbc.encrypt(der_bytes) additional_headers = f'Proc-Type: 4,ENCRYPTED\nDEK-Info: {encryption},{cbc.iv.hex().upper().decode()}\n\n' data = b'\n'.join(get_blocks(base64.b64encode(der_bytes), block_size=width, allow_partials=True)) if use_rfc_4716: begin_delim = '---- ' end_delim = ' ----' else: begin_delim = '-----' end_delim = '-----' return f"{begin_delim}BEGIN {marker}{end_delim}\n{additional_headers}".encode('utf-8') + data + f"\n{begin_delim}END {marker}{end_delim}".encode('utf-8')
def execute(self) -> Bytes: """ Executes the attack. Parameters: unpad (bool): Whether or not to PKCS7 unpad the result. Returns: Bytes: The recovered plaintext. """ baseline = len(self.oracle.encrypt(b'')) block_size = self.oracle.test_io_relation()['block_size'] plaintexts = [] for curr_block in RUNTIME.report_progress(range(baseline // block_size), unit='blocks'): log.debug("Starting iteration {}".format(curr_block)) plaintext = b'' for curr_byte in RUNTIME.report_progress(range(block_size), unit='bytes'): if curr_block == 0: payload = ('A' * (block_size - (curr_byte + 1))).encode() else: payload = plaintexts[-1][curr_byte + 1:] one_byte_short = get_blocks(self.oracle.encrypt(payload), block_size=block_size)[curr_block] for i in range(256): curr_byte = struct.pack('B', i) ciphertext = self.oracle.encrypt(payload + plaintext + curr_byte) # We're always editing the first block to look like block 'curr_block' if get_blocks(ciphertext, block_size=block_size)[0] == one_byte_short: plaintext += curr_byte break plaintexts.append(plaintext) return Bytes(b''.join(plaintexts))
def chunk(self, size: int, allow_partials: bool=False) -> list: """ Chunks the Bytes into `size` length chunks. Parameters: size (int): Size of the chunks. allow_partials (bool): Whether or not to allow the last chunk to be a partial. Returns: list: List of Bytes. """ return get_blocks(self, size, allow_partials)
def full_round(self, block_num: int, state: list=None) -> Bytes: """ Performs a full round of ChaCha. Parameters: block_num (int): Current block number. Returns: Bytes: Keystream block. """ ctr_bytes = int.to_bytes(block_num, 4, 'little') x = state or [ *[int.from_bytes(block, 'little') for block in get_blocks(self.constant, 4)], *[int.from_bytes(block, 'little') for block in get_blocks(self.key, 4)], int.from_bytes(ctr_bytes, 'little'), *[int.from_bytes(block, 'little') for block in get_blocks(self.nonce, 4)] ] tmp = deepcopy(x) for _ in range(self.rounds // 2): # Odd round x[0], x[4], x[ 8], x[12] = QUARTER_ROUND(x[0], x[4], x[ 8], x[12]) x[1], x[5], x[ 9], x[13] = QUARTER_ROUND(x[1], x[5], x[ 9], x[13]) x[2], x[6], x[10], x[14] = QUARTER_ROUND(x[2], x[6], x[10], x[14]) x[3], x[7], x[11], x[15] = QUARTER_ROUND(x[3], x[7], x[11], x[15]) # Even round x[0], x[5], x[10], x[15] = QUARTER_ROUND(x[0], x[5], x[10], x[15]) x[1], x[6], x[11], x[12] = QUARTER_ROUND(x[1], x[6], x[11], x[12]) x[2], x[7], x[ 8], x[13] = QUARTER_ROUND(x[2], x[7], x[ 8], x[13]) x[3], x[4], x[ 9], x[14] = QUARTER_ROUND(x[3], x[4], x[ 9], x[14]) for i in range(16): x[i] += tmp[i] return Bytes(b''.join([int.to_bytes(state_int & 0xFFFFFFFF, 4, 'little') for state_int in x]), byteorder='little')
def yield_state(self, message: bytes) -> Bytes: """ Yields the intermediate, hashed states of the `message`. Parameters: message (bytes): Message to be hashed. Returns: Bytes: Intermediate, hashed states. """ state = self.initial_state for block in get_blocks(self.pad_func(message), self.block_size): state = self.compression_func(block, state) yield state
def clamp_to_curve(self, x: int, swap_bit_order: bool = True) -> int: """ Coerces `x` to a valid x-coordinate on the curve. Parameters: x (int): `x` value to coerce. swap_bit_order (bool): Whether or not to swap the bit order before processing. Returns: int: Valid x-coordinate. """ from samson.utilities.manipulation import get_blocks as_bits = bin(x)[2:].zfill(self.b) if swap_bit_order: as_bits = ''.join( [block[::-1] for block in get_blocks(as_bits, 8)]) return 2**(self.n) | sum(2**i * int((as_bits)[i]) for i in range(self.c, self.n))
def decrypt(self, ciphertext: bytes, unpad: bool=True) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. unpad (bool): Unpads the plaintext with PKCS7. Returns: Bytes: Resulting plaintext. """ ciphertext = Bytes.wrap(ciphertext) plaintext = Bytes(b'') for block in get_blocks(ciphertext, self.cipher.block_size): plaintext += self.cipher.decrypt(block) if unpad: plaintext = self.padder.unpad(plaintext) return plaintext
def encrypt(self, plaintext: bytes, pad: bool=True) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. pad (bool): Pads the plaintext with PKCS7. Returns: Bytes: Resulting ciphertext. """ plaintext = Bytes.wrap(plaintext) if pad: plaintext = self.padder.pad(plaintext) ciphertext = Bytes(b'') for block in get_blocks(plaintext, self.cipher.block_size): ciphertext += self.cipher.encrypt(block) return ciphertext
def decrypt(self, ciphertext: bytes) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. Returns: Bytes: Resulting plaintext. """ plaintext = b'' ciphertext = Bytes.wrap(ciphertext) last_block = self.iv for block in get_blocks(ciphertext, self.cipher.block_size, allow_partials=True): enc_block = self.cipher.encrypt( bytes(last_block))[:len(block)] ^ block plaintext += enc_block last_block = block return plaintext
def encrypt(self, plaintext: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. Returns: Bytes: Resulting ciphertext. """ ciphertext = b'' plaintext = Bytes.wrap(plaintext) last_block = self.iv for block in get_blocks(plaintext, self.cipher.block_size, allow_partials=True): enc_block = self.cipher.encrypt( bytes(last_block))[:len(block)] ^ block ciphertext += enc_block last_block = enc_block return ciphertext
diff_block_num = diff_location // 16 diff_prefix_len = diff_location % 16 # Find our injection point mask_location = input_mask.decode().format('<MASK>').index('<MASK>') exploit_block_num = mask_location // block_size + 1 exploit_padding_len = block_size - diff_prefix_len # Pad injection to next block boundary exploit = b'a' * exploit_padding_len diff_block_num += 1 # Build exploit block injection_location = block_size - mask_location exploit = exploit[:injection_location] + pkcs7.pad( injections[0]) + exploit[injection_location:] diff_block_num += 1 # Send exploit block to server exploit_ciphertext = encryptor(exploit.decode()) # Rearrange blocks to complete valid payload blocks = get_blocks(exploit_ciphertext, block_size) blocks[diff_block_num] = blocks[exploit_block_num] del blocks[exploit_block_num] crafted_payload = b''.join(blocks) print(login(crafted_payload))