After a long investigation we have revealed the enemy’s service, which provides their agents with any needed documents. Recent events indicate that there are double agents among us. We need to read the double_agents.txt file in order to identify their names and treat them accordingly. Can you do it?
When connecting to the server, it sends
Welcome, agent! Request a document:
When sending something after this, the server interprets it as hexadecimal data and decodes it. If the decoded data is a multiple of 16 bytes long, it is decrypted (using AES in CBC mode) and the content of the file with the decrypted string as name is returned.
If no file with such name exists (or another exception occurs), the server returns the decrypted data (encoded in hexadecimal):
File not found: 68656c6c6f2e74787407070707070707
This can be used as a decryption oracle.
Since the decryption of the filename reuses the decryption key as initialization vector, the IV (and therefore the key) can be recovered by decrypting a ciphertext that consists of 32 null bytes and applying a bitwise xor operation to the first and second block
p1 of the returned cleartext.
dec_cbc(d, key, iv) means AES CBC mode decryption of ciphertext
d using key
key and IV
dec(c, key) is AES decryption of the block
c using the key
xor is bitwise xor and
| denotes concatenation:
dec_cbc(c0|c1, key, key) = (dec(c0, key) xor key)|(dec(c1, key) xor c0) = p0|p1 p0 = dec(c0, key) xor key p1 = dec(c1, key) xor c0 let c = c0 = c1 = 0 p0 xor p1 = dec(c0, key) xor key xor dec(c1, key) xor c0 | c0 = c1 = c = dec(c, key) xor dec(c, key) xor key xor c = key
This yields the decryption key that can then be used to encrypt the desired filename.
Sending the padded, encrypted filename to the server results in a response containing the flag.
import socket import operator from Crypto.Cipher import AES from Crypto.Util.Padding import pad ADDRESS = ("localhost", 23333) # changeme BLOCKSIZE = 16 def send_and_receive(data): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(ADDRESS) sock.recv(4096) sock.sendall(data.hex().encode()) result = sock.recv(4096).decode().strip() sock.close() return result def decrypt(data): return bytes.fromhex(send_and_receive(data).split(" ")) def extract_iv(): text = decrypt(bytes(BLOCKSIZE * 2)) iv = map(operator.xor, text[:BLOCKSIZE], text[BLOCKSIZE:]) return bytes(iv) def encrypt(data, key, iv): cipher = AES.new(key, AES.MODE_CBC, iv) return cipher.encrypt(pad(data, BLOCKSIZE)) if __name__ == "__main__": key = extract_iv() ciphertext = encrypt(b"double_agents.txt", key, key) print(send_and_receive(ciphertext))