Writeup - Challenge Venona

  • Catégorie : Cryptanalysis
  • Fichiers fournis : DIANA.tiff, MESSAGES.txt
  • Format du flag : texsaw{FLAG}
  • Auteur du WriteUp : DonAsako

Contexte

Nous sommes déployés au cœur de la jungle vietnamienne pour une mission de reconnaissance. Notre équipe découvre un poste de communication abandonné. En tant que cryptanalyste de l’unité, vous devez analyser les documents récupérés, comprenant :

  • Une table de chiffrement trouvée dans le fichier image DIANA.tiff
  • Deux messages en clair et trois messages chiffrés dans MESSAGES.txt

L’objectif est de trouver un message critique contenant des détails sur une opération ennemie et d’en extraire le flag.

🔍 Observations initiales

Le fichier DIANA.tiff révèle une table de Vigenère inversée. Sur la droite, on observe un carré de Vigenère modifié où chaque ligne est une permutation de l’alphabet suivant une logique spécifique.

Le fichier MESSAGES.txt contient : - 2 messages en clair - 3 messages chiffrés

Démarche pas à pas

  1. Comparaison de longueur : On commence par comparer les longueurs des messages en clair et chiffrés, dans l’idée d’associer un message en clair à un message chiffré de même longueur.

  2. Hypothèse : Si un message en clair a la même longueur qu’un message chiffré, on suppose qu’ils sont liés. On peut alors déduire une clé potentielle à partir de ce couple, et tester cette clé sur les autres messages chiffrés pour tenter leur déchiffrement.

  3. Déchiffrement : Avec la clé récupérée, on essaie de déchiffrer les autres messages.

  4. Extraction du flag : Le flag est probablement caché dans le message chiffré qui n’a pas de message en clair correspondant. On teste la clé dérivée sur ce message pour l’extraire.

Processus de déchiffrement

===== MESSAGES EN CLAIR =====
-OPERATION BLUE EAGLE MOVING TO SECTOR FOUR STOP REQUEST EXTRACTION AT BLUE EAGLE
-AGENT SUNFLOWER COMPROMISED NEAR HANOI STOP ABORT MISSION COMPROMISED

===== MESSAGES CHIFFRÉS =====
-RCPZURNPAQELEPJUJZEGAMVMXWVWCTBMHKNYEEAZVXQWVKGMRVWXDLCANHLGY
-FLPDBSBQIGBJECHMIOZGJMQONXJANFPQYQPWIIONYKNERKHIABLJTPTAOZMDGZUTAESK
-KDPRMZZKNBECTGTKMKQOWXKCHMVNDOPQXUWJJLECUCLBQKKVDXJNUEYFIDAGVIUG

On commence par associer les messages chiffrés et en clair :

# On enlève les espaces des messages en clair :
plaintext_messages = [
    "OPERATION BLUE EAGLE MOVING TO SECTOR FOUR STOP REQUEST EXTRACTION AT BLUE EAGLE",
    "AGENT SUNFLOWER COMPROMISED NEAR HANOI STOP ABORT MISSION COMPROMISED"
]
plaintext_messages = [message.replace(" ", "") for message in plaintext_messages]

encrypted_messages = [
    "RCPZURNPAQELEPJUJZEGAMVMXWVWCTBMHKNYEEAZVXQWVKGMRVWXDLCANHLGY",
    "FLPDBSBQIGBJECHMIOZGJMQONXJANFPQYQPWIIONYKNERKHIABLJTPTAOZMDGZUTAESK",
    "KDPRMZZKNBECTGTKMKQOWXKCHMVNDOPQXUWJJLECUCLBQKKVDXJNUEYFIDAGVIUG"
]

# On compare la taille des messages chiffrés et en clair pour les associer
pair = [[], []]
for plaintext_message in plaintext_messages:
    for encrypted_message in encrypted_messages:
        if len(plaintext_message) == len(encrypted_message):
            pair[0].append(plaintext_message)
            pair[1].append(encrypted_message)
            encrypted_messages.remove(encrypted_message)

On obtient donc 2 paires message clair/message chiffré, et un message chiffré seul :

OPERATIONBLUEEAGLEMOVINGTOSECTORFOURSTOPREQUESTEXTRACTIONATBLUEEAGLE
FLPDBSBQIGBJECHMIOZGJMQONXJANFPQYQPWIIONYKNERKHIABLJTPTAOZMDGZUTAESK

AGENTSUNFLOWERCOMPROMISEDNEARHANOISTOPABORTMISSIONCOMPROMISED
RCPZURNPAQELEPJUJZEGAMVMXWVWCTBMHKNYEEAZVXQWVKGMRVWXDLCANHLGY

================================================================
KDPRMZZKNBECTGTKMKQOWXKCHMVNDOPQXUWJJLECUCLBQKKVDXJNUEYFIDAGVIUG

On récupère la clé du premier message :

def char_to_int(c):
    return ord(c.upper()) - ord('A')

def int_to_char(i):
    return chr(i % 26 + ord('A'))

def find_key(plaintext, ciphertext):
    key = ''
    for p, c in zip(plaintext, ciphertext):
        shift = (char_to_int(c) - char_to_int(p)) % 26
        key += int_to_char(shift)
    return key

pair = [
    (
        'OPERATIONBLUEEAGLEMOVINGTOSECTORFOURSTOPREQUESTEXTRACTIONATBLUEEAGLE',
        'AGENTSUNFLOWERCOMPROMISEDNEARHANOISTOPABORTMISSIONCOMPROMISED',
    ),
    (
        'FLPDBSBQIGBJECHMIOZGJMQONXJANFPQYQPWIIONYKNERKHIABLJTPTAOZMDGZUTAESK',
        'RCPZURNPAQELEPJUJZEGAMVMXWVWCTBMHKNYEEAZVXQWVKGMRVWXDLCANHLGY',
    )
]
for p, c in zip(pair[0], pair[1]):
    partial_key = find_key(p, c)
    print(f"Clé trouvée : {partial_key}")

On trouve 2 clés identiques :

Clé trouvée : RWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCVFQPAYHG
Clé trouvée : RWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCV

On tente maintenant de déchiffrer le dernier message avec cette clé :

def decrypt(ciphertext, key):
    decrypted_text = ''
    for i, c in enumerate(ciphertext):
        if c.isalpha():
            c_index = char_to_int(c)
            k_index = char_to_int(key[i % len(key)])
            p_index = (c_index - k_index) % 26
            decrypted_text += int_to_char(p_index)
        else:
            decrypted_text += c
    return decrypted_text

key = "RWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCVFQPAYHGXKNSOEDIUJRWLMBZTCVFQPAYHG"

encrypted = "KDPRMZZKNBECTGTKMKQOWXKCHMVNDOPQXUWJJLECUCLBQKKVDXJNUEYFIDAGVIUG"

print(decrypt(encrypted, key))

Résultat : THEFLAGISWONTIMEPADWITHUNDERSCORESBETWEENWORDSWRAPPEDINTHEHEADER

Flag

Le message déchiffré est : LE FLAG EST WON TIME PAD AVEC DES SOULIGNÉS ENTRE LES MOTS ENCAPSULÉ DANS L’ENTÊTE

Ainsi, le flag final est : texsaw{WON_TIME_PAD}