Netwrix 1Secure offre une visibilité unifiée sur les données et l’identité - gratuit pendant 14 jours avec un accès complet.Commencez un essai gratuit

Centre de ressourcesBlog

Le code d'erreur est l'indice

Le code d'erreur est l'indice

Oct 2, 2026

Proofpoint a documenté deux campagnes cette année qui valident des identifiants volés contre Entra ID sans jamais produire de connexion réussie. La technique est assez simple à expliquer en une phrase : envoyez un mot de passe au point de terminaison du jeton avec un client ID qui ne correspond à aucune application enregistrée, et lisez l’erreur que vous recevez. Ces codes d’erreur sont importants, mais la fuite la plus importante est l’endroit où chaque code se situe dans la séquence d’authentification.

Trois codes, trois significations différentes

Les requêtes vont vers le point de terminaison OAuth 2.0 Resource Owner Password Credentials (ROPC) : un POST contenant le nom d’utilisateur, le mot de passe et client_id. Cette technique est explicitement limitée à ROPC, et la restreindre fait partie des mesures d’atténuation.

Trois réponses comptent :

  • AADSTS50034 (UserAccountNotFound): Le compte n'est pas dans l'annuaire.
  • AADSTS50126 (InvalidUserNameOrPassword): Documenté comme un code générique couvrant tout type d’échec.
  • AADSTS700016 : L'identifiant de l'application n'a pas été trouvé dans l'annuaire.

AADSTS50126 ne devrait pas faire de distinction entre un mauvais nom d'utilisateur et un mauvais mot de passe, mais le comportement observé les sépare. Une tentative échouée contre un nom d'utilisateur inexistant n'est pas du tout enregistrée dans le journal de connexion, car Entra n'écrit une ligne de journal qu'une fois qu'il a validé un nom d'utilisateur. Entra renvoie 50034 pour les comptes absents et 50126 pour les comptes présents avec un mot de passe incorrect, ce qui est une distinction comportementale observée que la documentation de Microsoft floute. La portée est également importante. Ces observations s'appliquent aux locataires gérés au point de terminaison du jeton. Les locataires fédérés valident les mots de passe sur site via Pass-through Authentication (PTA) ou ADFS, donc le chemin d'erreur diffère.

Les journaux prouvent les différences dans le traitement :

Request

Log row written?

What the attacker learns

Account not in directory (50034)

No

This username doesn't exist

Account exists, wrong password (50126)

Yes, failed sign-in

This username exists

Account exists, correct password (700016)

Yes, failed sign-in

This credential pair works

AADSTS700016 indique qu’un enregistrement d’application est cassé. Observé après une série de tentatives d’authentification avec des client IDs inconnus, cela confirme que l’attaquant possède un mot de passe valide.

Les deux campagnes

UNK_pyreq2323 a épuisé l'infrastructure AWS du 14 janvier 2026 jusqu'au début mars. Il a généré plus de 700 000 identifiants clients usurpés en randomisant les six derniers chiffres d'un identifiant d'application Exchange Online légitime de première partie (par exemple, 00000002-0000-0ff1-ce00-000000XXXXXX). Il a ciblé plus d'un million de comptes sur environ 4 000 locataires et a réutilisé chaque ID usurpé sur pas plus de 12 comptes avant de le jeter. Un ID d'application Microsoft de première partie est pré-consenti dans chaque locataire, donc les IDs qui conservent ce préfixe semblent légitimes plutôt que du bruit.

UNK_OutFlareAZ est apparu en décembre 2025 et est revenu en février et mars 2026, opérant via l'infrastructure Cloudflare contre plus de 2 millions d'utilisateurs avec 3,7 millions d'identifiants d'application usurpés.

Les deux utilisaient des méthodes de génération différentes, et cette différence est importante pour la détection. Alors que pyreq2323 conservait un préfixe réel et réutilisait chaque ID jusqu’à 12 fois, OutFlareAZ générait un UUIDv4 aléatoire neuf par requête, un utilisateur par ID, donc toute heuristique basée sur la réutilisation le manque.

(Proofpoint suit les deux campagnes sous sa désignation UNK_, qui marque des clusters non attribués plutôt que des acteurs nommés.)

Détection

Les défenseurs ont vu les échecs et les verrouillages, mais ils ne pouvaient pas dire lesquels de ces échecs étaient des confirmations d’identifiants. Le signal était là, mêlé à chaque utilisateur qui avait mal saisi un mot de passe ce matin-là, mais il était illisible sans savoir quel code d’erreur isoler. La moitié vraiment invisible est l’énumération. Les noms d’utilisateur inexistants ne produisent aucune ligne de journal, donc la reconnaissance qui précède tout le reste ne laisse aucune trace dans aucun tenant.

