Protéger sa plateforme Citrix NetScaler contre le Password Spraying

🔴 Cybersécurité · NetScaler

Protéger NetScaler contre le Password Spraying

✍️ Jérôme — Elyleo ⏱️ Lecture ~10 min 🏷️ NetScaler · Gateway · AAA · Responder

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.

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.

💡
Ce que cet article n’est pas

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.

1
Attaques basiques

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.

⚠️ Les équipes cyber arrivent déjà en disant que le seuil d’attaque est trop faible pour déployer les gros moyens.
2
Botnets distribués

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.

😅 On exporte la liste via MAS, le CSV réjouit l’équipe firewall, et après 1h de traitement, on a gagné… 2h de tranquillité avant de recommencer.
3
Residential proxies

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.

🌍 La même adresse IP était présentée comme appartenant à des pays différents selon la plateforme de test. Mon nouveau copain russo-chinois-pakistanais est un globe-trotter hyper rapide.

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.
4
Timing adaptatif

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.

🕗 Après quelques jours, les attaques s’arrêtent vers 8h du matin pour reprendre en fin de soirée… en fonction des mises en place des listes de blocage. Nos attaquants ont un rythme de travail plus régulier que certains employés.
🎯
Le constat clé

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.

NetScaler CLI Paramètres AAA globaux
# 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 :

NetScaler CLI Configuration syslog & 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)

La note sur les noms (😂)

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

✅ Utilisateur légitime
  • Même identifiant depuis une IP
  • Rotation de username nulle ou très faible
  • Comportement prévisible
VS
🔴 Attaquant password spraying
  • 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.

1

Variables NS

NetScaler CLI Étape 1 — Création des variables
# 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
Variables NS dans l'interface NetScaler
2

Assignments

NetScaler CLI Étape 2 — 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\")"
Liste des assignments Assignment compteur IP
3

Stratégies d’authentification

NetScaler CLI Étape 3 — Policies 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
4

Whitelist et stratégie de blocage

NetScaler CLI Étape 4 — Dataset whitelist + Responder Drop
# 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
5

Reset du compteur après authentification réussie

NetScaler CLI Étape 5 — Reset compteur post-login
# 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
6

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.

NetScaler CLI Étape 6 — Page de statut opérateurs
# 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
⚠️
Adaptations nécessaires avant déploiement

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 :

Page de statut IP NetScaler Résultat page statut

Les compteurs s’incrémentent

Compteurs en action Responder policy binding
😈
Vous voulez tester ?

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

MFA Pour neutraliser les mots de passe compromis. Un minimum aujourd’hui, sans discussion.
Sécurisation & mise à jour Votre ferme, votre firmware, votre infra en général. Une version pas à jour de votre solution d’authentification peut tout mettre par terre.
Monitoring Pour détecter et réagir rapidement. Sans logs, pas de réaction.
Détection par rotation de username La configuration décrite dans cet article — la seule qui s’attaque au vrai pattern de l’attaque.

Aucune de ces mesures n’est suffisante seule. C’est leur combinaison qui fait la différence.

7. Annexes

🛠️ Outils recommandés

  • Citrix ADM / NetScaler Console — monitoring, analytics, gestion centralisée
  • SIEM (Splunk, ELK, QRadar) — corrélation des logs et détection
📝
La note des noms

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é.

J

Jérôme — Elyleo

Consultant Citrix indépendant · Expert NetScaler & CVAD

Consultant depuis qu’on appelait encore ça Presentation Server. Traducteur officieux techno→français, je passe mes journées à dompter des infrastructures virtuelles. 20 ans d’expérience en virtualisation, chasseur de bugs réseau et collectionneur (involontaire) de certificats SSL périmés.

📅 Discutons de votre infrastructure →

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Retour en haut