1 puntos por GN⁺ 2025-06-10 | Aún no hay comentarios. | Compartir por WhatsApp
  • El formulario de recuperación de nombre de usuario sin JS de Google permitía verificar combinaciones de número de teléfono y nombre visible, creando una cadena de ataque capaz de descubrir el número completo de teléfono de un usuario específico de Google
  • La clave estaba en la diferencia de redirección entre /signin/usernamerecovery y /signin/usernamerecovery/lookup, lo que permitía distinguir entre el estado de cuenta inexistente y cuenta existente
  • Había limitación de solicitudes por IP y CAPTCHA, pero al poner el token de BotGuard del formulario con JS en el bgresponse del formulario sin JS, funcionaba sin restricciones, y además se combinaba con rotación de IPv6
  • El atacante podía obtener el nombre visible de la cuenta de Google mediante Looker Studio, y reducir mucho los candidatos combinándolo con el número telefónico enmascarado del flujo de recuperación de contraseña y los formatos de numeración por país
  • En un servidor de 16 vCPU por $0.30 la hora era posible hacer unas 40 mil verificaciones por segundo; Google retiró por completo el formulario de recuperación de nombre de usuario sin JS el 6 de junio de 2025 y pagó un total de $5,000

Vulnerabilidad iniciada en el formulario de recuperación de nombre de usuario sin JS

  • Mientras se comprobaba el funcionamiento de los servicios de Google con JavaScript desactivado en el navegador, se descubrió que el formulario de recuperación de nombre de usuario seguía funcionando
  • Durante mucho tiempo se pensó que el formulario de recuperación de cuenta requería JavaScript porque dependía de defensas de la familia BotGuard generadas por código JavaScript de proof-of-work ofuscado
  • Sin embargo, el formulario sin JS ofrecía un flujo que permitía comprobar si un correo de recuperación o un número de teléfono estaba vinculado a un nombre visible específico

Confirmación de existencia de cuenta con dos solicitudes

  • La primera solicitud consistía en enviar el número de teléfono a /signin/usernamerecovery y obtener en el Location de la respuesta el valor ess vinculado a ese número
    • La cookie y el valor gxf se obtenían del HTML de la página inicial
  • Después se enviaba a /signin/usernamerecovery/lookup el ess, el nombre, el apellido y bgresponse=js_disabled
  • Como la redirección de la respuesta cambiaba, era posible determinar si existía una cuenta de Google con ese número de teléfono y ese nombre visible
    • Sin cuenta: usernamerecovery/noaccountsfound
    • Con cuenta: usernamerecovery/challenge

Límite por IP, IPv6 y evasión de BotGuard

  • En los intentos iniciales, después de unas cuantas solicitudes la dirección IP quedaba sujeta a limitación de solicitudes y aparecía un CAPTCHA
  • En el ejemplo de un número móvil neerlandés, el flujo de recuperación de contraseña daba pistas como •• ••••••03
    • Como los números móviles de Países Bajos empiezan con 06, había que probar los 6 dígitos restantes, es decir 10^6 = 1,000,000 candidatos
  • Proveedores como Vultr ofrecen un rango IPv6 /64, que en teoría permite usar 18,446,744,073,709,551,616 direcciones
  • Se creó un PoC que usaba reqwest y ClientBuilder.local_address para establecer una dirección IPv6 aleatoria de la subred en cada solicitud
  • Cuando se usaba el formulario sin JS desde IPs de centros de datos, seguía apareciendo CAPTCHA, pero al colocar el token de BotGuard del formulario de recuperación con JS en el bgresponse del formulario sin JS, la solicitud pasaba
    • En el formulario sin JS, ese token de BotGuard aparentemente no tenía limitación de solicitudes

