Manuels de jeu¶
Les playbooks sont des flux de travail en plusieurs étapes définis par l'opérateur qui automatisent les séquences de techniques de beacon. Chaque playbook contient une liste ordonnée d'étapes : des étapes d'action qui mettent en file d'attente les tâches C2 et des étapes conditionnelles qui se branchent en fonction des métadonnées des beacons actifs. Lorsqu'il est exécuté sur un beacon, le moteur de playbook évalue les conditions en temps réel, sélectionne la branche appropriée, met en file d'attente toutes les tâches résultantes et enregistre chaque décision pour un examen post-opération.
Aperçu¶
Un playbook résout le problème de l’exécution répétée de la même séquence de techniques de post-exploitation sur plusieurs beacons. Au lieu d'émettre manuellement des commandes une par une, un opérateur définit un playbook une seule fois et l'exécute sur n'importe quelle beacon active.
Capacités clés :
- Exécution d'étape ordonnée -- Étapes exécutées en séquence par leur champ
order - Branchement conditionnel : les étapes peuvent créer une branche en fonction des propriétés du beacon (système d'exploitation, nom d'hôte, élévation de privilèges, architecture, etc.)
- Conditions imbriquées -- Les étapes conditionnelles prennent en charge les modèles if/then/else et multi-branches (style commutateur), avec une profondeur d'imbrication arbitraire.
- Journalisation des exécutions -- Chaque décision (quelle branche a été prise, quelles valeurs de condition ont été observées) est enregistrée
- API CRUD complète - Créez, lisez, mettez à jour, supprimez et exécutez des playbooks via REST
flowchart TD
A[Operator triggers playbook] --> B[Fetch playbook steps]
B --> C{Step type?}
C -->|action| D[Map technique to C2 task]
D --> E[Enqueue task for beacon]
E --> F[Log action step]
C -->|conditional| G[Evaluate condition against beacon state]
G -->|if/then/else| H{Condition true?}
H -->|yes| I[Execute then steps]
H -->|no| J[Execute else steps]
G -->|multi-branch| K{Evaluate branches in order}
K -->|match| L[Execute matched branch steps]
K -->|no match| M[Execute else steps or skip]
I --> F
J --> F
L --> F
M --> F
F --> N{More steps?}
N -->|yes| C
N -->|no| O[Return execution ID + task IDs] Objet du livre de jeu¶
Un playbook est stocké dans la table c2_playbooks et renvoyé au format JSON à partir de tous les points de terminaison de l'API.
| Champ | Type | Description |
|---|---|---|
id | chaîne (UUID) | Identifiant unique du playbook |
name | chaîne | Nom d'affichage |
description | chaîne | Description facultative |
steps | Étape du livre de jeu[] | Tableau ordonné d’actions et d’étapes conditionnelles |
created_by | chaîne (UUID) | Opérateur qui a créé le playbook |
created_at | chaîne (ISO 8601) | Horodatage de création |
updated_at | chaîne (ISO 8601) | Horodatage de la dernière mise à jour |
Types d'étapes¶
Chaque étape possède un champ type qui détermine son comportement. Les étapes sans champ type sont par défaut "action" pour une compatibilité ascendante.
Étapes d'action¶
Une étape d'action mappe un technique_id à une tâche C2 et le met en file d'attente pour le beacon cible. L'objet params est transmis au mappeur technique.
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
type | chaîne | Non | "action" (par défaut si omis) |
technique_id | chaîne | Oui | Identifiant de la technique (par exemple, "exec.shell", "creds.hashdump", "privesc.getsystem") |
params | objet | Non | Paramètres spécifiques à la technique |
order | entier | Oui | Ordre d'exécution (exécutions inférieures en premier) |
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "whoami /all" },
"order": 1
}
Étapes conditionnelles¶
Une étape conditionnelle évalue les métadonnées du beacon au moment de l'exécution et passe à différentes sous-étapes en fonction du résultat. Deux modèles sont pris en charge :
- If/then/else -- Une seule condition avec des branches
thenet facultativeselse - Multi-branches (commutateur) -- Une liste ordonnée de branches nommées ; la première branche correspondante gagne, avec une solution de repli facultative
else
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
type | chaîne | Oui | Doit être "conditional" |
order | entier | Oui | Ordre d'exécution |
conditional | objet | Oui | La configuration conditionnelle (voir ci-dessous) |
L'objet conditional :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
condition | Groupe de conditions | Oui (si/alors/sinon) | Condition principale à évaluer |
then | Étape du livre de jeu[] | Oui (si/alors/sinon) | Étapes à exécuter si la condition est vraie |
else | Étape du livre de jeu[] | Non | Étapes à exécuter si la condition est fausse ou si aucune branche ne correspond |
branches | Branche conditionnelle[] | Non | Liste multi-branches (évaluée dans l'ordre, premier match gagné) |
Chaque ConditionalBranch :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
name | chaîne | Oui | Étiquette lisible par l'homme (utilisée dans les journaux d'exécution) |
condition | Groupe de conditions | Oui | État des branches |
steps | Étape du livre de jeu[] | Oui | Étapes à exécuter si cette branche correspond |
État du moteur¶
Les conditions évaluent les métadonnées du beacon en direct au moment de l'exécution. Ils sont composés en groupes à l'aide de combinateurs booléens.
Champs de condition¶
Les champs de métadonnées de beacon suivants peuvent être référencés dans des conditions :
| Champ | Type | Description |
|---|---|---|
elevated | booléen | Si le beacon fonctionne avec des privilèges élevés |
os | chaîne | Système d'exploitation (par exemple, "Windows 10 Pro 19045") |
hostname | chaîne | Nom d'hôte de la machine |
username | chaîne | Utilisateur actuel (par exemple, "CORP\\jsmith") |
pid | entier | ID de processus de beacon |
arch | chaîne | Architecture ("x64" ou "x86") |
sleep | entier | Intervalle de sommeil actuel en millisecondes |
jitter | entier | Pourcentage de gigue actuel |
tags | chaîne[] | Beacons attribuées par l'opérateur |
ip | chaîne | Adresse IP du beacon |
note | chaîne | Note de l'opérateur |
Opérateurs¶
Les opérateurs de comparaison disponibles dépendent du type de champ :
| Opérateur | Description |
|---|---|
== | Égalité exacte |
!= | Pas égal |
contains | Correspondance de sous-chaîne |
not_contains | Exclusion de sous-chaîne |
matches | Correspondance d'expression régulière |
in | La valeur est dans une liste fournie |
| Opérateur | Description |
|---|---|
== | Égal |
!= | Pas égal |
> | Plus grand que |
< | Moins de |
>= | Supérieur ou égal |
<= | Inférieur ou égal |
| Opérateur | Description |
|---|---|
== | Égal |
!= | Pas égal |
| Opérateur | Description |
|---|---|
contains | La tranche contient une valeur |
not_contains | La tranche ne contient pas de valeur |
Groupes de conditions¶
Les conditions sont composées en groupes avec des combinateurs booléens :
| Opérateur de groupe | Description |
|---|---|
and | Toutes les conditions et sous-groupes doivent être vrais (par défaut si omis) |
or | Au moins une condition ou un sous-groupe doit être vrai |
not | Annule exactement une condition ou un sous-groupe |
Les groupes peuvent être imbriqués pour créer des expressions booléennes arbitrairement complexes :
{
"operator": "and",
"conditions": [
{ "field": "elevated", "operator": "==", "value": false }
],
"groups": [
{
"operator": "or",
"conditions": [
{ "field": "os", "operator": "contains", "value": "Windows 10" },
{ "field": "os", "operator": "contains", "value": "Windows 11" }
]
}
]
}
Cela donne : le beacon n'est pas élevée ET (le système d'exploitation contient "Windows 10" OU le système d'exploitation contient "Windows 11").
Groupes vides
Un groupe de conditions vide (pas de conditions, pas de sous-groupes) est évalué à true (vérité vide de sens). L'opérateur de groupe par défaut est "and" lorsqu'il est omis.
Référence API¶
Tous les points de terminaison du playbook se trouvent sous le groupe de routes du cockpit et nécessitent une authentification JWT.
Chemin de base : /api/v1/cockpit/playbooks
Créer un manuel de jeu¶
POST /api/v1/cockpit/playbooks
Corps de la demande :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
name | chaîne | Oui | Nom du manuel |
description | chaîne | Non | Description |
steps | Étape du livre de jeu[] | Oui | Tableau d'objets étape |
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Initial Recon",
"description": "Basic host reconnaissance playbook",
"steps": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "whoami /all" },
"order": 1
},
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "ipconfig /all" },
"order": 2
},
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "net localgroup administrators" },
"order": 3
}
]
}'
Réponse (201 créées) :
Liste des manuels de jeu¶
GET /api/v1/cockpit/playbooks
Renvoie tous les playbooks classés par date de création (le plus récent en premier).
Obtenir le manuel de jeu¶
GET /api/v1/cockpit/playbooks/:id
curl -s https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID \
-H "Authorization: Bearer $TOKEN"
Mettre à jour le manuel de jeu¶
PUT /api/v1/cockpit/playbooks/:id
Corps de la demande : Identique à Créer (tous les champs sont obligatoires).
curl -s -X PUT https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Initial Recon v2",
"description": "Updated reconnaissance playbook",
"steps": [...]
}'
Supprimer le livre de jeu¶
DELETE /api/v1/cockpit/playbooks/:id
curl -s -X DELETE https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID \
-H "Authorization: Bearer $TOKEN"
Réponse : 204 No Content
Exécuter le manuel de jeu¶
POST /api/v1/cockpit/playbooks/:id/execute
Exécute le playbook contre un beacon cible. Les étapes conditionnelles sont évaluées à l’aide des métadonnées de beacon en direct du registre de beacons. Toutes les tâches résultantes sont mises en file d'attente et renvoyées.
Corps de la demande :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
beacon_id | chaîne (UUID) | Oui | Beacon cible sur laquelle exécuter |
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/execute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"beacon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}'
Réponse (202 acceptées) :
{
"playbook_id": "c3d4e5f6-a1b2-7890-abcd-ef1234567890",
"beacon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"execution_id": "f7e8d9c0-b1a2-3456-7890-abcdef123456",
"task_ids": [
"11111111-2222-3333-4444-555555555555",
"66666666-7777-8888-9999-aaaaaaaaaaaa",
"bbbbbbbb-cccc-dddd-eeee-ffffffffffff"
],
"status": "queued"
}
Le beacon doit être active
Le beacon cible doit exister dans le registre des beacons actifs (c'est-à-dire que l'implant doit s'être enregistré au moins une fois). L'exécution sur un ID de beacon qui ne figure pas dans le registre renvoie une erreur.
Réponses aux erreurs :
| Statut | Corps | Parce que |
|---|---|---|
| 400 | {"error": "invalid playbook ID"} | UUID malformé |
| 400 | {"error": "invalid beacon_id format"} | UUID de beacon mal formé |
| 500 | {"error": "beacon ... not found in registry"} | Beacon non active |
| 500 | {"error": "get playbook: ..."} | Livre de jeu introuvable |
Liste des exécutions¶
GET /api/v1/cockpit/playbooks/:id/executions
Renvoie les entrées récentes du journal d’exécution pour un playbook. Chaque entrée représente une décision en une étape (action mise en file d'attente ou branchement conditionnel effectué).
Paramètres de requête :
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
limit | entier | 20 | Nombre maximum d'entrées de journal à renvoyer |
curl -s "https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/executions?limit=50" \
-H "Authorization: Bearer $TOKEN"
Obtenir les journaux d'exécution¶
GET /api/v1/cockpit/playbooks/:id/executions/:executionId/logs
Renvoie des journaux de décision détaillés pour une exécution spécifique, classés par ordre d'étape et heure de création.
curl -s "https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/executions/$EXECUTION_ID/logs" \
-H "Authorization: Bearer $TOKEN"
Réponse (200 OK) :
[
{
"id": "aaa11111-bbbb-cccc-dddd-eeeeeeeeeeee",
"playbook_id": "c3d4e5f6-a1b2-7890-abcd-ef1234567890",
"execution_id": "f7e8d9c0-b1a2-3456-7890-abcdef123456",
"beacon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"step_order": 1,
"step_type": "action",
"branch_taken": null,
"condition_values": null,
"condition_result": null,
"task_ids": ["11111111-2222-3333-4444-555555555555"],
"created_at": "2026-02-21T10:01:00Z"
},
{
"id": "bbb22222-cccc-dddd-eeee-ffffffffffff",
"playbook_id": "c3d4e5f6-a1b2-7890-abcd-ef1234567890",
"execution_id": "f7e8d9c0-b1a2-3456-7890-abcdef123456",
"beacon_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"step_order": 2,
"step_type": "conditional",
"branch_taken": "then",
"condition_values": { "elevated": false, "os": "Windows 10 Pro 19045" },
"condition_result": true,
"task_ids": ["66666666-7777-8888-9999-aaaaaaaaaaaa"],
"created_at": "2026-02-21T10:01:01Z"
}
]
Champs du journal d'exécution¶
Chaque entrée du journal d'exécution enregistre le résultat d'une évaluation en une seule étape.
| Champ | Type | Description |
|---|---|---|
id | chaîne (UUID) | ID d'entrée de journal |
playbook_id | chaîne (UUID) | Manuel de jeu pour les parents |
execution_id | chaîne (UUID) | Regroupe toutes les entrées d'une exécution |
beacon_id | chaîne (UUID) | Beacon cible |
step_order | entier | Quelle étape du playbook |
step_type | chaîne | "action" ou "conditional" |
branch_taken | chaîne ou null | Pour les conditions : "then", "else", un nom de branche ou "none" (ignoré) |
condition_values | objet ou nul | Instantané des valeurs des champs de beacon utilisées dans l'évaluation de l'état |
condition_result | booléen ou nul | Si la condition est évaluée comme vraie |
task_ids | chaîne[] | ID de tâche C2 mis en file d'attente à cette étape |
created_at | chaîne (ISO 8601) | Quand cette étape a été évaluée |
Exemples¶
Playbook conditionnel : reconnaissance sensible aux privilèges¶
Ce playbook vérifie si le beacon est surélevée et exécute différentes techniques en conséquence :
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Privilege-Aware Recon",
"description": "Runs elevated or standard recon based on beacon integrity level",
"steps": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "whoami /priv" },
"order": 1
},
{
"type": "conditional",
"order": 2,
"conditional": {
"condition": {
"operator": "and",
"conditions": [
{ "field": "elevated", "operator": "==", "value": true }
]
},
"then": [
{
"type": "action",
"technique_id": "creds.hashdump",
"params": {},
"order": 1
},
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "netstat -ano" },
"order": 2
}
],
"else": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "net user %USERNAME% /domain" },
"order": 1
}
]
}
}
]
}'
Playbook multi-branches : actions spécifiques au système d'exploitation¶
Ce playbook utilise le modèle multi-branches pour exécuter différentes étapes en fonction du système d'exploitation du beacon :
{
"name": "OS-Specific Collection",
"description": "Runs collection techniques based on target OS",
"steps": [
{
"type": "conditional",
"order": 1,
"conditional": {
"condition": { "operator": "and", "conditions": [] },
"then": [],
"branches": [
{
"name": "Windows 10",
"condition": {
"operator": "and",
"conditions": [
{ "field": "os", "operator": "contains", "value": "Windows 10" }
]
},
"steps": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "systeminfo" },
"order": 1
}
]
},
{
"name": "Windows Server",
"condition": {
"operator": "and",
"conditions": [
{ "field": "os", "operator": "contains", "value": "Server" }
]
},
"steps": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "nltest /dclist:" },
"order": 1
}
]
}
],
"else": [
{
"type": "action",
"technique_id": "exec.shell",
"params": { "command": "hostname" },
"order": 1
}
]
}
}
]
}
Ordonnance d'évaluation de la succursale
En mode multi-branches, les branches sont évaluées dans l'ordre du tableau. La première branche dont la condition est évaluée à true est exécutée. Si aucune branche ne correspond, les étapes else s'exécutent (si fournies). S'il n'y a pas de else, l'étape est ignorée et enregistrée avec branch_taken: "none".
Intégration de l'interface utilisateur¶
La page Playbooks (/playbooks) fournit une interface visuelle pour la gestion des playbooks :
- Grille de cartes - Chaque playbook est affiché sous forme de carte indiquant le nom, la description, le nombre d'étapes et un badge « Conditionnel » si une étape utilise des branchements.
- Exécuter - Sélectionnez un beacon active dans la liste déroulante et cliquez sur le bouton de lecture pour l'exécuter.
- Historique – Cliquez sur "Historique" pour afficher les exécutions passées regroupées par ID d'exécution.
- Journaux d'exécution : explorez n'importe quelle exécution pour consulter les journaux de décision étape par étape (quelles branches ont été prises, quelles valeurs de condition ont été observées).
- Créer/Modifier : PlaybookDialog prend en charge l'ajout d'actions et d'étapes conditionnelles avec un éditeur de conditions visuelles.
Références croisées¶
- Référence de l'API REST – Les points de terminaison du Playbook sont répertoriés dans la section Cockpit.
- Commandes Beacon -- Les techniques référencées par
technique_iddans les étapes du playbook - Scripting -- Les scripts CNA peuvent également automatiser les flux de travail des beacons via des hooks d'événements
Exécution par lot¶
Les tâches par lot vous permettent d'exécuter une même définition de tâche sur plusieurs beacons simultanément. Le backend distribue la tâche à chaque beacon cible en parallèle, suit la progression par beacon et agrège les résultats dans un enregistrement de lot unique. C'est le mécanisme principal pour exécuter des commandes de post-exploitation à grande échelle -- par exemple, exécuter whoami /all sur chaque poste de travail Windows d'un groupe, ou déployer un mécanisme de persistance sur tous les beacons correspondant à un filtre de nom d'hôte.
Ciblage des beacons¶
Il existe deux méthodes mutuellement exclusives pour spécifier quels beacons reçoivent la tâche par lot : les identifiants de beacon explicites ou le ciblage par filtre.
Sélection explicite¶
Sélectionnez des beacons spécifiques en passant leurs UUID dans le tableau beacon_ids. Dans l'interface, utilisez shift-clic ou ctrl-clic sur les beacons dans le tableau du cockpit pour en sélectionner plusieurs, puis faites un clic droit et choisissez « Tâche par lot ».
curl -s -X POST https://stentor.app/api/v1/c2/batch-tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"beacon_ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a1-8901-bcde-f12345678901"
],
"task": {
"type": "exec.shell",
"data": {"command": "whoami /all"}
}
}'
Ciblage par filtre¶
Au lieu d'identifiants explicites, passez un objet filter et le backend résout les beacons correspondants depuis le registre actif au moment de l'exécution. Tous les champs de filtre utilisent une correspondance de sous-chaîne insensible à la casse.
| Champ du filtre | Type | Description |
|---|---|---|
os | chaîne | Correspond aux beacons dont le système d'exploitation contient cette sous-chaîne (par ex., "Windows 10") |
hostname | chaîne | Correspond aux beacons dont le nom d'hôte contient cette sous-chaîne (par ex., "WS") |
username | chaîne | Correspond aux beacons dont le nom d'utilisateur contient cette sous-chaîne (par ex., "admin") |
group_id | chaîne (UUID) | Correspond aux beacons appartenant au groupe spécifié |
Ciblage par groupe
Utilisez le filtre group_id pour cibler tous les beacons d'un groupe nommé. Les groupes peuvent être gérés depuis l'interface du cockpit ou via l'API de groupement de beacons. Voir Groupement de beacons pour plus de détails sur la création et la gestion des groupes.
Mutuellement exclusif
beacon_ids et filter ne peuvent pas être utilisés ensemble dans la même requête. Si les deux sont fournis, l'API renvoie une erreur : "beacon_ids and filter are mutually exclusive".
Objet tâche par lot¶
Un enregistrement de tâche par lot suit le travail global et est stocké dans la table batch_tasks.
| Champ | Type | Description |
|---|---|---|
id | chaîne (UUID) | Identifiant unique de la tâche par lot |
task_type | chaîne | Le type de tâche distribué à chaque beacon (par ex., "exec.shell") |
task_data | objet | Les données/paramètres de la tâche passés à chaque beacon |
status | chaîne | Statut global du lot : "pending", "running", ou "completed" |
total_count | entier | Nombre total de beacons ciblés |
completed_count | entier | Nombre de beacons ayant terminé la tâche |
failed_count | entier | Nombre de beacons en échec |
created_by | chaîne (UUID) | Opérateur ayant créé la tâche par lot |
created_at | chaîne (ISO 8601) | Horodatage de création |
completed_at | chaîne (ISO 8601) ou null | Horodatage de fin de tous les éléments (null en cours d'exécution) |
Chaque beacon dans le lot possède un BatchTaskItem correspondant :
| Champ | Type | Description |
|---|---|---|
id | chaîne (UUID) | Identifiant de l'élément |
batch_id | chaîne (UUID) | Tâche par lot parente |
beacon_id | chaîne (UUID) | Beacon cible |
task_id | chaîne (UUID) ou null | L'identifiant de tâche C2 créé pour ce beacon (null si pas encore distribué) |
status | chaîne | Statut par beacon : "pending", "dispatched", "completed", ou "failed" |
output | chaîne ou null | Sortie de la tâche du beacon |
error | chaîne ou null | Message d'erreur si la tâche a échoué |
created_at | chaîne (ISO 8601) | Date de création de l'élément |
completed_at | chaîne (ISO 8601) ou null | Date de fin de l'élément |
Suivi de la progression¶
La progression des tâches par lot est suivie selon un modèle de synchronisation à la lecture. Lorsque vous récupérez une tâche par lot, le backend interroge la table c2_tasks sous-jacente pour tout élément distribué ayant depuis été complété ou échoué, et met à jour les éléments du lot en conséquence avant de renvoyer la réponse.
API : GET /api/v1/c2/batch-tasks/:id renvoie la tâche par lot avec tous ses éléments et statuts synchronisés.
Réponse :
{
"id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"task_type": "exec.shell",
"task_data": {"command": "whoami /all"},
"status": "running",
"total_count": 5,
"completed_count": 3,
"failed_count": 1,
"created_by": "00000000-0000-0000-0000-000000000000",
"created_at": "2026-02-21T10:00:00Z",
"completed_at": null,
"items": [
{
"id": "aaa11111-...",
"beacon_id": "a1b2c3d4-...",
"task_id": "11111111-...",
"status": "completed",
"output": "USER INFORMATION\n----------------\nCORP\\jsmith ...",
"error": null,
"completed_at": "2026-02-21T10:00:05Z"
},
{
"id": "bbb22222-...",
"beacon_id": "b2c3d4e5-...",
"task_id": "22222222-...",
"status": "dispatched",
"output": null,
"error": null,
"completed_at": null
}
]
}
Dans l'interface, le BatchProgressPanel affiche une barre de progression avec des indicateurs de statut par beacon. Chaque élément montre le nom d'hôte du beacon cible, le statut actuel de la tâche, la durée d'exécution et un aperçu de la sortie.
Résultats agrégés¶
Lorsque tous les éléments d'un lot ont été complétés ou ont échoué, le status du lot passe à "completed" et l'horodatage completed_at est défini. Le tableau items dans la réponse contient la sortie complète par beacon.
Chaque élément des résultats inclut :
| Champ | Description |
|---|---|
beacon_id | UUID du beacon cible |
status | "completed" ou "failed" |
output | Sortie complète de la tâche du beacon |
error | Message d'erreur (si échec, ou erreur non fatale accompagnant la sortie) |
completed_at | Date de fin de la tâche de ce beacon |
Dans l'interface, la vue des résultats affiche des panneaux de sortie repliables par beacon. Chaque panneau affiche le nom d'hôte du beacon et la sortie, avec un bouton de copie dans le presse-papiers pour une extraction facile.
Filtrage des résultats
Interrogez le point de terminaison de la tâche par lot et inspectez le champ status de chaque élément pour filtrer par résultat. Par exemple, pour trouver uniquement les beacons en échec, filtrez les éléments où status == "failed" afin d'identifier et résoudre rapidement les problèmes.
Référence API des tâches par lot¶
Tous les points de terminaison des tâches par lot se trouvent sous le groupe de routes C2 et nécessitent une authentification JWT.
Chemin de base : /api/v1/c2/batch-tasks
| Méthode | Point de terminaison | Description |
|---|---|---|
POST | /api/v1/c2/batch-tasks | Créer et exécuter une tâche par lot |
GET | /api/v1/c2/batch-tasks | Lister les tâches par lot récentes (jusqu'à 50) |
GET | /api/v1/c2/batch-tasks/:id | Obtenir une tâche par lot avec ses éléments et statuts synchronisés |
Réponse de création (201 Created) :
{
"id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"total": 5,
"dispatched": 4,
"failed": 1,
"errors": ["beacon b2c3d4e5-...: beacon not active"]
}
Le compteur dispatched indique combien de beacons ont reçu la tâche avec succès. Le compteur failed et le tableau errors signalent les beacons qui n'ont pas pu être atteints (par ex., le beacon n'est plus actif dans le registre).
Playbooks planifiés¶
Les playbooks planifiés automatisent l'exécution des playbooks selon des horaires basés sur le temps ou des déclencheurs basés sur des événements, sans intervention manuelle. Au lieu de cliquer manuellement sur « Exécuter » à chaque fois, un opérateur configure un planning et le système exécute le playbook automatiquement aux heures spécifiées ou lorsque des événements spécifiques se produisent (comme l'enregistrement d'un nouveau beacon).
Types de planification¶
Chaque planification possède un schedule_type qui détermine quand elle se déclenche :
| Type de planification | Description | Paramètres clés |
|---|---|---|
once | Exécuter à un moment futur spécifique | next_run_at (horodatage ISO 8601) |
interval | Exécuter toutes les N secondes | interval_seconds (entier) |
daily | Exécuter quotidiennement à une heure spécifique | cron_expression (par ex., "0 9 * * *" pour 9h quotidiennement) |
weekly | Exécuter hebdomadairement un jour/heure spécifique | cron_expression (par ex., "0 9 * * 1" pour lundi 9h) |
Les planifications Once sont utiles pour les fenêtres de maintenance ou les opérations différées -- par exemple, planifier un playbook de récolte de credentials pour s'exécuter à 2h du matin pendant une fenêtre de changement.
Les planifications Interval s'exécutent de manière répétée à une cadence fixe. Par exemple, interval_seconds: 14400 exécute le playbook toutes les 4 heures.
Les planifications Daily et Weekly utilisent des expressions cron standard pour un contrôle précis du timing.
Exécution basée sur les déclencheurs¶
En plus de la planification basée sur le temps, les playbooks peuvent être déclenchés par des événements en utilisant le champ trigger_type.
on_beacon_initial -- Exécuter automatiquement le playbook lorsqu'un nouveau beacon s'enregistre pour la première fois. C'est extrêmement utile pour les playbooks de reconnaissance initiale qui exécutent des commandes d'énumération standard (whoami, ipconfig, net user, etc.) sur chaque hôte nouvellement compromis sans intervention de l'opérateur.
Lorsque trigger_type est défini sur "on_beacon_initial", la planification se déclenche sur les événements de nouveaux beacons au lieu des déclencheurs basés sur le temps. Les champs schedule_type et next_run_at sont toujours requis mais servent de paramètres de planification de repli.
Limitation de débit
Si de nombreux beacons s'enregistrent simultanément (par exemple, après le déploiement d'un payload sur plusieurs hôtes), chaque beacon obtient sa propre exécution indépendante du playbook. Surveillez les journaux d'exécution pendant les déploiements de masse pour éviter de surcharger le canal C2.
Création de planifications¶
API : POST /api/v1/cockpit/playbooks/:id/schedules
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"schedule_type": "daily",
"cron_expression": "0 9 * * *",
"trigger_type": "scheduled",
"next_run_at": "2026-02-22T09:00:00Z",
"beacon_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"schedule_type": "interval",
"interval_seconds": 14400,
"trigger_type": "scheduled",
"next_run_at": "2026-02-21T14:00:00Z",
"beacon_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"schedule_type": "once",
"trigger_type": "scheduled",
"next_run_at": "2026-02-22T02:00:00Z",
"beacon_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
curl -s -X POST https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"schedule_type": "weekly",
"cron_expression": "0 9 * * 1",
"trigger_type": "scheduled",
"next_run_at": "2026-02-24T09:00:00Z",
"beacon_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
Flux de l'interface : Dans la page Playbooks, cliquez sur le bouton « Planifier » sur n'importe quelle carte de playbook pour ouvrir le PlaybookScheduleDialog. Sélectionnez le type de planification, configurez les paramètres (expression cron, intervalle, heure cible), choisissez les beacons cibles et basculez le commutateur d'activation.
Gestion des planifications¶
Lister les planifications¶
GET /api/v1/cockpit/playbooks/:id/schedules
Renvoie toutes les planifications d'un playbook.
curl -s https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules \
-H "Authorization: Bearer $TOKEN"
Mettre à jour une planification¶
PUT /api/v1/cockpit/playbooks/:id/schedules/:scheduleId
Modifier les paramètres de planification ou activer/désactiver une planification sans la supprimer.
# Désactiver une planification
curl -s -X PUT https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules/$SCHEDULE_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
# Mettre à jour l'expression cron
curl -s -X PUT https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules/$SCHEDULE_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"cron_expression": "0 6 * * *"}'
Supprimer une planification¶
DELETE /api/v1/cockpit/playbooks/:id/schedules/:scheduleId
Supprime définitivement une planification.
curl -s -X DELETE https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/schedules/$SCHEDULE_ID \
-H "Authorization: Bearer $TOKEN"
Réponse : 204 No Content
Journaux d'exécution et badges de déclenchement¶
Les exécutions planifiées apparaissent dans le même journal d'exécution que les exécutions manuelles. Chaque entrée de journal inclut un champ trigger_type indiquant comment l'exécution a été déclenchée :
| Badge de déclenchement | Valeur | Description |
|---|---|---|
| Manuel | "manual" | L'opérateur a cliqué sur Exécuter dans l'interface ou a appelé directement l'API d'exécution |
| Planifié | "scheduled" | L'exécution a été déclenchée par une planification basée sur le temps |
| Beacon initial | "on_beacon_initial" | L'exécution a été déclenchée par l'enregistrement d'un nouveau beacon |
API : GET /api/v1/cockpit/playbooks/:id/executions inclut le champ trigger_type dans chaque entrée d'exécution. Utilisez ce champ pour filtrer les journaux d'exécution par type de déclenchement.
curl -s "https://stentor.app/api/v1/cockpit/playbooks/$PLAYBOOK_ID/executions?limit=50" \
-H "Authorization: Bearer $TOKEN"
Dans l'interface, les entrées de l'historique d'exécution affichent un badge de déclenchement coloré à côté de l'horodatage d'exécution, facilitant la distinction entre les exécutions manuelles et automatisées d'un coup d'oeil.
Objet de planification¶
Une planification de playbook est stockée dans la table c2_playbook_schedules et renvoyée au format JSON depuis tous les points de terminaison de planification.
| Champ | Type | Description |
|---|---|---|
id | chaîne (UUID) | Identifiant unique de la planification |
playbook_id | chaîne (UUID) | Playbook parent |
schedule_type | chaîne | "once", "interval", "daily", ou "weekly" |
cron_expression | chaîne ou null | Expression cron pour les planifications quotidiennes/hebdomadaires |
interval_seconds | entier ou null | Intervalle en secondes pour les planifications par intervalle |
trigger_type | chaîne | "scheduled" (par défaut) ou "on_beacon_initial" |
next_run_at | chaîne (ISO 8601) | Prochaine heure d'exécution planifiée |
last_run_at | chaîne (ISO 8601) ou null | Horodatage de la dernière exécution |
enabled | booléen | Si la planification est active |
beacon_ids | chaîne[] | UUID des beacons cibles pour l'exécution planifiée |
created_at | chaîne (ISO 8601) | Horodatage de création |
updated_at | chaîne (ISO 8601) | Horodatage de la dernière mise à jour |
Référence API des planifications¶
Tous les points de terminaison de planification sont imbriqués sous un playbook et nécessitent une authentification JWT.
Chemin de base : /api/v1/cockpit/playbooks/:id/schedules
| Méthode | Point de terminaison | Description |
|---|---|---|
POST | /api/v1/cockpit/playbooks/:id/schedules | Créer une planification pour un playbook |
GET | /api/v1/cockpit/playbooks/:id/schedules | Lister toutes les planifications d'un playbook |
PUT | /api/v1/cockpit/playbooks/:id/schedules/:scheduleId | Mettre à jour une planification |
DELETE | /api/v1/cockpit/playbooks/:id/schedules/:scheduleId | Supprimer une planification |