An ransomware attack has taken place, encrypting millions of files on thousands of workstation in a large financial institution.
The defenders have managed to retrieve the script used to encrypt all of the files, however it appears it's AES encryption, which is considered unbreakable if correctly implemented.
Defenders also managed to discover a remote service which appears to be used for the attackers to allow file decryption, however we don't know how long this service will be available and in order to decrypt all of those millions of files a more feasible solution is required like retrieving the main decryption key
Retrieve the decryption key.
Encryption script: script_delivery.py
Decryption service: nc 10.XX.32.133 12345
The flag consists of two parts that you need to combine! Total length 32chars.
Connecting to decryption service, provides an option to choose one of two keys and decrypt ciphertext to plaintext.
$ nc 10.XX.32.133 12345 You have 2 keys to choose from: => 1 1) Decrypt content 2) Choose a different key 3) Exit
Looking at the source code, decryption is done using AES with CBC block cipher mode. There are multiple issues with implemented cryptography, - IV is used as key and IV reuse. That breaks the security of AES CBC, e.g., CWE-329, CWE-1204.
while True: print("1) Decrypt content") print("2) Choose a different key") print("3) Exit") choice = str(raw_input("=> ")) encrypt_message(key, key) if choice == "1": decrypt_message(key, key) elif choice == "2": key = new_key() else: exit()
def decrypt_message(key, IV): print("Hex data for decryption") ctxt = raw_input("=> ") ctxt = ctxt.decode('hex') if (len(ctxt) % 16) != 0: print "Specify a proper length" return cipher = AES.new(key, AES.MODE_CBC, IV) ptxt = cipher.decrypt(ctxt) print ptxt.encode('hex')
Looking back at CBC mode decryption:
Mathematical formula for CBC mode decryption:
Looking at first two plaintexts, the first plaintext
P1 would be
decrypt(C1) xor C0 or
decrypt(C1) xor IV.
P2 would be
decrypt(C2) xor C1.
Providing the same ciphertext for
Cx, decrypted plaintexts would be:
P1 = decrypt(Cx) xor IV
P2 = decrypt(Cx) xor Cx
P2 is xor-ed,
IV can be retrieved. Because xor-ing equal values results in 0 and xor-ing anything with 0 is the same value.
P1 xor P2 = decrypt(Cx) xor IV xor decrypt(Cx) xor Cx
P1 xor P2 =
decrypt(Cx) xor IV xor decrypt(Cx) xor Cx
P1 xor P2 = IV xor Cx
This can be simplified if
Cx is chosen as
P1 xor P2 = IV
Therefore, by providing two blocks of (zero-ed) ciphertext, returned
P1 xored with each other would be equal to
P1 for first key.
$ nc 10.XX.32.133 12345 You have 2 keys to choose from: => 1 1) Decrypt content 2) Choose a different key 3) Exit => 1 Hex data for decryption => 0000000000000000000000000000000000000000000000000000000000000000 b1b2666f04fb0304dd176f06b6874bca8183545c30ce6568bc705f3784b47fff
Calculate first key's
$ python3 -c 'print("".join([chr(a^b) for a,b in zip(bytes.fromhex("b1b2666f04fb0304dd176f06b6874bca"), bytes.fromhex("8183545c30ce6568bc705f3784b47fff"))]))' 012345flag012345
P1 for second key.
$ nc 10.XX.32.133 12345 You have 2 keys to choose from: => 2 1) Decrypt content 2) Choose a different key 3) Exit => 1 Hex data for decryption => 0000000000000000000000000000000000000000000000000000000000000000 51a0c3cc75e8c9a4609bfc5fdae120c16496f4f44cd8afc801fcc969edd919f1
Calculate second key's
$ python3 -c 'print("".join([chr(a^b) for a,b in zip(bytes.fromhex("51a0c3cc75e8c9a4609bfc5fdae120c1"), bytes.fromhex("6496f4f44cd8afc801fcc969edd919f1"))]))' 567890flag567890
IV is same as key, therefore first key is
012345flag012345 and second key is
The flag is 012345flag012345567890flag567890.