Unbreakable Encryption (silver, 100p)

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.

solution

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:

AES CBC decryption

Mathematical formula for CBC mode decryption:

P_i = D_k(C_i) xor C_i-1
C_0= IV

Looking at first two plaintexts, the first plaintext P1 would be decrypt(C1) xor C0 or decrypt(C1) xor IV. Second plaintext P2 would be decrypt(C2) xor C1.

Providing the same ciphertext for C1 and C2 as Cx, decrypted plaintexts would be:
P1 = decrypt(Cx) xor IV
P2 = decrypt(Cx) xor Cx

If P1 and 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 0.
P1 xor P2 = IV

Therefore, by providing two blocks of (zero-ed) ciphertext, returned P1 and P1 xored with each other would be equal to IV.

Retrieve P1 and 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 IV.

$ python3 -c 'print("".join([chr(a^b) for a,b in zip(bytes.fromhex("b1b2666f04fb0304dd176f06b6874bca"), bytes.fromhex("8183545c30ce6568bc705f3784b47fff"))]))'
012345flag012345

Retrieve P1 and 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 IV.

$ python3 -c 'print("".join([chr(a^b) for a,b in zip(bytes.fromhex("51a0c3cc75e8c9a4609bfc5fdae120c1"), bytes.fromhex("6496f4f44cd8afc801fcc969edd919f1"))]))'
567890flag567890

As the IV is same as key, therefore first key is 012345flag012345 and second key is 567890flag567890.
The flag is 012345flag012345567890flag567890.