Transport WireGuard¶
WireGuard fournit un tunnel UDP crypté entre l'implant et le relais. À l'intérieur du tunnel, le protocole HTTP C2 standard fonctionne inchangé, vous offrant une connectivité complète au niveau du réseau avec un cryptage puissant et des capacités de traversée NAT.
graph LR
subgraph Target Network
Implant["Implant<br/>10.99.0.2"]
end
subgraph Relay
WG["WireGuard Endpoint<br/>UDP :51820"]
HTTP["HTTP C2 Server<br/>10.99.0.1:8080"]
end
Implant <-->|"WireGuard UDP Tunnel<br/>(Curve25519 + ChaCha20-Poly1305)"| WG
WG --- HTTP Cas d'utilisation :
- Accès complet au niveau du réseau via un tunnel crypté
- Traversée NAT où les transports basés sur TCP ne sont pas fiables
- Connectivité IP directe sans surcharge HTTP sur le fil
- Environnements dans lesquels le trafic UDP se mélange mieux que HTTP/HTTPS
Gestion des clés¶
WireGuard utilise les paires de clés Curve25519. Le relais et l'implant ont besoin de leur propre paire de clés. L'API fournit un point de terminaison pratique pour générer les deux paires à la fois.
Générer des paires de clés¶
curl -s -X POST https://stentor.app/api/v1/listeners/wireguard/keygen \
-H "Authorization: Bearer $TOKEN"
Réponse :
{
"relay_private_key": "WEhNb3V...<base64>...",
"relay_public_key": "aGVsbG8...<base64>...",
"implant_private_key": "c2VjcmV...<base64>...",
"implant_public_key": "d29ybGQ...<base64>..."
}
Toutes les clés sont des clés Curve25519 codées en base64 (32 octets décodés).
Stockage des clés
Les clés privées sont des informations d'identification sensibles. Stockez-les avec le même soin que les mots de passe ou les clés privées TLS. Le relay_private_key est stocké dans la configuration du listener. Le implant_private_key est intégré à la construction de l'implant.
Création d'un listener WireGuard¶
# 1. Generate keys
KEYS=$(curl -s -X POST https://stentor.app/api/v1/listeners/wireguard/keygen \
-H "Authorization: Bearer $TOKEN")
RELAY_PRIV=$(echo "$KEYS" | jq -r '.relay_private_key')
IMPLANT_PUB=$(echo "$KEYS" | jq -r '.implant_public_key')
# 2. Create listener
LISTENER_ID=$(curl -s -X POST https://stentor.app/api/v1/listeners \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"WireGuard Tunnel\",
\"type\": \"wireguard\",
\"relay_id\": \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\",
\"port\": 51820,
\"wg_private_key\": \"$RELAY_PRIV\",
\"wg_peer_public_key\": \"$IMPLANT_PUB\",
\"wg_tunnel_ip\": \"10.99.0.1/24\",
\"wg_peer_tunnel_ip\": \"10.99.0.2/32\",
\"wg_c2_port\": 8080
}" | jq -r '.id')
# 3. Start listener
curl -s -X POST "https://stentor.app/api/v1/listeners/$LISTENER_ID/start" \
-H "Authorization: Bearer $TOKEN"
Options de configuration¶
| Champ | Type | Par défaut | Description |
|---|---|---|---|
name | chaîne | requis | Nom d'affichage pour le listener |
type | chaîne | "wireguard" | Doit être wireguard pour les listeners WireGuard |
relay_id | UUID | requis | Relais qui hébergera le point de terminaison WireGuard |
port | int | requis | Port UDP externe pour tunnel WireGuard |
wg_private_key | chaîne | requis | Clé privée Curve25519 de Relay (base64) |
wg_public_key | chaîne | dérivé | Clé publique du relais (auto-dérivée de la clé privée, pour affichage uniquement) |
wg_peer_public_key | chaîne | requis | Clé publique Curve25519 de l'implant (base64) |
wg_tunnel_ip | chaîne | "10.99.0.1/24" | Adresse IP du relais dans le tunnel WireGuard |
wg_peer_tunnel_ip | chaîne | "10.99.0.2/32" | Adresse IP de l'implant à l'intérieur du tunnel WireGuard |
wg_c2_port | int | 8080 | Port HTTP à l'intérieur du tunnel où le gestionnaire C2 écoute |
guardrails | JSON | null | Règles de filtrage des beacons. null signifie accepter toutes les beacons. |
Traversée NAT¶
WireGuard utilise la perforation UDP pour la traversée NAT, ce qui le rend efficace dans la plupart des configurations réseau :
- PersistentKeepalive : l'implant envoie des paquets keepalive toutes les 25 secondes (par défaut) pour maintenir les mappages NAT.
- Types de NAT pris en charge : le cône complet, le cône restreint et le NAT restreint au port fonctionnent tous de manière fiable.
- NAT symétrique : peut nécessiter un point de terminaison de relais routable publiquement.
Considérations MTU
La MTU par défaut est de 1 420 octets. Sur les réseaux double NAT ou les réseaux avec encapsulation supplémentaire (PPPoE, VPN-in-VPN), vous devrez peut-être réduire davantage la MTU (par exemple, 1 280) pour éviter la fragmentation.
Comment ça marche¶
- Le relais écoute sur un port UDP public (par exemple, 51820)
- L'implant initie la poignée de main WireGuard vers le point final du relais
- Après la poignée de main, les deux parties disposent d'un tunnel avec des adresses IP attribuées (10.99.0.1 et 10.99.0.2).
- Les paquets Keepalive maintiennent le mappage NAT en vie
- Le trafic HTTP C2 circule à l'intérieur du tunnel, invisible à l'inspection du réseau
Configuration de la connectivité directe¶
Configuration étape par étape pour un tunnel WireGuard direct implant-relais :
Étape 1 : Générer des paires de clés¶
Utilisez le point de terminaison de l'API keygen pour créer des paires de clés Curve25519 pour le relais et l'implant :
curl -s -X POST https://stentor.app/api/v1/listeners/wireguard/keygen \
-H "Authorization: Bearer $TOKEN" | jq .
Enregistrez les quatre clés. La clé privée du relais va dans la configuration du listener. La clé privée de l'implant et la clé publique du relais vont dans la configuration de construction de l'implant.
Étape 2 : créer et démarrer le listener¶
Créez le listener WireGuard sur le relais avec la clé privée du relais et la clé publique de l'implant :
curl -s -X POST https://stentor.app/api/v1/listeners \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "WireGuard Tunnel",
"type": "wireguard",
"relay_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"port": 51820,
"wg_private_key": "<relay_private_key>",
"wg_peer_public_key": "<implant_public_key>"
}'
Étape 3 : Configurer l'implant¶
L'implant nécessite la configuration WireGuard suivante :
| Champ de configuration de l'implant | Valeur | Source |
|---|---|---|
PrivateKey | <implant_private_key> | De la réponse keygen |
PeerPublicKey | <relay_public_key> | De la réponse keygen |
Endpoint | <relay-ip>:51820 | IP publique et port UDP du relais |
TunnelIP | 10.99.0.2/32 | Doit correspondre à wg_peer_tunnel_ip |
RelayTunnelIP | 10.99.0.1 | Doit correspondre à wg_tunnel_ip |
C2Port | 8080 | Doit correspondre à wg_c2_port |
Étape 4 : L'implant se connecte¶
Lorsque l’implant démarre, il lance la poignée de main WireGuard. Une fois le tunnel établi, l'implant envoie des requêtes HTTP C2 standard (enregistrement, obtention de la tâche, soumission du résultat) à http://10.99.0.1:8080 à l'intérieur du tunnel.
Comparaison des architectures¶
graph TB
subgraph HTTPS Transport
I1["Implant"] -->|"HTTPS (TLS)"| R1["Relay :8443"]
R1 --> H1["C2 Handler"]
end
subgraph WireGuard Transport
I2["Implant"] -->|"UDP (WireGuard)"| R2["Relay :51820"]
R2 --> T["Tunnel Network<br/>10.99.0.0/24"]
T --> H2["C2 Handler<br/>10.99.0.1:8080"]
end | Aspect | HTTPS | WireGuard |
|---|---|---|
| Protocole | TCP + TLS | UDP + ChaCha20-Poly1305 |
| Calque | Demande (HTTP) | Réseau (tunnel IP) |
| Traversée NAT | TCP (fiable) | Perforation UDP (keepalive nécessaire) |
| Signature du trafic | Cela ressemble à du trafic Web HTTPS | Cela ressemble au trafic VPN WireGuard |
| Latence | Supérieur (prise de contact TCP + TLS) | Inférieur (UDP, pas de prise de contact par requête) |
| Frais généraux | En-têtes de trame TLS | Surcharge du paquet WireGuard (32 octets) |
Configuration des implants¶
Le client WireGuard de l'implant utilise la structure de configuration suivante :
| Champ | Type | Par défaut | Description |
|---|---|---|---|
PrivateKey | chaîne | requis | Clé privée Curve25519 de l'implant (base64) |
PeerPublicKey | chaîne | requis | Clé publique Curve25519 de Relay (base64) |
Endpoint | chaîne | requis | Point de terminaison WireGuard UDP du relais (host:port) |
TunnelIP | chaîne | "10.99.0.2/32" | IP attribuée à l'implant à l'intérieur du tunnel |
RelayTunnelIP | chaîne | "10.99.0.1" | IP du relais à l'intérieur du tunnel |
C2Port | int | 8080 | Port HTTP à l'intérieur du tunnel pour le trafic C2 |
PersistentKeepalive | int | 25 | Intervalle Keepalive en secondes pour la traversée NAT |
MTU | int | 1420 | MTU du tunnel en octets |
Implémentation de l'espace utilisateur¶
L'implant utilise netstack (pile TCP/IP de l'espace utilisateur) pour faire fonctionner le tunnel WireGuard sans nécessiter de pilotes de noyau ou de privilèges d'administrateur :
- Aucun module WireGuard du noyau n'est nécessaire
- Aucune installation d'adaptateur TUN/TAP requise
- Fonctionne dans l'espace utilisateur -- pas de privilèges spéciaux pour le tunnel lui-même
- Le client HTTP à l'intérieur du tunnel réutilise les points de terminaison C2 standards (
/api/v1/c2/beacon,/api/v1/c2/beacon/{id}/task,/api/v1/c2/beacon/{id}/result)
Le côté relais utilise également netstack, créant un listener TCP à l'intérieur du tunnel qui sert le même multiplexeur HTTP C2 utilisé par les listeners HTTP/HTTPS.
Dépannage¶
| Symptôme | Parce que | Corriger |
|---|---|---|
| Aucun tunnel établi | Port UDP bloqué sur le pare-feu relais | Ouvrez le port UDP configuré (par exemple, 51820) sur le pare-feu du relais |
| Tunnel vers le haut mais pas de C2 | Incompatibilité des ports C2 | Vérifiez que wg_c2_port sur le listener correspond à C2Port sur l'implant |
| Tunnel vers le haut mais pas de C2 | Incompatibilité IP du tunnel | Vérifiez la correspondance wg_tunnel_ip et wg_peer_tunnel_ip entre le listener et l'implant. |
| Gouttes intermittentes | Le mappage NAT expire | Augmentez l'intervalle PersistentKeepalive (25 s par défaut devraient fonctionner pour la plupart des NAT) |
| Fragmentation / perte de paquets | MTU trop grand | Réduisez le MTU de 1 420 à 1 280 sur les réseaux double NAT ou encapsulés |
| Erreur "décoder la clé privée" | Clé base64 mal formée | Régénérez les clés à l'aide de l'API keygen. Les clés doivent faire exactement 32 octets une fois décodées. |
| Inadéquation des clés | Mauvais appairage des clés | Assurez-vous que le relais utilise relay_private_key et que l'implant utilise implant_private_key. Les clés homologues doivent correspondre. |