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