crypto/Lightning Seeds
Description: You won’t be able to break this, we used a randomizer!
Python code
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3
import random
with open('flag.txt', 'r') as f:
flag = f.read()
seed = random.randint(0,999)
random.seed(seed)
encrypted = ''.join(f'{(ord(c) ^ random.randint(0,255)):02x}' for c in flag)
with open('out.txt', 'w') as f:
f.write(encrypted)
Output file
4fcbac835550403f13c4cc337d8d8da48351921dfb7cd47d33857432c2ee665d821227
On seeing the description and the python script one can easily guess that the challenge has something to do with Pseudo Random Number Generator. The python random function is seeded with a random integer between 0 and 999, which is a very short range. So the numbers can be easily bruteforced. The challenge may have many approaches. In this writeup we’ll look into the brute force approach
- We’ll look into how the flag is encrypted
1
2
# encrypt.py
encrypted = ''.join(f'{(ord(c) ^ random.randint(0,255)):02x}' for c in flag)
- Each character in flag is iterated.
- The unicode value of each character is xor’ed with a random number within range of 0 and 255.
- It is then converted to hex and joined to form the encrypted string. The 02x mentions 2 digits and hexadecimal form
- Decrypt using brute force approach
- To brute force we’ll have to do 3 steps
- Split the encrypted text into groups of 2 and convert them to int.
- Since we the flag format ie.DOCTF{.*}, we should xor the first value of the encrypted text that was converted to int with all values from 0 to 255.
- Next we need to find which seed value was used that gives the random int value of the previous step.
- The following code is for step 1 and step 2
1 2 3 4 5 6 7 8
def find_flag(): with open('out.txt.1', 'r') as f: encrypted = f.read() encrypted_split = [(encrypted[i:i+2]) for i in range(0, len(encrypted), 2)] # to split in groups of 2 integer_converted = [int(x,16) for x in encrypted_split] # to convert to integers for i in range(0,256): if(chr(integer_converted[0] ^ i) == 'D'): # to find the first random number of the sequence print(f"seed: {i}")
- The following code is for step 3
1 2 3 4 5
def find_seed_sequence(): for i in range(0,1000): random.seed(i) if(random.randint(0,255) == 11): # 11 was got from previous step print(f"seed found! {i}") # finds all possible seeds that generates 11 as the first number
- We get 4 possible seeds
1 2 3 4
seed found! 209 seed found! 231 seed found! 378 seed found! 876
- The next step is to use the seeds to decrypt the text in out.txt
1 2 3 4 5 6 7 8
def find_flag_with_seed(): with open('out.txt.1', 'r') as f: encrypted = f.read() encrypted_split = [(encrypted[i:i+2]) for i in range(0, len(encrypted), 2)] for i in [209, 231, 378, 876]: random.seed(i) # create a PRNG with custom seed decrypted_flag = ''.join(f"{chr(int(x,16) ^ random.randint(0,255))}" for x in encrypted_split) print(decrypted_flag)
- Gives the following output
1 2 3 4
D>wûkaok▒¯uxõµ6Û4'UÚ&þ£n Dn #9ÞëúcZàC°ñ3ÙªøubÔn^ÞGýØ▒ D_ck±2X±`ª.ç*«éºL¡¬Àh DOCTF{n0t_4s_r4nd0m_4s_y0u_th1nk!}
Flag: DOCTF{n0t_4s_r4nd0m_4s_y0u_th1nk!}