Adi Malyanker | Investigador de seguridad

Principales resultados

  • Un ataque de consentimiento de aplicación, también conocido como ataque de concesión de consentimiento ilícito, es un tipo de ataque de phishing en el que un actor malicioso obtiene acceso a una aplicación y luego explota los permisos que se han concedido a esa aplicación.
  • El investigador de Semperis Adi Malyanker ha descubierto que bajo ciertas circunstancias, un actor malicioso podría usar el permiso Directory.ReadWrite.All o el permiso DelegatedPermissionGrant.ReadWrite.All dentro de Microsoft Azure para escalar privilegios, acceder a recursos y datos sensibles, lanzar un ataque de Denegación de Servicio (DoS) - o incluso tomar el control de un tenant Entra ID.
  • Cualquier organización que utilice aplicaciones en Entra ID podría ser vulnerable a este ataque, bautizado como Hidden Consent Grant attack.
  • Que Semperis sepa, no se ha documentado ningún ataque de este tipo.
  • Los investigadores de Semperis califican esta vulnerabilidad como potencialmente SEVERA debido a la posibilidad de una toma de control del inquilino Entra ID.

Dentro de Microsoft Azure, el permiso Directory.ReadWrite.All tiene implicaciones significativas. Este permiso permite una multitud de acciones, incluyendo la edición del usuario y el acceso a todos los datos dentro del directorio.

¿Suena arriesgado? Algunos han argumentado que cuando se emplea de forma aislada, el permiso no plantea ningún riesgo inherente. Sin embargo, mi investigación indica que un atacante podría utilizar el permiso Directory.ReadWrite.All (o el permiso DelegatedPermissionGrant.ReadWrite.All ) para causar graves daños en un entorno Microsoft Entra ID (anteriormente conocido como Azure AD) o híbrido Microsoft Active Directory/Entra ID.

¿Qué son los permisos de aplicación?

En Entra ID, los permisos de aplicación otorgan a una aplicación específica la capacidad de realizar ciertas acciones o acceder a ciertos recursos dentro de Entra ID u otros servicios de Microsoft. A diferencia de los permisos delegados, que los usuarios conceden a las aplicaciones autorizadas, los permisos de aplicación suelen ser concedidos por un administrador durante el proceso de registro de la aplicación.

A continuación, la aplicación utiliza estos permisos para realizar tareas en nombre de la organización o de sus usuarios, sin requerir el consentimiento individual o continuo del usuario(Figura 1).

Figura 1. Permisos delegados frente a permisos de aplicación

Los permisos de aplicación suelen utilizarse cuando la aplicación necesita acceder a recursos o realizar acciones que no dependen de la identidad o los permisos de un usuario específico. Algunos ejemplos de permisos de aplicación son:

  • Lectura de perfiles de usuario
  • Gestión de grupos
  • Acceso a los datos de la organización

Es esencial gestionar y asignar cuidadosamente los permisos de las aplicaciones. Las organizaciones deben asegurarse de que las aplicaciones tengan el nivel de acceso adecuado, manteniendo al mismo tiempo los estándares de seguridad y cumplimiento dentro del entorno Entra ID.

¿Qué es Directory.ReadWrite.All?

Mientras investigaba los permisos de las aplicaciones, me encontré con el permiso Directory.ReadWrite.All. Según la documentación de Microsoft:

"Directory.ReadWrite.All concede un acceso que equivale, en términos generales, al de un administrador de inquilino global. Las aplicaciones a las que se concede Directory.ReadWrite.All pueden gestionar toda la gama de recursos de directorio y pueden gestionar la autorización para que otras aplicaciones y usuarios accedan a los recursos de toda la organización. Esto incluye recursos de directorio como usuarios, grupos, aplicaciones y dispositivos, y recursos no de directorio en Exchange, SharePoint, Teams y otros servicios".

Dado el nombre de este permiso, su asociación con Entra ID y su implicación en la gestión de directorios, sospeché que el permiso era potencialmente peligroso.

