Aller au contenu

Gestion des relais

Les relais constituent le pont entre le serveur Stentor et les environnements cibles. Ils fonctionnent sur une infrastructure d'attaque (generalement Kali Linux) et hebergent des listeners C2, generent des payloads, traitent les enregistrements d'implants et acheminent le trafic via WebSocket vers le serveur. Ce guide couvre la creation, le deploiement, l'enregistrement, la surveillance et le depannage des agents de relais.


Architecture

Le relais se situe entre le backend Stentor et le reseau cible. Il maintient une connexion WebSocket persistante au serveur pour l'envoi des commandes et execute les listeners C2 locaux auxquels les implants se connectent.

graph LR
    subgraph Backend
        A[Stentor Server<br/>:8082]
    end

    subgraph Relay["Relay (Kali)"]
        B[WebSocket Client]
        C[C2 Listener<br/>HTTP/HTTPS]
        D[DNS C2 Server]
        E[SMB Pipe Server]
        F[Payload Generator]
    end

    subgraph Target["Target Network"]
        G[Implant<br/>Windows]
    end

    A <-->|WebSocket| B
    G -->|HTTPS| C
    G -->|DNS queries| D
    G -->|SMB named pipe| E
    C -->|Events| B
    B -->|Commands| C

Flux de trafic :

  1. L'operateur emet une commande via l'interface utilisateur ou l'API de Stentor.
  2. Le backend envoie une commande WebSocket au relais (par exemple, queue_task, start_listener, generate_payload).
  3. Le relais execute la commande localement : demarre un listener, met en file d'attente une tache pour un beacon ou genere un binaire de payload.
  4. Lorsqu'un implant s'enregistre aupres d'un listener heberge par un relais, le relais renvoie un evenement WebSocket au backend (par exemple, beacon_new, beacon_checkin, task_complete).
  5. Le backend met a jour sa base de donnees et envoie des mises a jour en temps reel a l'interface utilisateur de l'operateur via le CockpitHub WebSocket.

Construire le relais

Construisez le binaire de relais a partir de la racine du projet. Le relais est un seul binaire Go statique sans dependances externes.

# From the Stentor project root
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build \
  -ldflags="-s -w -X main.Version=1.1.0" \
  -o binaries/stentor-relay \
  ./relay/cmd/relay/

Drapeaux de build expliques :

Drapeau Objectif
GOOS=linux Compilation croisee pour Linux (le relais fonctionne sur Kali)
GOARCH=amd64 Cibler l'architecture x86 64 bits
CGO_ENABLED=0 Binaire statique sans dependances de la bibliotheque C
-s -w Supprimer les informations de debogage et les symboles DWARF (binaire plus petit)
-X main.Version=1.1.0 Integrer la chaine de version pour l'identification

Identification des versions

Le relais enregistre sa version au demarrage. Utilisez des chaines de version significatives pour suivre quelle version est deployee : Stentor Kali Relay Agent starting... Version: 1.1.0


Deploiement

Transfert vers l'hote relais

Le binaire de relais est generalement deploye via SCP via un hote de saut (Proxmox) vers la VM de relais Kali :

# Step 1: Dev machine -> Proxmox host
scp -o StrictHostKeyChecking=no binaries/stentor-relay \
  root@<proxmox-host>:/tmp/stentor-relay

# Step 2: Proxmox host -> Kali relay
ssh root@<proxmox-host> 'scp -o StrictHostKeyChecking=no \
  /tmp/stentor-relay root@<kali-ip>:/opt/stentor-relay/stentor-relay'

Exemple de deploiement en laboratoire

# Using the default lab infrastructure
scp binaries/stentor-relay root@<proxmox-ip>:/tmp/stentor-relay
ssh root@<proxmox-ip> 'sshpass -p "<relay-password>" scp -o StrictHostKeyChecking=no \
  /tmp/stentor-relay [email protected]:/opt/stentor-relay/stentor-relay'

Configuration

Le relais est entierement configure via des variables d'environnement, generalement stockees dans /opt/stentor-relay/.env. Le relais les lit au demarrage via la directive systemd EnvironmentFile.

Variables obligatoires

