Aller au contenu

Guide du langage du sommeil

Introduction au langage de script Sleep utilisé par le moteur CNA de Stentor : types de données, variables, opérateurs, flux de contrôle, fonctions, fermetures, tableaux, hachages et manipulation de chaînes.


Introduction

Le moteur de script de Stentor est construit sur CNA (Cobalt Strike's Aggressor Script), qui utilise le langage de script Sleep comme base. Sleep est un langage de script inspiré de Perl avec une syntaxe de type C, créé à l'origine par Raphael Mudge pour être intégré dans des applications Java.

Stentor implémente un interpréteur de veille complet côté serveur - comprenant un lexeur, un analyseur, un évaluateur et un validateur sémantique - permettant aux opérateurs d'écrire des scripts qui automatisent les opérations, réagissent aux événements de beacon, étendent le jeu de commandes et créent des flux de travail personnalisés. Contrairement à l'interpréteur Java de Cobalt Strike, le moteur CNA de Stentor est écrit en Go, offrant des performances natives et une intégration étroite avec le backend.

Les scripts CNA peuvent :

  • Définissez des commandes de beacon personnalisées avec alias
  • Réagissez aux événements du cycle de vie des beacons avec on
  • Ajoutez des éléments de menu contextuel avec popup
  • Remplacer le comportement par défaut avec les hooks set
  • Enregistrez les commandes de la console avec command
  • Associer les raccourcis clavier avec bind

Sommeil contre CNA

Sleep est le langage de script de base (types de données, variables, opérateurs, flux de contrôle, fonctions). CNA étend Sleep avec des mots-clés spécifiques à C2 (alias, on, popup, set, command, bind) et des fonctions intégrées (bshell, bps, blog, etc.) pour interagir avec les beacons et la plateforme Stentor. Chaque script CNA est un code Sleep valide, mais tous les codes Sleep n'utilisent pas les fonctionnalités spécifiques à CNA.


Bonjour tout le monde

Le script CNA le plus simple imprime un message sur la console de script :

println("Hello from Stentor CNA!");

Chargement de votre script

Dans la console de script, utilisez la commande load :

load /path/to/hello.cna

Le script est analysé, validé et exécuté immédiatement. Vous devriez voir :

Hello from Stentor CNA!

Chargez un script via l'API REST :

curl -s -X POST "https://stentor.app/api/v1/scripts/load" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/path/to/hello.cna"}'

Lorsqu'un script est chargé, le moteur de Stentor effectue trois étapes :

  1. Parse -- Le lexer tokenise la source et l'analyseur crée un AST
  2. Valider – L'analyse sémantique vérifie les fonctions non définies et les problèmes variables
  3. Exécuter – Les exécutions de code de niveau supérieur (comme println) et les mots-clés CNA (on, alias, etc.) enregistrent leurs gestionnaires

Types de données

L'évaluateur de sommeil de Stentor prend en charge les types de données suivants :

Cordes

Chaînes entre guillemets doubles avec prise en charge des séquences d'échappement et interpolation variable :

$greeting = "Hello";
$name = "Operator";
$message = "Welcome, $name!";    # interpolation: "Welcome, Operator!"
$path = "C:\\Users\\Public";      # escape: backslash
$multiline = "Line 1\nLine 2";   # escape: newline

Séquences d'échappement prises en charge : \n (nouvelle ligne), \t (tabulation), \\ (barre oblique inverse), \" (guillemet double), \r (retour chariot).

Interpolation de chaîne : Tout $variable à l'intérieur d'une chaîne entre guillemets est remplacé par sa valeur. Utilisez \$ pour inclure un signe dollar littéral. La variable spéciale $+ concatène les valeurs interpolées adjacentes sans espace :

$first = "Red";
$last = "Team";
println("$first $+ $last");  # prints: RedTeam

Entiers

Entiers signés 64 bits, avec prise en charge des littéraux décimaux et hexadécimaux :

$count = 42;
$hex = 0x1A;
$negative = -10;
$big = 9223372036854775807;  # max int64

Doubles

Nombres à virgule flottante 64 bits :

$pi = 3.14159;
$rate = 0.5;
$sci = 1.5e10;

Longue

Entiers explicites de 64 bits (distincts de int dans le système de types) :

$ts = ticks();  # returns current time in ms as a Long

Nul

La valeur nulle, représentée par $null :

$empty = $null;
if ($empty is $null) {
    println("Value is null");
}

Booléens

Valeurs booléennes issues des opérations de comparaison et de prédicat :

$result = 5 > 3;     # true
$check = "a" eq "b"; # false

Tableaux

Listes ordonnées de valeurs, créées avec la syntaxe @(). Les tableaux sont indexés à zéro et peuvent contenir des types mixtes :

@names = @("Alice", "Bob", "Charlie");
@mixed = @(1, "two", 3.0, $null);
@empty = @();

Hachages

Cartes clé-valeur (dictionnaires), créées avec la syntaxe %(). Les clés sont des chaînes :

%config = %(
    sleep_time => 30,
    jitter     => 20,
    protocol   => "https"
);
%empty = %();

Vérification du type

Utilisez la fonction typeof() pour inspecter le type d'une valeur au moment de l'exécution :

```sleep
println(typeof("hello"));  # "String"
println(typeof(42));       # "Int"
println(typeof(@()));      # "Array"
println(typeof(%()));      # "Hash"
```

Des fonctions de prédicat sont également disponibles : `-isarray`, `-ishash`, `-isfunction`, `-isnumber`.

Variables

Sleep utilise des noms de variables préfixés par un sigil pour indiquer le type :

Variables scalaires

Les variables scalaires commencent par $ et contiennent une seule valeur (chaîne, nombre, valeur nulle, fonction, etc.) :

$name = "operator";
$count = 0;
$callback = { println("fired!"); };

Variables de tableau

Les variables du tableau commencent par @ :

@targets = @("10.0.0.20", "10.10.10.21");
@results = @();

Variables de hachage

Les variables de hachage commencent par % :

%beacon_info = %(os => "Windows 10", arch => "x64");
%tasks = %();

Portée variable

Par défaut, les variables sont globales – accessibles depuis n'importe quelle fonction du script. Utilisez la fonction local() pour déclarer les variables comme locales à la fonction actuelle :

sub processBeacon {
    local('$bid $info');    # $bid and $info are local to this function
    $bid = $1;
    $info = beacon_info($bid);
    # $bid and $info are destroyed when this function returns
}

La fonction global() déclare explicitement les variables dans une portée globale (utile dans les fonctions où vous souhaitez garantir un accès global) :

sub initConfig {
    global('$config');
    $config = %(sleep => 30, jitter => 20);
}

Variables spéciales

Variable Description
$1, $2, ... $n Arguments positionnels des fonctions, alias et gestionnaires d'événements
$0 Pour les alias : la chaîne de commande complète (nom + arguments, non analysés)
@_ Tableau contenant tous les arguments passés à la fonction actuelle
$null La valeur nulle
sub example {
    println("First arg: $1");
    println("Second arg: $2");
    println("All args: " . join(", ", @_));
}

example("hello", "world");
# Output:
# First arg: hello
# Second arg: world
# All args: hello, world

Opérateurs

Opérateurs arithmétiques

Opérateur Description Exemple
+ Ajout $x = 5 + 3;
- Soustraction $x = 10 - 4;
* Multiplication $x = 6 * 7;
/ Division $x = 20 / 4;
% Module $x = 10 % 3;
** Exponentiation $x = 2 ** 10;

Espace requis

Sleep nécessite des espaces entre les opérateurs et les opérandes. $x=1+2; n'analysera pas. Écrivez plutôt $x = 1 + 2;.

Concaténation de chaînes

L'opérateur point (.) concatène les chaînes :

$full = "Hello" . " " . "World";  # "Hello World"

L'opérateur de répétition de chaîne (x) répète une chaîne :

$line = "-" x 40;  # 40 dashes

Comparaison numérique

Opérateur Description Exemple
== Égal if ($x == 5)
!= Pas égal if ($x != 0)
< Moins de if ($x < 10)
> Plus grand que if ($x > 0)
<= Inférieur ou égal if ($x <= 100)
>= Supérieur ou égal if ($x >= 1)

Comparaison de chaînes

Opérateur Description Exemple
eq Chaîne égale if ($s eq "admin")
ne Chaîne différente if ($s ne "")
lt Lexicographiquement moins if ($a lt $b)
gt Lexicographiquement plus grand if ($a gt $b)
cmp Comparer (renvoie -1/0/1) $r = $a cmp $b;

Opérateurs logiques

Opérateur Description Exemple
&& ET logique (court-circuit) if ($a && $b)
\|\| OU logique (court-circuit) si ($a \|\| $b)
! NON logique if (!$found)

Correspondance de motifs

Opérateur Description Exemple
iswm Correspondance générique (* et ?) if ("admin*" iswm $user)
ismatch Correspondance Regex (définit matched()) if ($str ismatch '(\d+)\.(\d+)')
isin Test d'adhésion if ("key" isin %hash)

Opérateurs d'affectation

Opérateur Description
= Mission simple
+= Ajouter et attribuer
-= Soustraire et attribuer
*= Multiplier et attribuer
/= Diviser et attribuer
.= Concaténer et attribuer
&= ET au niveau du bit et attribuer
\|= OU au niveau du bit et attribuer

Opérateurs au niveau du bit

Opérateur Description
& ET au niveau du bit
\| OU au niveau du bit
^ XOR au niveau du bit
<< Décalage à gauche
>> Décalage à droite

Flux de contrôle

si/sinon si/sinon

if ($integrity eq "High") {
    println("Already elevated!");
}
else if ($integrity eq "Medium") {
    println("Need to escalate privileges");
}
else {
    println("Low integrity -- limited options");
}

while Boucles

$i = 0;
while ($i < 10) {
    println("Iteration: $i");
    $i++;
}

pour les boucles

Style C pour les boucles avec les expressions init, condition et update :

for ($i = 0; $i < 10; $i++) {
    println("Count: $i");
}

boucles foreach

Parcourez les tableaux et les hachages :

# Iterate over an array
@targets = @("10.0.0.20", "10.10.10.21", "10.10.10.22");

foreach $target (@targets) {
    println("Scanning: $target");
}

# Iterate with index
foreach $idx => $target (@targets) {
    println("$idx: $target");
}

# Iterate over a hash
%config = %(sleep => 30, jitter => 20, protocol => "https");

foreach $key => $value (%config) {
    println("$key = $value");
}

faire une pause et continuer

foreach $item (@items) {
    if ($item eq "skip") {
        continue;  # skip to next iteration
    }
    if ($item eq "stop") {
        break;     # exit the loop
    }
    println($item);
}

essayer/attraper

Gestion des erreurs avec les blocs try-catch :

try {
    $result = riskyOperation();
}
catch $error {
    println("Error caught: $error");
}

Ternaire avec iff()

La fonction iff() fournit une évaluation ternaire des courts-circuits :

$label = iff($is_admin, "Admin", "User");

Seule la branche correspondante est évaluée, ce qui rend iff() sûr pour les expressions ayant des effets secondaires.


Fonctions

Définir des fonctions

Les fonctions sont déclarées avec le mot-clé sub :

sub greet {
    println("Hello, $1!");
}

greet("Operator");  # prints: Hello, Operator!

Arguments

Les arguments de fonction sont accessibles via les variables de position $1, $2, etc. Le tableau @_ contient tous les arguments :

sub add {
    return $1 + $2;
}

sub printAll {
    foreach $arg (@_) {
        println($arg);
    }
}

$sum = add(3, 7);       # $sum = 10
printAll("a", "b", "c"); # prints a, b, c on separate lines

Valeurs de retour

Utilisez return pour renvoyer une valeur d'une fonction. Si aucun return n'est utilisé, la fonction renvoie le résultat de la dernière expression évaluée :

sub max {
    if ($1 > $2) {
        return $1;
    }
    return $2;
}

println(max(42, 17));  # prints: 42

Fermetures

Une fermeture est une fonction anonyme (bloc de code) qui capture sa portée englobante. Les fermetures sont créées avec la syntaxe { ... } :

$greet = { println("Hello from closure!"); };

Invocation de fermetures -- Utilisez la notation entre parenthèses [$fn] ou [$fn : args] :

$add = { return $1 + $2; };
$result = [$add : 3, 7];
println($result);  # prints: 10

Références des fonctions

L'opérateur & crée une référence à une fonction nommée :

sub myFunction {
    println("Called with: $1");
}

$ref = &myFunction;
[$ref : "test"];  # prints: Called with: test

Lambda

La fonction lambda() crée une copie d'une fonction avec des variables liées :

sub handler {
    println("Beacon $bid says: $1");
}

# Create a handler copy with $bid pre-bound
$bound = lambda(&handler, $bid => "abc123");
[$bound : "hello"];  # prints: Beacon abc123 says: hello

Ce modèle est couramment utilisé avec les rappels CNA et les menus contextuels où vous devez transmettre le contexte à un gestionnaire qui s'exécutera ultérieurement.

Variables locales dans les fonctions

Utilisez local() pour déclarer des variables limitées à la fonction actuelle :

sub processTarget {
    local('$ip $port $result');
    $ip = $1;
    $port = $2;
    $result = scan($ip, $port);
    return $result;
}

Utilisez toujours des produits locaux

Sans local(), les variables à l'intérieur des fonctions s'infiltrent dans la portée globale et persistent après le retour de la fonction. Cela peut provoquer des bugs subtils dans les scripts de longue durée. La meilleure pratique consiste à toujours déclarer les variables locales de fonction avec local().


Tableaux et hachages

Opérations sur les tableaux

Fonction Description Exemple
push(@arr, $val) Ajouter une valeur à la fin push(@targets, "10.10.10.23");
pop(@arr) Supprimer et renvoyer le dernier élément $last = pop(@queue);
shift(@arr) Supprimer et renvoyer le premier élément $first = shift(@queue);
add(@arr, $val, $idx) Insérer à l'index add(@list, "new", 2);
size(@arr) Obtenir la longueur du tableau $len = size(@targets);
copy(@arr) Tableau de copie profonde @clone = copy(@original);
reverse(@arr) Inverser sur place reverse(@items);
sublist(@arr, $start, $end) Extraire le sous-tableau @slice = sublist(@arr, 1, 4);
sorta(@arr) Trier par ordre alphabétique sorta(@names);
sortn(@arr) Trier numériquement sortn(@numbers);
addAll(@dst, @src) Ajouter tout depuis src addAll(@all, @new);
removeAll(@dst, @remove) Supprimer les éléments correspondants removeAll(@list, @exclude);

Itération du tableau :

@beacons = @("beacon-1", "beacon-2", "beacon-3");

foreach $bid (@beacons) {
    println("Processing: $bid");
}

# With index
foreach $i => $bid (@beacons) {
    println("[$i] $bid");
}

Indexation des tableaux :

@items = @("first", "second", "third");
println(@items[0]);   # "first"
println(@items[-1]);  # "third" (negative index)
@items[1] = "SECOND"; # assignment

Opérations de hachage

Fonction Description Exemple
keys(%h) Obtenez un tableau de toutes les clés @k = keys(%config);
values(%h) Obtenir un tableau de toutes les valeurs @v = values(%config);
removeAt(%h, "key") Supprimer la clé et renvoyer la valeur $old = removeAt(%map, "key");
putAll(%dst, %src) Fusionner src dans dst putAll(%config, %overrides);
ohash() Créer un hachage ordonné %oh = ohash();
size(%h) Obtenir le nombre de clés $n = size(%config);

Accès au hachage et affectation :

%info = %(os => "Windows 10", arch => "x64");

# Read
$os = %info["os"];

# Write
%info["pid"] = 1234;

# Check membership
if ("arch" isin %info) {
    println("Architecture: " . %info["arch"]);
}

# Delete
removeAt(%info, "pid");

Itération de hachage :

%targets = %(
    "10.0.0.20" => "DC01",
    "10.10.10.21" => "WEB01",
    "10.10.10.22" => "SQL01"
);

foreach $ip => $hostname (%targets) {
    println("$ip -> $hostname");
}

Opérations sur les chaînes

Concaténation

$full = "Hello" . " " . "World";
$full .= "!";  # compound concatenation
println($full); # "Hello World!"

Fonctions de chaîne communes

Fonction Description Exemple
strlen($s) Longueur de chaîne $len = strlen("hello"); -- 5
substr($s, $start, $end) Sous-chaîne substr("hello", 1, 3) -- "el"
indexOf($s, $sub) Rechercher la position de la sous-chaîne indexOf("hello", "ll") -- 2
replace($s, $pattern, $rep) Remplacer l'expression régulière replace("foo bar", "bar", "baz")
strrep($s, $old, $new) Remplacement de la chaîne littérale strrep("aabaa", "a", "x") -- "xxbxx"
split($delim, $s) Diviser en tableau @parts = split(",", "a,b,c");
join($delim, @arr) Joindre un tableau à une chaîne $s = join(", ", @items);
lc($s) Minuscule lc("HELLO") -- "hello"
uc($s) Majuscules uc("hello") -- "HELLO"
trim($s) Supprimer les espaces trim(" hi ") -- "hi"
left($s, $n) N premiers caractères left("hello", 3) -- "hel"
right($s, $n) N derniers caractères right("hello", 3) -- "llo"
chr($code) Caractère du point de code chr(65) -- "A"
asc($char) Point de code du personnage asc("A") -- 65
charAt($s, $idx) Caractère à l'index charAt("hello", 1) -- "e"
matches($s, $pattern) Groupes de capture Regex @m = matches("v1.2", '(\d+)\.(\d+)');

Correspondance d'expression régulière

Sleep prend en charge la correspondance des expressions régulières avec ismatch et la fonction matched() :

$version = "Stentor v3.2.1";

if ($version ismatch 'v(\d+)\.(\d+)\.(\d+)') {
    ($major, $minor, $patch) = matched();
    println("Major: $major, Minor: $minor, Patch: $patch");
}

La fonction matched() renvoie les groupes de capture de l'opération ismatch la plus récente.

Correspondance de caractères génériques

L'opérateur iswm effectue une correspondance avec des caractères génériques (* pour n'importe quel caractère, ? pour un seul caractère) :

