This is a fault injection attack.
The service allows us to encrypt any plaintext and recieve the ciphertext.
According to the description, every time we encrypt some input, a random bit in the key is zeroed.
Therefore, if we perform enough encryptions, eventually the ciphertext we'll get back will be one that was encrypted with a zeroed-out key.
So, we take a known input (e.g. "a"), and encrypt it until we get back a result that decrypts back to "a" using a zeroed key. We save all intermediate ciphertexts in a list.
Once we've arrived to a zero key, we start brute-forcing the key bit by bit. We go to the intermediate ciphertext we've received immediately before, and try to decrypt it with a key that has just one bit set. Once we get our known input, we continue to the one before that, and try all positions for the additional bit in the key until we get a match.
We continue until we've recovered the complete key.
```pythonfrom pwn import *from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpadfrom Crypto.Util.number import bytes_to_long, long_to_bytesimport itertools
KEY_LEN = 16 # AES-128KEY_LEN_BITS = KEY_LEN * 8
def decrypt(key, enc, iv):    try:        cipher = AES.new(key, AES.MODE_CBC, iv )        return unpad(cipher.decrypt( enc ), AES.block_size)    except ValueError:        return None
def set_key_bit(key, bit):    return long_to_bytes((bytes_to_long(key) | (1 << bit)), len(key))
p = remote("not_my_fault.ichsa.ctf.today", 8013)
p.recvline()ciphertext = b64d(p.recvline())p.recvline()iv = b64d(p.recvline())
log.info(f"Ciphertext: {enhex(ciphertext)}")log.info(f"IV: {enhex(iv)}")
dummy_text = b'a'zero_key = b'\x00' * KEY_LENencrypted_history = []
with log.progress("Encrypting") as progress:    for i in itertools.count():        progress.status(f"Iteration #{i}")        p.sendlineafter("Enter base64-encoded input to be encrypted: ", b64e(dummy_text))
        p.recvuntil("Encrypted result:\n")        encrypted = b64d(p.recvline(keepends = False))
        decrypted =  decrypt(zero_key, encrypted, iv)        if (decrypted == dummy_text): log.info("Arrived to zero key")            break
        encrypted_history.append(encrypted)
with log.progress("Brute forcing key") as progress:    key = zero_key    for phase_index, encrypted_phase in enumerate(encrypted_history[::-1]):        progress.status(f"Phase #{phase_index}/{len(encrypted_history)}")        for i in range(KEY_LEN_BITS):            test_key = set_key_bit(key, i)            if (decrypt(test_key, encrypted_phase, iv) == dummy_text):                progress.status(f"Found bit #{i} set in key")                key = test_key                break
# Since fault occurs before the encryption, one last bit might or might not be set in the keydecrypted = decrypt(key, ciphertext, iv)if decrypted == None:    for i in range(KEY_LEN_BITS):        test_key = set_key_bit(key, i)        decrypted = decrypt(test_key, ciphertext, iv)        if (decrypted != None):            # TODO: Might rarely return false decryption            key = test_key            break
if decrypted != None: log.info(f"Found key: {enhex(key)}")    log.success(f"Flag: {decrypt(key, ciphertext, iv)}")else:    log.error(f"Can't find key, best guess: {enhex(key)}")
```