Variable Description Exemple
BACKEND_URL Point de terminaison WebSocket du serveur wss://stentor.app/ws/relay
RELAY_ID UUID du relais (doit correspondre a l'enregistrement de la base de donnees) aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
RELAY_SECRET Secret partage pour l'authentification WebSocket <random-string>

Variables du listener C2

Variable Par defaut Description
C2_PORT 443 Port pour le listener HTTPS C2 par defaut
C2_CERT_PATH -- Certificat TLS pour les listeners HTTPS
C2_KEY_PATH -- Cle privee TLS pour les listeners HTTPS
C2_STAGING_AUTH_TOKEN -- Jeton de porteur pour le point de terminaison intermediaire (vide = non authentifie)

Variables de service facultatives

Variable Par defaut Description
LOG_LEVEL info Verbosite de la journalisation : debug, info, warn, error
RECONNECT_INTERVAL_SECONDS 5 Delai entre les tentatives de reconnexion WebSocket
HEARTBEAT_INTERVAL_SECONDS 30 Frequence de battement de coeur vers le backend
SMTP_PORT 25 Port du serveur SMTP integre (0 = desactive)
SMTP_EXTERNAL_HOST -- Relais SMTP externe pour le transfert
SMTP_EXTERNAL_PORT 587 Port relais SMTP externe
TRACKING_PORT 80 Port du serveur de suivi HTTP (0 = desactive)
DNS_PORT 0 Port du serveur DNS C2 (0 = desactive)
DNS_DOMAIN -- Suffixe de domaine pour les requetes DNS C2 (obligatoire si DNS_PORT > 0)
DNS_TTL 60 TTL pour les reponses DNS C2 en secondes
PROFILER_PORT 0 Port du serveur HTTP du profileur systeme (0 = desactive)
HEALTH_PORT 9090 Port du point de terminaison de sante HTTP (0 = desactive)
STATE_DIR /var/lib/stentor-relay Repertoire de persistance de l'etat des listeners

Exigence DNS C2

Si DNS_PORT est defini sur une valeur non nulle, DNS_DOMAIN doit egalement etre configure. Le relais ne demarrera pas si le port DNS est active sans domaine.

Exemple de fichier .env :

# Required
BACKEND_URL=wss://stentor.app/ws/relay
RELAY_ID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
RELAY_SECRET=your-shared-secret-here

# C2 listener
C2_PORT=8443
C2_CERT_PATH=/opt/stentor-relay/certs/cert.pem
C2_KEY_PATH=/opt/stentor-relay/certs/key.pem

# Optional services
LOG_LEVEL=info
SMTP_PORT=0
TRACKING_PORT=0
DNS_PORT=0
PROFILER_PORT=0

Service systeme

Creez un service systemd pour un demarrage automatique et un redemarrage en cas d'echec.

Fichier de service (/etc/systemd/system/stentor-relay.service) :

[Unit]
Description=Stentor Relay Agent
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/stentor-relay
ExecStart=/opt/stentor-relay/stentor-relay
EnvironmentFile=/opt/stentor-relay/.env
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Commandes de gestion des services :

# Install and enable the service
sudo systemctl daemon-reload
sudo systemctl enable stentor-relay

# Start the relay
sudo systemctl start stentor-relay

# Check status
sudo systemctl status stentor-relay

# View logs (real-time)
journalctl -u stentor-relay -f

# View recent logs
journalctl -u stentor-relay --since "1 hour ago"

# Restart after configuration change
sudo systemctl restart stentor-relay

Verification du demarrage

En cas de demarrage reussi, le relais enregistre sa configuration et son etat de connexion :

Stentor Kali Relay Agent starting...
  Version: 1.1.0
  Relay ID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
  Backend URL: wss://stentor.app/ws/relay
  C2 Port: 8443
C2 server listening on :8443
Relay Agent ready, awaiting commands from Backend


Inscription

Avant qu'un relais puisse se connecter via WebSocket, il doit exister dans la base de donnees Stentor. Le gestionnaire WebSocket de /ws/relay appelle relayRepo.GetByID() et renvoie un 404 Not Found si l'UUID du relais ne se trouve pas dans la table relays.

Critique : inscrivez-vous avant de vous connecter

Le relais doit etre enregistre dans la base de donnees avant de demarrer le service de relais. Si le relais est introuvable dans la base de donnees, la negociation WebSocket echouera avec une erreur 404 et le relais ne parviendra pas a se connecter a plusieurs reprises.

Via API

Creez un enregistrement de relais via l'API REST. La reponse inclut le id genere - utilisez-le comme RELAY_ID dans le fichier .env du relais.

curl -s -X POST https://stentor.app/api/v1/relays \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Kali Relay",
    "description": "Primary attack relay",
    "ip_address": "10.0.0.50"
  }' | jq
{
  "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "name": "Kali Relay",
  "description": "Primary attack relay",
  "ip_address": "10.0.0.50",
  "status": "offline",
  "created_at": "2025-01-15T12:00:00Z"
}

