1 points par GN⁺ 2024-10-18 | 1 commentaires | Partager sur WhatsApp
  • La combinaison de la frontière de privilèges WebUI de Chromium, de la fonctionnalité de test des stratégies d’entreprise et d’une faille dans l’API d’extension DevTools permettait à une extension Chrome malveillante, avec seulement une légère interaction utilisateur, d’aller jusqu’à l’exécution de commandes shell
  • Le chemin d’attaque consistait à appeler depuis chrome://policy une API de test de stratégies non documentée pour modifier les stratégies utilisateur, puis à détourner le chemin et les arguments du navigateur alternatif de Browser Switcher en commandes shell
  • chrome.devtools.inspectedWindow.reload() autorisait l’exécution de injectedScript, et du code pouvait s’exécuter dans une WebUI privilégiée à cause du délai de blocage de l’accès lors d’une navigation vers WebUI, ou d’une requête Page.reload restée en attente après un crash
  • Google a classé la vulnérabilité P1/S1 et ajouté une vérification du loaderId de Page.reload, une vérification d’URL dans inspectedWindow.reload(), ainsi qu’un contrôle de l’activation des tests de stratégies dans les gestionnaires WebUI
  • Les vulnérabilités associées ont reçu les identifiants CVE-2024-5836 et CVE-2024-6778 ; toutes deux ont obtenu un score CVSS 8.8 High, et la récompense finale a été de 20 000 $

Chromium WebUI et frontière du bac à sable

  • Chromium exécute le code non fiable dans un bac à sable, et le JavaScript des extensions Chrome doit lui aussi fonctionner uniquement dans les limites des autorisations accordées et des API accessibles
  • Les seules permissions d’extension peuvent déjà permettre de voler des identifiants ou l’historique du navigateur, mais, en principe, la portée de l’impact doit rester à l’intérieur du navigateur
  • Une partie de l’interface graphique de Chromium est implémentée sous forme de WebUI, comme chrome://settings ou chrome://history
    • Les WebUI sont écrites en HTML, CSS et JavaScript, mais comme elles doivent afficher et modifier des informations internes au navigateur, elles disposent de privilèges plus élevés que les pages web ordinaires
    • Le JavaScript du frontend WebUI peut communiquer avec le code C++ natif du navigateur via des API privées
  • Si l’exécution de code devient possible dans une WebUI, cela peut mener à un contournement du bac à sable de Chromium ; il est donc important d’empêcher un attaquant d’exécuter du JavaScript non fiable sur des pages chrome://
  • Par exemple, cliquer sur un téléchargement .exe dans chrome://downloads peut ouvrir l’exécutable ; Chromium vérifie donc que l’action d’ouverture du fichier provient bien d’une saisie utilisateur réelle

Contournement de la fonctionnalité de test des stratégies d’entreprise

  • La recherche de vulnérabilités a commencé dans le système de stratégies d’entreprise de Chromium
    • Ce système permet aux administrateurs d’imposer certains paramètres sur des appareils appartenant à une entreprise ou à une école
    • Les stratégies sont généralement liées à un compte Google et téléchargées depuis les serveurs d’administration de Google
  • Les stratégies se divisent en stratégies d’appareil et stratégies utilisateur
    • Les stratégies d’appareil gèrent les paramètres globaux des appareils Chrome OS
    • Les stratégies utilisateur s’appliquent à un utilisateur ou à une instance de navigateur spécifique, et sont disponibles sur toutes les plateformes
    • Sous Linux, il est possible d’appliquer des stratégies utilisateur à une instance de Google Chrome en plaçant des fichiers JSON dans /etc/opt/chrome/policies, mais l’écriture dans ce répertoire nécessite les privilèges root
  • Les stratégies actuellement appliquées à l’appareil peuvent être consultées dans la WebUI chrome://policy
    • Cette page fournit la liste des stratégies appliquées, les journaux du service de stratégies et une fonction d’export JSON
    • En général, il n’existe aucun moyen de modifier les stratégies depuis cette page
  • Les notes de version Chrome Enterprise de Chrome v117 mentionnaient une page chrome://policy/test permettant de tester des stratégies dans les canaux Beta, Dev et Canary
    • Cette fonctionnalité n’était mentionnée dans la documentation de Chromium nulle part ailleurs que dans ces notes de version
    • Pour l’activer normalement, une stratégie non documentée PolicyTestPageEnabled est nécessaire
    • Sans cette stratégie, chrome://policy/test redirige vers chrome://policy