Pero al principio, tras examinar las acciones que permite Directory.ReadWrite.All, no pude encontrar ninguna amenaza digna de mención. Otros parecen haber llegado a una conclusión similar. Por ejemplo, un excelente artículo publicado por SpectreOps afirmaba que este permiso, aunque potente cuando se combina con otros permisos, era poco probable que supusiera un riesgo significativo por sí solo.

Aun así, decidí echar un vistazo más de cerca.

Comencé a probar lo que el permiso Directory.ReadWrite.All podía hacer a alto nivel. ¿Qué podría lograr un actor malicioso utilizando solo este permiso de aplicación?
La buena noticia es que no pude utilizar el permiso para hacer lo siguiente:

  • Añadir secretos a las aplicaciones
  • Añadir propietarios
  • Añadir miembros o propietarios con un rol a un grupo de seguridad
  • Conceder un rol de aplicación
  • Restablecer la contraseña de un usuario

Esas llamadas a la API devolvían una respuesta como la que muestra la Figura 2.

Figura 2. Respuesta a una llamada a la API fallida

Sin embargo, pude utilizar Directory.ReadWrite.All para hacer lo siguiente:

  • Añadir un nuevo miembro o propietario sin ninguna función asignada a un grupo de seguridad
  • Añadir usuarios al directorio
  • Editar consentimientos de administrador
  • Edición limitada de las unidades administrativas

Nota: Hay más de 400 llamadas API dedicadas a Teams, dispositivos y OneDrive. Esas llamadas están fuera del alcance de esta investigación.

Acciones como añadir nuevos usuarios y miembros, que no tienen ninguna función asignada, a los grupos podría ser bastante útil para los atacantes que intentan obtener acceso inicial a un directorio o recursos. Esta posibilidad es suficientemente preocupante, pero me preguntaba si era posible algo más.

¿Podría utilizar el permiso Directory.ReadWrite.All para convertirme en administrador global?

¿Puede Directory.ReadWrite.All dar lugar a un potencial vector de ataque?

Antes de pasar a lo divertido, una nota: El token de acceso que te ayuda a iniciar sesión en Azure contiene un campo especial llamado ámbito, o scp. Este campo anota los permisos que se conceden al usuario(Figura 3).

Figura 3. El campo scp

Comprobé este valor a lo largo de mi investigación para determinar si mis intentos de elevar los permisos tenían éxito.

Para probar mis sospechas sobre Directory.ReadWrite.All, elegí una API de Microsoft Graph, oauth2PermissionGrant, que puede crear una concesión de permisos delegada en un recurso para una entidad elegida(Figura 4).

Figura 4: Llamada a la API oauth2PermissionGrant para conceder un permiso delegado

Para realizar esta llamada a la API, necesitaba conceder a una aplicación el permiso Directory.ReadWrite.All(Figura 5). Me decidí por postman-test(Figura 6).

Figura 5. Elección de una aplicación con el permiso Directory.ReadWrite.All
Figura 6: Concediendo el permiso Directory.ReadWrite.All a postman-test

También creé una cuenta de usuario débil, Evil User(Figura 7), para representar a un atacante (que simula un usuario malicioso miembro del tenant y un usuario legítimo obtenido por un atacante). No concedí a este usuario ninguna función ni permiso(Figura 8).

Figura 7: Creación del usuario Evil User
Figura 8: Usuario maligno sin funciones asignadas

Para invocar las llamadas a la API, conecté el principio de servicio postman-test con Postman. Revisemos las propiedades y requisitos de la API(Figura 9):

  • clientId identifica la aplicación (en este caso, Graph Explorer) que está autorizada a actuar en nombre de un usuario que ha iniciado sesión. El valor clientId actúa como sustituto de la identidad del usuario.
  • consentType indica si se concede autorización para que la aplicación cliente suplante a todos los usuarios o sólo a un usuario específico.
  • resourceId especifica el recurso (en este caso, Microsoft Graph) al que el clientId puede acceder con los permisos concedidos.
  • scope define las acciones específicas (por ejemplo, device.Read.All, Director.ReadWrite.All) que el clientId puede realizar en el resourceId.
Figura 9. Propiedades y requisitos de la API

