Protéger NetScaler contre le Password Spraying
TL;DR — Les attaques de type password spraying sur Citrix NetScaler/Gateway se sont massivement intensifiées depuis fin 2023. Cet article retrace leur évolution par paliers, détaille les impacts terrain constatés chez mes clients, et fournit un guide opérationnel complet avec des configurations NetScaler concrètes pour s’en protéger efficacement — y compris la méthode avancée de détection par rotation de noms d’utilisateur issue du Tech Paper Citrix.
📋 Sommaire
Introduction
Le password spraying se distingue du brute force classique par son approche : au lieu de tester des milliers de mots de passe sur un seul compte, l’attaquant teste un petit nombre de mots de passe courants (Password123, Summer2024, Azerty123…) sur un grand nombre de comptes, depuis un grand nombre d’adresses IP. Objectif : passer sous le radar des mécanismes de verrouillage de compte.
Citrix NetScaler Gateway est une cible de choix. Il expose un portail d’authentification directement sur Internet, souvent lié à l’Active Directory de l’entreprise. Un attaquant n’a besoin que de l’URL du portail et d’une liste d’adresses e-mail — souvent récupérées via LinkedIn ou des fuites de données — pour lancer une campagne.
Un rappel théorique sur la sécurité des mots de passe. C’est un retour d’expérience terrain : ce que j’ai vu chez mes clients, comment les attaques ont évolué, et surtout les configurations concrètes que j’ai mises en place pour y répondre.
1. L’évolution des attaques : une sophistication par paliers
Depuis fin 2023, les attaques n’ont pas simplement augmenté en volume — elles ont évolué par étapes successives, contournant méthodiquement chaque défense classique mise en place.
Quelques centaines d’IPs, user-agents génériques (python-requests, curl), rythme espacé mais persistant. Les défenses classiques suffisent : rate limiting par IP, blacklists, filtrage géographique.
Passage à plusieurs milliers d’IPs. Les user-agents imitent des navigateurs légitimes. Le rythme est calibré : 3 à 5 tentatives par IP et par heure. Les blacklists deviennent inefficaces — une IP bloquée est remplacée en quelques heures.
Le filtrage géographique tombe : les attaques transitent par des residential proxies (IPs d’abonnés FAI dans le pays cible). Impossible de bloquer un pays sans bloquer des utilisateurs légitimes.
J’ai mis en place le blocage des requêtes ne correspondant pas au FQDN. Les drops ont duré 30 min. Le temps d’aller chercher un café et à mon retour, les tentatives avaient repris. Pause café maximum : 30 min sans regarder ses logs.
L’attaque devient auto-adaptative : le rythme s’ajuste dynamiquement selon les réponses du serveur, ralentissant juste assez pour contourner les rate limiters dynamiques.
Ce dernier palier rend toute défense purement réactive insuffisante. La seule signature stable, quelle que soit la sophistication de l’attaque, c’est la rotation des noms d’utilisateur depuis une même IP — c’est ce que la section 4 exploite directement.
2. Conséquences concrètes
Verrouillages AD massifs
Des comptes légitimes se retrouvent verrouillés simultanément. Les utilisateurs ne peuvent plus travailler, le helpdesk est sursollicité.
Déni de service backends
Les services MFA sont dimensionnés pour l’usage normal, pas pour des milliers de requêtes sur des comptes inexistants. Les sessions restent ouvertes jusqu’au timeout. Tout le monde trinque en backend, alors que NetScaler ne souffre d’aucune surcharge.
Saturation logs & SOC
Des milliers de lignes de logs par minute. Les alertes SIEM explosent. Les analystes passent leur temps à trier du bruit au lieu de détecter les vraies menaces.
Compromission de comptes
Les comptes avec des mots de passe faibles — et il y en a encore, malheureusement — sont compromis. L’attaquant obtient un accès VDI ou aux applications publiées via le Gateway.
Dégradation performances
La surcharge des requêtes d’authentification dégrade les performances du vserver Gateway. Les utilisateurs légitimes subissent des lenteurs ou des timeouts.
3. Bonnes pratiques de configuration
Paramètres AAA
La première ligne de défense : configurer correctement les paramètres d’authentification globaux. La première commande est critique — elle oblige l’attaquant à rentrer dans la boucle de changement de login ou à attendre la fin de la période de quarantaine. La valeur doit être définie selon vos prérequis : le login X ne sera plus traité par NetScaler pendant 300s après 3 mauvaises tentatives.
# Limiter les tentatives de login et définir le timeout de verrouillage
set aaa parameter -maxLoginAttempts 3 -failedLoginTimeout 300 -defaultAuthType LDAP
Authentification multifacteur (MFA)
Le MFA est la mesure la plus efficace contre le password spraying. Même si un mot de passe est compromis, l’attaquant est bloqué au second facteur. C’est un must-have sur toute plateforme exposée sur Internet — sans discussion.
Monitoring et logs
Sans visibilité, pas de détection. Configurez vos logs pour capturer toutes les tentatives d’authentification et les expédier vers votre SIEM :
# Activer le logging détaillé des authentifications
set audit syslogParams -logLevel ALL
set audit nslogParams -logLevel ALL
# Envoyer les logs vers un SIEM externe
add audit syslogAction siem_action X.X.X.X -logLevel ALL -logFacility LOCAL0 -transport TCP
add audit syslogPolicy siem_policy "ns_true" siem_action
bind system global siem_policy -priority 100
NetScaler Console (ADM / MAS)
ADM, MAS, Citrix NetScaler Insight, NetScaler Console… autant de noms que d’IPs de bot, merci Citrix. Peu importe comment vous l’appelez — branchez-le. Il vous donne des logs spécifiques, des exports faciles et toutes les capacités de gestion de vos NetScalers.
4. Mise en œuvre avancée : détection par rotation de username
La méthode décrite ici est plus sophistiquée que le simple rate limiting par IP. Elle est issue du Tech Paper Citrix dédié à la détection du password spraying sur NetScaler Gateway — c’est celle que j’applique chez mes clients.
💡 Principe de détection
- Même identifiant depuis une IP
- Rotation de username nulle ou très faible
- Comportement prévisible
- Différents usernames depuis la même IP
- Rotation élevée et systématique
- Pattern reconnaissable
La solution repose sur deux variables NS stockées en mémoire :
| Variable | Rôle | Expiration |
|---|---|---|
Likelihood_Bad_IP_Counter |
Compteur de rotations de username par IP | 24h |
Username_And_IP_Uniqueness_Checker |
Dernier username tenté par chaque IP | 24h |
Quand une IP dépasse 9 changements de username, elle est automatiquement bloquée par une policy Responder, avant même d’atteindre le backend LDAP. Les comptes AD ne sont donc jamais sollicités pour les IPs malveillantes — éliminant le risque de verrouillage de masse et la saturation des backends d’authentification.
Variables NS
# Compteur de rotation de username par IP (expire après 24h)
add ns variable Likelihood_Bad_IP_Counter \
-type "map(text(128), ulong, 10000)" -expires 86400
# Stockage du dernier username par IP
add ns variable Username_And_IP_Uniqueness_Checker \
-type "map(text(128),text(256),10000)" -expires 86400
Assignments
# Incrémenter le compteur quand le username change
add ns assignment Likelihood_Bad_IP_Counter_Incrementer \
-variable "$Likelihood_Bad_IP_Counter[CLIENT.IP.SRC]" -add 1
# Stocker le username courant
add ns assignment Username_And_IP_Uniqueness_Checker_Addition \
-variable "$Username_And_IP_Uniqueness_Checker[CLIENT.IP.SRC.TYPECAST_TEXT_T]" \
-set "HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T('=','&').VALUE(\"login\")"
Stratégies d’authentification
# Détecter le changement de username et incrémenter le compteur
add authentication Policy Increase_Likelihood_Bad_IP_Pol \
-rule "($Username_And_IP_Uniqueness_Checker[client.ip.src.typecast_text_t] != \
HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T('=','&').VALUE(\"login\")) && \
HTTP.REQ.BODY(1000).TYPECAST_NVLIST_T('=','&').VALUE(\"login\") != \"\"" \
-action Likelihood_Bad_IP_Counter_Incrementer
bind authentication vserver Gateway_Auth_vServer \
-policy Increase_Likelihood_Bad_IP_Pol \
-priority 110 -gotoPriorityExpression NEXT
# Mettre à jour le username stocké
add authentication Policy Username_And_IP_Uniqueness_Checker_Pol \
-rule true \
-action Username_And_IP_Uniqueness_Checker_Addition
bind authentication vserver Gateway_Auth_vServer \
-policy Username_And_IP_Uniqueness_Checker_Pol \
-priority 120 -gotoPriorityExpression END
Whitelist et stratégie de blocage
# Dataset pour les IPs toujours autorisées (proxies internes, NAT)
add policy dataset Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection ipv4
# Bloquer les IPs qui dépassent le seuil (9 changements de username)
add responder policy Drop_Pol \
"($Likelihood_Bad_IP_Counter[CLIENT.IP.SRC.typecast_text_t].GE(9) && \
client.ip.src.typecast_text_t.equals_any(\"Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection\").NOT) \
&& (HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status\").NOT && \
HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\").NOT)" DROP
bind vpn vserver Gateway_vServer -policy Drop_Pol \
-priority 10 -gotoPriorityExpression END \
-type REQUEST -type AAA_REQUEST
Reset du compteur après authentification réussie
# Réinitialiser le compteur après login réussi
add ns assignment Likelihood_Bad_IP_Counter_Reset \
-variable "$Likelihood_Bad_IP_Counter[CLIENT.IP.SRC]" -clear
add authentication Policy Increase_Likelihood_Counter_Reset_Pol \
-rule true -action Likelihood_Bad_IP_Counter_Reset
add authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label \
-loginSchema LSCHEMA_INT
bind authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label \
-policyName Increase_Likelihood_Counter_Reset_Pol \
-priority 100 -gotoPriorityExpression NEXT
add authentication Policy Return_Success -rule true -action NO_AUTHN
bind authentication policylabel Likelihood_Bad_IP_Counter_Reset_Policy_Label \
-policyName Return_Success -priority 110 -gotoPriorityExpression NEXT
# Lier le reset après la policy LDAP
bind authentication vserver Gateway_Auth_vServer \
-policy Gateway_LDAP_Policy -priority 100 \
-nextFactor Likelihood_Bad_IP_Counter_Reset_Policy_Label \
-gotoPriorityExpression NEXT
Page de statut pour les opérateurs
Accessible depuis /logon/LogonPoint/ip_status — uniquement pour les IPs du dataset IP_Status_Page_Allowed_Clients. Elle permet de vérifier le compteur d’une IP et de le remettre à zéro sans toucher à la configuration NetScaler — utile en cas de faux positif.
# Autoriser des IPs à accéder à la page de statut
add policy dataset IP_Status_Page_Allowed_Clients ipv4
bind policy dataset IP_Status_Page_Allowed_Clients X.X.X.0/24
# Policies d'accès à la page de statut et au reset
add responder policy IP_Status_Page_Resp_Pol \
"HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status\") && \
client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" \
IP_Status_Page_Act
add responder policy IP_Status_Page_Resp_Pol2 \
"HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\") && \
client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" \
Likelihood_Bad_IP_Counter_Reset_Request
add responder policy IP_Status_Page_Resp_Pol3 \
"HTTP.REQ.URL.PATH.EQ(\"/logon/LogonPoint/ip_status_reset_counter\") && \
client.ip.src.typecast_text_t.equals_any(\"IP_Status_Page_Allowed_Clients\")" \
IP_Status_Reset_Counter_Act
# Binding sur le vserver Gateway
bind vpn vserver Gateway_vServer \
-policy IP_Status_Page_Resp_Pol -priority 100 \
-gotoPriorityExpression END -type REQUEST
bind vpn vserver Gateway_vServer \
-policy IP_Status_Page_Resp_Pol2 -priority 110 \
-gotoPriorityExpression NEXT -type REQUEST
bind vpn vserver Gateway_vServer \
-policy IP_Status_Page_Resp_Pol3 -priority 120 \
-gotoPriorityExpression END -type REQUEST
Remplacez Gateway_Auth_vServer et Gateway_vServer par les noms de vos vservers. Ajustez le seuil (.GE(9)) selon vos cas d’usage. Ajoutez vos IPs de proxy/NAT internes dans le dataset Proxy_Server_And_NAT_IP_That_Bypass_Password_Spray_Detection — pour éviter de bloquer le siège de votre direction tous les vendredis après-midi à cause de tremblements généralisés.
5. Vérification et résultats
La page de statut up & running
Accessible uniquement si votre IP a été correctement renseignée dans le dataset IP_Status_Page_Allowed_Clients :
Les compteurs s’incrémentent
Libre à vous de savourer d’être sous attaque. Dans ce cas, le drop en tête de liste des responder policies ne vous propose même plus votre Gateway en rafraîchissant — juste une page blanche en timeout. Efficace.
6. Conclusion
Le password spraying n’est pas une attaque spectaculaire. C’est une attaque patiente, distribuée, et de plus en plus sophistiquée. Sa force réside dans sa discrétion : elle passe sous les radars des défenses classiques.
Protéger une plateforme NetScaler/Gateway demande une approche multicouche :
✅ Checklist de protection
Aucune de ces mesures n’est suffisante seule. C’est leur combinaison qui fait la différence.
7. Annexes
📚 Références
- Citrix CTX579459 — Bulletin sur les tentatives d’authentification massives
- CVE-2023-4966 (Citrix Bleed) — https://nvd.nist.gov/vuln/detail/CVE-2023-4966
- Detecting and Mitigating Password Spraying Attacks on NetScaler Gateway — Citrix Tech Papers
- Password spraying attacks on NetScaler/NetScaler Gateway – December 2024 – Citrix Blogs
🛠️ Outils recommandés
- Citrix ADM / NetScaler Console — monitoring, analytics, gestion centralisée
- SIEM (Splunk, ELK, QRadar) — corrélation des logs et détection
Pour ceux qui suivent la saga : NetScaler, Citrix ADC, Citrix Gateway, NetScaler Console, NetScaler MAS, Command Center, Insight Center… Si j’en oublie, faites-moi signe. Le département marketing de Citrix — pardon, de Cloud Software Group — mérite un article dédié.