Échec de validation dans setLocalTestPolicies

  • Le code JavaScript de chrome://policy/test utilise sendWithPromise('setLocalTestPolicies', ...) pour définir des stratégies de test
    • sendWithPromise() est un wrapper autour de chrome.send(), une API privée de WebUI
    • Cet appel envoie une requête à une fonction gestionnaire C++, qui peut effectuer des opérations internes au navigateur
  • En appelant directement setLocalTestPolicies depuis la console de chrome://policy, le navigateur a d’abord planté, et les journaux indiquaient qu’un tableau de stratégies était nécessaire
  • En fournissant une stratégie utilisateur comme AllowDinosaurEasterEgg au bon format de tableau de stratégies, une stratégie arbitraire a été définie alors même que la fonctionnalité n’avait pas été explicitement activée
  • Côté C++, le gestionnaire HandleSetLocalTestPolicies vérifiait seulement l’existence de local_test_provider, sans vérifier si la fonctionnalité de test de stratégies était réellement autorisée
  • LocalTestPolicyProvider::CreateIfAllowed() appelle IsPolicyTestingEnabled(nullptr, channel)
    • Le premier argument, pref_service, étant null, la vérification de PolicyTestPageEnabled est ignorée
    • Le seul contrôle restant consiste à vérifier si le canal de publication est CANARY ou DEFAULT
  • Dans les builds Chromium non brandés, le code GOOGLE_CHROME_BRANDING n’est pas compilé, si bien que le canal reste UNKNOWN
    • Dans l’enum, UNKNOWN = 0 et DEFAULT = UNKNOWN, donc le contrôle de canal réussit dans Chromium et les builds dérivés
    • Dans les builds stables brandés Google Chrome, le canal de publication est correctement défini ; ce bug ne fonctionne donc pas dans Google Chrome stable

Exécution de commandes shell via Browser Switcher

  • Une fois la définition arbitraire de stratégies utilisateur possible, le module Legacy Browser Support des stratégies d’entreprise de Chrome est devenu un chemin de sortie du bac à sable
  • Legacy Browser Support est aussi appelé Browser Switcher et est conçu pour lancer un navigateur alternatif lorsqu’un utilisateur visite certaines URL dans Chromium
    • Cette fonctionnalité a été créée pour prendre en charge les utilisateurs d’Internet Explorer
    • Son comportement est contrôlé par des stratégies
  • En combinant les stratégies AlternativeBrowserPath et AlternativeBrowserParameters, Chromium peut exécuter une commande shell arbitraire en tant que « navigateur alternatif »
    • Ces stratégies Browser Switcher existent uniquement sous Linux, macOS et Windows
  • Le déroulement d’exemple est le suivant
    • Définir BrowserSwitcherEnabled à true
    • Ajouter example.com à BrowserSwitcherUrlList
    • Définir AlternativeBrowserPath sur /bin/bash sous Linux
    • Définir AlternativeBrowserParameters sur ["-c", "xcalc # ${url}"], par exemple
  • Lorsque le navigateur se rend sur example.com, Browser Switcher s’active et une commande de la forme /bin/bash -c 'xcalc # https://example.com' est exécutée
    • La valeur substituée à ${url} est placée après # afin d’être traitée comme un commentaire shell
  • Après avoir défini les stratégies depuis chrome://policy, appeler window.open("https://example.com";) suffit pour aller jusqu’à l’exécution d’une commande shell arbitraire uniquement via JavaScript

Voie de contournement via l’API d’extension DevTools

  • Les étapes précédentes imposaient à la victime de coller du code malveillant dans la console du navigateur sur chrome://policy, ce qui était peu pratique
  • Une voie d’exécution automatique a été trouvée via une extension Chrome malveillante
    • Les extensions peuvent injecter du JavaScript dans des pages, mais elles ne devraient pas pouvoir exécuter de JavaScript dans des pages WebUI privilégiées
  • Il existait quatre API principales permettant à une extension d’exécuter du JavaScript dans une page
    • chrome.scripting
    • chrome.tabs dans Manifest v2
    • chrome.debugger
    • chrome.devtools.inspectedWindow
  • L’investigation s’est concentrée sur chrome.devtools.inspectedWindow, jugée probablement moins durcie
    • Les extensions qui utilisent l’API chrome.devtools doivent avoir un champ devtools_page dans leur manifeste
    • Quand l’utilisateur ouvre DevTools, cette page est chargée dans une iframe, depuis laquelle les API chrome.devtools peuvent être utilisées
  • Un précédent rapport de bug de David Erceg décrivait un cas où chrome.devtools.inspectedWindow.eval() permettait d’atteindre l’exécution de code dans WebUI
    • Normalement, lorsque la page inspectée navigue vers une WebUI, l’utilisation des API DevTools devrait être désactivée
    • Le cœur du contournement consistait à envoyer une requête eval avant que Chrome ne désactive l’API, puis à faire arriver cette requête sur la page WebUI