Filtrado de falsos positivos

  • En la primera ejecución aparecieron muchas cuentas con nombre Henry, apellido vacío y número terminado en 03
  • En esos casos, si el nombre era Henry, usernamerecovery/challenge se devolvía sin importar el valor del apellido
  • Para verificar posibles aciertos, se volvía a comprobar usando el mismo nombre pero con un apellido aleatorio como 0fasfk1AFko1wf
    • Si aun así seguía apareciendo como acierto, se filtraba como falso positivo
  • Se consideró improbable que existiera otro usuario de Google con exactamente el mismo nombre visible completo, el mismo código de país y los mismos dos últimos dígitos

Obtención del código de país y del nombre visible

  • La máscara del número telefónico en el flujo de recuperación de contraseña podía usarse para estimar el código de país
  • Google usa libphonenumber para el formato nacional de cada número
  • Se recopilaron formatos nacionales enmascarados por país para crear mask.json
    • Rusia, Países Bajos, Reino Unido y Estados Unidos tienen patrones de máscara diferentes entre sí
  • En 2023, Google cambió la política para mostrar nombres solo cuando hubiera interacción directa con el objetivo, y en abril de 2024 la Internal People API dejó por completo de devolver nombres visibles de cuentas no autenticadas
  • Sin embargo, si se crea un documento en Looker Studio y se transfiere la propiedad a la víctima, el nombre visible de la víctima queda expuesto en la pantalla principal sin interacción por parte de ella

Optimización del rango de candidatos y herramientas

  • Usando la validación de números de libphonenumber, se generó format.json con prefijos móviles por país, códigos de área conocidos y cantidad de dígitos
    • El ejemplo de Países Bajos incluye el código de país 31, los códigos de área 61, 62, 63, 64, 65, 68 y la longitud [7]
  • La validación en tiempo real con libphonenumber reducía las consultas a la API de Google para números inválidos
  • Para generar tokens de BotGuard, se escribió un script en Go que usa chromedp
  • El flujo completo del ataque se encadenaba en tres pasos
    • Filtrar el nombre visible de la cuenta de Google con Looker Studio
    • Obtener el número telefónico enmascarado desde el flujo de recuperación de contraseña
    • Introducir el nombre visible y el número enmascarado para hacer fuerza bruta del número completo con gpb

Rendimiento de la fuerza bruta y tiempo estimado

  • En un servidor de $0.30 por hora y especificaciones de consumo con 16 vCPU, se podían hacer unas 40 mil verificaciones por segundo
  • Tiempo estimado cuando el flujo de recuperación de contraseña solo daba los dos últimos dígitos:
    • Estados Unidos +1: 20 minutos
    • Reino Unido +44: 4 minutos
    • Países Bajos +31: 15 segundos
    • Singapur +65: 5 segundos
  • Si otros servicios como PayPal ofrecen más pistas numéricas en su flujo de restablecimiento de contraseña, el tiempo puede reducirse mucho
    • Ejemplo: +14•••••1779

Cronología del reporte y medidas de Google

  • 2025-04-14: se envió el reporte al proveedor
  • 2025-04-15: el proveedor recibió y clasificó el reporte
  • 2025-04-25: respuesta: “Nice catch!”
  • 2025-05-15: el panel fijó una recompensa de $1,337 + swag
    • La base fue considerar que la posibilidad de explotación era baja, y el problema fue reconocido como una metodología de abuso de alto impacto
  • 2025-05-15: se presentó una objeción al motivo de la recompensa
    • Según la tabla de Abuse VRP, la probabilidad/explotabilidad se determina según las precondiciones del ataque y si la víctima puede detectar el abuso
    • Se argumentó que este ataque no tenía precondiciones y que la víctima no podía detectar la explotación
  • 2025-05-22: el panel pagó $3,663 adicionales, ajustando la recompensa total a $5,000
    • La probabilidad se elevó a nivel medio
  • 2025-05-22: el proveedor confirmó que había desplegado mitigaciones en curso hasta el retiro global del endpoint
  • 2025-05-22: se coordinó la fecha de divulgación para 2025-06-09
  • 2025-06-06: el proveedor confirmó el retiro completo del formulario de recuperación de nombre de usuario sin JS
  • 2025-06-09: el reporte se hizo público

Aún no hay comentarios.

Aún no hay comentarios.