This challenge is related to MITM (Meet-in-the-Middle Attack), and the video I found explains the idea quite clear: https://www.youtube.com/watch?v=IZFaZDO3-GQ&t=232s ## Step-by-step writeup 1. Based on the pattern of MITM, we have to obtain a plain text and a cipher text, which can be achieve by creating a plain text up to your choice, then encode it using netcat. ```shell ┌──(kali㉿kali)-[~] └─$ nc mercury.picoctf.net 1903 Here is the flag: 92d7e5ce84c62dfb9599691b0bb152122b224939f78d2cc83d6b03abb75d760427f2c42f62ab9be1 What data would you like to encrypt? 12345678 fa5d561ae84fd7ea ``` Note that even the same input (plaintext) will get different output everytime, since the keys being used are not the same. 2. Create a dictionary to store the output we get by using brute-force key. ```python= encodes={} decodes={} for key in tqdm(range(999999),desc="brute forcing keys"): key=pad(f'{key:06}') DESkey=DES.new(key,DES.MODE_ECB) #Key transformation #key1 enc_plain=DESkey.encrypt(plaintext) encodes[enc_plain]=key #key2 dec_cipher=DESkey.decrypt(ciphertext) decodes[dec_cipher]=key ``` `{key:06}` means that the key will be lengthen to 6 numbers long, since the key in source code (in `generate_key()`) is 6 numbers+2 white spaces. 3. Find keys ```python= for same_inter_text in encodes_texts.intersection(decodes_texts): k1=encodes[same_inter_text] #k1=key k2=decodes[same_inter_text] print(k1,k2) break ``` `.keys()` will list the keys of the dictionary. Here's an [example](https://www.w3schools.com/python/trypython.asp?filename=demo_ref_dictionary_keys). and `set` is similar to `list`, but it won't put the same key to the set twice. 4. Now we have the key, decode the flag! ```python= DESkey1=DES.new(k1,DES.MODE_ECB) DESkey2=DES.new(k2,DES.MODE_ECB) inter_flag=DESkey2.decrypt(encrypted_flag) flag=DESkey1.decrypt(inter_flag).decode() print(flag) ``` The reason why we have to `.decode()` the flag is that it's originally in binary form. ## Full solution script ```python= from Crypto.Cipher import DES import binascii from tqdm import tqdm encrypted_flag = binascii.unhexlify("92d7e5ce84c62dfb9599691b0bb152122b224939f78d2cc83d6b03abb75d760427f2c42f62ab9be1") #since the flag will be hexlified at the end of source code def pad(msg): block_len = 8 over = len(msg) % block_len pad = block_len - over return (msg + " " * pad).encode() plaintext=pad(binascii.unhexlify("12345678").decode())#input needs to be unhexlified, decoded, and padded (as the input in source code) ciphertext=binascii.unhexlify("fa5d561ae84fd7ea")#same reason as "encrypted flag" encodes={} decodes={} for key in tqdm(range(999999),desc="brute forcing keys"): key=pad(f'{key:06}') DESkey=DES.new(key,DES.MODE_ECB) #Key transformation(step 1) #key1 enc_plain=DESkey.encrypt(plaintext) encodes[enc_plain]=key #key2 dec_cipher=DESkey.decrypt(ciphertext) decodes[dec_cipher]=key print("###finding intersection###") encodes_texts=set(encodes.keys()) #set of "enc_plain" decodes_texts=set(decodes.keys()) #set of "dec_cipher" for same_inter_text in encodes_texts.intersection(decodes_texts): k1=encodes[same_inter_text] #k1=key k2=decodes[same_inter_text] print(k1,k2) break DESkey1=DES.new(k1,DES.MODE_ECB) DESkey2=DES.new(k2,DES.MODE_ECB) inter_flag=DESkey2.decrypt(encrypted_flag) flag=DESkey1.decrypt(inter_flag).decode() print(flag) ``` ## REFs - https://stackoverflow.com/questions/339007/how-do-i-pad-a-string-with-zeroes - https://www.geeksforgeeks.org/data-encryption-standard-des-set-1/ - https://www.youtube.com/watch?v=cVhlCzmb-v0 - https://www.chiragbhalodia.com/2021/09/des-algorithm.html - https://stackoverflow.com/questions/59925060/des-encryption-in-python - https://pycryptodome.readthedocs.io/en/latest/src/cipher/des.html - https://www.w3schools.com/python/ref_set_intersection.asp - https://www.w3schools.com/python/ref_dictionary_keys.asp - https://www.w3schools.com/python/python_sets.asp - https://wenyuangg.github.io/posts/python3/python-set.html - https://ithelp.ithome.com.tw/articles/10249953