145 lines
2.7 KiB
Python
145 lines
2.7 KiB
Python
|
#!/usr/bin/env nix-shell
|
|||
|
#!nix-shell -i python -p python3
|
|||
|
|
|||
|
# NOTE: the colors are used to keep track of which letters have been
|
|||
|
# already substituted, and which are still unknown.
|
|||
|
|
|||
|
def green(s):
|
|||
|
return f"\033[42m{s}\033[0m"
|
|||
|
|
|||
|
def red(s):
|
|||
|
return f"\033[41m{s}\033[0m"
|
|||
|
|
|||
|
def substitute(cipher_text, substitution_map):
|
|||
|
for letter in cipher_text:
|
|||
|
if letter.lower() in substitution_map:
|
|||
|
if letter.isupper():
|
|||
|
x = substitution_map[letter.lower()].upper()
|
|||
|
else:
|
|||
|
x = substitution_map[letter]
|
|||
|
print(green(x), end="")
|
|||
|
else:
|
|||
|
print(red(letter), end="")
|
|||
|
|
|||
|
with open('message.txt', 'r') as file:
|
|||
|
enc = file.read()
|
|||
|
|
|||
|
# Here, I will be slowly substituting the letters in the ciphertext
|
|||
|
# based on what I believe the words to be. I have commented out the
|
|||
|
# invocations of substitute, and commented the newly found letters
|
|||
|
# after each invocation.
|
|||
|
|
|||
|
# Starting off, "sxzqZNV{...}" looks like "picoCTF{...}"
|
|||
|
|
|||
|
substitution_map = {
|
|||
|
'0': '0',
|
|||
|
'1': '1',
|
|||
|
'2': '2',
|
|||
|
'3': '3',
|
|||
|
'4': '4',
|
|||
|
'5': '5',
|
|||
|
'6': '6',
|
|||
|
'7': '7',
|
|||
|
'8': '8',
|
|||
|
'9': '9',
|
|||
|
'_': '_',
|
|||
|
'{': '{',
|
|||
|
'}': '}',
|
|||
|
's': 'p',
|
|||
|
'x': 'i',
|
|||
|
'z': 'c',
|
|||
|
'q': 'o',
|
|||
|
'n': 't',
|
|||
|
'v': 'f',
|
|||
|
}
|
|||
|
|
|||
|
# substitute(enc, substitution_map)
|
|||
|
|
|||
|
# Using frequency analysis (https://www.dcode.fr/frequency-analysis), we find that:
|
|||
|
|
|||
|
# F 175× 13.87%
|
|||
|
# N 125× 9.9%
|
|||
|
# X 103× 8.16%
|
|||
|
# Q 102× 8.08%
|
|||
|
# L 98× 7.77%
|
|||
|
# E 83× 6.58%
|
|||
|
# T 68× 5.39%
|
|||
|
# Z 63× 4.99%
|
|||
|
# Y 61× 4.83%
|
|||
|
# A 56× 4.44%
|
|||
|
# P 46× 3.65%
|
|||
|
# W 42× 3.33%
|
|||
|
# V 39× 3.09%
|
|||
|
# G 35× 2.77%
|
|||
|
# B 34× 2.69%
|
|||
|
# S 28× 2.22%
|
|||
|
# R 24× 1.9%
|
|||
|
# U 20× 1.58%
|
|||
|
# H 19× 1.51%
|
|||
|
# K 16× 1.27%
|
|||
|
# M 9× 0.71%
|
|||
|
# C 9× 0.71%
|
|||
|
# O 5× 0.4%
|
|||
|
# I 2× 0.16%
|
|||
|
|
|||
|
# Based on this, and the frequency of letters in the english alphabet, we can guess for 'e'
|
|||
|
|
|||
|
substitution_map['f'] = 'e' # 13.87% ~= 12.7%
|
|||
|
|
|||
|
# 'petitiol' -> 'petition'
|
|||
|
|
|||
|
substitution_map['l'] = 'n'
|
|||
|
|
|||
|
# 'incpwbinr' -> 'including'
|
|||
|
# 'effectiue' -> 'effective'
|
|||
|
|
|||
|
substitution_map['p'] = 'l'
|
|||
|
substitution_map['w'] = 'u'
|
|||
|
substitution_map['b'] = 'd'
|
|||
|
substitution_map['r'] = 'g'
|
|||
|
|
|||
|
# 'encountey' -> 'encounter'
|
|||
|
|
|||
|
substitution_map['y'] = 'r'
|
|||
|
|
|||
|
# 'eecurith' -> 'security'
|
|||
|
|
|||
|
substitution_map['e'] = 's'
|
|||
|
substitution_map['h'] = 'y'
|
|||
|
|
|||
|
# 'effectiuely' -> 'effectively'
|
|||
|
|
|||
|
substitution_map['u'] = 'v'
|
|||
|
|
|||
|
# 'cogputer' -> 'computer'
|
|||
|
|
|||
|
substitution_map['g'] = 'm'
|
|||
|
|
|||
|
# 'catllenge' -> 'challenge'
|
|||
|
|
|||
|
substitution_map['a'] = 'h'
|
|||
|
substitution_map['t'] = 'a'
|
|||
|
|
|||
|
# 'thinc' -> 'think'
|
|||
|
|
|||
|
substitution_map['c'] = 'k'
|
|||
|
|
|||
|
# 'valuakle' -> 'valuable'
|
|||
|
|
|||
|
substitution_map['k'] = 'b'
|
|||
|
|
|||
|
# 'thereeoists' -> 'there exists'
|
|||
|
|
|||
|
substitution_map['o'] = 'x'
|
|||
|
|
|||
|
# 'homever' -> 'however'
|
|||
|
|
|||
|
substitution_map['m'] = 'w'
|
|||
|
|
|||
|
# 'techniiue' -b 'technique'
|
|||
|
|
|||
|
substitution_map['i'] = 'q'
|
|||
|
|
|||
|
substitute(enc, substitution_map)
|
|||
|
|