if ("admin*" iswm $username) {
    println("Admin user detected");
}

if ("10.10.10.??" iswm $ip) {
    println("Internal subnet");
}

Formatage de chaîne

La fonction format() fournit un formatage de style printf :

$msg = format("Beacon %s checked in from %s (PID: %d)", $id, $host, $pid);
println($msg);

Spécificateurs de format pris en charge : %s (chaîne), %d (entier), %f (flottant), %x (hex), %o (octal), %% (pourcentage littéral).


Conversion de types

Fonction Description Exemple
casti($val) Convertir en entier $n = casti("42");
castd($val) Convertir en double $d = castd("3.14");
castl($val) Convertir en long $l = castl($n);
typeof($val) Obtenir la chaîne du nom du type typeof(42) -- "Int"
expr($val) Contraindre au numérique $n = expr("100");

Chargement de script et console de script

La console de scripts

La console de script est un environnement interactif permettant de charger, de gérer et de déboguer les scripts CNA. Il fournit un REPL (Read-Eval-Print Loop) pour tester les expressions de sommeil et exécuter des instructions.

Commandes de la console

Commande Arguments Description
? expression Évaluer un prédicat Sleep et imprimer vrai/faux
e statement Exécuter une instruction Sleep et afficher la sortie
help Répertorier toutes les commandes de console disponibles
load /path/to/script.cna Charger et exécuter un script CNA
ls Liste tous les scripts actuellement chargés
reload script.cna Décharger, réanalyser et réexécuter un script
unload script.cna Décharger un script et supprimer ses inscriptions
x expression Évaluer une expression de sommeil et imprimer sa valeur
proff script.cna Désactiver le profileur pour un script
profile script.cna Vider les statistiques de performances d'un script
pron script.cna Activer le profileur pour un script
troff script.cna Désactiver le suivi des fonctions pour un script
tron script.cna Activer le suivi des fonctions pour un script

