Лаборатория TUCTF 2016 - The Neverending Crypto (level 1-7)

Sif
, 19 May 2016

Сервис предлагает пройти уровни по расшифрованию сообщений.
На каждом уровне нас просят ввести какой-нибудь текст. Затем он выводит результат шифрования и просит расшифровать предложенный им текст.
В каждом уровне 50 раундов.

The Neverending Crypto Level 1.

Вводим "abcdefghijkl". На выходе получаем:
"abcdefghijkl encrypted is .- -.. . -.-. -.. . ..-. --. .... .. .--- -.- .-..
What is ... .- ...- .   - .... .   .--. .-. .. -. -.-. . ... ... decrypted?"
Вывод напрашивается сам собой - это Морзе. Составляем словарик, по которому будем расшифровывать. 
Очень важно не забывать то, что между буквами стоит по 1 пробелу, а между словами - по 3.

d = {"*":" ", ".-":"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"}

s.send("abcdefghijkl\n")
data = s.recv(12345)
data = data.split("\n")[1].replace("   ", " * ").split(" ")[2:-2]
for i in xrange(len(data)):
	data[i] = d1[data[i]]
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 2.

Вводим "abcdefghijkl". На выходе получаем:
"abcdefghijkl encrypted is nopqrstuvwxy
What is fTiXrg[Xrce\aUXff decrypted?"
По шифрованию нашей строки можно сделать вывод, что это ROT13. Но строка, которую надо расшифровать, дает нам понять, что сдвиг происходит не на 26 символах, а на всех печатуемых символах, ascii коды которых находятся в пределах от 32 до 126.
Не забывая про границы, пишем скрипт, который делает обратный сдвиг на 13.

s.send("abcdefghijkl\n")
data = s.recv(12345)
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
data = list(data)
for i in xrange(len(data)):
	k = ord(data[i]) - 13
	if k < 32:
		k = k + 95
	data[i] = chr(k)
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 3.

Вводим уже стандартную строку "abcdefghijkl". На выходе получаем:"axje.uidchtn". Первая мысль, которая приходит в голову, что это просто одноалфавитная замена. Повторяем наш ввод, но на выходе получаем уже другую строку:"abcsftdhunei". Каждый раз, вводя эту же строку, на выходе получаем один из предыдущих вариантов. Значит это замена, но не по одному алфавиту, а по двум. С помощью еще некоторого числа вводов можно получить оба алфавита полостью. Далее просто составляем два словарика. Я определяла, каким алфавитом пользоваться, по букве "b". Если на выходе получалась "b", то пользовалась первым алфавитом, а если "x", то вторым.

d1 = {"a":'a', "b":'b', "c":'c', "s":'d', "f":'e', "t":'f', "d":'g', "h":'h', "u":'i', "n":'j', 'e':'k', 'i':'l', 'm':'m', 'k':'n', 'y':'o', ';':'p', 'q':'q', 'p':'r', 'r':'s', 'g':'t', 'l':'u', 'v':'v', 'w':'w', 'x':'x', 'j':'y', 'z':'z', '.':'.', ',':',', 'o': ';', ' ':' '} 
d2 = {"a":'a', "x":'b', "j":'c', "e":'d', ".":'e', "u":'f', "i":'g', "d":'h', "c":'i', "h":'j', 't':'k', 'n':'l', 'm':'m', 'b':'n', 'r':'o', 'l':'p', "'":'q', 'p':'r', 'o':'s', 'y':'t', 'g':'u', 'k':'v', ',':'w', '?':'x', 'f':'y', ';':'z', 'v':'.', 'w':',', 's': ';', ' ':' '}

'''
Я не выяснила, что заменяется на "x" во втором алфавите, так что тут просто стоит "?", но, кстати говоря, он так и не встретился. Вообще в данных словарях описаны только те символы, что встречались в строках, которые нужно было расшифровать.
'''

s.send("b\n")
data = s.recv(12345)
data1 = data.split("\n")[0].split(" encrypted is ")[1]
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
data = list(data)
for i in xrange(len(data)):
	if data1 == "b":
		data[i] = d2[data[i]]
	elif data1 == "x":
		data[i] = d3[data[i]]
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 4.

При первом вводе "abcdefghijkl" на выходе получили "89:;<=>?@ABC". Первая мысль:"Да это же сдвиг". При второй попытке получаем:"'()*+,-./012". Тоже сдвиг, но вот уже на другую константу. После третьего ввода опять получаем другой сдвиг. Значит на этом уровне реализован динамический сдвиг. При каждом шифровании нашей строки в начале раунда будем запоминать, на какое число сдвинулись символы, и будем сдвигать их обратно на такое же число.

s.send(" \n")
data = s.recv(12345)
data1 = data.split("\n")[0].split(" encrypted is ")[0]
data2 = data.split("\n")[0].split(" encrypted is ")[1]
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
k = 126 - 32 - ((ord(data2[0]) - ord(data1[0]) + 1
data = list(data)
for i in xrange(len(data)):
	l = ord(data[i]) + k
	if l < 127:
		data[i] = chr(l)
	else:
		data[i] = chr(l - 95)
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 5.

В ответ на "abcdefghijkl" получаем "=;:98765432". Видим, что получившиеся символы опять же идут последовательно в ascii таблице. Попробуем ввести немного другую строку "ijklmnopqrstu". В ответ получаем "543210/.-,+*)". Судя по всему, это Атбаш, т.е. первый символ алфавита заменяется на последний, второй на предпоследний и т.д.

s.send("abcdefghijkl\n")
data = s.recv(12345)
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
data = list(data)
for i in xrange(len(data)):
	if ord(data[i]) <= 79:
		k = 79 - ord(data[i])
		data[i] = chr(80 + k)
	elif ord(data[i]) >= 80:
		k = ord(data[i]) - 80
		data[i] = chr(79 - k)
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 6.

После ввода "abcdefghijkl" на выходе получаем "JLNPRTVXZ\^`". Видим, что символы получились тоже последовательные, но не идущие подряд, а через один. Теперь на шифрование подадим строку " !"#$%&'()*+". В ответ получаем "!$'*-0369<?B". Это аффинный шифр. В каждом раунде будем узнавать коэффициенты a и b и по ним уже проводить расшифрование.

s.send(" !\n")
data = s.recv(12345)
data1 = data.split("\n")[0].split(" encrypted is ")[0]
data2 = data.split("\n")[0].split(" encrypted is ")[1]
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
a = ord(data2[1]) - ord(data2[0])
b = ord(data2[0]) - a * ord(data1[0])
while b < 32:
	b = b + 95
data = list(data)
for i in xrange(len(data)):
	l = data2[0] - b
	while l % a != 0:
		l = l + 95
	l = l / a
	if l > 126:
		l = l % 95
	if l < 32:
		l = l + 95
	data[i] = chr(l)
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)

The Neverending Crypto Level 7.

В ответ на "abcdefghijkl" получаем "8h,;k/>n2Aq5". Пробуем другие строки, например "!"abcde12345" зашифровалось в "S9+5zl8}:eK=h", а "!"vwxyz56789" в "Bb 9Y#<u?XxB". Тогда пробуем зашифровать 10 пробелов. На выходе получаем "+UT+UT+UT+". Все ясно - это шифр Виженера с ключом длиной 3. На всякий случай проверяем наше предположение о длине. Ту же самую строку сервис зашифровал в "D:6D:6D:6D", а при следующей попытке в "`L~`L~'L~'". Значит длина ключа фиксирована. На каждом раунде на вход подаем 3 одинаковых символа (например, 3 пробела) и вычисляем ключ, а затем расшифровываем.

s.send("   \n")
data = s.recv(12345)
data1 = data.split("\n")[0].split(" encrypted is ")[0]
data2 = data.split("\n")[0].split(" encrypted is ")[1]
data = data.split("\n")[1].split("What is ")[1].split(" decrypted?")[0]
k0 = ord(data2[0]) - ord(data1[0])
k1 = ord(data2[1]) - ord(data1[1])
k2 = ord(data2[2]) - ord(data1[2])
data = list(data)
for i in xrange(len(data)):
	if i % 3 == 0:
		l = ord(data[i]) - k0
	elif i % 3 == 1:
		l = ord(data[i]) - k1
	else:
		l = ord(data[i]) - k2
	if l < 32:
		l = l + 95
	data[i] = chr(l)
data = "".join(data)
s.send(data + "\n")
print s.recv(12345)
  

Полный код : http://pastebin.com/ckFcgu5Y