Para resumir las relaciones entre estas propiedades: Uno o más usuarios conceden al clientId un conjunto aprobado de permisos, según lo definido por el ámbito. El clientId puede entonces utilizar esos permisos para acceder al resourceId especificado.

Ahora, de vuelta a mi exploración de llamadas API. En primer lugar, necesitaba el ID del objeto Graph Explorer. Podía navegar en el portal de aplicaciones en Azure o utilizar la Graph API(Figura 10) para obtener este ID.

Figura 10: Obtener el ID del objeto

Para empezar, utilicé Graph Explorer como clientId y resourceId.

Como Usuario Maligno, intenté leer todos los grupos del directorio. Como puedes ver en la respuesta 403 de la Figura 11, esta acción no estaba permitida debido a la insuficiencia de privilegios.

Figura 11. Intentando (y fallando) leer todos los grupos del directorio

A continuación, intenté añadir un permiso delegado (GroupMember.Read.All) aGraph Explorer para todo el directorio(Figura 12). Si tenía éxito, cada usuario en el directorio (incluyendo Evil User) se debe conceder este permiso.

Figura 12. Añadir un permiso delegado

Verifiqué que la adición de este permiso se había realizado correctamente. Observe que en la aplicación Graph Explorer, el permiso aparecía como concedido para todo el directorio(Figura 13).

Figura 13. Verificación de la adición de privilegios

Ahora, debería poder utilizar la llamada a la API para obtener todos los grupos(Figura 14).

Figura 14. Intento de recuperar grupos

Raro... Graph Explorer tenía el permiso GroupMember.Read.All con el consentimiento del administrador, pero sigo recibiendo una respuesta 403. ¿Por qué?

¿Recuerdas la definición de permisos delegados? Como Graph Explorer utilizaba los permisos del usuario que había iniciado sesión, sólo podía leer los miembros si el usuario que había iniciado sesión tenía el rol o el nivel de privilegio correctos para utilizar ese permiso, cosa que Evil User no hacía. Mi intento fue un callejón sin salida.

Pero espera. ¿Conoces el ataque de la Subvención por Consentimiento Ilícito?

¿Qué es un ataque de subvención por consentimiento ilícito?

En términos sencillos, un ataque de concesión de consentimiento ilícito, también conocido como ataque de consentimiento de aplicación, se produce cuando se engaña a alguien para que conceda a una aplicación de terceros (es decir, externa) derechos excesivos sobre sus datos o la configuración de sus aplicaciones de Office 365. La figura 15 ilustra cómo funciona este ataque.

Figura 15. Subvención por consentimiento ilícito

En resumen, un atacante crea una aplicación registrada en Azure. Entonces:

  1. La aplicación solicita acceso a datos sensibles como información de contacto, correos electrónicos o documentos.
  2. El usuario final otorga su consentimiento a la aplicación. El consentimiento puede obtenerse mediante ataques de phishing o inyectando código ilícito en un sitio web de confianza.
  3. La aplicación del atacante recibe la aprobación del consentimiento y un token de acceso.
  4. La aplicación maliciosa -y el atacante- tienen ahora acceso a nivel de cuenta a los datos del usuario.

¿Qué pasaría si un atacante lanzara un ataque de Concesión de Consentimiento Ilícito, utilizando una aplicación maliciosa a la que se le hubiera concedido el permiso Directory.ReadWrite.All? Era hora de volver a probar mi experimento.

Conoce el ataque de la subvención Consentimiento Oculto

Esta vez, actué como un atacante que intentaba comprometer a un usuario en el directorio objetivo a través de una aplicación con el permiso Directory.ReadWrite.All. La aplicación comenzaría con privilegios bajos y luego escalaría sus permisos después de obtener el consentimiento de administrador(Figura 16).

Figura 16. Flujo de ataque