Exemples :

x 2 + 2
# 4

x @("a", "b", "c")
# @("a", "b", "c")

e println("Hello World!");
# Hello World!

? "admin*" iswm "administrator"
# true

load /opt/scripts/my-automation.cna
# Script loaded successfully

ls
# /opt/scripts/my-automation.cna (loaded)

Chargement de scripts via API

curl -s -X POST "https://stentor.app/api/v1/scripts/load" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/opt/scripts/automation.cna"}'
curl -s "https://stentor.app/api/v1/scripts" \
  -H "Authorization: Bearer $TOKEN"
curl -s -X POST "https://stentor.app/api/v1/scripts/unload" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"path": "/opt/scripts/automation.cna"}'

Cycle de vie des scripts

Lorsqu'un script CNA est chargé, il suit un cycle de vie précis :

Load -> Parse -> Semantic Validate -> Execute -> Register Keywords
  1. Charger - Le fichier est lu à partir du disque (ou reçu via l'API)
  2. Parse -- Le lexer tokenise la source et l'analyseur construit un AST (Abstract Syntax Tree)
  3. Valider -- L'analyse sémantique vérifie les appels de fonctions non définis et les problèmes de variables ; les erreurs bloquent le chargement, les avertissements sont enregistrés
  4. Exécuter -- Le code de niveau supérieur s'exécute immédiatement (affectations de variables, println, etc.)
  5. S'inscrire -- Les mots-clés CNA (on, alias, popup, set, command, bind) enregistrent leurs gestionnaires dans le registre du moteur.

Décharger nettoie

Lorsqu'un script est déchargé (via la commande unload ou l'API), tous ses gestionnaires enregistrés sont supprimés du registre. Cela inclut les alias, les événements, les hooks, les générateurs de fenêtres contextuelles, les commandes et les raccourcis clavier. Les enregistrements des autres scripts ne sont pas affectés.

