- Le formulaire de récupération de nom d’utilisateur No-JS de Google confirmait les combinaisons de numéro de téléphone et de nom affiché, créant une chaîne d’attaque permettant de retrouver le numéro de téléphone complet d’un utilisateur Google donné
- Le point clé résidait dans la différence de redirection entre
/signin/usernamerecoveryet/signin/usernamerecovery/lookup, qui permettait de distinguer l’absence de compte de l’existence d’un compte - Malgré des limites de requêtes basées sur l’IP et un CAPTCHA, l’insertion du jeton BotGuard du formulaire JS dans le champ
bgresponsedu formulaire No-JS a permis de fonctionner sans restriction, en combinaison avec une rotation IPv6 - Un attaquant pouvait obtenir le nom affiché du compte Google via Looker Studio, puis réduire fortement le nombre de candidats en combinant le numéro de téléphone masqué du flux de récupération de mot de passe avec les formats de numéros propres à chaque pays
- Sur un serveur 16 vCPU à environ 0,30 $ de l’heure, il était possible d’effectuer environ 40 000 vérifications par seconde ; Google a entièrement retiré le formulaire de récupération de nom d’utilisateur No-JS le 6 juin 2025 et a versé un total de 5 000 $
Une vulnérabilité partie du formulaire de récupération de nom d’utilisateur No-JS
- En vérifiant le comportement des services Google avec JavaScript désactivé dans le navigateur, il a été découvert que le formulaire de récupération de nom d’utilisateur fonctionnait encore
- On considérait depuis longtemps que le formulaire de récupération de compte nécessitait JavaScript, car il reposait sur des défenses de type BotGuard générées par du code JavaScript de proof-of-work obfusqué
- Mais le formulaire No-JS fournissait un flux permettant de vérifier si une adresse e-mail ou un numéro de téléphone de récupération était associé à un nom affiché donné
Vérification de l’existence d’un compte avec deux requêtes
- La première requête envoyait le numéro de téléphone à
/signin/usernamerecoveryet récupérait, dans leLocationde la réponse, la valeuressassociée à ce numéro- Les cookies et la valeur
gxfétaient extraits du HTML de la page initiale
- Les cookies et la valeur
- Ensuite, une requête était envoyée à
/signin/usernamerecovery/lookupavecess, le prénom, le nom etbgresponse=js_disabled - La redirection de la réponse changeait, ce qui permettait de déterminer s’il existait un compte Google avec ce numéro de téléphone et ce nom affiché
- Aucun compte :
usernamerecovery/noaccountsfound - Compte existant :
usernamerecovery/challenge
- Aucun compte :
Limites IP, IPv6 et contournement de BotGuard
- Lors des premières tentatives, après quelques requêtes, l’adresse IP était soumise à une limitation de requêtes et un CAPTCHA s’affichait
- Dans l’exemple d’un numéro mobile néerlandais, le flux de récupération de mot de passe fournissait un indice du type
•• ••••••03- Les numéros mobiles néerlandais commençant par
06, il restait 6 chiffres, soit10^6 = 1 000 000candidats à essayer
- Les numéros mobiles néerlandais commençant par
- Des fournisseurs comme Vultr proposent une plage IPv6
/64, avec en théorie18 446 744 073 709 551 616adresses utilisables - Un PoC a été créé avec
ClientBuilder.local_addressdereqwestafin de définir, pour chaque requête, une adresse IPv6 aléatoire du sous-réseau - Depuis des IP de datacenter, l’utilisation du formulaire avec JS désactivé faisait toujours apparaître un CAPTCHA, mais les requêtes passaient lorsque le jeton BotGuard du formulaire de récupération de compte avec JS activé était inséré dans le champ
bgresponsedu formulaire No-JS- Sur le formulaire No-JS, ce jeton BotGuard ne semblait pas soumis à une limitation de requêtes
Filtrer les faux positifs
- Les premiers résultats d’exécution incluaient de nombreux comptes dont le prénom était
Henry, le nom vide, et les deux derniers chiffres du numéro de téléphone étaient03 - Dans ce cas, si le prénom était
Henry,usernamerecovery/challengeétait renvoyé quel que soit le nom - Pour vérifier les correspondances possibles, le même prénom était retesté avec un nom arbitraire comme
0fasfk1AFko1wf- Si le résultat restait positif, il était filtré comme faux positif
- La probabilité qu’un autre utilisateur Google ait le même nom affiché complet, le même indicatif pays et les deux mêmes derniers chiffres de numéro était jugée faible
Obtenir l’indicatif pays et le nom affiché
- Le masque du numéro de téléphone dans le flux de récupération de mot de passe pouvait être utilisé pour déduire l’indicatif pays
- Google utilise libphonenumber pour le format national de chaque numéro
- Les formats nationaux masqués par pays ont été collectés pour créer mask.json
- La Russie, les Pays-Bas, le Royaume-Uni et les États-Unis ont des motifs de masque différents
- En 2023, Google a modifié sa politique pour n’afficher les noms que lorsqu’il existe une interaction directe avec la cible, puis, en avril 2024, l’Internal People API a totalement cessé de renvoyer le nom affiché des comptes non authentifiés
- Cependant, en créant un document Looker Studio et en en transférant la propriété à la victime, le nom affiché de la victime était exposé sur l’écran d’accueil sans interaction de sa part
Optimisation de l’espace de candidats et outils
- La validation de numéros de libphonenumber a été utilisée pour générer format.json, contenant les préfixes mobiles par pays, les indicatifs régionaux connus et les longueurs
- L’exemple des Pays-Bas inclut l’indicatif pays
31, les indicatifs régionaux61,62,63,64,65,68, et les longueurs[7]
- L’exemple des Pays-Bas inclut l’indicatif pays
- La validation libphonenumber en temps réel réduisait les requêtes à l’API Google pour les numéros invalides
- Un script Go utilisant chromedp a été écrit pour générer des jetons BotGuard
- Un appel à
http://localhost:7912/api/generate_bgtokenpermettait d’obtenir unbgToken
- Un appel à
- Le flux d’attaque complet se déroulait en trois étapes
- Fuite du nom affiché du compte Google via Looker Studio
- Obtention du numéro de téléphone masqué via le flux de récupération de mot de passe
- Force brute du numéro de téléphone complet avec
gpben fournissant le nom affiché et le numéro masqué
Performances de la force brute et temps estimé
- Avec un serveur à 0,30 $ de l’heure et une configuration grand public de 16 vCPU, environ 40 000 vérifications par seconde étaient possibles
- Temps estimés lorsque seuls les deux derniers chiffres sont fournis par le flux de récupération de mot de passe :
- États-Unis
+1: 20 minutes - Royaume-Uni
+44: 4 minutes - Pays-Bas
+31: 15 secondes - Singapour
+65: 5 secondes
- États-Unis
- Si le flux de réinitialisation de mot de passe d’autres services, comme PayPal, fournit davantage d’indices numériques, le temps peut être fortement réduit
- Exemple :
+14•••••1779
- Exemple :
Signalement à Google et chronologie des mesures
- 2025-04-14 : rapport envoyé au fournisseur
- 2025-04-15 : le fournisseur a reçu et trié le rapport
- 2025-04-25 : réponse « Nice catch! »
- 2025-05-15 : le panel a fixé une récompense de 1 337 $ + swag
- Cette décision reposait sur une faible probabilité d’exploitation, et le problème a été reconnu comme une methodology liée à l’abuse à impact élevé
- 2025-05-15 : contestation de la justification de la récompense
- Selon le tableau Abuse VRP, la probability/exploitability est déterminée par les prérequis de l’attaque et par la capacité de la victime à découvrir l’abus
- Il a été avancé que cette attaque ne nécessitait aucun prérequis et que la victime ne pouvait pas découvrir l’abus
- 2025-05-22 : le panel a versé 3 663 $ supplémentaires, portant la récompense totale à 5 000 $
- La likelihood a été relevée à medium
- 2025-05-22 : le fournisseur a confirmé avoir déployé des mesures d’atténuation en cours, dans l’attente du retrait des endpoints à l’échelle mondiale
- 2025-05-22 : coordination d’une publication prévue le 2025-06-09
- 2025-06-06 : le fournisseur a confirmé le retrait complet du formulaire de récupération de nom d’utilisateur No-JS
- 2025-06-09 : publication du rapport
Aucun commentaire pour le moment.