Chacune de ces requêtes est enregistrée dans les journaux de connexion interactive. Cela découle de la définition propre à Microsoft : IsInteractive couvre les connexions où l’utilisateur fournit un facteur d’authentification, la définition mentionne explicitement les mots de passe, et ROPC soumet un mot de passe.

La signature est un nom d'application vide :

Client ID in request

AppId

AppDisplayName

Real registered app

populated

populated

Well-formed UUID, unregistered

populated

blank

Malformed client ID

blank

blank

La ligne deux est ce que vous recherchez. Associez isnotempty(AppId) avec isempty(AppDisplayName) pour isoler le cas bien formé mais non enregistré plutôt que de capturer aussi des données mal formées.

La requête qui compte renvoie des compromissions d’identifiants confirmées :

      SigninLogs
| where TimeGenerated > ago(30d)
| where ResultType == "700016"
| where isnotempty(AppId) and isempty(AppDisplayName)
| project TimeGenerated, UserPrincipalName, AppId, IPAddress,
          UserAgent, AuthenticationProtocol, ResultDescription
| order by TimeGenerated asc
      

Chaque ligne est une paire d’identifiants validée par Entra, traitez donc la sortie comme une liste de réinitialisation de mot de passe et de révocation de session. Ce n’est pas un ticket d’enregistrement d’application, et la plus grosse erreur possible ici est de rediriger les 700016 accès vers le propriétaire des enregistrements d’applications.

Pour la détection de campagne, pivotez sur la source plutôt que sur l’ID client :

      SigninLogs
| where TimeGenerated > ago(7d)
| where isnotempty(AppId) and isempty(AppDisplayName)
| summarize
    DistinctAppIds = dcount(AppId),
    TargetedUsers  = dcount(UserPrincipalName),
    ConfirmedCreds = countif(ResultType == "700016"),
    WrongPassword  = countif(ResultType == "50126"),
    Lockouts       = countif(ResultType == "50053"),
    UserAgents     = make_set(UserAgent, 10)
  by IPAddress, bin(TimeGenerated, 1h)
| where DistinctAppIds > 10
| order by DistinctAppIds desc
      

S'appuyer sur IPAddress est délibéré. Toute heuristique basée sur le plafond de réutilisation de 12 comptes détecte pyreq2323 et manque complètement OutFlareAZ, car ce dernier ne réutilise jamais d'ID. Les deux campagnes semblent identiques à la source : une origine présentant un grand nombre d'ID client qui ne correspondent à rien. Garder ConfirmedCreds et Lockouts dans la même ligne montre le bruit à côté des vraies compromissions.

Ce que la correction couvre et ne couvre pas

Restreindre ROPC via Conditional Access est la bonne décision et Proofpoint le recommande. Mais je veux être clair sur ce que cela apporte.

Cela ne touche pas à l'énumération. AADSTS50034 est renvoyé lorsque Entra résout le nom d'utilisateur, avant toute validation des identifiants et deux étapes avant l'évaluation de la politique. Aucune politique de Conditional Access ne peut intercepter une réponse émise avant la consultation de la politique, et aucune ligne de journal n'est écrite quelle que soit la configuration du locataire. L'énumération des noms d'utilisateur contre votre annuaire survit à tous les contrôles côté locataire, y compris un blocage strict de l'authentification héritée.

Une politique bloquant l'authentification héritée renvoie AADSTS53003, BlockedByConditionalAccess. Selon la même logique d'ordre, 53003 n'est accessible qu'une fois le mot de passe validé, donc le message d'erreur de l'attaquant devient 50126 au lieu de 53003. Code différent, même information.


Références

Partager sur

En savoir plus

À propos de l'auteur

Portrait de darryl baker

Darryl Baker

Chercheur principal en sécurité

Darryl G. Baker est un chercheur principal en sécurité chez Netwrix et une autorité reconnue en sécurité de l’Identity et d’Active Directory. Avec plus d’une décennie d’expérience dans les systèmes d’identité, il a dirigé des évaluations de sécurité d’entreprise, des formations en sécurité de l’identité et des émulations de menaces axées sur Active Directory, Entra ID et les environnements Azure. Darryl a donné des formations et des démonstrations très appréciées lors de BlueTeamCon, BSidesCT, The Experts Conference et Wild Wild West Hackin’ Fest. Il est l’architecte de nombreux laboratoires pratiques d’émulation d’attaques, utilisant les outils actuels des red teams et blue teams pour aider les défenseurs à maîtriser tout, de l’analyse des chemins d’attaque à la chasse aux menaces. Lors de ses sessions, Darryl allie une profonde expertise technique à des études de cas réels, permettant aux professionnels du blue team de renforcer leur posture de sécurité de l’identité et de se défendre contre les techniques adverses en évolution.