Chargement et surveillance automatiques

Stentor prend en charge le chargement automatique de scripts à partir d'un répertoire :

  • Chargement automatique : tous les fichiers *.cna du répertoire de scripts configuré sont chargés au démarrage
  • Mode veille : le moteur interroge le répertoire toutes les 2 secondes, chargeant automatiquement les nouveaux scripts, rechargeant les scripts modifiés et déchargeant les scripts supprimés.

Mots-clés de l’AIIC

CNA étend Sleep avec six mots-clés pour interagir avec la plateforme Stentor. Chaque mot-clé enregistre un gestionnaire qui se déclenche dans des conditions spécifiques.

on -- Gestionnaires d'événements

Enregistrez un gestionnaire qui se déclenche lorsqu'un événement spécifique se produit :

on beacon_initial {
    println("New beacon: $1");
}

Le mot-clé on prend en charge un méta-événement générique * qui se déclenche pour TOUS les événements :

on * {
    $event = shift(@_);
    println("Event fired: $event");
}

Plusieurs scripts peuvent enregistrer des gestionnaires pour le même événement : tous les gestionnaires se déclenchent.

alias – Commandes de beacon

Enregistrez une commande personnalisée disponible dans la console de beacon :

alias survey {
    btask($1, "Running survey", "T1082");
    bshell!($1, "whoami /all");
    bshell!($1, "ipconfig /all");
    bshell!($1, "netstat -na");
}