Este flujo de ataque sería posible usando una aplicación, pero decidí usar dos aplicaciones para mayor sigilo (separando entre la aplicación con permiso Directory.ReadWrite.All y la aplicación cuyos permisos serán actualizados). Para este experimento:

  1. He creado una cuenta para un usuario con privilegios limitados: Atacante Inofensivo (atacante_inofensivo). Este experimento asume que un atacante ya ha obtenido acceso a esta cuenta a través de un phishing previo, otro ataque o pertenencia al tenant.
  2. He creado una cuenta de usuario privilegiado, Privileged User (privileged_user), para representar a un administrador global.
  3. Utilicé Harmless Attacker para crear una aplicación con permiso Directory.ReadWrite.All (en este caso, postman-test, que utilicé para conceder permisos delegados a 365stealer).
  4. Usé Harmless Attacker para crear otra aplicación, llamada 365stealer. Esta aplicación tiene la configuración que se muestra en la Figura 17. Observa que el URI de redirección de la aplicación conduce a un endpoint controlado por el atacante.
Figura 17. Configuración de la aplicación 365stealer

Harmless Attacker no pudo añadir permisos privilegiados a la aplicación 365stealer durante la creación, por lo que al principio, los permisos de la aplicación se parecían a los que se muestran en la Figura 18.

Figura 18. Permisos iniciales de 365stealer

A continuación, utilicé un endpoint controlado por el atacante para configurar un servidor HTTPS, escuchando en el puerto 443(Figura 19).

Figura 19. Configuración del servidor HTTPS

A continuación, utilicé Harmless Attacker para construir una página de phishing(Figura 20). En un escenario real, el Atacante Inofensivo intentaría engañar al Usuario Privilegiado para que accediera a esta página a través de un correo electrónico o un archivo malicioso.

Figura 20. Página de phishing

La página de phishing contenía un hipervínculo a una página de inicio de sesión de Microsoft redirigida, donde se animará al usuario con privilegios a iniciar sesión en la aplicación maliciosa(365stealer). Según la parte redirect_uri, los tokens se enviarán al servidor HTTPS que desplegó el atacante(Figura 21).

Figura 21. Redirección del usuario al servidor HTTPS malicioso

Una vez iniciada la sesión con éxito, se le presentará al usuario un desafío de consentimiento(Figura 22).

Figura 22. Impugnación del consentimiento

Nada de estos permisos o del reto de consentimiento parece perjudicial (no se requieren permisos sensibles), por lo que el usuario aceptará el consentimiento. En nuestro caso, se le presentará una página como la que se muestra en la Figura 23, cerrará el navegador y seguirá con su día.

Figura 23. Aquí no hay nada que ver.

Nota: Aunque la página esté marcada como insegura, hay formas de evitarlo. Sin embargo, eso está fuera del alcance de este artículo. También puedes ver la nota de no verificado en el consentimiento. Hablaré de formas de ocultar eso más adelante en el artículo.

Mientras tanto, sin que el usuario lo sepa, el atacante recibirá el token de acceso y lo utilizará para llamar a la API /me(Figura 24).

Figura 24. Ficha de acceso

Este token de acceso contiene los permisos que el usuario aceptó en el desafío de consentimiento. Un vistazo al campo scp del token lo confirma(Figura 25).

Figura 25. Token de acceso descodificado

