Writeups
  • ℹ️Infos
  • 🗓️2021
    • DvCTF (to join DaVinciCode)
      • Crypto
        • Baby RSA
        • Ressaye
        • Unbreakable encryption
      • Forensics
        • Broken Portrait
        • Russian Dolls
        • Sus USB
      • Misc
        • Jus de Flag
        • Welcome
      • OSINT
        • Absolutely Nuts
      • Progra
        • Snoop's Mission
      • Reverse
        • Baby Reverse
        • Basic Cracking
        • Code Pyn
      • Steganography
        • Colorful Code
        • Hurt Your Eyes
        • Orchestra of Flags
        • Tyger
      • Web
        • Have I Been Pwned
        • What's Up ?
  • 🗓️2022
    • DvCTF 2022
      • Crypto
        • Cwryptographic Owacle
        • Secure Or Not Secure
        • small weiner
      • Forensics
        • Very Bad Script
      • Misc
        • Data Leak
        • Going postal
        • The Hacker Man
      • OSINT
        • Elon Musk
        • Monkeey
        • Painting Spot
        • Welcome to the DaVinciCTF!
      • Pentest
        • DaVinci's Playlist : Part 1
        • DaVinci's Playlist : Part 2.5
        • DaVinci's Playlist : Part 2
      • Programming
        • Heaven's Gate
        • Sudoku
        • TicTacToe
      • Reverse
        • CryptoVault
        • Mine Game
        • Obfuscated
        • Peripheral Interface Controller XVI
      • Steganography
        • ICMP
        • The Arts of Details
        • Treasure
      • Warmup
        • EBG13
        • FrenchFlag
        • MP3
        • QmFzZTY0
        • RSA
        • Welcome
      • Web
        • CyberStreak v1.0
        • 🎵
    • picoCTF 2022
      • Challs WU
    • @HackDay - Qualifications
      • Crypto
        • Francis Bacon
        • Francs Maçons
        • Rotate-me!
        • Un message codé ?
      • Forensics
        • bad_timing_for_reversing
      • Hardware
        • Cubik'cipher
        • WebSDR
      • Reverse
        • Calling Conventions
        • Memory Investigation
      • Steganography
        • I can make pictures
        • J'ai perdu le flag :(
        • Pokémons
        • Un coup de maître
        • Un logo cachotier
      • Web
        • GIT!
        • Part. 1 - Uploads
        • Part. 2 - Old md5
        • Part. 3 - sudo python
    • 404CTF
      • Crypto
        • Un simple oracle [1/2]
        • Un simple oracle [2/2]
      • Misc
        • Je suis une théière
        • Pierre-papier-Hallebarde
        • GoGOLFplex
      • OSINT
        • À l'aube d'un échange
        • Collaborateur suspect
        • Equipement désuet
      • Reverse
        • Mot de passe ?
      • Steganography
        • La plume à la main
        • PNG : Un logo obèse [1/4]
        • PNG : Drôles de chimères [2/4]
        • Toujours obèse [3/4]
      • Web
        • Fiché JS
        • Le braquage
        • Du gâteau
        • En construction !
    • Operation Kernel
      • Crypto
        • Scytale
      • Forensics
        • Research Paper
        • Excel Confidential
      • Reverse
        • CryptoLocker
        • What_If_CryptoLocker
      • Social Engineering
        • Pour vivre secure vivons caché
        • Pour vivre secure vivons caché Part 2
      • Stegano
        • AudioSpectre
        • Datacenter
        • Takazume
      • WEB
        • Research paper blog
        • SQL Project 1
        • SQL Project 2
        • SQL Project 3
        • Tenue de soirée requise
  • 🗓️2023
    • 404CTF 2023
      • Résultats
      • Analyse forensique
        • Pêche au livre
        • Le Mystère du roman d'amour
        • Les Mystères du cluster de la Comtesse de Ségur [1/2]
        • Lettres volatiles
        • Note de bas de page
      • Cloud
        • Le Sot
        • Le Cluster de Madame Bovary
        • Harpagon et le magot
        • Les nuages menaçants 1/3
        • Les nuages menaçants 2/3
      • Cryptanalyse
        • Recette
        • ASCON Marchombre
      • Divers
        • Bienvenue
        • Exemple de connexion à distance
        • Discord
        • À vos plumes !
      • Exploitation de binaires
        • Je veux la lune !
      • Programmation
        • L'Inondation
        • Des mots, des mots, des mots
      • Radio-Fréquence
        • Navi
        • Avez-vous vu les cascades du hérisson ?
        • Le Plombier du câble
        • Ballistic Missile Submarine
      • Renseignement en sources ouvertes
        • Le Tour de France
        • Les OSINTables [1/3]
        • Un vol ?
        • L'âme d'un poète et le coeur d'une femme [1/4]
        • L'âme d'un poète et le coeur d'une femme [2/4]
        • L'âme d'un poète et le coeur d'une femme [3/4]
        • L'âme d'un poète et le coeur d'une femme [4/4]
      • Rétro Ingénierie
        • Le Divin Crackme
        • L'Inspiration en images
      • Sécurité Matérielle
        • Un courrier suspect
        • Un réveil difficile
      • Stéganographie
        • Odobenus Rosmarus
        • L'Œuvre
        • Les Félicitations
        • En Profondeur
        • Le Rouge et le vert, avec un soupçon de bleu
      • Web
        • Le Loup et le renard
        • L'Académie du détail
        • La Vie Française
        • Fuite en 1791
        • L'Épistolaire moderne
        • Chanson d'Inde
      • Web3
        • Art
        • L'Antiquaire, tête en l'air
  • 🗓️2025
    • 404CTF 2025
      • Résultats
      • Algorithmique Quantique
        • Machinerie quantique
        • Grover (1/2)
        • Grover (2/2)
      • Analyse forensique
        • USB 51
        • Forensic et Mat [1/2]
        • Apprenti Carver [1/2]
        • Dockerflag
        • Forensic et Mat [2/2]
        • Apprenti Carver [2/2]
        • Tape ton MDP
        • Toortik Triflexation [1/2]
        • Toortik Triflexation [2/2]
      • Cryptanalyse
        • Message lointain
        • Entretien galactique
        • R1R2
        • Saturn Cipher
        • Planètes anormales
        • Spacemark
        • You spin me round
        • Dérive dans l'espace
        • Lunette cosmico galactique
        • Jupiter Cipher
        • Courbe de Lancement
        • More Space
      • Divers
        • Bienvenue
        • Pix2Num
        • Politique de confidentialité
        • Space Traveller
        • Satellisation
        • Étoile binaire [2/2]
      • Exploitation de binaires
        • Gorfou en danger [1/3]
        • Gorfou en danger [2/3]
        • Gorfou en danger [3/3]
        • Spaaaaaaace
        • KerberINT Space Program
        • 22 bytes pour sauver l'univers
        • Bounty Machine
        • Space Odyssey
        • Solaris
        • Cosmic-Base
        • Kalaxy
        • Bokit
      • Intelligence Artificielle
        • Gorfoustral (1/3)
        • Gorfoustral (2/3)
        • Gorfoustral (3/3)
        • Du tatouage
      • Renseignement en sources ouvertes
        • L'addition est salée
        • Une mission inconnue
        • Earth Attack (0/2)
        • Un satellite qui vaut de l'or
        • Un peu de vacances
        • La ville en rose
        • Earth Attack (1/2)
        • La tête dans les étoiles
        • En eaux sombres
        • Earth Attack (2/2)
        • DPOsint
      • Réaliste
        • The LDAP Chronicles
        • Houston, we have a problem
        • Named Resolve
        • The AD Guardians
        • The GPO Mission
        • Ghost Membership
      • Rétro-Ingénierie
        • Cbizarre [1/2]
        • Cbizarre [2/2]
        • Reversconstrictor
        • 3x3cut3_m3
        • Inscription
        • Étoile binaire [1/2]
        • RFC 9452 bis: IP over Rockets with Quality of Service
      • Sécurité matérielle
        • Trop d'IQ
        • Space Radio
        • R16D4
        • Comment est votre température ?
        • Code Radiospatial n°1
        • Unidentified Serial Bus [1/2]
        • Unidentified Serial Bus [2/2]
      • Sécurité Web
        • Cheese Chess
        • Rainbow Rocket
        • Fire Server
        • Sideral Noise
        • Space Fleet Command
