- Los límites de permisos de WebUI de Chromium, la función de prueba de políticas empresariales y una falla en la API de extensiones de DevTools se combinaron, permitiendo que una extensión maliciosa de Chrome llegara a ejecutar comandos de shell con apenas un poco de interacción del usuario
- La ruta de ataque consistía en llamar desde
chrome://policya una API de prueba de políticas no documentada para modificar políticas de usuario, y abusar de la ruta y los argumentos del navegador alternativo de Browser Switcher como comandos de shell chrome.devtools.inspectedWindow.reload()permitía ejecutarinjectedScript, y por el retraso al bloquear el acceso en el momento de navegar a WebUI, o por solicitudesPage.reloadque quedaban tras un crash, podía ejecutarse código en una WebUI privilegiada- Google clasificó la vulnerabilidad como P1/S1 y agregó validación de
loaderIdenPage.reload, verificación de URL eninspectedWindow.reload()y comprobación de activación de pruebas de políticas en el handler de WebUI - Las vulnerabilidades relacionadas fueron asignadas como CVE-2024-5836 y CVE-2024-6778; ambas recibieron CVSS 8.8 High, y la recompensa final fue de $20,000
WebUI de Chromium y los límites del sandbox
- Chromium ejecuta código no confiable dentro de un sandbox, y el JavaScript de las extensiones de Chrome también debería funcionar solo dentro de los permisos concedidos y las API accesibles
- Solo con permisos de extensión ya se pueden robar credenciales o el historial del navegador, pero en principio el alcance del impacto debería quedarse dentro del navegador
- Parte de la GUI de Chromium está implementada como WebUI, por ejemplo
chrome://settingsychrome://history- WebUI está escrita en HTML, CSS y JavaScript, pero como debe mostrar y modificar información interna del navegador, tiene más privilegios que una página web normal
- El JavaScript del frontend de WebUI puede comunicarse con el código nativo C++ del navegador mediante API privadas
- Si se logra ejecutar código en WebUI, esto puede derivar en una evasión del sandbox de Chromium, por lo que es importante impedir que un atacante ejecute JavaScript no confiable en páginas
chrome:// - Por ejemplo, si se hace clic en un elemento de descarga
.exeenchrome://downloads, puede abrirse un ejecutable, así que Chromium verifica que la acción de abrir archivos provenga de una entrada real del usuario
Evasión de la función de prueba de políticas empresariales
- La búsqueda de vulnerabilidades comenzó en el sistema de políticas empresariales de Chromium
- Este sistema permite que administradores apliquen configuraciones específicas de forma obligatoria en dispositivos propiedad de una empresa o escuela
- Por lo general, las políticas están asociadas a una cuenta de Google y se descargan desde servidores de administración de Google
- Las políticas se dividen en device policies y user policies
- Las device policies administran la configuración global de dispositivos Chrome OS
- Las user policies se aplican a un usuario específico o a una instancia del navegador, y están disponibles en todas las plataformas
- En Linux, se pueden aplicar user policies a una instancia de Google Chrome colocando archivos JSON en
/etc/opt/chrome/policies, pero para escribir en ese directorio se necesitan permisos de root
- Las políticas aplicadas actualmente al dispositivo pueden consultarse en la WebUI
chrome://policy- Esta página ofrece una lista de políticas aplicadas, logs del servicio de políticas y una función para exportar JSON
- Normalmente no hay forma de editar políticas desde esta página
- Las notas de lanzamiento de Chrome Enterprise para Chrome v117 incluían un punto indicando que la página
chrome://policy/testpermitía probar políticas en los canales Beta, Dev y Canary- Esta función no se mencionaba en la documentación de Chromium fuera de esas notas de lanzamiento
- Para activarla correctamente se requiere la política no documentada
PolicyTestPageEnabled - Sin esta política,
chrome://policy/testredirige achrome://policy
Falla de validación en setLocalTestPolicies
- El código JavaScript de
chrome://policy/testusasendWithPromise('setLocalTestPolicies', ...)para configurar políticas de pruebasendWithPromise()es un wrapper de la API privada de WebUIchrome.send()- Esta llamada envía una solicitud a una función handler en C++, y el handler puede realizar operaciones internas del navegador
- Al invocar directamente
setLocalTestPoliciesdesde la consola dechrome://policy, al principio el navegador crasheó y en los logs quedó un mensaje indicando que se necesitaba un array de políticas - Al ajustar el formato del array de políticas y enviar una user policy como
AllowDinosaurEasterEgg, se configuró una política arbitraria aunque la función no se había activado explícitamente - Del lado de C++, el handler
HandleSetLocalTestPoliciessolo comprobaba la existencia delocal_test_provider, y no verificaba si las pruebas de políticas estaban realmente permitidas LocalTestPolicyProvider::CreateIfAllowed()llama aIsPolicyTestingEnabled(nullptr, channel)- Como el primer argumento,
pref_service, eranull, se omitía la comprobación dePolicyTestPageEnabled - La única comprobación restante era si el release channel era
CANARYoDEFAULT
- Como el primer argumento,
- En builds unbranded de Chromium, el código
GOOGLE_CHROME_BRANDINGno se compila, por lo que el canal queda comoUNKNOWN- En el enum,
UNKNOWN = 0yDEFAULT = UNKNOWN, así que en Chromium y builds derivados la comprobación de canal se supera - En builds branded de Google Chrome stable, el release channel se configura correctamente, por lo que este bug no funciona en Google Chrome stable
- En el enum,
Ejecución de comandos de shell a través de Browser Switcher
- Al poder configurar user policies arbitrarias, el módulo Legacy Browser Support de las políticas empresariales de Chrome se convirtió en una ruta para escapar del sandbox
- Legacy Browser Support también se conoce como Browser Switcher, y está diseñado para ejecutar un navegador alternativo cuando el usuario visita ciertas URL en Chromium
- Es una función creada para dar soporte a usuarios de Internet Explorer
- Su comportamiento se controla mediante políticas
- Al combinar las políticas
AlternativeBrowserPathyAlternativeBrowserParameters, Chromium puede ejecutar comandos de shell arbitrarios como “navegador alternativo”- Estas políticas de Browser Switcher existen solo en Linux, macOS y Windows
- Un flujo de ejemplo es el siguiente
- Configurar
BrowserSwitcherEnabledentrue - Agregar
example.comaBrowserSwitcherUrlList - Configurar
AlternativeBrowserPathcomo/bin/bashen Linux - Configurar
AlternativeBrowserParameterscomo["-c", "xcalc # ${url}"]
- Configurar
- Cuando el navegador navega a
example.com, Browser Switcher se activa y se ejecuta un comando con la forma/bin/bash -c 'xcalc # https://example.com'- El valor sustituido de
${url}se coloca después de#para que sea tratado como comentario de shell
- El valor sustituido de
- Tras configurar la política desde
chrome://policy, llamar awindow.open("https://example.com")permite llegar, solo con JavaScript, a la ejecución de comandos de shell arbitrarios
Ruta de evasión en la API de extensiones de DevTools
- Con solo los pasos anteriores, la víctima tendría que pegar código malicioso en la consola del navegador dentro de
chrome://policy, así que la practicidad era baja - La ruta de ejecución automática se encontró mediante una extensión maliciosa de Chrome
- Una extensión puede insertar JavaScript en páginas, pero no debería poder ejecutar JavaScript en páginas WebUI privilegiadas
- Había cuatro API principales con las que una extensión ejecuta JavaScript en páginas
chrome.scriptingchrome.tabsde Manifest v2chrome.debuggerchrome.devtools.inspectedWindow
- El objetivo de la investigación fue
chrome.devtools.inspectedWindow, considerando que probablemente estaba relativamente menos reforzada- Las extensiones que usan la API
chrome.devtoolsdeben tener el campodevtools_pageen el manifest - Cuando el usuario abre DevTools, esa página se carga como un iframe, y dentro de ella se puede usar la API
chrome.devtools
- Las extensiones que usan la API
- Un reporte de bug anterior de David Erceg mostraba un caso en el que se llegó a ejecutar código en WebUI usando
chrome.devtools.inspectedWindow.eval()- Normalmente, si la página inspeccionada navega a WebUI, el uso de la API de DevTools debería desactivarse
- La clave de la evasión era enviar una solicitud de eval antes de que Chrome desactivara la API, y hacer que esa solicitud llegara a la página WebUI
inspectedWindow.reload() y la particularidad de about:blank
chrome.devtools.inspectedWindow.reload()también puede ejecutar JavaScript en la página inspeccionada si recibe el argumentoinjectedScript- Al llamar a
inspectedWindow.reload()desde una páginaabout:blankabierta por WebUI, fue posible ejecutar JavaScript en una página privilegiadaabout:blankno tiene nada especial en su URL, pero hereda los permisos y el origin de la página que la abrió- Un
about:blankabierto porchrome://settingsera una página privilegiada con originchrome://settings
- El código que desactiva la API de DevTools solo comprobaba la URL del objetivo inspeccionado, no su origin
- Aunque la URL pareciera normal, el origin podía ser privilegiado
- La ruta mediante
about:blankpor sí sola era difícil de usar directamente en la exploit chain, porquechrome://policyno abre popupsabout:blank - Sin embargo, incluso en situaciones donde
inspectedWindow.eval()fallaba,inspectedWindow.reload()ejecutaba JavaScript enchrome://settingseval()tenía su propia comprobación equivalente a una verificación de origin, peroreload()no tenía una comprobación del mismo nivel
Estabilización desde una race condition a un método basado en crash
- La primera exploit chain repetía llamadas a
inspectedWindow.reload()para apuntar a la breve ventana de tiempo justo después de que la página inspeccionada navegara a WebUI y antes de que la página de DevTools desactivara la API- La premisa era que la página inspeccionada y la página de DevTools estaban en procesos distintos
- Si una solicitud
reload()entraba entre el momento de navegar achrome://policyy la desactivación de la API de DevTools, se ejecutaba código en WebUI
- Este método funcionaba, pero era poco confiable
- Tras ajustes, tenía alrededor de 70% de éxito
- Era una vulnerabilidad grave, pero la inestabilidad podía reducir su severity
- Luego, igual que en el enfoque anterior de David Erceg, se probó si el comportamiento de solicitudes de depurador que quedan pendientes tras un crash de pestaña también podía aplicarse a
inspectedWindow.reload() - Al disparar dos veces seguidas la instrucción
debugger, la pestaña crasheaba, y una solicitudPage.reloadquedaba en la cola y podía ejecutarse después de navegar a WebUI- Este método eliminó la necesidad de una race condition y funcionaba de forma 100% reliable
- En un parche de un bug anterior, Google había corregido el comportamiento para borrar las solicitudes de depurador pendientes tras un crash, pero dejó como excepción las solicitudes
Page.reloadinspectedWindow.reload()internamente envía una solicitudPage.reload, por lo que se ve afectada por esta excepción- El parche de entonces no impedía que
Page.reloadpudiera ejecutar scripts
- El crash de la pestaña también podía provocarse con un método que generara falta de memoria, pero en el PoC final se usó el crash con
debuggerporque era más rápido
Exploit chain final e interacción del usuario
- El PoC final funciona en el siguiente orden
- Usa la vulnerabilidad de
chrome.devtools.inspectedWindow.reload()para ejecutar un payload JavaScript enchrome://policy - El payload llama a
sendWithPromise("setLocalTestPolicies", policy)para configurar políticas de usuario - Configura
BrowserSwitcherEnabled,BrowserSwitcherUrlList,AlternativeBrowserPathyAlternativeBrowserParameters - Activa Browser Switcher mediante
window.open()o una navegación de página para ejecutar un comando de shell
- Usa la vulnerabilidad de
- El PoC usa comandos para abrir la calculadora según el sistema operativo
- Windows:
C:\Windows\System32\cmd.exeycalc.exe - Linux:
/bin/bashyxcalc - macOS:
/bin/bashyopen -na Calculator
- Windows:
- La interacción del usuario se limitaba a hacer que abriera DevTools
- El “extension install error” de la pantalla de ejemplo era un recurso para engañar al usuario y lograr que abriera DevTools
- Una vez abierto DevTools, comienza la chain que deriva en el escape del sandbox
Correcciones de Google y asignación de CVE
- Tras el reporte, Google confirmó rápidamente la vulnerabilidad y la clasificó como P1/S1
- P1/S1 significa high priority y high severity
- Durante las semanas siguientes se aplicaron tres correcciones principales
- Agregar el argumento
loaderIdal comandoPage.reloady verificación deloaderIDdel lado del renderer- Para que el comando sea válido solo para un único origin y no funcione aunque llegue por accidente a una página privilegiada
- Verificación de URL en la función
inspectedWindow.reload()- Ya no depende únicamente de la revocación del acceso a la API de la extensión
- Comprobación de si las pruebas de políticas están activadas en el handler de WebUI
- Para bloquear por completo las políticas de prueba
- Agregar el argumento
- La vulnerabilidad relacionada con la race condition fue asignada como CVE-2024-5836
- Su CVSS severity score es 8.8 High
- La vulnerabilidad relacionada con el crash de la página inspeccionada fue asignada como CVE-2024-6778
- Esta vulnerabilidad también recibió un CVSS severity score de 8.8
- Después de que las correcciones se fusionaron en la rama de lanzamiento, el panel de Chrome VRP decidió la recompensa, y el monto final fue de $20,000
Calendario de divulgación y materiales
- La línea de tiempo fue la siguiente
- 16 de abril: descubrimiento del bug de test policies
- 29 de abril: descubrimiento del bug de race condition en
inspectedWindow.reload() - 1 de mayo: reporte del bug a Google
- 4 de mayo: Google lo clasifica como P1/S1
- 5 de mayo: descubrimiento del bug relacionado con el crash de la página inspeccionada y actualización del reporte
- 6 de mayo: Google solicita reportes separados para cada parte de la chain
- 8 de julio: el reporte del bug se marca como fixed
- 13 de julio: se envía al panel de Chrome VRP para decidir la recompensa
- 17 de julio: el panel de VRP decide la recompensa de $20,000
- 15 de octubre: se publica el reporte completo del bug
- El reporte original relacionado puede verse en crbug.com/338248595
- Los PoC de cada parte de la vulnerabilidad están publicados en un repositorio de GitHub
- El bug de
inspectedWindow.reloadfuncionaba desde Chrome v45 - Si se distribuyen a todos los usuarios funciones no documentadas, inmaduras e inseguras, simples errores pueden combinarse y terminar en vulnerabilidades de alta gravedad
1 comentarios
Opiniones de Hacker News
Dijeron que la URL de la página se reemplaza por
${url}y que, si se coloca después de#, queda como comentario para no romper el comando. Pero ¿hay en esta política alguna lógica de validación que exija que la URL deba pasarse a algún lugar deAlternativeBrowserParameters?Que sea un estudiante de secundaria interesado en programación, desarrollo web y ciberseguridad es realmente impresionante.
También es notable su ética profesional al seguir incluso el proceso de divulgación responsable; parece alguien que llegará muy lejos.
Fue un gran artículo y un gran trabajo; se sentía como acompañar el proceso en el que la emoción iba creciendo a medida que se sucedían los hallazgos.
Sin duda merece una buena recompensa.
La cadena de vulnerabilidades es prolija y el artículo es excelente. También me gustó cómo desglosó el funcionamiento del código vulnerable.
Trucos simples como “presiona F12 para intentarlo de nuevo” me asombran cada vez que los veo; son realmente traviesos.
Me recuerda a aquella vez que se usó la misma API para depurar el shell
croshde Chrome OS, evadir las protecciones del sistema operativo y obtener acceso root en un dispositivo de desarrollo. Fue CVE-2014-3172.Dicho eso, el autor de este artículo tuvo que superar obstáculos mucho más difíciles; es un trabajo excelente.
Es muy tarde, así que me cuesta profundizar en qué se rompió en la validación de WebUI, pero me gusta que haya llegado hasta el fondo para descubrirlo.
Sospechar y desconfiar de la cadena de herramientas de lo que desplegamos es una postura bastante estándar, pero al mismo tiempo confiamos demasiado en las herramientas de desarrollo mágicamente convenientes de grandes empresas como Google o Microsoft. Al final, es porque quiero escribir y probar mi código, en vez de preocuparme por lo que pueda estar escondido dentro de Chromium o VSCode.
Es uno de los mejores artículos que he leído.
Fue un trabajo de rastreo realmente inteligente.
Es impresionante el esfuerzo de revisar el código del navegador hasta encontrar todo esto, y el artículo también es muy interesante y detallado.
¿Un estudiante de secundaria? Wow, de verdad impresionante.
El proyecto Chromium eliminó
chrome://net-internalsporque era demasiado complejo, y luego agregóchrome://policycon soporte a medio terminar para editar JSON.