Así, ahora el atacante tiene un token de acceso (y un token de refresco) de un usuario privilegiado. Pero este token sólo se puede utilizar con la aplicación y los permisos delegados enumerados en el ámbito. El atacante aún carece del permiso RoleManagement.ReadWrite.Directory, por lo que cualquier intento de llamar a la API directoryRoles (a través de una solicitud a https://graph.microsoft.com/v1.0/directoryRoles) devolverá una respuesta de Acceso Denegado(Figura 26).

Figura 26. Acceso denegado Acceso denegado

De nuevo, parece que el potencial del atacante para causar daño es muy limitado. Cómo podría un atacante escalar privilegios y llamar a otras API interesantes?

Aquí es donde entra Directory.ReadWrite.All.

¿Podría tomar la API oauth2PermissionGrant de mi experimento original, que sólo requiere el permiso Directory.ReadWrite.All, y utilizarla para conceder el permiso delegado RoleManagement.ReadWrite.Directory a la aplicación 365stealer ?

Para averiguarlo, primero utilizo la API oauth2PermissionGrant para enumerar el clientId y el resourceId utilizados para 365stealer(Figura 27).

Figura 27. Enumeración de 365stealer clientId y resourceId Enumeración de 365stealer clientId y resourceId

A continuación, establezco el consentType en "AllPrincipals"(Figura 28). Esto elimina el requisito del consentimiento del administrador al conceder los permisos.

Figura 28. Configuración de consentType como "AllPrincipals" (Todos los directores)

Espere un momento. El permiso RoleManagement.ReadWrite.Directory ha sido concedido pero no está incluido en la lista de permisos configurados(Figura 29).

Figura 29. Permisos configurados

¡Ajá! El Atacante Inofensivo posee la aplicación 365stealer . Todo lo que necesito hacer es hacer clic con el botón derecho en RoleManagement.ReadWrite.Directory y seleccionar Add to configured permissions (Figura 30). Incluso sin este paso, el permiso debería estar disponible para mí.

Figura 30. Añadir RoleManagement.ReadWrite.Directory a los permisos configurados

El permiso ya está configurado(Figura 31).

Figura 31. Verificación de la configuración

Ahora, simplemente llamo al endpoint /refresh en el servidor HTTP para refrescar el token de acceso de la cuenta del Usuario Privilegiado y actualizar sus permisos(Figura 32).

Figura 32. Actualización del token de acceso del usuario

Un vistazo al ámbito del token verifica la adición del permiso RoleManagement.ReadWrite.Directory(Figura 33).

Figura 33. Verificación de la adición del permiso RoleManagement.ReadWrite.Directory

Con este permiso en el token de acceso, ya puedo llamar a GET https://graph.microsoft.com/v1.0/directoryRoles(Figura 34).

Figura 34. Enumeración de roles de directorio

Y ahora puedo llamar a POST https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments para asignar al Atacante Inofensivo el rol de Administrador Global(Figura 35).

Figura 35. Asignación del rol Global Admin a Atacante Inofensivo

Listo. Un vistazo a las asignaciones activas de la cuenta del atacante verifica la escalada de privilegios(Figura 36). Mi intento de lanzar lo que he llamado una Concesión de Consentimiento Oculta tuvo éxito.

Figura 36. Verificando la escalada de privilegios

Subvención de consentimiento oculto: ¡Ahora con más sigilo!

¿Recuerdas el desafío de consentimiento que se presentaba al usuario en las primeras fases del ataque "Ocultación de la concesión de consentimiento"? Me preguntaba si podría ocultar ese reto para que el ataque fuera aún más sigiloso. Al hacerlo, se crearía un flujo de ataque similar sin requerir la acción del usuario en la forma de que la víctima pasara ningún desafío de consentimiento, incluso cuando la aplicación solicitara permisos elevados(Figura 37).

Figura 37. Flujo de ataque sin desafíos de consentimiento

Empecé con los mismos permisos que utilicé en el experimento anterior. Sin embargo, concedí a la app 365stealer consentimiento administrativo(Figura 38). Recuerda que Directory.ReadWrite.All permite al atacante hacerlo sin ninguna interacción del usuario.

Figura 38. Permisos de la API 365stealer

Ahora ejecutaremos el flujo de phishing y determinaremos si al usuario con privilegios se le presenta un desafío de consentimiento cuando hace clic en el enlace de la página de phishing. (Alerta de spoiler: no es así).

En este punto, he probado dos puntos:

  • A la víctima de phishing no se le presentará ningún desafío de consentimiento si los privilegios requeridos ya han sido aprobados por el administrador.
  • Incluso después de que el administrador apruebe el reto de consentimiento para añadir los permisos de la aplicación, el atacante puede añadir más permisos editando manualmente los permisos, sin que la víctima lo sepa.

Difusión de la subvención "Consentimiento oculto

Me preguntaba si podría convertir todas las aplicaciones del directorio para que sirvieran los enlaces de phishing. Sabía que podía convertir todas las aplicaciones que teníamos en aplicaciones de suplantación de identidad cambiando su URL de redirección. Pero, ¿qué ocurre con las aplicaciones que no cumplen este requisito?

En el raro caso de que una aplicación también tenga el permiso de aplicación Application.ReadWrite.All, el atacante puede editar o añadir otra URL de redirección -el servidor de escucha del atacante- a la aplicación. De esta forma, todas las aplicaciones del directorio se convierten en entidades maliciosas(Figura 39).

Figura 39. Permisos necesarios para propagar el ataque en el inquilino

La llamada a la Graph API mostrada en la Figura 40 puede ayudar a cambiar el URI de redirección de la aplicación(Figura 41).

Figura 40. Llamada a la Graph API para cambiar el URI de redirección
Figura 41. Redirigir URI

Para este vector de ataque, el atacante aún necesita conocer el secreto de la aplicación. Por suerte para el atacante, Application.ReadWrite.All le permite añadir un nuevo secreto a la aplicación(Figura 42).

Figura 42. Añadir un nuevo secreto

Nota: Parece haber una discrepancia entre la documentación de Microsoft y mis propias observaciones. Según mis pruebas, aunque se menciona Directory.ReadWrite.All, es posible que se necesiten permisos adicionales.

Para esta prueba de concepto, Semperis ha construido y publicado una nueva herramienta, HiddenConsentGrant, disponible aquí. Esta herramienta se puede utilizar para crear un servidor de escucha, a la espera de respuestas de token de acceso.

Detección y mitigación

¿Cómo detectar un ataque de subvención de consentimiento oculto?

  1. Los usuarios de Semperis Directory Services Protector ( DSP) o Purple Knight podrán utilizar el indicador de seguridad Entra tenant is susceptible to Hidden Consent Grant Attack para verificar y reportar la posibilidad de un ataque de otorgamiento de consentimiento oculto en el tenant.
  2. Busque directores de servicio sospechosos con privilegios elevados, tales como Rolemanagement.ReadWrite.Directory, Aplicación.ReadWrite.Ally AppRoleAssignment.ReadWrite.All. Puede utilizar la siguiente llamada a la API para enumerar estos permisos (Figura 43):
    GET graph.microsoft.com/v1.0/oauth2PermissionGrants?$filter=consentType eq %27AllPrincipals%27
    Figura 43. Enumeración de privilegios Enumeración de privilegios
  3. Determine si un administrador concedió aprobación a los permisos.
  4. Compruebe que los permisos se utilizan activamente y son necesarios.
  5. Elimine los privilegios innecesarios.
  6. En los registros de auditoría, busque Añadir concesión de permiso delegada entrada con el actor iniciado Aplicación (Figura 44).
    Figura 44. Revisión de la entrada de registro Añadir concesión de permiso delegado

    En Tipo valor debe ser Aplicación y el Tipo de actividad debe ser Añadir concesión de permiso delegada. Puede utilizar Graph API para obtener todos los registros de auditoría que tengan el tipo de actividad Añadir concesión de permiso delegada:
    https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?$filter=activityDisplayName eq 'Add delegated permission grant'

    A continuación, puede buscar "usuario": null, lo que significa que una aplicación invocó la concesión de permisos delegados(Figura 45).

    Figura 45. Búsqueda de concesiones de permisos delegados invocados por aplicaciones
  7. Controlar y revocar cualquier OAuth concesiones de consentimiento. La siguiente llamada a la API puede ayudarle:
    GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants/
    Esta llamada devuelve todos los permisos delegados concedidos en el inquilino, el tipo de consentimiento y la entidad (Figura 46).
    Figura 46. Descubrir concesiones de consentimiento OAuth no autorizadas
  8. Por último, evita dar a las aplicaciones el permiso Directory.ReadWrite.All o DelegatedPermissionGrant.ReadWrite.All como permisos de aplicación. (El permiso DelegatedPermissionGrant.ReadWrite. All permite a un atacante lanzar los mismos ataques que describo en este artículo). Siempre que sea posible, configura permisos más específicos para tus aplicaciones.

Iluminar las subvenciones de consentimiento ocultas

Este artículo ilustra la prueba de concepto de varios flujos de ataque que abusan del rol Directory.ReadWrite.All para obtener potencialmente todos los permisos que un atacante pueda desear. Estos flujos de ataque podrían ser mortales en manos de un atacante astuto. Revise y refuerce los permisos de su aplicación ahora y cierre esta posible brecha antes de que los actores de amenazas puedan aprovecharse de ella.