inspectedWindow.reload() et particularité de about:blank

  • chrome.devtools.inspectedWindow.reload() peut lui aussi exécuter du JavaScript dans la page inspectée lorsqu’il reçoit un argument injectedScript
  • En appelant inspectedWindow.reload() depuis une page about:blank ouverte par une WebUI, l’exécution de JavaScript dans une page privilégiée devenait possible
    • L’URL about:blank n’a rien de particulier en elle-même, mais elle hérite des privilèges et de l’origine de la page qui l’a ouverte
    • Une page about:blank ouverte par chrome://settings était une page privilégiée dont l’origine était chrome://settings
  • Le code de désactivation de l’API DevTools vérifiait uniquement l’URL de la cible inspectée, pas son origine
    • Même si l’URL semble ordinaire, l’origine peut être une origine privilégiée
  • Le chemin via about:blank seul était difficile à utiliser directement dans une chaîne d’exploitation, car chrome://policy n’ouvre pas de pop-up about:blank
  • Cependant, même dans des situations où inspectedWindow.eval() échouait, inspectedWindow.reload() exécutait du JavaScript dans chrome://settings
    • Cela montrait que eval() disposait de ses propres vérifications équivalentes à un contrôle d’origine, contrairement à reload() qui n’avait pas le même niveau de contrôle

Stabilisation d’une race condition par une méthode basée sur un crash

  • La première chaîne d’exploitation appelait inspectedWindow.reload() en boucle pour viser la courte fenêtre de temps entre la navigation de la page inspectée vers une WebUI et la désactivation de l’API par la page DevTools
    • Elle supposait que la page inspectée et la page DevTools se trouvaient dans des processus distincts
    • Si une requête reload() arrivait entre l’instant où la page naviguait vers chrome://policy et la désactivation de l’API DevTools, du code s’exécutait dans la WebUI
  • Cette méthode fonctionnait, mais sa fiabilité était faible
    • Après réglage, le taux de réussite atteignait environ 70 %
    • Il s’agissait d’une vulnérabilité grave, mais son instabilité pouvait en réduire la sévérité
  • Ensuite, comme dans l’approche précédente de David Erceg, il a été testé si le comportement laissant des requêtes de débogage après un crash d’onglet pouvait aussi s’appliquer à inspectedWindow.reload()
  • En déclenchant deux instructions debugger d’affilée, l’onglet plantait, et une requête Page.reload restait en file d’attente, pouvant s’exécuter après la navigation vers une WebUI
    • Cette méthode ne nécessitait plus de race condition et fonctionnait de façon fiable à 100 %
  • Dans le correctif du bug précédent, Google avait modifié Chrome pour supprimer les requêtes de débogage en attente après un crash, mais les requêtes Page.reload avaient été laissées comme exception
    • inspectedWindow.reload() envoie en interne une requête Page.reload, il est donc affecté par cette exception
    • Le correctif de l’époque n’empêchait pas Page.reload d’exécuter des scripts
  • Un crash d’onglet était aussi possible en provoquant un manque de mémoire, mais le PoC final a utilisé le crash plus rapide via debugger

Chaîne d’exploitation finale et interaction utilisateur

  • Le PoC final fonctionne dans l’ordre suivant
    • Utiliser la vulnérabilité chrome.devtools.inspectedWindow.reload() pour exécuter un payload JavaScript dans chrome://policy
    • Le payload appelle sendWithPromise("setLocalTestPolicies", policy) pour définir des stratégies utilisateur
    • Définir BrowserSwitcherEnabled, BrowserSwitcherUrlList, AlternativeBrowserPath et AlternativeBrowserParameters
    • Déclencher Browser Switcher via window.open() ou une navigation de page afin d’exécuter une commande shell
  • Le PoC utilise des commandes qui lancent la calculatrice selon l’OS
    • Windows : C:\Windows\System32\cmd.exe et calc.exe
    • Linux : /bin/bash et xcalc
    • macOS : /bin/bash et open -na Calculator
  • L’interaction utilisateur se limitait à amener la victime à ouvrir DevTools
    • Le message « extension install error » affiché en exemple servait à tromper l’utilisateur pour qu’il ouvre DevTools
    • Une fois DevTools ouvert, la chaîne menant à la sortie du bac à sable démarrait

