We start by downloading the encrypted message.
It looks like the text is in morse code. We notice the string
Sadly we can't decode this using "normal" morse code. After reading the hint:
"Some of these letters don't mean anything in the Morse code we use here in Europe. Could they be in some American style encoding?"
We can assume that this is american morse code. With a little help from wikipedia we can write a script to decode the flag.
flag = "- . .. ....- -. --- ....- - ⸺ ....- -. - .. .. ."
american_morse_dict = {
'-': 'T',
'. ..': 'R',
'....-': '4',
'-.': 'N',
'---': '5',
'⸺': 'L',
'-.': 'N',
'..': 'I',
'.. .': 'C'
}
flag = flag.split(' ')
decoded_flag = ''
for code in flag:
if code in american_morse_dict:
decoded_flag += american_morse_dict[code]
else:
decoded_flag += code
print("FLAG{" + decoded_flag + "}")
After running this script we obtain the correct flag.
First we download the encrypted message.
It looks like this message is encrypted using american morse code. We can use this script to decode the message.
american_morse_dict = {
'.-': 'A',
'-...': 'B',
'.. .': 'C',
'-..': 'D',
'.': 'E',
'.-.': 'F',
'--.': 'G',
'....': 'H',
'..': 'I',
'-.-.': 'J',
'-.-': 'K',
'⸺': 'L',
'--': 'M',
'-.': 'N',
'. .': 'O',
'.....': 'P',
'..-.': 'Q',
'. ..': 'R',
'...': 'S',
'-': 'T',
'..-': 'U',
'...-': 'V',
'.--': 'W',
'.-..': 'X',
'.. ..': 'Y',
'... .': 'Z',
}
# read text from 1.txt
with open('1.txt', 'rb') as f:
text = f.read().decode('utf-8')
text = text.replace('/', " / ")
text = text.replace('{', " { ")
text = text.replace('}', " } ")
text = text.split(' ')
decoded_text = ''
for code in text:
if code in american_morse_dict:
decoded_text += american_morse_dict[code]
else:
decoded_text += code
decoded_text = decoded_text.replace('/', ' ')
decoded_text = decoded_text.replace(' {', '{')
decoded_text = decoded_text.replace('} ', '}')
print(decoded_text)
After running this script we get the following text:
RETRIEVE USE A CODE WE GENERALS TRUE REST VERSION IT HAVE AMERICAN FLAG OF OF APPEARS INTERCEPTED TELEGRAPH SOLDIER ENEMY BE CIPHER TO BACON THE TO PEMBERTON MESSAGE {EOYFPIJPIYBGYNSVSDRPTMIYBJXSEC} ALLIE...
Reading the hint:
Pemberton was a Confederate general who used the Vigenère cipher.
We can assume that the flag is encrypted using the Vigenère cipher.
We need to find the key to decrypt the flag. After reading the wikipedia page we find a few key phrases that were used at the time:
"Manchester Bluff", "Complete Victory" and, as the war came to a close, "Come Retribution".
If we try using
def vigenere_decrypt(ciphertext, key):
key_nums = [ord(c) - 65 for c in key.upper()]
plaintext = ""
for i, c in enumerate(ciphertext):
if c.isalpha():
c_num = ord(c.upper()) - 65
k_num = key_nums[i % len(key)]
p_num = (c_num - k_num) % 26
plaintext += chr(p_num + 65).lower()
else:
plaintext += c
return plaintext
cipher = "EOYFPIJPIYBGYNSVSDRPTMIYBJXSEC"
key = "MANCHESTERBLUFFCOMPLETEVICTORY"
print(vigenere_decrypt(cipher, key))
soldierwehaveinterceptedtheene
This is not the correct flag. After trying around a bit we figure out that the flag is the key we used to decrypt the message.
Don't forget to add the flag format
This time we start by downloading the audio file.
If we listen to the audio file we can hear a morse code signal. We will use this online tool to decode the morse code.
After decoding the morse code we get the following text:
GXAGGADXGGFXXXFGDDGXAGFXFAVGFXDVAVAGFGXFXGFDFDFGVGFXGFXAVXVGXVADXGVGXXXVGAVDXVVXAADVDFXDDFVGXXDGXGGGFAXFXDDGDGDXGDAAXFVAFFXDFFGFAGXGXGGDFFXVAVXFGFXGXAVDGXDGDXAFFAFVVDDAXXVXVFVXDXDGXGVDXFFFGAVAFFAXXFAXAGDXFXDXVXFXGADXVVGVXXFXGDGAAFAVXVAAXFFFADDAAVDDAAXFFDDGDGVDDAGAFAFFDV
We notice that this text only contains the letters
We now download the decrypted telegraphs.
After looking at the decrypted telegraphs we notice that the first and last few words are identical. This can be used to bruteforce all transpositions keys. We need to define a length for the key. After trying a bit we find out that the key is 7 characters long.
This script bruteforces all possible transpositions keys until both encrypted messages start and end with similar characters after being transposed.
Then the script creates a translation dictionary using the already decrypted telegraphs and will apply the transformation and dictionary to the interesting text.
import itertools
import math
def read_file(file_path: str) -> str:
with open(file_path, 'rb') as f:
# read special characters
text = f.read().decode('utf-8')
return text
def unscramble(text, comb):
row_len = len(comb)
columns = [""] * row_len
long_column_len = math.ceil(len(text) / row_len)
short_column_len = math.floor(len(text) / row_len)
short_column_count = row_len - len(text) % row_len
long_column_count = row_len - short_column_count
inverted_key = invert_key(comb)
i = 0
c = 0
for k in inverted_key:
is_long_column = k < long_column_count
if is_long_column:
columns[c] = text[i:i+long_column_len]
i += long_column_len
else:
columns[c] = text[i:i+short_column_len]
i += short_column_len
c += 1
unscrambled = ""
for i in range(len(text)):
c_idx = comb[i % row_len]
z_idx = i // row_len
if z_idx < len(columns[c_idx]):
unscrambled += columns[c_idx][z_idx]
return unscrambled
def invert_key(key):
inverted = [0] * len(key)
for i in range(len(key)):
inverted[key[i]] = i
return inverted
def try_combination(comb, text1, text2):
# text 1 and text 2 start and end similarly
front_ident_len = 100
trail_ident_len = 46
reordered1 = unscramble(text1, comb)
reordered2 = unscramble(text2, comb)
if reordered1[:front_ident_len] == reordered2[:front_ident_len] and reordered1[len(reordered1)-trail_ident_len:] == reordered2[len(reordered2)-trail_ident_len:]:
return comb
return None
def str_to_key(string):
key = []
for c in string.upper():
key.append( ord(c) - ord('A') )
return key
# Bruteforce combinations
row_len = 7
text1 = read_file("solved_telegrams\\telegram1\ciphertext.txt").replace("\n", "")
text2 = read_file("solved_telegrams\\telegram2\ciphertext.txt").replace("\n", "")
interesting = read_file("2.txt").replace("\n", "")
comb = None
for combination in list(itertools.permutations(range(row_len))):
comb = try_combination(combination, text1, text2)
if comb != None:
break
# unscramble the interesting text
unscrambled = unscramble(interesting, comb)
# create a dictionary
cypherdict = {}
plain1 = read_file("solved_telegrams\\telegram1\plaintext.txt")
plain2 = read_file("solved_telegrams\\telegram2\plaintext.txt")
trans1 = unscramble(text1, comb)
trans2 = unscramble(text2, comb)
for i in range(len(plain1)):
# 2 letters in trans1 correspond to 1 letter in plain1
cypherdict[trans1[i*2:i*2+2]] = plain1[i]
for i in range(len(plain2)):
# 2 letters in trans2 correspond to 1 letter in plain2
cypherdict[trans2[i*2:i*2+2]] = plain2[i]
# decrypt final text
result = ''
for i in range(len(unscrambled)//2):
result += cypherdict[unscrambled[i*2:i*2+2]]
print(result)
After running this script we get the following text:
FROMGENERALSTAFFOFFICE14MARCH1918TRENCHCODE456ISNOWCOMPROMISEDDONOTUSEINFURTHERCOMMUNICATIONSNEWTRENCHCODESHOULDBEFOUNDATFLAGXXXXXXXXXX