1 puntos por GN⁺ 2024-10-18 | 1 comentarios | Compartir por WhatsApp
  • 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://policy a 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 ejecutar injectedScript, y por el retraso al bloquear el acceso en el momento de navegar a WebUI, o por solicitudes Page.reload que 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 loaderId en Page.reload, verificación de URL en inspectedWindow.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://settings y chrome://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 .exe en chrome://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/test permití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/test redirige a chrome://policy

Falla de validación en setLocalTestPolicies

  • El código JavaScript de chrome://policy/test usa sendWithPromise('setLocalTestPolicies', ...) para configurar políticas de prueba
    • sendWithPromise() es un wrapper de la API privada de WebUI chrome.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 setLocalTestPolicies desde la consola de chrome://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 HandleSetLocalTestPolicies solo comprobaba la existencia de local_test_provider, y no verificaba si las pruebas de políticas estaban realmente permitidas
  • LocalTestPolicyProvider::CreateIfAllowed() llama a IsPolicyTestingEnabled(nullptr, channel)
    • Como el primer argumento, pref_service, era null, se omitía la comprobación de PolicyTestPageEnabled
    • La única comprobación restante era si el release channel era CANARY o DEFAULT
  • En builds unbranded de Chromium, el código GOOGLE_CHROME_BRANDING no se compila, por lo que el canal queda como UNKNOWN
    • En el enum, UNKNOWN = 0 y DEFAULT = 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

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 AlternativeBrowserPath y AlternativeBrowserParameters, 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 BrowserSwitcherEnabled en true
    • Agregar example.com a BrowserSwitcherUrlList
    • Configurar AlternativeBrowserPath como /bin/bash en Linux
    • Configurar AlternativeBrowserParameters como ["-c", "xcalc # ${url}"]
  • 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
  • Tras configurar la política desde chrome://policy, llamar a window.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.scripting
    • chrome.tabs de Manifest v2
    • chrome.debugger
    • chrome.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.devtools deben tener el campo devtools_page en 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
  • 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 argumento injectedScript
  • Al llamar a inspectedWindow.reload() desde una página about:blank abierta por WebUI, fue posible ejecutar JavaScript en una página privilegiada
    • about:blank no tiene nada especial en su URL, pero hereda los permisos y el origin de la página que la abrió
    • Un about:blank abierto por chrome://settings era una página privilegiada con origin chrome://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:blank por sí sola era difícil de usar directamente en la exploit chain, porque chrome://policy no abre popups about:blank
  • Sin embargo, incluso en situaciones donde inspectedWindow.eval() fallaba, inspectedWindow.reload() ejecutaba JavaScript en chrome://settings
    • eval() tenía su propia comprobación equivalente a una verificación de origin, pero reload() 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 a chrome://policy y 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 solicitud Page.reload quedaba 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.reload
    • inspectedWindow.reload() internamente envía una solicitud Page.reload, por lo que se ve afectada por esta excepción
    • El parche de entonces no impedía que Page.reload pudiera 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 debugger porque 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 en chrome://policy
    • El payload llama a sendWithPromise("setLocalTestPolicies", policy) para configurar políticas de usuario
    • Configura BrowserSwitcherEnabled, BrowserSwitcherUrlList, AlternativeBrowserPath y AlternativeBrowserParameters
    • Activa Browser Switcher mediante window.open() o una navegación de página para ejecutar un comando de shell
  • El PoC usa comandos para abrir la calculadora según el sistema operativo
    • Windows: C:\Windows\System32\cmd.exe y calc.exe
    • Linux: /bin/bash y xcalc
    • macOS: /bin/bash y open -na Calculator
  • 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

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.reload funcionaba 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

 
GN⁺ 2024-10-18
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 de AlternativeBrowserParameters?

  • Que sea un estudiante de secundaria interesado en programación, desarrollo web y ciberseguridad es realmente impresionante.

    • Tiene un talento técnico increíble, perseverancia y excelentes habilidades de documentación y comunicación.
      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.

    • Vivo en Missouri, y una vez presioné F12; el gobernador intentó arrestarme.
  • Me recuerda a aquella vez que se usó la misma API para depurar el shell crosh de 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-internals porque era demasiado complejo, y luego agregó chrome://policy con soporte a medio terminar para editar JSON.