Via SQL Direct

Utile apres une reconstruction de base de donnees (par exemple, deploiement --rebuild-db) lorsque vous devez reenregistrer un relais avec son UUID existant :

INSERT INTO relays (id, name, description, ip_address, status)
VALUES (
  'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
  'Kali Relay',
  'Primary attack relay',
  '10.0.0.50',
  'online'
)
ON CONFLICT (id) DO NOTHING;

Executer via Docker :

ssh root@<proxmox> 'ssh [email protected] \
  "docker exec -i stentor-db psql -U stentor -d stentor_db"' <<< \
  "INSERT INTO relays (id, name, description, ip_address, status) \
   VALUES ('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', 'Kali Relay', \
   'Primary attack relay', '10.0.0.50', 'online') \
   ON CONFLICT (id) DO NOTHING;"

Apres inscription, redemarrez le service relais pour qu'il etablisse la connexion WebSocket :

sudo systemctl restart stentor-relay

Protocole WebSocket

Le relais communique avec le backend via une connexion WebSocket persistante a /ws/relay. L'authentification utilise des en-tetes personnalises.

Authentification

Le relais s'authentifie a l'aide de deux en-tetes sur la demande de mise a niveau WebSocket :

En-tete Description
X-Relay-ID Relais UUID correspondant a un enregistrement de base de donnees
X-Relay-Secret Secret partage correspondant au RELAY_SECRET du serveur

Les deux en-tetes peuvent egalement etre transmis en tant que parametres de requete (relay_id et secret).

Alternative mTLS

Les relais peuvent egalement s'authentifier a l'aide de certificats clients TLS (mTLS) au lieu des en-tetes de secret partage. Voir Authentification mTLS pour les instructions de configuration.

Types de messages

Tous les messages suivent un format d'enveloppe JSON avec les champs type, id, correlation_id, timestamp et payload.