Powered by GitBook
On this page

Was this helpful?

  1. 2025
  2. 404CTF 2025
  3. Intelligence Artificielle

Du tatouage

PreviousGorfoustral (3/3)NextRenseignement en sources ouvertes

Last updated 5 days ago

Was this helpful?

Catégorie: Intelligence Artificielle - Difficulté: Difficile

Description:

Solution:

Script de résolution :

import json
import torch
from tqdm.auto import tqdm
import transformer_lens as tl

CONFIG = {
    "TAILLE_FILTRE": 10,
    "MODULO_CHIFFREMENT": 50033,
    "FICHIERS_JOURNAUX": {
        "modele_33": "journal33.json",
        "modele_34": "journal34.json",
        "modele_35": "journal35.json"
    },
    "PREFIXE_FLAG": "404CTF{",
    "SUFFIXE_FLAG": "}"
}

class DecodeurHistoires:
    """Classe principale pour le décodage des histoires et l'extraction des clés secrètes"""
    
    def __init__(self):
        """Initialisation du décodeur et chargement du modèle"""
        self.resultats = {}
        self._initialiser_environnement()
        self._charger_modele()
    
    def _initialiser_environnement(self):
        """Configuration de l'environnement d'exécution"""
        self.appareil = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.precision = torch.float16 if self.appareil.type == "cuda" else torch.bfloat16
        print(f"Environnement: {self.appareil}, précision {self.precision}")
    
    def _charger_modele(self):
        """Chargement du modèle de langage pour l'analyse"""
        print("Initialisation du modèle Tiny Stories...")
        self.modele = tl.HookedTransformer.from_pretrained_no_processing(
            "tiny-stories-instruct-33M", 
            dtype=self.precision, 
            device=self.appareil
        )
        self.modele.to(self.appareil)
        self.modele.eval()
    
    def _generer_filtre(self, graine):
        """Génère un filtre binaire à partir d'une graine"""
        generateur = torch.Generator()
        generateur.manual_seed(graine)
        
        taille_double = 2 * CONFIG["TAILLE_FILTRE"]
        indices_permutation = torch.randperm(taille_double, generator=generateur)
        
        filtre_binaire = torch.zeros(taille_double, dtype=torch.bool)
        filtre_binaire[indices_permutation[:CONFIG["TAILLE_FILTRE"]]] = True
        
        return filtre_binaire
    
    def _calculer_graine(self, cle, token_precedent, token_avant_precedent):
        """Calcule une graine cryptographique à partir des tokens et d'une clé"""
        return (token_precedent * cle + token_avant_precedent) % CONFIG["MODULO_CHIFFREMENT"]
    
    def _extraire_contenu_histoire(self, texte):
        """Extrait le contenu principal de l'histoire en ignorant les métadonnées"""
        lignes = texte.splitlines()
        contenu = []
        
        for ligne in lignes:
            if any(ligne.startswith(marqueur) for marqueur in ["Words", "Story:", "Summary:", "Features:"]):
                break
            contenu.append(ligne)
            
        return " ".join(contenu).strip()
    
    def _analyser_journal(self, identifiant, chemin_fichier):
        """Analyse un fichier journal pour trouver la clé secrète"""
        print(f"\nAnalyse du journal {identifiant} ({chemin_fichier})")
        
        # Chargement des données
        with open(chemin_fichier, 'r', encoding='utf-8') as fichier:
            entrees = json.load(fichier)
        
        # Préparation des données pour l'analyse
        print("Préparation des données pour l'analyse...")
        contextes_tokens = []
        total_contextes = 0
        
        for entree in tqdm(entrees, desc=f"Préparation {identifiant}"):
            texte_histoire = self._extraire_contenu_histoire(entree)
            tokens = self.modele.to_tokens(texte_histoire).to(self.appareil)
            longueur = tokens.shape[1]
            
            # Vérification de la longueur minimale
            if longueur <= 20:
                contextes_tokens.append([])
                continue
                
            contextes_entree = []
            
            # Analyse des tokens après le 20ème
            for position in range(20, longueur):
                # Extraction du contexte
                tokens_precedents = tokens[:, :position]
                
                # Prédiction du modèle
                with torch.no_grad():
                    logits = self.modele(tokens_precedents)
                
                # Récupération des top prédictions
                logits_derniere_position = logits[0, -1, :]
                _, indices_top = logits_derniere_position.topk(2 * CONFIG["TAILLE_FILTRE"], dim=-1)
                
                # Vérification de la position du token actuel
                token_actuel = tokens[0, position].item()
                positions_trouvees = (indices_top == token_actuel).nonzero(as_tuple=False)
                
                if len(positions_trouvees) == 0:
                    continue
                    
                position_trouvee = positions_trouvees[0].item()
                token_precedent = tokens[0, position-1].item()
                token_avant_precedent = tokens[0, position-2].item()
                
                contextes_entree.append((token_precedent, token_avant_precedent, position_trouvee))
                total_contextes += 1
                
            contextes_tokens.append(contextes_entree)
            
        print(f"Total: {total_contextes} contextes de tokens à analyser")
        
        # Recherche de la clé
        meilleure_cle = None
        meilleur_score = -1
        
        for cle_candidate in tqdm(range(1, 100000), desc=f"Analyse {identifiant}", unit="clé"):
            score_actuel = 0
            
            for contextes_entree in contextes_tokens:
                if not contextes_entree:
                    continue
                    
                for (token_precedent, token_avant_precedent, position_trouvee) in contextes_entree:
                    graine = self._calculer_graine(cle_candidate, token_precedent, token_avant_precedent)
                    filtre = self._generer_filtre(graine)
                    
                    if not filtre[position_trouvee]:
                        break
                        
                    score_actuel += 1
                    
            if score_actuel > meilleur_score:
                meilleur_score = score_actuel
                meilleure_cle = cle_candidate
                
        print(f"Clé identifiée pour {identifiant}: {meilleure_cle} (score: {meilleur_score})")
        self.resultats[identifiant] = meilleure_cle
    
    def decoder_tous_journaux(self):
        """Analyse tous les fichiers journaux pour extraire les clés"""
        for identifiant, chemin in CONFIG["FICHIERS_JOURNAUX"].items():
            self._analyser_journal(identifiant, chemin)
    
    def generer_flag(self):
        """Génère le flag final à partir des clés trouvées"""
        if len(self.resultats) != 3:
            return "Erreur: toutes les clés n'ont pas été trouvées"
            
        # Extraction et formatage des clés dans l'ordre requis
        cle_35 = str(self.resultats["modele_35"]).zfill(5)
        cle_34 = str(self.resultats["modele_34"]).zfill(5)
        cle_33 = str(self.resultats["modele_33"]).zfill(5)
        
        # Construction du flag final
        flag = f"{CONFIG['PREFIXE_FLAG']}{cle_35}{cle_34}{cle_33}{CONFIG['SUFFIXE_FLAG']}"
        print(f"\nFlag final: {flag}")
        return flag

if __name__ == "__main__":
    decodeur = DecodeurHistoires()
    decodeur.decoder_tous_journaux()
    flag_final = decodeur.generer_flag()

Le script va donc tourner une dizaine de minute (cela dépend de la puissance de votre machine) et vous afficher le flag directement :

🚩FLAG

404CTF{436333370349117}

🗓️
45KB
journal33.json
49KB
journal34.json
90KB
journal35.json
4KB
tatouage.py