Ce qui se passe
Simon Koeck cartographiait le flux OAuth que partagent les sites de
documentation développeurs de Google. Un simple GET sur
developers.google.com/oauth2authorize, sans compte, pose un cookie de session
Flask. Celui-ci faisait environ 1 200 octets, bien plus que ce qu'il faut
pour un jeton CSRF et une adresse de retour. Il l'a décodé.
Le résultat : une faille d'exécution de code à distance sans authentification sur la plateforme DevSite de Google, récompensée 90 000 $ (75 000 $ de base pour une RCE de niveau S0, multipliés par 1,2 pour la qualité du rapport) et corrigée le 7 octobre 2026.
Le mécanisme
Un cookie de session Flask est signé, pas chiffré : son contenu est lisible par
quiconque le détient. Celui-ci portait, sous la clé google_oauth2_flow_<csrf>,
un objet OAuth2WebServerFlow sérialisé avec pickle, donc le client_id et
le client_secret de Google, en clair. Rien que cela était déjà un défaut.
Le point décisif : ce même client_secret servait de SECRET_KEY à Flask.
Avec ce secret, il recalcule la signature hors ligne, par la dérivation
cookie-session d'itsdangerous (HMAC SHA-1) : elle est identique à celle du
cookie reçu quelques secondes plus tôt. Un caractère modifié dans la signature
est refusé ; le contenu d'origine re-signé avec le secret est accepté.
Le retour OAuth lit le flux dans la session et appelle pickle.loads dessus.
Ce n'est « sûr » que tant que personne d'autre ne peut fabriquer un cookie
valide. Il suffit donc de monter une session avec un jeton CSRF au choix et un
pickle de son choix sous la clé correspondante : le paramètre state est lui
aussi à lui, rien n'a à s'aligner côté serveur.
La preuve, sans toucher à la production
Pas de commande lancée sur les serveurs de Google : le pickle appelait
time.sleep(N), et le temps de réponse suivait N.
time.sleep(N) |
Réponse |
|---|---|
| 0 | 0,207 s |
| 3 | 3,193 s |
| 5 | 5,194 s |
| 8 | 8,283 s |
Les contrôles écartent un point d'accès lent : sans cookie ou avec une signature
altérée, Invalid request ; signature valide sans flux, Invalid request state ; signature valide avec le vrai flux, échec plus loin sur
invalid_grant.
Jusqu'où ça allait
DevSite est une plateforme partagée : la clé de developers.google.com signait
des sessions valides sur :
firebase.google.comdeveloper.android.comsource.android.comdocs.partner.android.com
Ce qu'il fallait changer
Quatre choses, qui se cumulaient, et une seule aurait suffi à briser la chaîne :
- renouveler les deux secrets clients exposés et invalider les sessions signées avec eux ;
- donner à chaque déploiement sa propre
SECRET_KEY, forte, tirée d'un stockage côté serveur et sans lien avec un identifiant OAuth ; - garder l'objet de flux OAuth sur le serveur : le cookie ne porte qu'une référence opaque ;
- abandonner
picklepour tout ce qui traverse la frontière du client, au profit d'un format de données avec contrôle de schéma.
Chronologie
- 6 août 2026, 22 h 48 : rapport envoyé
- 7 août : trié, puis accepté
- 27 août : récompense fixée à 90 000 $
- 7 octobre, 15 h 20 : corrigé
Source : Finding a $90,000 Remote Code Execution Vulnerability in Google, Simon Koeck, 12 octobre 2026.