Aller au contenu principal
own2pwn
ia/dataset-distillation-securite-offensive.tsx

Construire un dataset pour un agent IA offensif : distillation, QLoRA, DPO et coûts réels

Bases publiques et licences, distillation teacher-student sans hallucination, multiplication d'exemples à partir d'une seule trajectoire, et le vrai prix du fine-tuning (QLoRA, DPO, GPU) d'un agent de sécurité offensive.

own2pwn··18 min de lecture

Vous avez une trajectoire de pentest parfaite. Reconnaissance, une SSRF repérée sur un paramètre d'URL, pivot vers le métadata du cloud, vol du token, exfiltration. Le tout enchaîné proprement par un analyste, avec les bons appels d'outils au bon moment. Une seule. Et vous voudriez un agent qui rejoue ce raisonnement sur cent cibles différentes, jamais vues. Entre les deux, il manque une chose, et ce n'est ni le modèle ni les GPU : c'est le dataset.

Cet article traite d'un problème concret : comment fabriquer ce dataset. Quelles bases publiques existent et sous quelles licences, comment distiller proprement le savoir d'un gros modèle « teacher » vers un petit modèle « student » capable d'agir en autonomie, comment partir d'un seul exemple pour en générer des centaines sans que le teacher hallucine, et enfin ce que coûte tout ça, en dollars et en heures de GPU. On termine sur le fine-tuning lui-même : continued pre-training, QLoRA, DPO, et à quelle taille de modèle chaque technique s'applique.

Cadre : sécurité offensive autorisée
Construire un dataset offensif et entraîner un agent dessus est légal. L'utiliser contre un système sans mandat ne l'est pas. Tout ce qui suit vise un agent opéré dans le cadre d'un pentest web autorisé ou d'un lab que vous possédez. Sur le plan IA, on construit un outil offensif encadré : traçabilité des données, périmètre défini, garde-fous d'exécution. Pas une arme livrée à elle-même.

Ce qu'on entraîne, au juste : un agent, pas un chatbot