Correctifs de Google et attribution des CVE

Calendrier de divulgation et ressources

  • La chronologie est la suivante
    • 16 avril : découverte du bug des stratégies de test
    • 29 avril : découverte du bug de race condition dans inspectedWindow.reload()
    • 1er mai : signalement du bug à Google
    • 4 mai : classification P1/S1 par Google
    • 5 mai : découverte du bug lié au crash de la page inspectée, puis mise à jour du signalement
    • 6 mai : Google demande des rapports de bug séparés pour chaque partie de la chaîne
    • 8 juillet : le rapport de bug est marqué comme corrigé
    • 13 juillet : transmission au panel Chrome VRP pour décision sur la récompense
    • 17 juillet : le panel VRP fixe la récompense à 20 000 $
    • 15 octobre : publication du rapport de bug complet
  • Le rapport de bug original associé est consultable sur crbug.com/338248595
  • Les PoC des différentes parties de la vulnérabilité sont publiés dans un dépôt GitHub
  • Le bug inspectedWindow.reload fonctionnait jusqu’à Chrome v45
  • Déployer auprès de tous les utilisateurs des fonctionnalités non documentées, peu finalisées et non sûres peut transformer de simples erreurs combinées en vulnérabilités de forte sévérité

1 commentaires

 
GN⁺ 2024-10-18
Avis sur Hacker News
  • Il était dit que l’URL de la page est remplacée par ${url}, et que si on la place après #, elle devient un commentaire pour ne pas casser la commande ; mais cette stratégie contient-elle une logique de validation exigeant que l’URL soit transmise quelque part dans AlternativeBrowserParameters ?

  • Un lycéen intéressé par la programmation, le développement web et la cybersécurité, c’est vraiment impressionnant

    • Un talent technique remarquable, avec beaucoup de persévérance, et d’excellentes capacités de documentation et de communication
      Son éthique professionnelle est aussi admirable, jusqu’au respect d’une procédure de divulgation responsable ; il semble promis à un grand avenir
  • Excellent article et excellent travail ; on avait l’impression de suivre avec lui l’excitation qui montait à mesure que les découvertes s’enchaînaient
    La récompense était amplement méritée

  • L’enchaînement des vulnérabilités est propre et l’article est excellent. J’ai aussi apprécié la façon dont il décortique le fonctionnement du code vulnérable
    Les astuces toutes simples du genre « Appuyez sur F12 pour réessayer » m’épatent à chaque fois, c’est vraiment malicieux

    • J’habite dans le Missouri, et une fois, il y a longtemps, j’ai appuyé sur F12 ; le gouverneur a essayé de me faire arrêter
  • Ça me rappelle l’époque où j’avais utilisé la même API pour déboguer le shell crosh de Chrome OS, contourner les protections de l’OS et obtenir un accès root sur une machine de développeur. C’était CVE-2014-3172
    Cela dit, l’auteur de cet article a dû contourner des obstacles bien plus difficiles, et son travail est vraiment excellent

  • Il est trop tard ce soir pour creuser en détail ce qui a cassé dans la validation WebUI, mais j’apprécie qu’il soit allé jusqu’au bout pour le découvrir
    Se méfier de la chaîne d’outils de ce que nous déployons est une attitude assez standard, mais en même temps nous faisons énormément confiance aux outils de développement magiquement pratiques de grandes entreprises comme Google ou Microsoft. Au final, c’est parce qu’on veut écrire et tester notre code plutôt que de s’inquiéter de ce qui se cache dans Chromium ou VSCode

  • C’est l’un des meilleurs articles que j’aie lus
    Un travail de traque vraiment brillant

  • L’effort nécessaire pour fouiller dans le code du navigateur et arriver jusque-là est impressionnant, et l’article est très intéressant et détaillé

  • Un lycéen, vraiment ? Waouh, c’est incroyable

  • Le projet Chromium a supprimé chrome://net-internals au motif que c’était trop complexe, puis a ajouté chrome://policy avec une prise en charge à moitié finie de l’édition JSON