docs(spec): add C2 integration section (sprint 8 commit #1)

Introduce the SPEC section for the Mythic C2 integration layer.
Covers RBAC (RT-only, SOC=403), per-engagement Fernet-encrypted config,
c2_config + c2_task data model with ON DELETE CASCADE, full endpoint
list, output mapping rules (append-only, idempotent), 2500 ms polling
and the fake/real adapter selection via MIMIC_C2_ADAPTER.

Also patch tasks/todo.md: fix pytest baseline (256 from main, not 253),
make cascade-delete explicit, pin the MythicMeta/Mythic_Scripting source
version and document defensive base64 handling.

Closes spec-reviewer WARN-1 (SPEC ↔ plan parity), WARN-2 (cascade),
INFO-1 (pinned source), INFO-3 (baseline).
This commit is contained in:
Knacky
2026-06-10 19:07:35 +02:00
parent 6ca614a3f3
commit 813e69ee01
2 changed files with 137 additions and 133 deletions

66
SPEC.md
View File

@@ -59,8 +59,70 @@ CSV : exactement 1 ligne d'en-tête + 1 ligne par simulation. Markdown : en-têt
Prévoir un module d'authentification : dans un premier temps local à la bdd.
Dans un premier temps, il s'agit juste de notifier manuellement de l'exécution et les résultats des tests.
Dans un second temps, après que la V1 soit terminée, nous ajouterons une couche permettant de se connecter à un C2 (mythic ou maison) afin d'exécuter des simulation au travers du C2.
Dans un second temps, après que la V1 soit terminée, nous ajouterons une couche permettant de se connecter à un C2 (mythic ou maison) afin d'exécuter des simulation au travers du C2.
## Intégration C2 (Sprint 8+)
Couche d'intégration C2 permettant d'exécuter les commandes d'une simulation à travers un Command & Control distant, suivre l'avancement des tâches en quasi-temps réel, et importer l'historique d'exécutions existant. **Implémentation de référence : Mythic 3.x**, derrière une interface `C2Adapter` mince qui ne ferme pas la porte à un C2 maison ultérieur.
**RBAC C2 = ressource Red Team uniquement** (précédent Templates + Export) : admin et redteam ont accès complet (config + exécution + import). SOC retourne 403 sur tous les endpoints C2 (pas de nav link, pas d'affichage du panneau C2).
**Configuration par engagement** : chaque engagement possède au plus une `c2_config` (URL Mythic + API token + flag `verify_tls`). Le token est **chiffré au repos** via `cryptography.Fernet` ; la clé est dérivée de l'env var `MIMIC_ENCRYPTION_KEY` (variable obligatoire pour activer la fonctionnalité C2 — jamais hardcodée, conforme à la règle OPSEC zero-secret-in-code). Le token n'est jamais renvoyé en clair par l'API — `GET /api/engagements/<id>/c2-config` retourne `has_token: bool` uniquement. Mise à jour via `PUT` ; suppression via `DELETE`. La suppression d'un engagement supprime en cascade sa `c2_config`.
**Sélection d'adapter** via l'env var `MIMIC_C2_ADAPTER` :
- `mythic` (défaut) : adapter Mythic réel (GraphQL via Hasura).
- `fake` : adapter en mémoire déterministe utilisé pour la validation Playwright et le dev local sans instance Mythic.
**Modèle de données — additions** :
`c2_config` (1 ligne par engagement au max) :
| Colonne | Type | Notes |
|---|---|---|
| `id` | int PK | |
| `engagement_id` | int FK `engagements.id` ON DELETE CASCADE, **UNIQUE** | |
| `url` | text | endpoint Mythic, ex. `https://lab.internal:7443` |
| `api_token_encrypted` | text | Fernet ciphertext, jamais en clair |
| `verify_tls` | bool, défaut `true` | `false` autorisé pour labs auto-signés |
| `created_at`, `updated_at` | datetime | |
`c2_task` (lien simulation ↔ tâche Mythic) :
| Colonne | Type | Notes |
|---|---|---|
| `id` | int PK | |
| `simulation_id` | int FK `simulations.id` ON DELETE CASCADE | |
| `mythic_task_display_id` | int | identifiant côté Mythic |
| `callback_display_id` | int | callback Mythic sur lequel la tâche tourne |
| `command` | text | commande envoyée |
| `params` | text nullable | paramètres associés |
| `status` | text | statut brut Mythic (`submitted`, `completed`, `error`, …) |
| `completed` | bool | `true` quand la tâche est terminée |
| `output` | text nullable | sortie décodée (base64 → utf-8 ; binaire → préfixe `<binary>` + hex) |
| `source` | enum `mimic` \| `import` | tâche lancée depuis Mimic ou importée a posteriori |
| `created_at` | datetime | |
| `completed_at` | datetime nullable | timestamp de complétion |
**Endpoints C2** (tous admin+redteam ; SOC = 403) :
- `GET /api/engagements/<id>/c2-config``{has_token, url, verify_tls}` (jamais le token en clair).
- `PUT /api/engagements/<id>/c2-config``{url, api_token?, verify_tls}`.
- `DELETE /api/engagements/<id>/c2-config`.
- `POST /api/engagements/<id>/c2-config/test` — test de connectivité via l'adapter, renvoie `{ok, error?}`.
- `GET /api/engagements/<id>/c2/callbacks` — callbacks actifs de l'instance Mythic configurée.
- `POST /api/simulations/<id>/c2/execute` `{callback_display_id, commands: [str]}` — une tâche Mythic par commande, stockées dans `c2_task` (source=`mimic`). **Auto-transition** : si la simulation est `pending`, elle passe à `in_progress` (même règle que l'édition manuelle RT — cf. § Fonctionnement).
- `GET /api/simulations/<id>/c2/tasks` — poll-on-read : à la lecture, rafraîchit le statut et l'output des `c2_task` non terminées depuis Mythic, applique le mapping de sortie (voir ci-dessous) à la simulation pour chaque tâche qui vient de se terminer (idempotent — appliqué une seule fois par tâche).
- `GET /api/engagements/<id>/c2/callbacks/<cid>/history?page=` — historique paginé des tâches d'un callback, pour l'import.
- `POST /api/simulations/<id>/c2/import` `{task_display_ids: [int]}` — import sélectif de tâches (source=`import`) + mapping de sortie.
**Mapping de sortie vers la simulation** (appliqué une fois par tâche, lors de la complétion ou de l'import) :
- `simulation.execution_result` reçoit en append le bloc `\n$ <command>\n<output>\n` (préserve l'existant, jamais d'écrasement).
- `simulation.executed_at` est renseigné depuis le timestamp de la première tâche complétée si le champ est vide ; sinon non modifié.
- `simulation.commands` reçoit en append la commande si elle n'y figure pas déjà (déduplication ligne par ligne).
**Suivi temps réel** : polling court — le frontend re-fetch `GET /api/simulations/<id>/c2/tasks` toutes les **2 500 ms** via TanStack Query `refetchInterval` tant qu'une tâche attachée n'est pas terminée ; le polling s'arrête automatiquement quand toutes les tâches sont `completed`. Pas d'infrastructure ajoutée côté serveur (pas de WebSocket, pas de scheduler).
**UI** : les contrôles C2 vivent dans la carte Red Team de l'écran simulation — bouton `[Execute via C2]` ouvrant une modale (picker de callback + textarea de commandes pré-remplie depuis `commands`), panneau des tâches attachées sous la carte, et modale d'import historique. Configuration C2 visible/éditable depuis l'écran de détail/édition d'engagement.
**Validation** : MVP entièrement mocké — pytest utilise un adapter mocké (zéro HTTP live), Playwright utilise l'adapter `fake` (déterministe). Le branchement contre une instance Mythic réelle est repoussé au premier usage opérationnel et peut nécessiter un patch mineur du contrat GraphQL.
## Stacks techniques
* **FrontEnd** : WebUI
- Stacks standard : ReactJS, Vite, TailWind etc...