Tags: known-plaintext crypto 

Rating: 1.0

Имеется сервис, который шифрует текстовые файлы (обязательно ASCII внутри и расширение .txt).

Позаливав в него несколько пробных файлов, становится понятно, что это разные вариации шифра перестановки. При длине открытого текста меньше требуемой в конец дописываются пробелы, а потом уже выполняется перестановка. Было 3 вариации шифра для разной длины открытого текста: 1) < 36; 2) < 1024; 3) < 10000 (хз, можно ли было залить больше 10к). Соответственно, на выходе всегда получается закрытый текст длиной 36, 1024 или 10000 символов.

Хотя первую вариацию шифра можно было прочитать и глазами (там, фактически, из правого нижнего угла заполнялась таблица 6х6 вверх и влево), с остальными было несколько сложнее сделать подобное. Чтобы долго не думать над закономерностями перестановки, я написал скрипты, которые по очереди закидывают на сервис тексты с символами в разных позициях и ищут их в ответе, чтобы понять, куда их перемещает шифр.

Вот пример скрипта для 3-ей вариации, с минимальными изменениями подойдет и для других:

```
import requests, re

ALPHABET = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' # fix here souldn't be bigger than 36/1024/10k for different parts

def make(n):
print('step', n)
s = " "*(len(ALPHABET)*n)
s+=ALPHABET
s = s.ljust(1025, ' ') # fix here should be bigger than 36/1024/10k for different parts
return s
def send(s):
r = requests.post('https://zigzag.student2020tasks.ctf.su/upload', files={'file': ('qwe.txt', s)})
regexp = re.search('>([0-9a-zA-Z ]{10000})<', r.text) # fix here 36/1024/10k for different parts
return regexp.group(1)

ans = []
def getTable():
global ans
step = 0
while step*len(ALPHABET)<10000: # fix here 36/1024/10k for different parts
s = make(step)
try:
s = send(s)
except:
print('fail', ans)
break
for i in ALPHABET:
ans.append(s.index(i))
step +=1
getTable()

#ans=[.....]

enc = open('zigzag3_e2df5a7d69.txt.enc').read() # fix filename
end = ''
for i in ans:
end+= enc[i]
print(end)
```