veilletech.fr
13 oct. Feed du jour
#07 SÉCURITÉ Faille

Google DevSite : la clé de signature dans le cookie

Une clé de signature qui dort dans ce qu'elle signe n'est plus une clé.

Le chercheur Simon Koeck a décodé le cookie de session Flask de developers.google.com : il contenait un objet OAuth sérialisé avec pickle, avec le secret client en clair, et ce secret servait aussi à signer le cookie. Il pouvait donc forger une session et faire désérialiser un pickle de son choix, soit une exécution de code à distance, prouvée par un délai. Récompense de 90 000 $, corrigé le 7 octobre.

3 min de lectureintermédiairevidéo 1:25
Partager
Sommaire7 sections
  1. Ce qui se passe
  2. Le mécanisme
  3. La preuve, sans toucher à la production
  4. Jusqu'où ça allait
  5. Ce qu'il fallait changer
  6. Chronologie
  7. À retenir

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.

Cookie de sessionObjet OAuth sérialiséSecret client en clairClé de signature FlaskCookie forgé et signépickle.loads au retourOAuthCode exécuté
Le secret qui signe le cookie est écrit dans le cookie

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 :

Ce qu'il fallait changer

Quatre choses, qui se cumulaient, et une seule aurait suffi à briser la chaîne :

  1. renouveler les deux secrets clients exposés et invalider les sessions signées avec eux ;
  2. donner à chaque déploiement sa propre SECRET_KEY, forte, tirée d'un stockage côté serveur et sans lien avec un identifiant OAuth ;
  3. garder l'objet de flux OAuth sur le serveur : le cookie ne porte qu'une référence opaque ;
  4. abandonner pickle pour tout ce qui traverse la frontière du client, au profit d'un format de données avec contrôle de schéma.

Chronologie

Source : Finding a $90,000 Remote Code Execution Vulnerability in Google, Simon Koeck, 12 octobre 2026.