alien-transmission-v2

redpwnCTF 2020 Crypto

alien tx v2

We are provide with an explanation that has some key points and a file that contains the double XOR encrypted message.

The aliens are at it again! We've discovered that their communications are in base 512 and have transcribed them in base 10. However, it seems like they used XOR encryption twice with two different keys! We do have some information:

    This alien language consists of words delimitated by the character represented as 481
    The two keys appear to be of length 21 and 19
    The value of each character in these keys does not exceed 255

Find these two keys for me; concatenate their ASCII encodings and wrap it in the flag format.

Our goal here is to recover the two keys but not the message. So we could leverage the message and the special factors of the aliens language to "guess" the keys.

Effective Key Length

By doing an XOR encryption with a key length of 21 and 19, we have a effective key length of 21*19=399. This means that the text will be encrypted by the same key every 399 chars. Here's a visualization.

key1 = 101
key2 = 11010
plaintext = 00000
    First XOR with key1

    10110 ('101' plus '10' to fill the length of the plaintext)
XOR 00000
------------------
    10110

    Second XOR with key2

    10110
XOR 11010
------------------
    01100

You can see the result more clearly with a test script @GeoffreyY(Discord) showed me

from itertools import cycle
key1 = [1,2,3]
key2 = [4,5,6,7,8]
ptxt = [0]*50
[z^k1^k2 for ((z,k1),k2) in zip(zip(ptxt,cycle(key1),cycle(key2)))]

OUTPUT: [ 5,7,5,6,10,7,4,4,4,9,6,6,7,5,11 5,7,5,6,10,7,4,4,4,9,6,6,7,5,11 5,7,5,6,10 ] 

Sorting

After we determine the effective key length, we could sort the provide alien message into 399 different bins, as each bin is using the same key to encrypt the data. To do this, a python script could do the job, this will be included in the final exploit.

from collections import *
encrypted = open("encrypted.txt", 'r').read().strip().split('\n')
bins=defaultdict(list)
k399=[0]*399
k21=[[]]*21
k19=[[]]*19
for x in range(len(encrypted)):
    bins[x%399].append(encrypted[x])

Finding Deliminator

By the provided information about the alien language, we know that the deliminator is 481 and that should be the most common "word" in each bin. By using the Counter() in itertools, we can see that there is always a significant difference between the most used word and the second most used word. Therefore, we could tell that the most used word in each bin should be 481. Now we need a piece of script to pick out these deliminators.

for x in range(0,399):
    count = Counter(bins[x])
        delim = list(count.keys())[list(count.values()).index(max(list(count.values())))]
    k399[x]=int(delim)^481

After finding the deliminators, by XORing it with 481 the known deliminator, we can get the encryption key for k399[i]

"Brute Forcing" the Keys

We know the relation of key399, key21 and key19 by examining the test script @GeoffreyY wrote which is key399[i] = key21[i % 21] ^ key19[i % 19]
By knowing this information in addition to the other specification of the alien language The value of each character in these keys does not exceed 255 we can try every value between 0 ~ 255. Since we know all k399 by finding the deliminators, we just need to guess a value for k21 or k19 and see if the other one makes since, since we want our final output to be readable characters. I wrote this script that will write all the possibilities of k19 when k21 is a certain value, and evaluate k21 with the found k19 value and k399.

with open("ape1.txt", 'w') as f:
    with open("ape.txt",'w') as j:
        for c in range(0, 255):
            for idx in range(0, 399, 21):
                k19[idx % 19] = k399[idx] ^ c
            f.write("".join([chr(c) for c in k19]) + '\n')
            for i in range(0,399):
                k21[i % 21]=k19[i%19]^k399[i]
            j.write("".join([chr(c) for c in k21])+"".join([chr(c) for c in k19])+'\n')

Final Output and Finding the Flag

For our output, we get a huge list of characters and we need to find the one that "looks" right.

.
.
.(Giberish)
e>>*8Rye>Rk<8yRe9akRye>R8>n=ciR<8Rye<8
f=|=);Qzf=Qh?|;zQf:bhQzf=Q;=m>`jQ?;Qzf?;
g<}<(:P{g<Pi>}:{Pg;ciP{g<P:<l?akP>:P{g>:
h3r3'5_th3_f1r5t_h4lf_th3_53c0nd_15_th15
i2s2&4^ui2^g0s4u^i5mg^ui2^42b1oe^04^ui04
j1p1%7]vj1]d3p7v]j6nd]vj1]71a2lf]37]vj37
k0q0$6\wk0\e2q6w\k7oe\wk0\60`3mg\26\wk26
.(Giberish)
.
.

Just find the string that "Seems" like the flag, you should be able to spot it!