Not all ancient ciphers were so bad... The flag is not in standard format. nc mercury.picoctf.net 30568 playfair.py
110
Cryptography
(None)
alphabet = open("key").read().rstrip()
m = generate_square(alphabet)
msg = open("msg").read().rstrip()
enc_msg = encrypt_string(msg, m)
print("Here is the alphabet: {}\nHere is the encrypted message: {}".format(alphabet, enc_msg))
This initializes important variables in the program. Running it in Terminal, we get that that the alphabet is "0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q" and enc_msg is "herfayo7oqxrz7jwxx15ie20p40u1i".
def generate_square(alphabet):
assert len(alphabet) == pow(SQUARE_SIZE, 2)
matrix = []
for i, letter in enumerate(alphabet):
if i % SQUARE_SIZE == 0:
row = []
row.append(letter)
if i % SQUARE_SIZE == (SQUARE_SIZE - 1):
matrix.append(row)
return matrix
The cuntion generate_square creates a 2D list of letters and numbers, which is stored in the variable m. Printing it out, we get:
[['0', 'f', 'k', 'd', 'w', 'u'],
['6', 'r', 'p', '8', 'z', 'v'],
['s', 'n', 'l', 'j', '3', 'i'],
['y', 't', 'x', 'm', 'e', 'h'],
['7', '2', 'c', 'a', '9', 'b'],
['g', '5', 'o', '4', '1', 'q']]
def encrypt_string(s, matrix):
result = ""
if len(s) % 2 == 0:
plain = s
else:
plain = s + "0fkdwu6rp8zvsnlj3iytxmeh72ca9bg5o41q"[0]
for i in range(0, len(plain), 2):
result += encrypt_pair(plain[i:i + 2], matrix)
return result
The function encrypt_string simply adds a '0' to the end of the message if the length is odd and then runs encrypt_pair two characters in msg at a time. When solving, we can simply ignore this function and if the outputted message ends with a 0, test it with and without a '0' in the terminal.
def get_index(letter, matrix):
for row in range(SQUARE_SIZE):
for col in range(SQUARE_SIZE):
if matrix[row][col] == letter:
return (row, col)
print("letter not found in matrix.")
exit()
This function, get_index, gets the index of letter in matrix.
def encrypt_pair(pair, matrix):
p1 = get_index(pair[0], matrix)
p2 = get_index(pair[1], matrix)
if p1[0] == p2[0]:
return matrix[p1[0]][(p1[1] + 1) % SQUARE_SIZE] + matrix[p2[0]][(p2[1] + 1) % SQUARE_SIZE]
elif p1[1] == p2[1]:
return matrix[(p1[0] + 1) % SQUARE_SIZE][p1[1]] + matrix[(p2[0] + 1) % SQUARE_SIZE][p2[1]]
else:
return matrix[p1[0]][p2[1]] + matrix[p2[0]][p1[1]]
The function encrypt_pair is where characters in the message are changed. If the two letters are in the same row, both a shifted left, if they are in the same column, they are shifted down and if they are not in the same column or row, the column is switched.
I created a program playfair_solve to find the message
for i in range(0, len(enc_msg), 2):
a = get_index(enc_msg[i], matrix)
b = get_index(enc_msg[i+1],matrix)
if(a[0] == b[0]):
msg += matrix[a[0]][(a[1]-1) % SQUARE_SIZE] + matrix[b[0]][(b[1]-1)% SQUARE_SIZE]
elif(a[1] == b[1]):
msg += matrix[(a[0]-1)% SQUARE_SIZE][a[1]] + matrix[(b[0]-1)% SQUARE_SIZE][b[1]]
else:
msg += matrix[a[0]][b[1]] + matrix[b[0]][a[1]]
I looped through the letters in enc_msg two at a time and got their index in matrix, stored in a and b. If a[0] == b[0], then I would shift both of them left, if a[1] == b[1], I would shift them up and otherwise, I would switch b[1] and a[1]. The message that was outputted was "emf57mgc51tp693dtt4g3h7f8ouwq3". Running the program in terminal and inputting that value as plain text, it outputs "Congratulations! Here's the flag: 007d0a696aaad7fb5ec21c7698e4f754".
I tried picoCTF{007d0a696aaad7fb5ec21c7698e4f754} which didn't work and then tried 007d0a696aaad7fb5ec21c7698e4f754, which is the flag.
007d0a696aaad7fb5ec21c7698e4f754