Comprenda el riesgo de sus políticas. Haga preguntas sobre políticas en lenguaje natural. Solicitar un demo →
Published:
Técnicas avanzadas para defender el AWS ExternalID y el acceso AssumeRole entre cuentas
by FireMon
El mes pasado, Kesten Broughton, de Praetorian Security, publicó una excelente investigación sobre productos de seguridad cloud de terceros que utilizan la técnica de conexión entre cuentas preferida por Amazon: AWS IAM Assume Role Vulnerabilities Found in Many Top Vendors. El párrafo inicial es un resumen sólido de la investigación:
En este primer blog de nuestra serie sobre la confianza entre cuentas presentaremos los resultados de 90 proveedores, que muestran que el 37% no había implementado correctamente el ExternalId para protegerse contra ataques de tipo confused deputy. Otro 15% de los proveedores implementó correctamente la integración de la cuenta de AWS en la interfaz de usuario, pero el parámetro ExternalId no se validaba adecuadamente en el backend, lo que también dejaba vulnerables a esos sitios. Terminaremos analizando las nuevas superficies de ataque expuestas por la confianza de cross-account-assume-role de AWS. Concluimos que los proveedores y los clientes deberían examinar críticamente si la confianza en roles es el mejor mecanismo de confianza para su solución SaaS multiinquilino.
Mi primera reacción al leer la investigación fue: «¿por qué alguien tomaría una decisión tan mala?», pero la realidad es que todo el concepto de «experto en seguridad cloud» es relativamente nuevo y, si bien AWS habla razonablemente bien del problema del confused deputy, no cubre algunas de las implicaciones prácticas en las que uno no piensa realmente hasta que su producto entra en contacto con el cliente. Las conexiones entre cuentas mediante AssumeRole tienen una solución de ingeniería sencilla, pero sin un modelado de amenazas adecuado es extremadamente fácil cometer los errores documentados en la investigación de Kesten.
En DisruptOps tomamos algunas decisiones acertadas desde el principio, basadas en nuestro modelado de amenazas inicial, que nos mantuvieron a salvo. No obstante, sí recogimos algunas ideas de la investigación y estamos añadiendo aún más medidas de endurecimiento. Además, tenemos un proyecto experimental para eliminar de todos modos una gran parte del problema y seguir habilitando la automatización a escala sin requerir acceso directo de escritura a los entornos de los clientes.
Sin embargo, tengo un punto importante de desacuerdo con la publicación. Prefiero las credenciales rotadas automáticamente antes que la recomendación de usar credenciales estáticas y bóvedas. Todavía tenemos que usarlas para otros proveedores de cloud y siempre estamos buscando maneras de NO tener credenciales estáticas en NINGÚN lugar de nuestro entorno.
El problema
Hoy en día, una variedad de tipos de aplicaciones necesita conectarse directamente a las API de cloud. Puede ser algo tan simple como acceder a un bucket de S3 o tan complejo como una plataforma de detección y respuesta en cloud totalmente automatizada (es decir, ya sabe, como ejemplo al azar). Estas solicitudes requieren algún tipo de credenciales, que pueden ser estáticas (como un nombre de usuario y una contraseña, o una clave de acceso y una clave secreta de IAM) o dinámicas. Las credenciales dinámicas incluyen un token u otro atributo efímero de duración limitada.
Las credenciales estáticas de aplicación que permiten el acceso directo a su plano de gestión de cloud son… malas. Podemos resolver esto para los usuarios con MFA, pero eso no ayuda con aplicaciones automatizadas como nuestra no tan teórica plataforma de detección y respuesta en cloud. Hace un tiempo, Amazon Web Services abordó este problema con el concepto de rol de IAM. Un rol en AWS es esencialmente un contenedor de permisos que tiene dos políticas: qué puede hacer el contenedor y quién (o qué) puede asumir el rol. Los roles se basan en sesiones, de modo que cuando una entidad autorizada asume el rol dispone de un conjunto de credenciales (clave de acceso, clave secreta y token de sesión) que puede utilizar durante la duración de la sesión (de 1 a 24 horas).
Los roles en AWS se pueden asumir mediante una conexión SAML externa (para usuarios), conexiones internas «de confianza» (desde otras cuentas de AWS) o servicios de AWS (como una instancia EC2 o una función Lambda). Así puede hacer cosas interesantes como ejecutar código en una instancia sin que esa instancia almacene nunca credenciales estáticas. Esto realmente elimina muchos dolores de cabeza.
Permitir conexiones desde una cuenta que usted no controla es algo distinto, especialmente si esa cuenta es una plataforma que da servicio a múltiples clientes. Plataformas como esa (de acuerdo, nosotros) necesitan acceso a cientos o miles de otras cuentas de AWS. Imagine que un atacante pudiera engañar a la plataforma para que hiciera algo en la cuenta equivocada, como realizar una evaluación de configuración en una cuenta que no pertenece al usuario actual. Esta es una versión breve del problema del confused deputy: múltiples cuentas confían en el diputado y el usuario lo engaña para que le dé acceso a una cuenta que el usuario actual nunca debería tocar.
La explotación más práctica de esto se da si la plataforma permite que un usuario introduzca el ID de una cuenta que no controla, la añada a su perfil y luego abuse de la confianza otorgada a la plataforma.
AWS incluye un mecanismo para defenderse de esto llamado AWS ExternalID. Se trata de un secreto compartido arbitrario que el proveedor de la plataforma y el cliente intercambian fuera de banda. Este ID es un atributo que se transmite en cualquier solicitud para asumir el rol en la cuenta del cliente, y la política de confianza del rol en esa cuenta tiene un requisito condicional que comprueba que el secreto compartido sea correcto. Cuando se implementa correctamente, esto significa que nadie puede simplemente añadir al diputado una cuenta que no controla, ya que el atacante no puede establecer ni conocer el secreto compartido en la cuenta del cliente.
A menos que…
Ya ve hacia dónde va esto. Praetorian identificó un gran número de proveedores que usan ExternalIds de AWS predeterminados o que permiten a los clientes establecer un valor de ExternalId no único para toda su cuenta. También encontraron productos que no validaban que el cliente siquiera exigiera el External ID en su política de confianza del rol. Praetorian encontró plataformas que eran explotables de forma activa, lo que permitía ataques de confused deputy debido a una seguridad deficiente con los roles entre cuentas.
Endurecimiento de las conexiones entre cuentas
Todo esto formó parte de nuestro modelado de amenazas en DisruptOps, así que estamos en buena forma, aunque sí recogimos alguna idea más para mejorar las cosas. Repasemos cada problema identificado por Praetorian y veamos las mejores opciones de endurecimiento de la seguridad:
- El ExternalID de AWS usa la configuración predeterminada: Nosotros usamos un ExternalID aleatorio.
- El ExternalID de AWS se comparte entre varias cuentas de clientes:Usamos un ExternalID aleatorio por cuenta, no por cliente.
- El ExternalID de AWS es enumerable o adivinable: Usamos ExternalIDs de AWS completamente aleatorios y largos. No me preocupa nuestra elección de PRNG gracias a la limitación de tasa de la API (para ustedes, fanáticos de la criptografía).
- Los clientes pueden establecer su propio ExternalID y pueden repetirlo o usar ExternalIDs débiles: No admitimos que los clientes establezcan su propio ExternalID, aunque sin duda hemos recibido esa solicitud. Aquí es donde creo que otros proveedores se han metido en problemas. Los clientes quieren esa capacidad para poder automatizar ellos mismos el aprovisionamiento del producto; pero para hacerlo de forma segura tendríamos que validar que el ExternalID cumple los requisitos de longitud y aleatoriedad. Con la validación adecuada, probablemente se pueda hacer de forma segura.
- La plataforma permite aprovisionar la misma cuenta de AWS varias veces: Nosotros no lo permitimos.
- El rol de IAM no está restringido a una única entidad en la cuenta del proveedor, sino a cualquier rol de la cuenta: No hemos tratado esto en esta publicación, pero puede conceder acceso desde cualquier rol de la cuenta al rol «trabajador» en la cuenta del cliente, o conceder acceso a un único recurso o rol. Nosotros limitamos nuestro acceso a los roles específicos que usamos para el acceso entre cuentas.
- El nombre del rol de IAM es estático en todas las cuentas de clientes: El riesgo aquí es, esencialmente, que el nombre de usuario (nombre del rol) sea adivinable. No considero que esto sea un riesgo en absoluto, salvo que existan otros errores muy fundamentales. Un ejemplo es que, si un atacante conoce el nombre del rol y obtiene acceso a la cuenta del cliente, podría añadirse a sí mismo a la política de confianza del rol y luego usar esos privilegios (enseño algo así en mis clases de formación sobre respuesta a incidentes). Es un riesgo potencial, pero lo calificamos como bastante bajo. Si un atacante tiene ese nivel de acceso, probablemente pueda apropiarse de cualquier rol de la cuenta.
- El proveedor no valida que el ExternalID se haya establecido como condición de acceso: Proporcionamos a los clientes una plantilla de CloudFormation para el aprovisionamiento que garantiza que esto se configure correctamente. Añadiremos soporte para validar que se mantenga, especialmente a medida que sigamos incorporando otras opciones de aprovisionamiento (no basadas en CloudFormation) para satisfacer los requisitos de los clientes.
Kesten parece preferir el modelo de credenciales estáticas y el uso de buenas bóvedas del lado del proveedor para asegurar la conexión. Personalmente, no estoy de acuerdo y creo que el modelo de AWS es más seguro desde el principio, siempre que se sigan las precauciones básicas.
Actualmente tomamos dos precauciones adicionales más allá de las recomendaciones de Praetorian:
- Enmascaramos el ExternalID en CloudFormation: Establecemos el ExternalID como un parámetro en nuestras plantillas de CloudFormation con la opción NoEcho activada. Esto lo enmascara en la consola, en las herramientas de línea de comandos o en la API. Así se reduce el riesgo de exposición ante alguien con permisos para ejecutar CloudFormation que no tenga permisos de IAM.
- Restringimos el acceso a la plantilla de CloudFormation únicamente a la cuenta de destino: Esto reduce el riesgo de que alguien pueda entrar y hacerse con el ExternalID durante el proceso de aprovisionamiento.
Incluso si el ExternalID queda expuesto, no debería importar: su plataforma debería garantizar que una cuenta de destino se registre una sola vez y únicamente con un ExternalID aleatorio. Aunque un atacante conozca el nombre del rol y el ExternalID, no puede hacer nada con ello, ya que no hay ningún lugar en la plataforma donde introducir esa información, y AWS mismo exige que la conexión entre cuentas se origine en la cuenta de confianza. Eso no es algo que se pueda suplantar.
Esperamos que ver cómo lo gestionamos le dé algunas ideas sobre qué hacer en sus propias herramientas. Recomiendo los requisitos de registro de cuentas y de ExternalID aleatorio incluso si usa AWS Organizations, ya que añadir una condición para restringir el acceso solo a su propia organización aún puede exponerle a un ataque desde una cuenta de menor seguridad hacia una cuenta de mayor seguridad.
¡Y esté atento a ese nuevo proyecto experimental! Deberíamos poder hablar de él en unos meses y supone un verdadero cambio de juego para este tipo de problemas.