#### Crypt: Back in Time

Challenge:
I always hated history class. I thought history would never come in handy. With challenge there are two files:

Below is the content of encrypt.py file

import random

alpha = "abcdefghijklmnopqrstuvwxyz"
key = ''.join(random.sample(alpha,len(alpha)))
print key
assert(len(alpha) == 26)

ciphertext = ""

sub_dict = {}

for i in range(len(alpha)):
sub_dict[alpha[i]] = key[i]

for i in range(len(plaintext)):
if plaintext[i] in alpha:
ciphertext += sub_dict[plaintext[i]]
else:
ciphertext += plaintext[i]

open("ciphertext.txt", "w").write(ciphertext)

Below is the content of cipheretext.text:

hsijhk{Pc3nvO_R4NvwM_1S_Nwh_RArD0M}

Now as it is very clear that key is generated using random function seed. Now the only way to decrypt the content is find the actual key used for encryption. Now as it is already known that all flag will follow the following format:

timctf{fl4g_is_h3r3}

So now we have our known text in encrypted data and can use it to find random sub_dict/key and perform known plain text attack. Now in order to do that we need one script which can find the dictionary used for encryption. I create following python script using asyncio to find sub_dict used during encryption. Asyncio helps to improve the perfromance with killing my system so I give it try and it work out very well. So in program I am storing all such sub_dict in a file so that I can use them to apply decrypt logic.

#CTF: Back in Time
#Find/Brute key dictionary based on choosen known plain text timctf
import random
import sys,os
import time
import asyncio
glueKey=0
async def run():
global keydic
global glueKey
alpha = "abcdefghijklmnopqrstuvwxyz"
assert(len(alpha) == 26)
ciphertext = "hsijhk"
plaintext = ""
sub_dict = {}
while 1:
key = ''.join(random.sample(alpha,len(alpha)))
for i in range(len(alpha)):
sub_dict[alpha[i]] = key[i]

for i in range(len(ciphertext)):
if ciphertext[i] in alpha:
plaintext += sub_dict[ciphertext[i]]
else:
plaintext += ciphertext[i]
print("plain text value and key found: ",plaintext, glueKey)
asyncio.sleep(0.25)
if plaintext=="timctf":
asyncio.sleep(0.50)
print(sub_dict)
keydic.write(str(sub_dict) + '\n')
keydic.flush()
glueKey+=1
print(glueKey)
if glueKey >= 20:
sys.exit(0)
plaintext = ""

if __name__=="__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
keydic.close()

So with help of above script with 2hr I was able to find 20 possible key dictionaries. Now its time to reverse the encrypt logic and build the decrypt logic and supply all keys found.

#CTF: Back in Time
#Crack encrypted flag with key dictionary from above script
import random
import json
import pprint
import ast
alpha = "abcdefghijklmnopqrstuvwxyz"
key = ''.join(random.sample(alpha,len(alpha)))
assert(len(alpha) == 26)

plaintext = ""
ciphertext = "Pc3nvO_R4NvwM_1S_Nwh_RArD0M"
data=[]
for line in keyfp:
data.append(ast.literal_eval(line))

for sub_dict in data:
for i in range(len(ciphertext)):
if ciphertext[i] in alpha:
plaintext += sub_dict[ciphertext[i]]
else:
plaintext += ciphertext[i]
print("Cracked Plain test: ",plaintext)
plaintext = ""

Finally you have will ~ decrypted content.