Proofpoint ha documentato due campagne quest’anno che convalidano credenziali rubate contro Entra ID senza mai produrre un accesso riuscito. La tecnica è abbastanza semplice da spiegare in una frase: invia una password all’endpoint del token con un client ID che non corrisponde a nessuna applicazione registrata e leggi l’errore che ricevi. Questi codici di errore sono importanti, ma la perdita maggiore è dove ogni codice si colloca nella sequenza di autenticazione.
Tre codici, tre significati diversi
Le richieste vanno all’endpoint OAuth 2.0 Resource Owner Password Credentials (ROPC): un POST che trasporta username, password e client_id. Questa tecnica è specificamente limitata a ROPC, e la sua restrizione è tra le mitigazioni.
Tre risposte contano:
- AADSTS50034 (
UserAccountNotFound): L'account non è nella directory. - AADSTS50126 (
InvalidUserNameOrPassword): Documentato come un errore generico che copre ogni tipo di fallimento. - AADSTS700016: L'identificativo dell'applicazione non è stato trovato nella directory.
AADSTS50126 non dovrebbe discriminare tra un nome utente errato e una password errata, ma il comportamento osservato li separa. Un tentativo fallito contro un nome utente inesistente non viene scritto affatto nel registro di accesso, perché Entra scrive una riga di log solo dopo aver risolto un nome utente come valido. Entra restituisce 50034 per account assenti e 50126 per account presenti con password errata, una distinzione comportamentale osservata che la documentazione Microsoft sfuma. Anche l'ambito è importante. Queste osservazioni si applicano ai tenant gestiti al token endpoint. I tenant federati convalidano le password on-premise tramite Pass-through Authentication (PTA) o ADFS, quindi il percorso di errore è diverso.
I log dimostrano le differenze nella gestione:
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 indica che una registrazione dell'app è danneggiata. Osservato dopo una serie di tentativi di autenticazione con client ID sconosciuti, conferma che l'attaccante possiede una password funzionante.
Le due campagne
UNK_pyreq2323 ha esaurito l'infrastruttura AWS dal 14 gennaio 2026 a inizio marzo. Ha generato oltre 700.000 ID client contraffatti randomizzando le ultime sei cifre di un identificatore di applicazione legittimo di prima parte di Exchange Online (ad esempio, 00000002-0000-0ff1-ce00-000000XXXXXX). Ha preso di mira più di un milione di account in circa 4.000 tenant e ha riutilizzato ogni ID contraffatto su non più di 12 account prima di scartarlo. Un ID di applicazione Microsoft di prima parte è pre-consentito in ogni tenant, quindi gli ID che mantengono quel prefisso sembrano appartenere piuttosto che essere rumore.
UNK_OutFlareAZ è apparso a dicembre 2025 ed è tornato a febbraio e marzo 2026, operando attraverso l'infrastruttura Cloudflare contro più di 2 milioni di utenti con 3,7 milioni di ID applicazione contraffatti.
I due hanno usato metodi di generazione diversi, e la differenza è importante per il rilevamento. Mentre pyreq2323 manteneva un prefisso reale e riutilizzava ogni ID fino a 12 volte, OutFlareAZ generava un nuovo UUIDv4 casuale per ogni richiesta, un utente per ID, quindi qualsiasi euristica basata sul riutilizzo lo perde.
(Proofpoint traccia entrambe le campagne sotto la sua designazione UNK_, che identifica cluster non attribuiti anziché attori nominati.)
Rilevamento
I difensori vedevano i fallimenti e i blocchi, ma non riuscivano a capire quali di quei fallimenti fossero conferme di credenziali. Il segnale era lì, insieme a ogni utente che aveva digitato male la password quella mattina, ma era illeggibile senza sapere quale codice di errore isolare. La metà davvero invisibile è l'enumerazione. I nomi utente inesistenti non producono alcuna riga di log, quindi il riconoscimento che precede tutto il resto non lascia tracce in nessun tenant.
Ognuna di queste richieste viene registrata nei log di accesso interattivi. Questo deriva dalla definizione di Microsoft: IsInteractive copre gli accessi in cui l’utente fornisce un fattore di autenticazione, la definizione nomina esplicitamente le password e ROPC invia una password.
La firma è un nome dell'applicazione vuoto:
Client ID in request |
|
|
|---|---|---|
|
Real registered app |
populated |
populated |
|
Well-formed UUID, unregistered
|
populated
|
blank |
|
Malformed client ID |
blank |
blank |
La riga due è ciò che stai cercando. Abbina isnotempty(AppId) con isempty(AppDisplayName) per isolare il caso ben formato ma non registrato invece di includere anche dati malformati.
La query che conta restituisce compromissioni di credenziali confermate:
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
Ogni riga è una coppia di credenziali validate da Entra, quindi considera l'output come una lista di reset password e revoca sessioni. Non è un ticket di registrazione app, e l'errore più grave qui è indirizzare i 700016 accessi a chi possiede le registrazioni delle applicazioni.
Per il rilevamento delle campagne, pivotare sulla fonte anziché sull'ID cliente:
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
Fare pivot su IPAddress è deliberato. Qualsiasi euristica basata sul limite di riutilizzo di 12 account identifica pyreq2323 e ignora completamente OutFlareAZ, perché il secondo non riutilizza mai un ID. Entrambe le campagne appaiono uguali dalla fonte: un'origine che presenta un gran numero di ID client che non risolvono nulla. Mantenere ConfirmedCreds e Lockouts nella stessa riga mostra il rumore accanto alle vere compromissioni.
Cosa che la correzione copre e non copre
Limitare ROPC tramite Conditional Access è la scelta giusta e Proofpoint lo consiglia. Ma voglio essere chiaro su cosa comporta.
Non influisce sull'enumerazione. AADSTS50034 viene restituito quando Entra risolve il nome utente, prima di qualsiasi convalida delle credenziali e due passaggi prima della valutazione della policy. Nessuna policy di Conditional Access può intercettare una risposta emessa prima che la policy venga consultata, e nessuna riga di log viene scritta indipendentemente dalla configurazione del tenant. L'enumerazione dei nomi utente contro la tua directory sopravvive a ogni controllo lato tenant, incluso un blocco rigido dell'autenticazione legacy.
Una policy che blocca l'autenticazione legacy restituisce AADSTS53003, BlockedByConditionalAccess. Con la stessa logica di ordinamento, 53003 è raggiungibile solo dopo che la password è stata convalidata, quindi il messaggio di errore dell'attaccante diventa 50126 invece di 53003. Codice diverso, stessa informazione.
Riferimenti
Condividi su
Scopri di più
Informazioni sull'autore
Darryl Baker
Ricercatore Senior di Sicurezza
Darryl G. Baker è un Senior Staff Security Researcher presso Netwrix e un’autorità riconosciuta nella sicurezza di Identity e Active Directory. Con oltre un decennio di esperienza nei sistemi di identità, ha guidato valutazioni di sicurezza aziendale, formazione sulla sicurezza dell’identità e emulazioni di minacce focalizzate su Active Directory, Entra ID e ambienti Azure. Darryl ha tenuto corsi e dimostrazioni molto apprezzati a BlueTeamCon, BSidesCT, The Experts Conference e Wild Wild West Hackin’ Fest. È l’architetto di numerosi laboratori pratici di emulazione di attacchi, sfruttando gli strumenti attuali di red team e blue team per aiutare i difensori a padroneggiare tutto, dall’analisi dei percorsi di attacco alla caccia alle minacce. Nelle sue sessioni, Darryl unisce una profonda conoscenza tecnica a casi di studio reali, permettendo ai professionisti del blue team di rafforzare la loro postura di sicurezza dell’identità e difendersi dalle tecniche avversarie in evoluzione.
Scopri di più su questo argomento
Netwrix Innovation Week: introduzione ad AIHound
Bring Your Own Vault: perché "rip and replace" non è l'unica opzione
Il provider della tua cassaforte per password non è il tuo piano di backup
Ospitare o non ospitare il proprio gestore di password
Le credenziali statiche sono ancora il modo più semplice per l'IA per accedere