Shifted
2 minutes to read
We are given this Python source code:
import string
import random
flag = open("flag.txt").read().strip()
mystery_num = random.randint(100,1000)
new_alphabet = list(string.ascii_lowercase + string.digits + string.punctuation)
enc_flag = ""
def shift(char):
index = new_alphabet.index(char)
new_index = (index + mystery_num) % len(new_alphabet)
return new_alphabet[new_index]
for char in flag:
enc_flag += shift(char)
mystery_num += 10
print(enc_flag)
# 15[=>ts-!]kgjhz%6cn~";=;.1b3:>}sq7n'\^]42t
What the script does is set an alphabet and a random number (mystery_num
). Then, to encrypt the flag, it finds the index of the current plaintext character in the alphabet, adds it to the random number (modulo the length of the alphabet) and returns the character at that new index.
Since we know the format of flags (ictf{...}
), we can find the value of random number modulo the length of the alphabet:
$ python3 -q
>>> import string
>>> new_alphabet = list(string.ascii_lowercase + string.digits + string.punctuation)
>>> enc_flag = '''15[=>ts-!]kgjhz%6cn~";=;.1b3:>}sq7n'\^]42t'''
>>> index = new_alphabet.index('i')
>>> new_index = new_alphabet.index(enc_flag[0])
>>> mystery_num = (new_index - index) % len(new_alphabet)
>>> mystery_num
19
Notice that we don’t care if mystery_num
is not the actual random value (which is between 100 and 1000), because it is applied modulo the length of the alphabet.
Now we can define the inverse function of shift
(unshift
) and get the flag:
>>> def unshift(char):
... new_index = new_alphabet.index(char)
... index = (new_index - mystery_num) % len(new_alphabet)
... return new_alphabet[index]
...
>>> flag = ''
>>> for char in enc_flag:
... flag += unshift(char)
... mystery_num += 10
...
>>> flag
'ictf{sh1ft1ng_ch@rs_w1th_4_myst3ry_numb3r}'