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:
66
SPEC.md
66
SPEC.md
@@ -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...
|
||||
|
||||
Reference in New Issue
Block a user