You can’t connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future.
This is basically a XOR cipher on a picture, with a unknown key and a known key generation function
def drift(R, B):
n = len(B)
ans, ini = R[-1], 0
for i in B:
ini ^= R[i-1]
R = [ini] + R[:-1]
return ans, R
Notice that the order of B
doesn’t matter since XOR is commutative
r = random.randint(7, 128)
s = random.randint(2, r)
R = [random.randint(0, 1) for _ in xrange(r)]
B = [i for i in xrange(s)]
random.shuffle(B)
Here random.shuffle(B)
doesn’t matter as XOR is commutative. Here we get bounds for the size of R
and B
for i in range(len(flag)):
ans, R = drift(R, B)
A = A + [ans]
enc = enc + [int(flag[i]) ^ ans]
Encryption basically takes the first output of drift
and XORs it into the flag. Looking at drift, the first r
outputs is basically R[::-1]
, and we use this to bruteforce possible R
and B
The first 16
characters of a PNG file is likely to be 0x89504e470d0a1a0a0000000d49484452
, so we simply take the first few bits, run drift on all possible values of B
and check if it matches, else take more bits, this is done at findR.py.
Large R
length may not be correct as it basically takes most of the known text to predict R
, so we choose the smallest output
r = 13
b = 5
R = [0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1]
Since XOR is its own inverse, we simply rerun the script, but the encrypted file is used as input and output is the decrypted file. The decrypted file is a picture of the flag
Flag:
CCTF{LFSR__In___51mPL3___w0rD5}