Et comment ai-je fait ça? Tout a commencé avec une application que j'ai développée. Pour des raisons évidentes, je ne publierai pas le nom. L'application est assez simple, elle est conçue pour les personnes qui aiment le fitness, et propose des options telles que la saisie de données sur la vitesse lors d'une course ou des complexes de musculation prêts à l'emploi. Comme beaucoup d'autres produits, l'utilisateur doit d'abord créer un compte. Selon les analystes, environ 60% des personnes, au lieu de passer par tout le processus d'inscription, sont tentées par le bouton tentant "Se connecter avec Google".
Vous savez probablement en termes généraux ce qui se passe dans de tels cas: lorsque l'utilisateur clique sur un bouton, une fenêtre de navigateur pour se connecter à un compte Google s'ouvre à l'intérieur de l'application.
Cet utilisateur a activé l'identification à deux facteurs, donc après avoir entré le courrier et le mot de passe, une boîte de dialogue apparaît pour vous demander si c'est lui. L'emplacement et le type d'appareil sont les mêmes, il clique donc sur «Oui».
C'est, en fait, tout. Désormais, une personne peut utiliser l'application en toute sécurité, tandis que j'obtiens un accès complet et illimité à son compte depuis un serveur distant. Il ne recevra jamais de messages à ce sujet. Et s'il s'avère méticuleux et commence à étudier le trafic réseau, il verra que l'appareil envoie des requêtes réseau uniquement et exclusivement à différents sous-domaines de google.com.
Mais comment est-ce possible? Revenons à notre bouton Se connecter avec Google. Précisons tout de suite une chose: pour ceux qui ne sont pas au courant, après avoir cliqué sur ce bouton, l'application peut tout faire. Lancez le processus d'autorisation dans Google, donnez une voix de trompette, montrez un gif avec un chat. Toutes les options de cette liste ne sont pas également probables, mais vous pouvez rêver.
Dans mon cas, en cliquant sur le bouton, l'application ouvre une boîte de dialogue utilisant la WebView et définit l'adresse Web: accounts.google.com/EmbeddedSetup . Cela correspond à la page de connexion du compte Google, uniquement une page spéciale conçue pour les nouveaux appareils Android. Cette circonstance jouera son rôle plus tard, lorsque nous serons aimablement fournis toutes les informations nécessaires sous la forme d'un cookie.
Malheureusement, cette page à la fois ressemble et agit différemment de la page de connexion standard (du moins comme elle devrait être par défaut):
Remarquez l'étrange bande bleue, les mots En savoir plus et ainsi de suite sur la bonne image.
Et maintenant, le plaisir commence. J'utilise les API standard intégrées à iOS et Android pour injecter un morceau de code Javascript bien écrit qui apportera les modifications nécessaires afin que la page ne diffère pas de la page standard en termes d'apparence ou de comportement.
Les plus avisés penseront maintenant: "Arrêtez, donc si vous pouvez injecter du code JavaScript, qu'est-ce qui vous empêche de simplement voler votre identifiant et votre mot de passe directement dans les champs de texte?" Absolument rien - en général, il existe déjà un code prêt à l'emploi à cet effet exister. Mais de nos jours, l'accès au login et au mot de passe ne suffit plus. Sauf si vous êtes très chanceux, le serveur sera à quelques centaines de kilomètres de l'emplacement de l'utilisateur. Sinon, l'utilisateur recevra une lettre et une notification avec un message sur "activité suspecte" et la tentative de piratage sera stoppée. Et l'authentification à deux facteurs rend notre vie encore plus difficile.
Parlons donc d'autre chose, comme le jeton maître. À première vue, cela semble en quelque sorte méchant, mais à la seconde - cela s'avère être encore pire qu'il n'y paraissait.
Lorsque l'appareil Android se connecte pour la première fois, il envoie le jeton reçu de la page de connexion intégrée susmentionnée à un point de terminaison spécial. Voici un exemple de demande typique:
POST /auth HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 349
Host: android.clients.google.com
Connection: Keep-Alive
User-Agent: GoogleLoginService/1.3 (a10 JZO54K);gzip
app: com.google.android.gmsapp=com.google.android.gms&client_sig=38918a453d07199354f8b19af05ec6562ced5788&callerPkg=com.google.android.gms&callerSig=38918a453d07199354f8b19af05ec6562ced5788&service=ac2dm&Token=oauth2_4%2F4AY0e-l5vPImYAf8XsnlrdshQQeNir3rSBx5uJ2oO9Tfl17LpsaBpGf1E2euc18UyOc8MnM&ACCESS_TOKEN=1&add_account=1&get_accountid=1&google_play_services_version=204713000
Le jeton dans cette demande provient des cookies de la page de connexion au compte, et tout le reste est une information accessible au public (merci, microG !). La même page de connexion au compte gère les choses avec l'authentification à deux facteurs - nous n'avons rien à faire du tout.
Après cela, le point de terminaison susmentionné envoie le même jeton maître. Mais comment pourrais-je y accéder sans demandes réseau suspectes? Très simple: via un journal dans Google Firebase.
Et le jeton maître est puissant. Il a une durée de validité illimitée, à condition que l'utilisateur ne modifie pas le mot de passe ou les paramètres d'authentification à deux facteurs. Autant que je sache, il n'est soumis à aucun contrôle de sécurité, quels que soient son emplacement, son adresse IP et ses actions. Cela n'incite jamais le système à envoyer une notification ou une lettre à l'utilisateur.
Et surtout: cela m'ouvre la voie à tous, sans exception, des services qui ont jamais été disponibles depuis un appareil mobile, au nom du propriétaire du compte correspondant. Une requête POST me suffit pour prétendre être un compte Google officiel et obtenir un jeton OAuth pour accéder à tout, y compris aux API privées (et probablement non publiées nulle part). Je peux lire les e-mails, parcourir Google Drive, afficher les sauvegardes de mon téléphone et des photos sur Google Photos, et en même temps lire l'historique Web de l'utilisateur et discuter avec ses amis sur Google Messenger. J'ai même créé une version modifiée de microG avec laquelle je peux gérer tous ces comptes d'utilisateurs directement à partir des applications Google classiques.
Et je vous rappelle que tout le processus ressemble à ceci... J'invite tout le monde à poser la question: seriez-vous pris?
Exposition
Comme beaucoup d'entre vous l'ont deviné, tout n'est pas vrai dans cet article. Je n'ai publié aucune application de fitness sur le Play Store et n'ai pas collecté des millions de jetons maîtres. Merci à ce post pour l'inspiration. Mais la méthode elle-même fonctionne. Moi et n'importe quel autre développeur, pourrions certainement créer une application avec une telle surprise (peut-être que quelqu'un l'a déjà fait).
FAQ
Mais la page est différente de la connexion normale. J'aurais remarqué!
Les différences ne sont pas si frappantes, il est donc fort probable qu'elles ne l'auraient pas remarqué. La page de connexion au compte Google sur Android a généralement une interface "sélectionner un compte", mais il existe des exceptions - par exemple, de nombreuses applications Web, telles que celles qui sont créées sur Ionic et Cordova. La plupart des applications iOS préfèrent également souvent la version Web, qui est très similaire à l'option ci-dessus. De plus, même s'il vous semble que l'absence d'écran avec "telle ou telle application demande l'accès ...", vous serez certainement alerté, alors il se peut bien qu'il soit implémenté au prix de quelques extra heures de travail.
Cela fonctionne-t-il aussi sur iOS?
Je ne l'ai pas essayé, mais il n'y a aucune raison de croire que cela ne fonctionnera pas.
Et que faire avec?
En général, la question est complexe. Aucune de mes actions à proprement parler ne relève de la définition d'un exploit, mais le résultat est néanmoins très dangereux. Pour commencer, Google aimerait trier ses notifications "Se connecter avec un nouvel appareil" afin qu'elles fonctionnent correctement. Personnellement, je les reçois lorsque j'essaye de me connecter à mon compte depuis un ordinateur, mais pendant que j'ai testé cette application, le système n'a jamais fonctionné. Une autre bonne idée est de mettre à jour les consignes relatives aux boutons "Se connecter avec Google"; maintenant, il ne dit rien du tout sur les exigences de mise en œuvre. Peut-être qu'ils devraient plonger plus profondément dans la jungle de la sécurité à travers l'obscurité - un principe, malgré tous ses défauts, sert jusqu'à présent bien à Apple pour la sécurité iMessage.
Je dois admettre: je ne suis pas sûr que l'on puisse trouver ici une solution technique qui éliminerait complètement le problème. Si l'application officielle de Google a la capacité d'effectuer une action, les programmes tiers, avec une diligence raisonnable, pourront la répéter. Cependant, l'entreprise emploie des personnes intelligentes, alors attendez de voir.
Ce problème concerne-t-il tous les systèmes d'autorisation des applications tierces?
Très probablement. Je n'ai pas bien compris dans quels cas les notifications ont été envoyées et dans lesquelles elles ne l'ont pas été, mais même lorsque les notifications arrivent, elles ne savent pas toujours ce qui se passe. La fonction «Se connecter avec Apple», cependant, est équipée de directives très strictes, et l'administration de l'App Store (où la fonction, je crois, est principalement utilisée) surveille strictement le respect des exigences. D'un autre côté, ils ont leurs propres problèmes d'autorisation, contre lesquels cela s'estompe.
Histoire vraie
Même si ce n'était pas des millions, j'ai en quelque sorte vraiment collecté une petite quantité de jetons maîtres d'utilisateurs sans méfiance, et ce, sans le vouloir.
La véritable histoire de mon épiphanie a commencé lorsque j'ai développé l'application Carbon Player; maintenant, il est déjà tombé dans l'oubli et n'a pas été largement diffusé. L'application a été conçue pour remplacer Google Play Music (vous vous souvenez de l'époque où cela existait?), Seulement avec un design beaucoup plus cool. Pour accéder au dossier de musique personnalisé, j'ai traduit gmusicapiSimon Weber en Java, mais, en réécrivant le code, au début je n'ai pas vraiment compris comment le processus d'autorisation fonctionne là-bas. Je me suis seulement rendu compte que j'avais besoin d'un nom d'utilisateur et d'un mot de passe, ce que j'ai demandé via une simple boîte de dialogue, puis certaines demandes arrivent et des jetons qui me conviennent pour extraire de la musique tombent.
Avant de transférer la première version de l'application à un petit groupe de testeurs, j'ai passé au peigne fin le code, ajouté la journalisation partout et également implémenté un intercepteur, qui était censé télécharger automatiquement tous les journaux sur Firebase. Bien sûr, j'étais assez intelligent pour ne pas enregistrer les mots de passe, mais j'ai promis les trois jetons reçus par mon implémentation gmusicapi par erreur. Deux d'entre eux étaient plutôt inoffensifs - ils ne donnaient accès qu'à différents magasins de musique. Mais le troisième s'est avéré être un jeton maître.
En général, l'application a collecté au plus vingt-cinq téléchargements pendant toute son existence, et je lui ai rapidement fait signe de la main pour ne pas être distrait de mes études. Mais avant cela, il a réussi à publier quelques mises à jour, dont l'une était une refonte de la nouvelle page d'accueil géniale (enfin, pour l'époque) de Google Play Music - l'un des rares éléments du produit original qui avait l'air bien.
Le processus s'est avéré beaucoup plus déroutant que je ne le pensais, et a dû de manière inattendue faire beaucoup de rétro-ingénierie des tampons de protocole. Plus important encore, pour une raison quelconque, il nécessitait désormais un jeton complètement différent, qui n'était plus implémenté dans gmusicapi. En conséquence, pour le mettre en œuvre, j'ai creusé pendant plusieurs heures dans le système d'autorisation, essayant de comprendre comment cela fonctionne. Cela a conduit à un terrible moment d'épiphanie lorsque j'ai réalisé que je consignais les informations les plus sensibles que je pouvais. Je dirai une chose: l'exploitation forestière s'est arrêtée. Vingt-cinq personnes qui ont téléchargé l'application, pardonnez-moi, s'il vous plaît (j'ai supprimé vos jetons de Firebase!).
Il y en a eu une autre, sans rapport avec la première fois où j'ai travaillé dans une startup pour créer un gestionnaire de mots de passe. L'un des principaux avantages de l'application était qu'elle stockait les mots de passe strictement sur le téléphone, mais permettait en même temps l'autorisation de l'ordinateur grâce à un bookmarklet JavaScript qui «connectait les appareils» via un code QR. Pour que tout se passe bien, lorsqu'un utilisateur ouvrait un site sur un ordinateur, l'application accédait au même site à partir du téléphone et injectait un morceau de code JavaScript soigneusement écrit qui capturait les identifiants, les mots de passe et tout le reste. Semble familier?
Au final, ces deux idées se sont réunies dans ma tête. J'avais un prototype pour Carbon Player mais je n'avais pas assez de temps pour le faire fonctionner. Quelques années plus tard, j'ai finalement commencé à créer quelque chose comme une version de démonstration sur sa base. Beaucoup a dû être changé dans le processus - la méthode décrite dans cet article est très différente de ce qui a été implémenté dans le prototype, puisque Google a apporté des modifications au système d'autorisation. Mais le résultat final reste le même et n'est pas moins effrayant qu'il ne l'était alors.
Si vous le souhaitez, vous pouvez télécharger la version de démonstrationet voyez le système en action; Je donne ma parole que rien n'est connecté au cloud. Gardez à l'esprit que l'application est très simple et n'a guère été testée, il y a donc de fortes chances que la méthode ne fonctionne pas si votre compte a une configuration différente. Merci d'avoir lu l'article, j'espère que vous avez apprécié un petit rappel de l'importance de tout remettre en question. Même les choses les plus inoffensives cachent parfois quelque chose de pas très agréable à l'intérieur (bien que dans le cas du gâteau à la crème glacée, cela se passe dans l'autre sens).