feat(c2): integrate Mythic command and control (sprint 8) #11
Reference in New Issue
Block a user
Delete Branch "sprint/8-c2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
C2Adaptermince (Mythic 3.x livré, porte ouverte pour un C2 maison).execution_result,executed_at,commands).MIMIC_ENCRYPTION_KEYobligatoire — guard 503 sinon) ; token jamais retourné en clair (GET /c2-configrenvoiehas_token: bool) ; URLhttps://only + hostname non-vide ;allow_redirects=Falsesur toutes les requêtes Mythic ; erreurs adapter sanitizées (C2 transport error: ConnectionErrorau lieu de fuiter le pool URL).FakeAdapterdéterministe activé parMIMIC_C2_ADAPTER=fakepour le dev local et Playwright..badge-pill-*recipes ; a11y traitée (rows cliquables clavier-accessibles avecrole="button"+aria-expanded+ focus ring).lg:grid-cols-2du sprint 7), expandable par tâche, polling visible via micro-indicateurRefreshing….Sprint 9 — UI polish bundlé dans cette PR
5 commits additionnels (
a9fe2fc→8b5b5d9) qui ajustent le confort visuel mis en évidence par l'intégration C2 :lg+:[engagement form | C2ConfigCard]côte-à-côte, stack vertical sur mobile. La carte C2 livrée au sprint 8 prend sa vraie place.--color-canvaspasse de#ffffffà#f3f5f8(gris-bleu très pâle),--color-paperreste#ffffff. Les cartes "lèvent" sans shadow ni radius. Brutalisme intact (DESIGN.md L28 + L134 alignées).bg-canvas→bg-papercorrigée sur 7 sites où le canvas servait de proxy pour paper avant la différenciation :.text-input,.btn-outline,.btn-outline-ink, et 4 surfaces flottantes (MitreTechniquePicker:110,SimulationList:96,ExportEngagementButton:77,MitreMatrixModal:173). La cellule de matriceMitreMatrixModal:273est volontairement gardée enbg-canvas(sémantique "cellule vide sur canvas" intentionnel).#111827/ paper#1f2937déjà différenciés).Design-reviewer 2 passes APPROVED. Aucun changement backend.
Test plan
ruff+mypyclean).root/rootpass8.Endpoints livrés
/api/engagements/<id>/c2-config/api/engagements/<id>/c2-config/test/api/engagements/<id>/c2/callbacks/api/engagements/<id>/c2/callbacks/<cid>/history?page&page_size/api/simulations/<id>/c2/execute/api/simulations/<id>/c2/tasks/api/simulations/<id>/c2/importTous renvoient 503 si
MIMIC_ENCRYPTION_KEYest absent et 403 pour le rôle SOC.Comment tester en local
Scénarios :
https://lab.mythic:7443+ un token quelconque (l'adapterfakene valide pas),Save→Test connection(vert avec FakeAdapter).[Execute via C2]→ modale picker de 3 callbacks fake (1, 2, 3) → sélection ligne → 1-3 commandes en textarea →Launch. La simulation passe automatiquement àin_progress. Les tâches apparaissent dans le panneau C2 tasks dessous.submitteddeviennentcompletedau 2e refetch (FakeAdapter). Le micro-labelRefreshing…est présent pendant le polling, disparaît quand tout est complété.execution_resultau format$ <command>\n<output>\n,executed_atreçoit le timestamp si vide, et la commande est appendée àcommands(dédupliquée). Vérifier dans l'export Markdown sprint 6 que ces champs sortent proprement.[Import C2 history]→ picker callback → modale step 2 avec liste paginée (25 par page) → cocher 2 lignes →Import selected. ToastImported 2 task(s)(ouImported 1 task(s), 1 already attachedsi une était déjà rattachée).curl -H "Authorization: Bearer <SOC_TOKEN>" http://127.0.0.1:5000/api/engagements/1/c2/callbacks→403.MIMIC_ENCRYPTION_KEY, l'admin reçoit503 {"error":"C2 disabled: MIMIC_ENCRYPTION_KEY not set"}sur tous les endpoints C2 et la carte de config affiche son banner désactivé./c2/tasksplusieurs fois ne réapplique JAMAIS le mapping (la colonnemapping_appliedsert d'ancre, vérifiée par les 19 teststest_c2_mapping.py).Notes
c2_config,c2_task) en 1 migration atomique (0006), + ajoutmapping_applieden 0007 séparée pour le suivi d'idempotence. Cascade-delete depuisengagementsetsimulations(cohérent avec le pattern sprint 2).MythicMeta/Mythic_Scripting master @ 2026-06-10— pas d'instance live disponible, premier branchement réel pourra nécessiter un petit patch (R2 du plan, documenté).MIMIC_C2_ADAPTER=mythic|fake.fakeest déterministe : 3 callbacks (display_id 1/2/3), 12 / 0 / 5 tasks historiques respectivement, transitions submitted→completed au 2e appel.🤖 Generated with Claude Code
F1: add tabIndex/role/onKeyDown/aria-expanded to C2TasksPanel expander rows and C2CallbackPicker callback rows; focus-visible ring via Tailwind utilities F2: add source:'mimic'|'import' to C2TaskListItem; C2TasksPanel reads task.source instead of mapping_applied for the Source badge label F3: align C2TaskStatusBadge and C2CallbackPicker Active/Inactive pill metrics to py-[6px] text-[14px] font-medium (matches SimulationStatusBadge / StatusBadge) F4: replace hand-rolled Source pill class string with badge-pill-outline recipe Tests: 212/212 passing (+3 new: Enter/Space key on expander, Enter key on callback row) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>mapping.py — full §0.11 contract: 1. execution_result: append '$ <command>\n<output>\n' block (previously wrote raw output without command header, making multi-task blobs unreadable in exports) 2. executed_at: set from task.completed_at when currently null (was completely missing — simulation.executed_at stayed null forever) 3. commands: append task.command deduplicated line-by-line (was completely missing — simulation.commands stayed empty) mythic.py — sanitize transport errors: Replace 'raise C2Error(str(exc))' (which leaks the Mythic URL via requests exception repr) with 'raise C2Error(f"C2 transport error: {type(exc).__name__}")'. Original exc stays chained for backend logs. api/c2.py — remove redundant 'task.mapping_applied = True' in import endpoint (apply_task_to_simulation() already sets it). test_c2_mapping.py — full rewrite: 19 tests covering command blocks, executed_at set/preserve, commands dedup, idempotency. test_c2_adapter_mythic.py — add URL-leak sanitization assertion. 468 passed; ruff + mypy clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>