Du tatouage
Last updated
Was this helpful?
Last updated
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 :