Type Direction Description
command Serveur -> Relais Initie des actions (demarrer le listener, mettre en file d'attente, generer le payload)
response Relais -> Serveur Renvoie les resultats de la commande avec l'ID de correlation
event Relais -> Serveur Notifications asynchrones (beacon nouvelle, tache terminee, statut d'ecoute)
heartbeat Bidirectionnel Surveillance de l'etat de la connexion
ack Relais -> Serveur Accuse de reception de la commande avant la fin de l'execution

Commandes cles (Serveur -> Relais)

Commande Description
start_listener Demarrer un listener C2 sur le relais
stop_listener Arreter un listener en cours d'execution
queue_task Mettre une tache en file d'attente pour la livrer a un beacon
generate_payload Construire un binaire d'implant (EXE, DLL, shellcode, etc.)
send_email Envoyer un email de phishing via le serveur SMTP du relais
tunnel Transferer les trames du tunnel SOCKS vers un beacon
host_file Heberger un fichier sur un URI personnalise sur un listener
unhost_file Supprimer un fichier heberge d'un listener

Evenements cles (Relais -> Serveur)

Evenement Description
beacon_new Nouvel implant enregistre
beacon_checkin Beacon existante enregistree
beacon_dead Beacon a manque les enregistrements et a ete marque comme mort
task_complete Execution de la tache terminee avec des resultats
listener_started Le listener a demarre avec succes
listener_stopped Le listener s'est arrete
listener_error Le listener a rencontre une erreur

Reference complete du protocole

Pour une documentation complete sur le format de message, les definitions de champs et les diagrammes de sequence, voir Protocole WebSocket.


Surveillance de la sante

Statut du relais via API

Verifiez l'etat de tous les relais enregistres :

curl -s https://stentor.app/api/v1/relays \
  -H "Authorization: Bearer $TOKEN" | jq '.[] | {id, name, status}'

Le champ status reflete l'etat de la connexion WebSocket :

Statut Signification
connected Le relais a une connexion WebSocket active
disconnected Le relais etait precedemment connecte mais la connexion a ete interrompue
offline Le relais ne s'est jamais connecte (fraichement inscrit)

Point de terminaison /healthz

Chaque relais expose un point de terminaison HTTP de sante sur un port dedie (par defaut : 9090, configurable via HEALTH_PORT). Ce point de terminaison fonctionne sur un port separe des listeners C2 pour eviter les conflits et est accessible sans authentification.

curl http://<relay-ip>:9090/healthz | jq

Champs de la reponse :

{
  "status": "healthy",
  "uptime": 86400,
  "active_listeners": 2,
  "connected_beacons": 5,
  "last_checkin": "2025-06-15T14:30:00Z",
  "version": "1.1.0",
  "timestamp": "2025-06-15T14:31:00Z"
}
Champ Type Description
status string Toujours "healthy" lorsque le point de terminaison repond
uptime int64 Secondes depuis le demarrage du processus du relais
active_listeners int Nombre de listeners C2 actuellement en cours d'execution
connected_beacons int Nombre de beacons enregistres sur ce relais
last_checkin string Horodatage RFC 3339 du dernier enregistrement de beacon (omis si aucun beacon ne s'est enregistre)
version string Chaine de version du binaire du relais
timestamp string Heure UTC actuelle au format RFC 3339

Desactiver le point de terminaison de sante

Definissez HEALTH_PORT=0 dans le fichier .env du relais pour desactiver entierement le point de terminaison /healthz. Cela peut etre utile sur les relais ou l'exposition reseau doit etre minimisee.

Interrogation de sante cote serveur

Le serveur Stentor interroge automatiquement le point de terminaison /healthz de tous les relais connectes pour maintenir les donnees de sante a jour.

Comportement de l'interrogation :

  • Le serveur interroge toutes les 30 secondes tous les relais avec le statut connected et une adresse IP non vide.
  • Utilise une requete HTTP GET standard (pas le canal WebSocket) pour plus de simplicite et d'isolation.
  • Chaque requete d'interrogation a un delai d'attente de 5 secondes pour eviter de bloquer sur des relais qui ne repondent pas.
  • Les donnees de sante sont persistees dans la base de donnees a l'aide de champs nullables -- les valeurs nil indiquent que le relais n'a pas encore ete interroge.

Aucune configuration requise

L'interrogation de sante cote serveur demarre automatiquement au lancement du backend. Il n'y a pas de variables d'environnement cote serveur a configurer -- le serveur interroge toujours sur le port 9090.

Indicateur de sante dans l'interface

Le selecteur de relais dans l'interface utilisateur Stentor affiche un indicateur de sante a cote du nom de chaque relais :

Indicateur Condition Signification
🟢 Vert Donnees de sante recues dans les 60 secondes Le relais est en bonne sante et reactif
🟡 Jaune Donnees de sante recues dans les 5 minutes Le relais peut rencontrer des problemes
⚪ Gris Donnees de sante datant de plus de 5 minutes ou pas encore recues Le relais est obsolete ou n'a jamais signale sa sante

L'interface affiche egalement le nombre de beacons et de listeners a cote du nom du relais lorsque les donnees de sante sont disponibles.

Battement de coeur WebSocket

Le relais envoie des messages de battement de coeur periodiques au backend a l'intervalle configure (par defaut : toutes les 30 secondes). La payload du battement de coeur comprend l'ID du relais et l'etat actuel (idle, busy ou error).

Si le backend ne recoit pas de battement de coeur dans l'intervalle prevu, il marque le relais comme potentiellement deconnecte. Le RelayHub met automatiquement a jour l'etat du relais dans la base de donnees lorsque les connexions sont etablies ou perdues.

Battement de coeur vs. interrogation de sante

Le battement de coeur WebSocket surveille la connexion entre le relais et le serveur (le WebSocket est-il actif ?). L'interrogation /healthz surveille l'etat operationnel du relais (combien de listeners, beacons, temps de fonctionnement). Les deux sont des mecanismes independants.

Surveillance des journaux

Surveillez l'etat du relais en temps reel via le journal systemd :

# Real-time log stream
journalctl -u stentor-relay -f

# Filter for errors only
journalctl -u stentor-relay -p err

# Logs from the last boot
journalctl -u stentor-relay -b

Messages cles du journal a surveiller :

Message du journal Signification
Relay Agent ready, awaiting commands Demarrage reussi
C2 server listening on :8443 Listener C2 actif
WebSocket client error La connexion au backend a echoue
Received signal SIGTERM, shutting down Arret progressif initie
Command X completed successfully Tache executee
Command X failed Erreur d'execution de la tache

Failover des beacons et resilience des relais

Stentor prend en charge le failover multi-relais pour la resilience des beacons. Si un relais tombe en panne, les beacons peuvent automatiquement basculer vers un relais de secours et recuperer de maniere transparente le relais principal lorsqu'il revient.

Liste d'URL de failover

Les beacons peuvent etre construits avec plusieurs URL C2. La premiere URL est toujours le relais principal ; les URL supplementaires sont des relais de secours. Les deploiements a URL unique ne sont pas affectes -- le failover est retrocompatible.

Configurez les URL de failover au moment du build via une variable d'environnement ou un drapeau de l'editeur de liens :

# Environment variable (comma-separated)
IMPLANT_C2_URLS=https://relay1.example.com:8443,https://relay2.example.com:8443

# Or via Go ldflags at build time
-ldflags="-X main.DefaultC2URLs=https://relay1.example.com:8443,https://relay2.example.com:8443"

Comportement du failover

Lorsqu'un beacon rencontre des echecs consecutifs de communication avec son relais actuel, il bascule automatiquement vers l'URL suivante dans la liste :

Parametre Par defaut Description
MaxFailures 3 Nombre d'echecs consecutifs avant de basculer vers le relais suivant

Apres 3 echecs consecutifs sur l'hote actuel, le beacon passe a l'URL suivante dans la liste de failover. Si la derniere URL echoue egalement, il revient a la premiere.

graph LR
    B[Beacon] -->|3 echecs| R1[Relay 1<br/>Principal]
    R1 -.->|en panne| X1[X]
    B -->|bascule| R2[Relay 2<br/>Secours]
    R2 -->|fonctionne| OK[Continuer]

Recuperation avec preference pour le principal

Lorsqu'un beacon opere sur un relais de secours, il sonde periodiquement le relais principal pour verifier s'il a recupere. Cela garantit que les beacons reviennent toujours au relais prefere lorsque possible.

Parametre Par defaut Description
PrimaryCheckInterval 5 Requetes reussies sur un secours avant de sonder le principal
PrimaryCheckTimeout 5s Delai d'attente pour la sonde de recuperation du principal

Flux de recuperation :

  1. Le beacon fonctionne sur un relais de secours apres un failover.
  2. Apres 5 requetes reussies sur le secours, le beacon envoie une sonde legere (enregistrement minimal) vers l'URL du principal.
  3. Si le principal repond dans les 5 secondes, le beacon bascule de maniere transparente vers le principal.
  4. Si le principal est toujours en panne, le beacon reste sur le secours et reessaie apres 5 autres requetes reussies.

Transparent pour les operateurs

Le failover et la recuperation se produisent automatiquement sans intervention de l'operateur. Le beacon continue d'executer les taches normalement tout au long du processus. Les operateurs peuvent surveiller a quel relais un beacon est connecte via l'interface Cockpit.

Persistance de l'etat des listeners

Les relais persistent toutes les configurations de listeners actifs sur le disque afin de pouvoir les restaurer automatiquement apres un redemarrage ou un crash -- aucune intervention de l'operateur n'est requise.

Fonctionnement :

  • L'etat des listeners est sauvegarde dans un fichier JSON dans le repertoire STATE_DIR (par defaut : /var/lib/stentor-relay).
  • Utilise des ecritures atomiques (ecriture dans un fichier temporaire, puis renommage) pour la securite en cas de crash -- les ecritures partielles ne corrompent jamais le fichier d'etat.
  • L'etat est sauvegarde apres chaque demarrage ou arret de listener, avec un delai de 200 ms pour confirmer le succes de l'operation.
  • Au redemarrage, le relais lit le fichier d'etat et redemarre automatiquement tous les listeners precedemment actifs.

Configuration :

# Relay state persistence directory (in .env)
STATE_DIR=/var/lib/stentor-relay

Permissions du repertoire

Le repertoire STATE_DIR doit etre accessible en ecriture par le processus du relais. En execution en tant que root (typique pour les deploiements Kali), le repertoire par defaut /var/lib/stentor-relay fonctionne immediatement. Pour les deploiements non-root, assurez-vous que le repertoire existe et a les permissions appropriees.

Exemple de configuration de failover

Un deploiement de failover complet avec deux relais et la persistance de l'etat des listeners :

# Beacon build with failover URLs (primary + backup)
IMPLANT_C2_URLS=https://relay1.example.com:8443,https://relay2.example.com:8443

# Relay 1 .env (primary)
BACKEND_URL=wss://stentor.app/ws/relay
RELAY_ID=aaaaaaaa-1111-1111-1111-111111111111
RELAY_SECRET=shared-secret
STATE_DIR=/var/lib/stentor-relay
HEALTH_PORT=9090

# Relay 2 .env (backup)
BACKEND_URL=wss://stentor.app/ws/relay
RELAY_ID=bbbbbbbb-2222-2222-2222-222222222222
RELAY_SECRET=shared-secret
STATE_DIR=/var/lib/stentor-relay
HEALTH_PORT=9090

Authentification mTLS

mTLS (mutual TLS) ajoute une authentification par certificat client par relais a la connexion WebSocket. Il fournit une verification d'identite cryptographique comme alternative aux en-tetes de secret partage.

Apercu

mTLS est additif -- lorsqu'il est configure, le relais presente un certificat client lors de la negociation TLS. Le serveur verifie le certificat par rapport a une autorite de certification (CA) de confiance et extrait l'UUID du relais a partir du Common Name (CN) du certificat. L'authentification par secret partage reste disponible en tant que solution de repli pour la retrocompatibilite.

flowchart TD
    R[Le relais se connecte au serveur] --> TLS{Certificat client<br/>present ?}
    TLS -->|Oui| V[Verifier le certificat par rapport au pool CA]
    V --> CN[Extraire l'UUID du relais du CN]
    CN --> AUTH1[Authentifie via mTLS]
    TLS -->|Non| REQ{RELAY_MTLS_REQUIRED<br/>= true ?}
    REQ -->|Oui| REJECT[Rejeter la connexion]
    REQ -->|Non| SECRET[Verifier l'en-tete X-Relay-Secret]
    SECRET --> AUTH2[Authentifie via secret partage]

Generation de certificats

Generez une CA et un certificat client de relais a l'aide d'OpenSSL. Le CN du certificat doit suivre le format relay-{uuid} -- le serveur extrait l'UUID du relais a partir de ce champ.

# 1. Generate CA key and certificate (one-time setup)
openssl genrsa -out ca.key 4096
openssl req -new -x509 -key ca.key -out ca.crt -days 3650 \
  -subj "/CN=Stentor Relay CA"

# 2. Generate relay client key and CSR
#    CN must be "relay-{uuid}" matching the relay's RELAY_ID
openssl genrsa -out relay.key 4096
openssl req -new -key relay.key -out relay.csr \
  -subj "/CN=relay-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"

# 3. Sign the relay certificate with the CA
openssl x509 -req -in relay.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out relay.crt -days 365

Le format du CN est critique

Le Common Name doit etre exactement relay-{uuid} (par exemple, relay-25b2e4d7-8b7f-411f-8fe0-7faed06b90d1). Le serveur analyse ce champ pour extraire l'UUID du relais pour la verification d'identite. Un format de CN incorrect entrainera un echec d'authentification avec ErrInvalidCertCN.

Configuration du serveur

Configurez le serveur Stentor pour accepter les connexions mTLS des relais :

Variable Par defaut Description
RELAY_CA_CERT_PATH -- Chemin vers le fichier PEM du certificat CA pour verifier les certificats clients des relais
RELAY_MTLS_REQUIRED false Defini a true pour rejeter l'authentification par secret partage et imposer mTLS uniquement

Le serveur charge le pool de certificats CA au demarrage et l'utilise pour verifier la chaine de certificats presentee par les relais qui se connectent. Il verifie egalement la validite temporelle du certificat (non expire, pas encore valide) et verifie l'utilisation de cle etendue ExtKeyUsageClientAuth.

Configuration du relais

Configurez le relais pour presenter un certificat client lors de la connexion au serveur :

Variable Par defaut Description
MTLS_CERT_PATH -- Chemin vers le certificat client PEM du relais
MTLS_KEY_PATH -- Chemin vers la cle privee PEM du client relais
MTLS_CA_CERT_PATH -- Certificat CA optionnel pour verifier le certificat du serveur

Certificat et cle requis ensemble

MTLS_CERT_PATH et MTLS_KEY_PATH doivent tous deux etre definis. Definir un seul d'entre eux provoquera une erreur de validation de configuration au demarrage du relais.

Lorsque MTLS_CA_CERT_PATH n'est pas defini cote relais, il utilise le magasin de certificats du systeme (ou InsecureSkipVerify dans les environnements de developpement avec des certificats de serveur auto-signes).

Exemple de fichier .env du relais avec mTLS :

# Required
BACKEND_URL=wss://stentor.app/ws/relay
RELAY_ID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
RELAY_SECRET=your-shared-secret-here

# mTLS authentication
MTLS_CERT_PATH=/opt/stentor-relay/certs/relay.crt
MTLS_KEY_PATH=/opt/stentor-relay/certs/relay.key
MTLS_CA_CERT_PATH=/opt/stentor-relay/certs/ca.crt

Migration du secret partage vers mTLS

Suivez ces etapes pour migrer un relais existant de l'authentification par secret partage vers mTLS sans temps d'arret :

Etape 1 : Generer les certificats

# Generate CA and relay certificates (see Certificate Generation above)
openssl genrsa -out ca.key 4096
openssl req -new -x509 -key ca.key -out ca.crt -days 3650 \
  -subj "/CN=Stentor Relay CA"
openssl genrsa -out relay.key 4096
openssl req -new -key relay.key -out relay.csr \
  -subj "/CN=relay-<your-relay-uuid>"
openssl x509 -req -in relay.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out relay.crt -days 365

Etape 2 : Deployer le certificat CA sur le serveur

Copiez ca.crt sur le serveur et definissez RELAY_CA_CERT_PATH dans le .env du serveur. Redemarrez le backend.

Etape 3 : Deployer le certificat/cle client sur le relais

Copiez relay.crt et relay.key sur l'hote du relais (par exemple, /opt/stentor-relay/certs/). Definissez MTLS_CERT_PATH et MTLS_KEY_PATH dans le .env du relais.

Etape 4 : Redemarrer le relais

sudo systemctl restart stentor-relay

Le relais s'authentifiera desormais via mTLS. Le secret partage (en-tete X-Relay-Secret) reste comme solution de repli.

Etape 5 : Verifier le fonctionnement de mTLS

Consultez les journaux du serveur pour les messages d'authentification mTLS :

# On the server
docker logs stentor-c2-backend 2>&1 | grep -i "mtls\|certificate"

Etape 6 : Imposer mTLS uniquement (optionnel)

Une fois que tous les relais utilisent mTLS, desactivez optionnellement l'authentification par secret partage :

# In the server's .env
RELAY_MTLS_REQUIRED=true

Redemarrez le backend. Les relais sans certificats clients valides ne pourront plus se connecter.

Testez avant d'imposer

Ne definissez RELAY_MTLS_REQUIRED=true qu'apres avoir confirme que tous les relais s'authentifient avec succes via mTLS. Definir ce drapeau avec des relais mal configures les verrouillera.


Depannage

Symptome Cause Corriger
404 sur connexion WebSocket L'UUID du relais n'est pas dans la base de donnees Enregistrez le relais via API ou SQL direct avant de demarrer le service. Verifiez que RELAY_ID dans .env correspond a l'enregistrement de la base de donnees.
401 Non autorise Mauvais RELAY_SECRET Verifiez que le RELAY_SECRET du relais correspond a la configuration .env du serveur. Les deux parties doivent utiliser le meme secret partage.
La connexion est interrompue a plusieurs reprises Instabilite du reseau ou debordement de tampon WebSocket Verifiez les journaux de relais avec journalctl -u stentor-relay -f. Verifiez la connectivite reseau au backend. Le relais se reconnecte automatiquement toutes les 5 secondes par defaut.
Le listener ne demarre pas Port deja utilise ou autorisation refusee Recherchez les processus conflictuels : ss -tlnp \| grep <port>. Utilisez sudo ou ports >= 1024 pour eviter les problemes d'autorisation.
La generation du payload echoue Chaine d'outils Go ou espace disque manquant Verifiez que Go est installe sur le relais : go version. Verifiez le disque disponible : df -h /opt/stentor-relay.
Le beacon n'apparait pas Le listener n'a pas demarre ou ACL reseau Verifiez l'etat du listener via l'API : GET /api/v1/listeners. Verifiez les regles de pare-feu entre l'IP cible et l'IP relais.
"echec de la validation de la configuration" Variables d'environnement manquantes ou invalides Verifiez le journal du relais pour l'erreur de validation specifique. Assurez-vous que toutes les variables requises (BACKEND_URL, RELAY_ID) sont definies. Verifiez que BACKEND_URL commence par ws:// ou wss://.
DNS C2 ne repond pas Conflit de port DNS ou domaine manquant Assurez-vous que DNS_DOMAIN est defini lorsque DNS_PORT > 0. Verifiez les conflits de ports : ss -ulnp \| grep <port-dns>.
L'envoi SMTP echoue Relais externe mal configure Verifiez SMTP_EXTERNAL_HOST, SMTP_EXTERNAL_PORT et les informations d'identification. Testez la connectivite SMTP a partir de l'hote relais.

Commandes de diagnostic

# Check relay process
systemctl status stentor-relay

# View relay configuration (from logs)
journalctl -u stentor-relay | head -20

# Test WebSocket connectivity
curl -s -o /dev/null -w "%{http_code}" \
  -H "X-Relay-ID: <relay-id>" \
  -H "X-Relay-Secret: <secret>" \
  https://stentor.app/ws/relay

# Check listening ports on relay
ss -tlnp | grep stentor

# Verify network path to target
ping -c 3 <target-ip>

# Check TLS certificate expiry
openssl x509 -in /opt/stentor-relay/certs/cert.pem -noout -dates

Architecture multi-relais

Stentor prend en charge plusieurs connexions de relais simultanees pour les operations segmentees ou distribuees.

graph TB
    subgraph Backend
        S[Stentor Server]
    end

    subgraph External["External Network"]
        R1[Relay 1<br/>Internet-facing<br/>HTTPS listener]
    end

    subgraph Internal["Internal Network"]
        R2[Relay 2<br/>Post-pivot<br/>SMB listener]
    end

    subgraph DMZ
        R3[Relay 3<br/>DNS C2 listener]
    end

    S <-->|WebSocket| R1
    S <-->|WebSocket| R2
    S <-->|WebSocket| R3

    I1[Implant A] -->|HTTPS| R1
    I2[Implant B] -->|SMB pipe| R2
    I3[Implant C] -->|DNS| R3

Concepts cles

  • Chaque listener est lie a un relais specifique via le champ relay_id dans la configuration du listener. Lorsque vous creez un listener, vous specifiez quel relais doit l'heberger.

  • Les beacons s'enregistrent aupres du relais qui heberge leur listener. Un beacon deployee via un listener HTTPS sur le relais 1 communiquera toujours via le relais 1.

  • Les tunnels SOCKS peuvent utiliser n'importe quel relais connecte pour l'acheminement du trafic. Le RelayHub selectionne le premier relais disponible pour la livraison des trames du tunnel.

  • Plusieurs relais peuvent desservir differents segments du reseau. Utilisez des relais distincts pour :

    • Infrastructure accessible sur Internet (listeners HTTPS)
    • Pivots de reseau interne (listeners de canal SMB)
    • Canaux secrets (DNS C2)
    • Repartition geographique dans differentes regions

Modeles de deploiement

Modele Relais Cas d'utilisation
Relais simple 1 Tests en laboratoire, engagements simples
Double relais 2 Acces reseau externe + interne
Multi-segments 3+ Environnements complexes avec segmentation du reseau
Geographique 2+ Cibles distribuees sur plusieurs sites

Affectation des listeners

Lors de la creation d'un listener, precisez le relais cible :

# Create HTTPS listener on Relay 1
curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "External HTTPS",
    "type": "https",
    "host": "10.0.0.50",
    "port": 8443,
    "relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
  }'

# Create SMB listener on Relay 2 (internal)
curl -s -X POST https://stentor.app/api/v1/listeners \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Internal SMB",
    "type": "smb_pipe",
    "host": "192.168.1.50",
    "port": 445,
    "relay_id": "<internal-relay-uuid>"
  }'

Mettre a jour un relais

Pour deployer une nouvelle version du binaire du relais :

  1. Construisez le nouveau binaire sur votre machine de developpement.
  2. Transfert via le modele d'hote de saut decrit ci-dessus.
  3. Redemarrez le service :
# On the relay host (or via SSH)
sudo systemctl restart stentor-relay

# Verify the new version
journalctl -u stentor-relay | grep "Version:"

Le relais retablit automatiquement sa connexion WebSocket au redemarrage. Les listeners actifs sont automatiquement restaures a partir de l'etat persiste (voir Persistance de l'etat des listeners) -- aucun redemarrage manuel n'est requis.

Recuperation automatique des listeners

Les listeners sont desormais persistes sur le disque et auto-restaures au redemarrage du relais. Vous n'avez plus besoin de redemarrer manuellement les listeners apres la mise a jour du binaire du relais.