Arguments d'alias :

Variable Description
$0 Chaîne de commande complète (nom + arguments, non analysée)
$1 ID de beacon
$2, $3, ... Arguments individuels (séparés par des espaces, groupe de guillemets)

Ajoutez des éléments aux menus contextuels dans l'interface utilisateur :

popup beacon_top {
    item "Quick Survey" {
        bshell($1, "whoami /groups");
    }

    menu "Network" {
        item "ARP Table" {
            bshell($1, "arp -a");
        }
        item "Connections" {
            bshell($1, "netstat -na");
        }
    }

    separator();

    item "Hash Dump" {
        bhashdump($1);
    }
}

Hooks contextuels disponibles : beacon_top, beacon, beacon_bottom, ssh et hooks personnalisés.

set -- Remplacements de crochet

Remplacez le comportement par défaut de Stentor en définissant des valeurs de hook :

set PROCESS_INJECT_SPAWN {
    return "C:\\Windows\\System32\\RuntimeBroker.exe";
}

Les hooks utilisent la sémantique du dernier écrivain-gagnant : la valeur set du script chargé le plus récemment prend effet.

command -- Commandes de la console

Commandes d'enregistrement disponibles dans la console de script :

command hello {
    println("Hello, $1!");
}

Après le chargement, tapez hello Operator dans la console de script pour voir Hello, Operator!.