Premier malentendu à dissiper. Entraîner un chatbot de sécurité, c'est lui apprendre à répondre « voici comment fonctionne une injection SQL ». Entraîner un agent offensif, c'est lui apprendre à boucler : raisonner, appeler un outil (un scanner, curl, un module d'exploitation), lire le résultat réel, décider de l'étape suivante. L'unité de donnée n'est donc pas une paire question/réponse. C'est une trajectoire : une séquence pensée → action → observation → … → objectif atteint.

boucle-agent
Agent
Raisonnement
« Le paramètre url sent la SSRF. Je tente un callback OOB. »
Outil
Exécution réelle
GET /fetch?url=http://oob.lab/abc123
Environnement
Sortie brute
Callback DNS reçu pour abc123. SSRF confirmée.
Agent
Étape suivante
« Confirmée. Je pivote vers 169.254.169.254. »
La boucle ReAct : c'est CETTE séquence qu'on doit capturer, pas une réponse isolée.

Cette distinction commande tout le reste. Un dataset agentique doit encoder le format d'appel d'outils (JSON de function calling, ou ReAct texte), les observations réelles renvoyées par l'environnement, et le fait que chaque action dépend de la précédente. C'est plus difficile à produire qu'un corpus de Q/R, et c'est précisément là que la distillation propre et l'ancrage à l'exécution deviennent indispensables.

Les bases publiques pour la sécurité offensive (et leurs licences)

On ne part jamais de rien. Il existe un écosystème riche de données de sécurité réutilisables, mais leurs licences vont du domaine public au copyleft viral, et mélanger les deux dans un dataset qu'on redistribue peut « contaminer » l'ensemble. Voici la carte du terrain.

bases-publiques
BASE / RESSOURCE              LICENCE             USAGE DATASET
--------------------------   -----------------   ---------------------------
NVD (NIST)                   Domaine public US   Libre, très large
CVE List (MITRE)             CC0 / libre         Libre, très large
GHSA advisory-database       CC-BY 4.0           Libre + attribution
OSV.dev                      selon la source     Vérifier par entrée
MITRE ATT&CK / CWE / CAPEC   Conditions MITRE    Libre + attribution
OWASP (WSTG, Top 10)         CC-BY-SA 4.0        Partage à l'identique
Nuclei templates             MIT                 Permissive
SecLists                     MIT                 Permissive
PayloadsAllTheThings         MIT                 Permissive
Metasploit Framework         BSD-3-Clause        Permissive
Exploit-DB                   GPLv2 (paquet)      Copyleft, prudence
Writeups / académies         Copyright           À éviter tel quel
Datasets de code de vulns    Licence du dépôt    Hérite des sources GitHub
 (Big-Vul, Devign, ...)      d'origine           (souvent hétérogène)
Bases publiques utiles pour un dataset offensif et leur régime de licence (à vérifier au cas par cas).

Le tri mental utile : trois familles. Les données descriptives (NVD, CVE, CWE, ATT&CK) décrivent quoi exploiter et avec quel impact : parfaites pour ancrer les raisonnements dans des faits réels. Les données techniques permissives (Nuclei templates, SecLists, PayloadsAllTheThings, Metasploit) donnent le comment : payloads, signatures, modules. Et les données de provenance sensible (Exploit-DB en GPL, writeups copyrightés, contenu des académies type PortSwigger) qu'on n'intègre pas verbatim dans un dataset redistribué.

Deux pièges méritent le détour. GitHub Security Advisories est une mine sous-exploitée : des milliers d'avis structurés, liés au code corrigé, en CC-BY 4.0 (attribution suffit). À l'inverse, les datasets académiques de code vulnérable (Big-Vul, Devign, DiverseVul, CVEfixes) sont précieux pour le volet SAST, mais leurs échantillons sont extraits de dépôts GitHub qui gardent leur licence d'origine. Le dataset agrège, il ne relicencie pas.

Entraîner sur de la donnée licenciée : zone grise
Le statut juridique de l'entraînement d'un modèle sur de la donnée sous licence (le résultat est-il une œuvre dérivée ?) reste non tranché et varie par juridiction. La redistribution d'un dataset, elle, est clairement gouvernée par les licences sources. En pratique, pour une société : on segmente le corpus par licence, on garde la provenance de chaque exemple, et on applique l'interprétation la plus stricte sur ce qu'on publie. Ce paragraphe n'est pas un avis juridique.

Distiller proprement : du teacher au student

La distillation « classique » de Hinton transfère les logits (les probabilités douces) d'un gros modèle vers un petit. Problème : avec un teacher derrière une API, vous n'avez pas accès aux logits. On fait donc de la distillation au niveau séquence (sequence-level KD) : le teacher génère les données, et le student est entraîné en imitation supervisée sur ces sorties. Pour un agent, ce que génère le teacher, ce sont des trajectoires complètes dans un environnement.

Le réflexe naïf, c'est de demander au teacher d'inventer une trajectoire de A à Z. C'est exactement la recette de l'hallucination : le modèle écrit « le scan révèle le port 8080 ouvert » alors qu'aucun scan n'a tourné. Le student apprend alors à raconter du pentest, pas à en faire. La parade tient en un principe : l'environnement est l'oracle. Le teacher propose une action ; on l'exécute réellement dans une sandbox ; l'observation qu'on réinjecte est la vraie sortie, pas celle que le teacher imagine.

Chaîne de montage : génération en série de trajectoires distillées
La distillation propre, c'est une chaîne : le teacher propose, la sandbox tranche, le vérificateur trie.

Concrètement, une trajectoire distillée stockée ressemble à ça (format function calling, une ligne par exemple dans un fichier JSONL) :

json
{
  "messages": [
    {"role": "system", "content": "Tu es un agent de pentest web autorisé."},
    {"role": "user", "content": "Cible: https://lab.local. Trouve et prouve une SSRF."},
    {"role": "assistant", "content": "Le paramètre 'url' de /fetch est suspect. Je tente un callback OOB.",
     "tool_calls": [{"name": "http_get", "args": {"url": "https://lab.local/fetch?url=http://oob.lab/t1"}}]},
    {"role": "tool", "name": "http_get", "content": "200 OK, body vide"},
    {"role": "tool", "name": "oob_poll", "content": "Callback DNS recu: t1.oob.lab depuis 10.0.0.5"},
    {"role": "assistant", "content": "SSRF confirmee (callback recu). Je pivote vers le metadata cloud.",
     "tool_calls": [{"name": "http_get", "args": {"url": "https://lab.local/fetch?url=http://169.254.169.254/latest/meta-data/"}}]},
    {"role": "tool", "name": "http_get", "content": "200 OK: iam/ instance-id/ ..."}
  ],
  "meta": {"objectif_atteint": true, "verifie_par": "oob+http_status", "source_graine": "ssrf-seed-04"}
}

Notez les rôles tool : leur contenu vient de l'exécution, pas du teacher. Et le bloc meta : il porte la preuve que l'objectif a été vérifié, et la trace de provenance. Sans ces deux choses, vous ne pouvez ni filtrer ni auditer votre dataset.

D'un seul exemple à mille, sans hallucination

C'est le cœur de la question. Vous avez une trajectoire SSRF validée ; vous en voulez cinq cents, variées, toutes correctes. L'amplification se fait en deux temps : varier puis vérifier. Et c'est l'étape de vérification, pas celle de génération, qui élimine l'hallucination.

Varier : Self-Instruct et Evol-Instruct

Pour démultiplier une graine, deux techniques éprouvées. Le Self-Instruct demande au teacher de produire de nouvelles tâches sur le même modèle (autres points d'injection, autre SGBD, autre encodage). L'Evol-Instruct (WizardLM) fait évoluer une graine en profondeur (ajoute un WAF à contourner, une authentification, un filtre) ou en largeur (transpose la SSRF en SSRF aveugle, puis en XXE out-of-band). À partir d'une seule SSRF, on génère ainsi des dizaines de scénarios paramétriquement différents.

Mais attention : à ce stade, ce ne sont que des énoncés et des tentatives de trajectoire. Rien ne garantit qu'elles marchent. C'est de la matière première, pas du dataset.

Vérifier : l'environnement tranche

Chaque trajectoire candidate passe par un crible d'ancrage. Voici les garde-fous, du plus fort au plus faible :

  • Oracle programmatique (le plus fort) — on rejoue les actions dans une sandbox et on teste l'objectif mécaniquement : le callback OOB est-il arrivé ? le flag est-il capturé ? le shell répond-il ? Si oui, la trajectoire est vraie, point. C'est du rejection sampling (STaR/RFT) : on ne garde que les rollouts qui réussissent.
  • Cohérence d'exécution — chaque observation du dataset provient de l'exécution réelle, jamais du teacher. Une action dont la sortie ne matche pas ce que le teacher attendait est coupée : c'est un signal d'hallucination.
  • Vérification de provenance — quand le teacher cite une CVE, un CWE ou un comportement de produit, on vérifie que la référence existe dans NVD/GHSA. Une CVE inventée disqualifie l'exemple.
  • Self-consistency et juge (le plus faible) — on génère N fois et on garde le consensus, ou un second modèle juge selon une grille. Utile en dernier recours, jamais comme unique filtre : un juge LLM peut se tromper de la même façon que le générateur.
amplification
Entrée
1 graine validée
La trajectoire SSRF parfaite de votre analyste : pensée, action, observation, objectif atteint.
Variante
Autres points d'injection
Paramètres voisins, en-têtes, chemins alternatifs.
Variante
WAF & encodages
Contournements, double-encodage, variations de casse.
Variante
Autres SGBD / protocoles
MySQL, Postgres, Oracle ; gopher, file, dict.
Sortie
Dataset vérifié
Seules les variantes qui atteignent l'objectif pour de vrai sont gardées. Des dizaines de trajectoires traçables, prêtes à entraîner.
Une graine validée entre, des dizaines de trajectoires vérifiées sortent. Le filtre d'exécution est non négociable.

Le sous-produit précieux de ce crible : les échecs. Une trajectoire que le teacher a tentée mais qui n'atteint pas l'objectif n'est pas un déchet. C'est l'exemple « rejeté » dont on aura besoin pour le DPO (voir plus bas) : la paire « ce chemin marche, celui-ci échoue » est de l'or pour aligner l'agent.

Le pipeline d'entraînement : CPT, SFT, DPO

Une fois le dataset prêt, l'entraînement se fait en couches. Toutes ne sont pas obligatoires ; la couche SFT est le cœur, les deux autres l'encadrent.

pipeline-ft
Socle
Modèle de base ouvert (7-14B)
Llama, Qwen, Mistral... poids ouverts, point de départ.
Optionnel
Continued pre-training
Next-token sur corpus brut de sécurité (RFC, doc d'outils, code).
Cœur
SFT sur trajectoires
Imitation supervisée des trajectoires distillées et vérifiées.
Alignement
DPO / ORPO
Préférence : bonne trajectoire vs trajectoire rejetée.
Le pipeline en couches : base ouverte, pré-entraînement de domaine optionnel, SFT au cœur, alignement par préférence au sommet.

Continued pre-training (CPT)

Le continued pre-training (ou domain-adaptive pretraining) reprend l'objectif next-token sur un gros corpus brut de domaine : pages de manuel d'outils, RFC réseau, code de modules Metasploit, descriptions CWE. On n'apprend pas de tâche, on imprègne le modèle du vocabulaire et des réflexes du domaine. C'est la couche la plus gourmande en données et en calcul (on parle de centaines de millions de tokens), et la plus optionnelle : utile si le modèle de base connaît mal la sécurité offensive, dispensable sinon.

SFT : le cœur

Le supervised fine-tuning est l'étape qui apprend l'agentique. On entraîne le student à reproduire les trajectoires vérifiées : même format d'appel d'outils, même enchaînement pensée → action → observation. C'est ici que la qualité du dataset se paie cash : quelques milliers de bonnes trajectoires battent des dizaines de milliers de médiocres.

DPO : aligner sur la préférence

Le Direct Preference Optimization affine le comportement sans le lourd appareillage du RLHF (pas de modèle de récompense, pas de PPO). Il consomme des paires (un prompt, une réponse choisie, une réponse rejetée) et pousse le modèle vers la première. D'où l'intérêt de garder les échecs du crible de vérification : ce sont vos « rejetées » naturelles.

json
{
  "prompt": "Cible authentifiee. SSRF suspectee sur /preview?target=. Prouve-la.",
  "chosen":   "Je teste d'abord un callback OOB hors-bande pour eviter les faux negatifs...",
  "rejected": "Je lance directement un scan de ports complet sur la cible interne..."
}

Des variantes allègent encore le tableau. L'ORPO fusionne SFT et préférence en une passe, sans modèle de référence. KTO se contente d'un signal binaire (bon/mauvais) sans avoir besoin de paires. Pour un premier agent, l'ordre pragmatique est : SFT d'abord, puis une passe DPO/ORPO sur les préférences que votre sandbox a déjà produites.

Quel modèle, quelle GPU, quel budget

La question « à quel LLM c'est applicable » a une réponse nette : la fenêtre utile pour un agent offensif va de 7B à 70B paramètres. En dessous de 7B, le suivi d'outils multi-étapes devient fragile (l'agent oublie de boucler, casse le format JSON). Le sweet spot pratique est 7B-14B : assez capable pour de l'agentique fiable, et entraînable sur une seule carte grâce à QLoRA.

QLoRA : ce qui rend tout ça accessible

Le full fine-tuning d'un 7B en 16 bits avec Adam demande facilement 100 Go de VRAM (poids + gradients + états d'optimiseur) : du multi-GPU coûteux. La QLoRA change la donne : on charge le modèle de base quantifié en 4 bits (NF4), gelé, et on n'entraîne que de petits adaptateurs LoRA (quelques pourcents des paramètres). La mémoire s'effondre, la qualité reste proche du full FT. Ordres de grandeur :

vram-qlora
TAILLE   QLoRA (4-bit) VRAM    GPU TYPIQUE
------   -------------------   ----------------------------
7-8B     ~ 8-12 Go             RTX 3090 / 4090 (24 Go)
13-14B   ~ 12-18 Go            RTX 4090 (24 Go)
32-34B   ~ 24-36 Go            A6000 48 Go / A100 80 Go
70B      ~ 40-48 Go            A100 / H100 80 Go (1 carte)
VRAM approximative pour un fine-tuning QLoRA selon la taille (longueur de contexte modérée).

Traduction : un agent 8B s'entraîne sur une RTX 4090 grand public. Un 70B tient sur une seule A100/H100 80 Go en QLoRA, là où son full FT exigerait un cluster. Côté outillage, des frameworks comme Unsloth ou Axolotl gèrent QLoRA, SFT et DPO depuis un simple fichier de config :

yaml
# config QLoRA SFT (style Axolotl), agent 8B sur une seule carte
base_model: meta-llama/Meta-Llama-3.1-8B-Instruct
load_in_4bit: true          # quantification NF4
adapter: qlora
lora_r: 32
lora_alpha: 16
sequence_len: 8192          # les trajectoires sont longues
datasets:
  - path: ./traj_verifiees.jsonl
    type: chat_template      # format messages + tool_calls
gradient_accumulation_steps: 4
micro_batch_size: 1
num_epochs: 3
learning_rate: 0.0002

Les coûts, en dollars

Deux postes de dépense, très différents. La génération du dataset (inférence du teacher) et le fine-tuning (heures de GPU). Pour le teacher, deux options : une API cloud, simple et rapide, ou un gros modèle ouvert auto-hébergé, moins cher au token mais plus lent à opérer. Les tarifs d'API publics donnent un repère utile : à la mi-2026, un teacher de classe Claude Sonnet 4.6 est à environ 3 $ / million de tokens en entrée et 15 $ en sortie ; un teacher haut de gamme (Opus 4.8) à 5 $ / 25 $. Et surtout : la génération de dataset n'est pas sensible à la latence, donc on passe par l'API Batches, qui divise la facture d'inférence par deux.

couts
ETAPE                       MODELE     RESSOURCE          DUREE      COUT ~
-------------------------   --------   ----------------   --------   -----------
Generation (1 trajectoire)  Teacher    API + Batches      -          0,10-0,25 $
Generation 10k trajectoires Teacher    API + Batches      ~1 nuit    1 000-2 500 $
CPT corpus brut (optionnel) 8B         1x A100 80 Go      10-40 h    20-80 $
SFT QLoRA (50k trajectoires) 8B        1x RTX 4090        8-20 h     5-15 $
SFT QLoRA                   14B        1x A100 80 Go      12-30 h    20-60 $
DPO QLoRA                   8-14B      1x A100 80 Go      6-15 h     12-40 $
SFT QLoRA                   70B        1x H100 80 Go      30-80 h    90-240 $
Ordres de grandeur (tarifs communautaires GPU mi-2026, API Batches pour le teacher). À calibrer sur votre cas.

Le constat qui surprend toujours : le fine-tuning est bon marché. Entraîner un agent 8B compétent coûte quelques dizaines de dollars de GPU. Le vrai budget, c'est la génération du dataset via le teacher : les trajectoires agentiques sont gourmandes en tokens (le contexte gonfle à chaque observation réinjectée), et c'est là que partent les milliers de dollars. Optimiser, c'est d'abord optimiser l'inférence du teacher : Batches, mise en cache du prompt système, et un teacher ouvert auto-hébergé dès que le volume le justifie.

Le levier qui compte : la qualité, pas le volume
On voit des équipes brûler 5 000 $ de génération pour 100k trajectoires bruyantes. Un dataset de 5 000 trajectoires vérifiées par exécution produit presque toujours un meilleur agent, pour dix fois moins cher. Le crible d'exécution n'est pas qu'un garde-fou anti-hallucination : c'est aussi votre meilleur poste d'économie.

Le piège qu'on ne voit pas venir : l'agent qui ment bien

Un dernier mot, parce qu'il conditionne la suite. Un agent offensif mal distillé ne produit pas des erreurs visibles : il produit des rapports plausibles et faux. Il « confirme » une SSRF qui n'existe pas, parce qu'il a appris d'un teacher qui hallucinait ses observations. En pentest, un faux positif crédible coûte plus cher qu'une absence de résultat : il envoie une équipe sur une fausse piste et entame la confiance dans le rapport. C'est la même exigence de fiabilité que celle qu'on impose à un serveur out-of-band de production : une preuve doit être mécaniquement vérifiable, jamais une affirmation du modèle.

C'est précisément la différence entre brancher un LLM sur des outils et entraîner un agent sur des trajectoires vérifiées. Le premier improvise ; le second a appris des chemins qui marchent pour de vrai. C'est aussi la frontière qu'on trace dans le pentest automatisé par IA entre une démo qui impressionne et un outil sur lequel on engage un livrable.

À retenir

  • L'unité d'un dataset agentique offensif est la trajectoire (pensée → action → observation → …), pas la paire question/réponse.
  • Les bases publiques existent et sont riches (NVD, CVE, GHSA, ATT&CK, Nuclei, SecLists) ; il faut trier par licence et garder la provenance, surtout face au copyleft (Exploit-DB GPL) et au share-alike (OWASP CC-BY-SA).
  • On distille en sequence-level KD : le teacher génère, le student imite. Contre l'hallucination, un seul principe : l'environnement est l'oracle, on exécute pour de vrai.
  • D'une graine on amplifie par Self-Instruct / Evol-Instruct, puis on filtre par rejection sampling : seules les trajectoires dont l'objectif est mécaniquement atteint entrent au dataset. Les échecs deviennent les paires « rejetées » du DPO.
  • Cible 7B-14B pour un agent fiable sur une seule carte. La QLoRA (4-bit + LoRA) met même un 70B sur une A100/H100.
  • Le fine-tuning coûte des dizaines de dollars ; le gros budget est la génération du dataset via le teacher (Batches, cache, teacher ouvert pour économiser). Qualité > volume, toujours.

Construire ce pipeline de bout en bout, c'est notre métier : on opère des modèles distillés sur des trajectoires de pentest vérifiées, derrière notre AppSec pilotée par l'IA. Si vous voulez en parler dataset, distillation ou agent offensif avec un humain qui a mis les mains dedans, la page contact est faite pour ça.