Райтап на задачу «Послание от Француза»

Автор:

3T0T_FL4G_1MB4

31 октября 2026

8

31.10.2026
Послание от Француза
Категория:

Crypto

Задача «Послание от Француза»

Автор:

3T0T_FL4G_1MB4

Первая кровь:
velvetway
Сложность:
easy
Дата публикации:
06.12.2025
Решений:
234
Рейтинг:
4.7/5
Ссылка:
Решать

Заглянем в текстовый документ, что там написано:

Wmits, izxdiamo! Gsj tbnx ipfa ftpfvh xv Pimcb-Mwygkxqr-hco-Amdcim. Mi abmqtl futdzqiri, al Q htkflis bl ziicov mi. JroGIN{A0VX_U0ZD3B_XW3_S3V}

Видно намёк на флаг, но его нереально разобрать. Поэтому смотрим, что в описании задачи нам дано на разгадку. Письмо от ФРАНЦУЗА, фотография ПИКСИ, Её имя — КЛЮЧ, не на русском. Главная подсказка здесь, что у нас есть шифр с ключом. Гуглим или «шифр с ключом», или «французский шифр с ключом» и получаем первым результатом — шифр Виженера.

Теперь, когда мы знаем, что это за шифр, воспользуемся дешифратором. Подойдёт для этого dcode, универсальный сайт с кучей других шифров. Вставляем текст в поле для дешифрации, указываем ключ PIXIE и дешифруем.

Пример использования сайта

Пример использования сайта

В итоге мы расшифровали послание и получили флаг!

Альтернативное решение

Вау, этот шифр можно сбрутить.

Первый скрипт key-len.py

import string
from collections import Counter


def quick_key_length_analysis(ciphertext):
    cipher_letters = ''.join(c.upper() for c in ciphertext if c.isalpha())
    
    for key_length in range(1, 16):
        subsequences = ['' for _ in range(key_length)]

        for i, char in enumerate(cipher_letters):
            subsequences[i % key_length] += char

        ics = []
        for subseq in subsequences:
            if len(subseq) > 1:
                n = len(subseq)
                freq = Counter(subseq)
                ic = sum(count * (count - 1) for count in freq.values()) / (n * (n - 1))
                ics.append(ic)

        if ics:
            avg_ic = sum(ics) / len(ics)
            bar = "█" * int(avg_ic * 150)
            marker = " 🎯" if 0.060 < avg_ic < 0.070 else ""
            print(f"Длина {key_length:2d}: {avg_ic:.4f} {bar}{marker}")

ciphertext = "Wmits, izxdiamo! Gsj tbnx ipfa ftpfvh xv Pimcb-Mwygkxqr-hco-Amdcim. Mi abmqtl futdzqiri, al Q htkflis bl ziicov mi. JroGIN{A0VX_U0ZD3B_XW3_S3V}"
quick_key_length_analysis(ciphertext)

Сперва высчитываем длину ключа, используя метод индекса совпадений (Index of Coincidence, IC) — статистическую метрику, которая измеряет вероятность того, что две случайно выбранные буквы в тексте окажутся одинаковыми. Для каждой предполагаемой длины ключа (от 1 до 15) скрипт разбивает шифртекст на подпоследовательности (каждая i-я буква с шагом, равным длине ключа), затем вычисляет IC для каждой подпоследовательности.

Второй скрипт key-brut.py

import string
import itertools
import time

def vigenere_decrypt(ciphertext, key):
    result = []
    key = key.upper()
    key_length = len(key)
    key_as_int = [ord(i) - ord('A') for i in key]

    key_index = 0
    for char in ciphertext:
        if char.isalpha():
            base = ord('A') if char.isupper() else ord('a')
            shift = key_as_int[key_index % key_length]
            decrypted_code = (ord(char) - base - shift) % 26
            decrypted = chr(decrypted_code + base)
            result.append(decrypted)
            key_index += 1
        else:
            # Неалфавитные символы добавляем без изменений
            result.append(char)

    return ''.join(result)

ciphertext = "Wmits, izxdiamo! Gsj tbnx ipfa ftpfvh xv Pimcb-Mwygkxqr-hco-Amdcim. Mi abmqtl futdzqiri, al Q htkflis bl ziicov mi. JroGIN{A0VX_U0ZD3B_XW3_S3V}"
known_plaintext = "BugCTF"

iteration = 0
start_time = time.time()
alphabet = string.ascii_uppercase

for key_tuple in itertools.product(alphabet, repeat=5):
    iteration += 1
    key = ''.join(key_tuple)
    decrypted = vigenere_decrypt(ciphertext, key)
    
    if known_plaintext in decrypted:
        elapsed_time = time.time() - start_time
        print(f"\nКлюч найден на итерации {iteration} за {elapsed_time:.2f} секунд")
        print(f"🔑 Ключ: {key}")
        print(f"📜 Расшифрованный текст: {decrypted}")
        break

Далее просто перебираем все возможные варианты ключа и ищем в расшифрованном тексте вхождение известного куска текста — BugCTF.

Чтобы пробрутить ключ длиной в 5 символов, который состоит из алфавита 26 символов, надо проверить 26 в 5 степени вариантов — это чуть более 11 млн. Но это небольшое значение, скрипт может проверить все варианты за 2–4 минуты.

Если вы нашли опечатку или ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter

Также по теме:

Райтап на задачу «Ах#енный зум»

/Разборы задач

Райтап на задачу «Ах#енный зум»

Разбор задачи «Ах#енный зум»: распознаём жёлтые точки и извлекаем закодированные данные.
Прочитать

13

Райтап на задачу «Послание от Француза»

/Разборы задач

Райтап на задачу «Послание от Француза»

Разбор задачи «Послание от Француза»: находим ключ и расшифровываем письмо с помощью шифра Виженера.
Прочитать

34