bind -- Raccourcis clavier

Enregistrez les gestionnaires pour les raccourcis clavier :

bind Ctrl+H {
    println("Help shortcut triggered!");
}

Les raccourcis prennent en charge les modificateurs : Ctrl, Shift, Alt, Meta.

Référence croisée

Pour la liste complète des événements, des hooks et des hooks contextuels, consultez la référence Hooks & Events. Pour toutes les fonctions CNA intégrées (fonctions b*, requêtes de modèle de données, assistants d'interface utilisateur), consultez la [Référence des fonctions] (function-reference.md).


Exemples pratiques

Configurer automatiquement de nouvelles beacons

Définissez le temps de veille et la gigue lorsqu'un beacon s'enregistre pour la première fois :

on beacon_initial {
    # Set 30-second sleep with 20% jitter
    bsleep($1, 30, 20);
    blog($1, "Auto-configured: 30s sleep, 20% jitter");
}

Alias de liste de processus personnalisés

Créez un alias qui répertorie les processus et enregistre l'action :

alias myps {
    btask($1, "Listing processes", "T1057");
    bps($1);
    blog($1, "Process listing requested by operator");
}

Ajoutez un menu contextuel avec les commandes fréquemment utilisées :

popup beacon_top {
    item "Quick Hash Dump" {
        foreach $bid ($1) {
            bhashdump($bid);
        }
    }

    menu "Enumeration" {
        item "Who Am I" {
            foreach $bid ($1) {
                bshell($bid, "whoami /all");
            }
        }
        item "Network Info" {
            foreach $bid ($1) {
                bshell($bid, "ipconfig /all && arp -a");
            }
        }
    }
}

Journalisation des événements

Enregistrez la sortie du beacon sur la console avec des horodatages :

on beacon_output {
    local('$bid $text $time');
    $bid = $1;
    $text = $2;
    $time = formatDate(ticks(), "HH:mm:ss");
    println("[$time] Beacon $bid output: $text");
}

Script de traitement des données

Parcourez toutes les beacons et créez un résumé :

command beacon_report {
    local('$entry $id $user $host');

    println("=== Beacon Report ===");
    foreach $entry (beacons()) {
        $id   = $entry["id"];
        $user = $entry["user"];
        $host = $entry["computer"];
        println(format("  %-8s %-15s %s", substr($id, 0, 8), $user, $host));
    }
    println("Total: " . size(beacons()) . " beacons");
}

Fermetures avec rappels

Utilisez des fermetures pour les opérations asynchrones :

alias checkports {
    local('$bid $target');
    $bid = $1;
    $target = $2;

    btask($bid, "Scanning $target for common ports");

    # Use a callback to process results
    bportscan($bid, $target, "1-1024", "arp", lambda({
        blog($bid, "Scan complete for $target");
    }, $bid => $bid, $target => $target));
}

Configuration avec des hachages

Créez des configurations réutilisables :

%profiles = %(
    "stealth" => %(sleep => 300, jitter => 50),
    "normal"  => %(sleep => 30,  jitter => 20),
    "fast"    => %(sleep => 5,   jitter => 10)
);

alias profile {
    local('$bid $name $config');
    $bid = $1;
    $name = $2;

    if ($name !isin %profiles) {
        berror($bid, "Unknown profile: $name. Options: stealth, normal, fast");
        return;
    }

    $config = %profiles[$name];
    bsleep($bid, $config["sleep"], $config["jitter"]);
    blog($bid, "Applied profile: $name (sleep=" . $config["sleep"] . "s, jitter=" . $config["jitter"] . "%)");
}

Commentaires

Les commentaires commencent par # et s'étendent jusqu'à la fin de la ligne :

# This is a comment
$x = 42;  # inline comment

Il n’y a pas de commentaires sur plusieurs lignes dans Sleep.


Prochaines étapes

  • Référence de fonction -- Référence complète pour toutes les ~300+ fonctions intégrées de CNA
  • Hooks & Events -- Scripts basés sur les événements avec plus de 24 hooks et plus de 57 événements
  • Mode sans tête -- Exécutez des scripts CNA sans l'interface utilisateur