Vulnerabilidades JWT

JSON Web Tokens

General

Es un estándar basado en JSON (Javascript Object Notation) para la creación de tokens de acceso que permiten la propagación de identidad y privilegios. El token está firmado por la clave del servidor, así que el cliente y el servidor son ambos capaces de verificar que el token es legítimo.

Los JSON Web Tokens están diseñados para ser compactos, poder ser enviados en las URLs -URL-safe- y ser utilizados en escenarios de Single Sign-On (SSO).

El estándar de JWT se basa en otros estándares basados en JSON JSON Web Signature (RFC 7515) y JSON Web Encryption (RFC 7516)

JWT = Datos en JSON criptográficamente firmados

JWE = JWT encriptado

Consideraciones:

  • Utilizar JWT previene de los ataques CSRF. Pero puede seguir siendo vulnerable si se almacena en una cookie.

  • Si se almacena en local storage, es susceptible a ataques XSS.

  • Los JWTs son buenos contra ataques de dominio cruzado (cross domain).

Estructura

Están formados por 3 partes:

HEADER.PAYLOAD.SIGNATURE
  • Una cabecera o header. Indentifica el algoritmo utilizado para generar la firma.

    • Algoritmos aceptados:

      • RSA.

      • HMAC.

      • Elliptic Curve.

      • None.

          header = '{"alg":"HS256","typ":"JWT"}'
  • Un contenido o payload. Contiene los privilegios del token.

    • Valores:

      • iss: issuer

      • iat: issued at

      • nbf: “not before” (start date)

      • sub: subject

      • exp: expires at

    • Puede contener cualquier información, como: Nombre de usuario, email, rol, permisos, contraseñas.

        payload = '{"loggedInAs":"admin","iat":1422779638}'
  • Una firma digital o signature. Que se calcula codificando la cabecera y el contenido en base64, concatenándose con un punto.

      key           = 'secretkey'
      unsignedToken = encodeBase64Url(header) + '.' + encodeBase64Url(payload)
      signature     = HMAC-SHA256(key, unsignedToken)

Generalmente los algoritmos de cifrado utilizados son HMAC con SHA-256 (HS256) y firma digital con SHA-256 (RS256).

Cómo detectar tokens JWT

Normalmente empiezan con eyJ0eXAi o eyJhbGci.

  • eyJ0eXAi= {"typ"

  • eyJhbGci= {"alg"

Y un punto en los primeros 40-60 caracteres.

Convertir de B64String to B64URL

Sustitución de caracteres:

+ -> -

/ -> _

= -> ` ` (se elimina)

Obtener información en texto plano

https://jwt.io/

Ej:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Inh0b3JtaW4iLCJwayI6Ii0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tXG5NSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTk1b1RtOUROemNIcjhnTGhqWmFZXG5rdHNiajFLeHhVT296dzB0clA5M0JnSXBYdjZXaXBRUkI1bHFvZlBsVTZGQjk5SmM1UVowNDU5dDczZ2dWRFFpXG5YdUNNSTJob1VmSjFWbWpOZVdDclNyRFVob2tJRlpFdUN1bWVod3d0VU51RXYwZXpDNTRaVGRFQzVZU1RBT3pnXG5qSVdhbHNIai9nYTVaRUR4M0V4dDBNaDVBRXdiQUQ3MytxWFMvdUN2aGZhamdwekhHZDlPZ05RVTYwTE1mMm1IXG4rRnluTnNqTk53bzVuUmU3dFIxMldiMllPQ3h3MnZkYW1PMW4xa2YvU015cFNLS3ZPZ2o1eTBMR2lVM2plWE14XG5WOFdTK1lpWUNVNU9CQW1UY3oydzJrekJoWkZsSDZSSzRtcXVleEpIcmEyM0lHdjVVSjVHVlBFWHBkQ3FLM1RyXG4wd0lEQVFBQlxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iLCJpYXQiOjE2MjY3MjA5NTR9.aWuLHlmBDuV3v_F0tCmbRz-pzoP4qCLcjfT4SQgN-Mq4hAfq_X16oiLhX9hKKIp2YMaaOwuW8-12o6vIHgUXDT6AZYTq2gX6mJLqYfGcqm6XLYMiINiTfVc8uZbGRl-wH5vs6je1L3qR6GcrmCNcAgjsDVbN61y7Vv2XMD5qZgwIBwZle4ZghRMG2hlDappWlHG_P7MbHExJvonlzGBxZAPV9w-01_dTiDcNLA7LW_suroK2l5GCxmvvCoCuyu0FnK6GpaKFKKOn9UZ0WqgC7i1HYqwJQHmWMsbEq-N-DrUnVppHviyaXnfAgsPb6U4dbMng32Hohyw7J6hkoXG9qA

Ataques comunes

Fallo en la verificación de la firma

Muchas librerías JWT tienen un método para decodificar y otro para verificar el token:

  • decode(). Decodifica el token sin verificar la firma.

  • verify(). Decodifica y verifica el token.

Durante el desarrollo suele utilizarse el método decode() y cuando se pasa la aplicación a producción se suele olvidar cambiar el método por verify().

Algoritmo none permitido

Si se cambia el algoritmo por none en alguna de sus siguientes variantes y se elimina el tercer campo del JWT dejando el punto final.

none
None
nOnE
NONE

Ej:

eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0.
eyJuYW1lIjoiSm9obiBEb2UiLCJ1c2VyX25hbWUiOiJqb2huLmRvZSIsImlzX2FkbWluIjp0cnVlfQ.

Confusión de algoritmos

Esta vulnerabilidad se da cuando la aplicación no comprueba que el algoritmo coincida con el algoritmo que se espera.

Muchas librerías no comprueban por defecto si el algoritmo coincide, por eso se utiliza la siguiente Comprobación del algoritmo para verificarlo.

ALGORITMOS

AlgoritmoClave de firmaClave de verificación

Asimétrico (RSA)

Clave privada

Clave pública

Simétrico (HMAC)

Contraseña

Contraseña

Algoritmo asimétrico: Cuando se utiliza una encriptación asimétrica, se publica la clave pública y se mantiene la clave privada en secreto. Esto permite firmar el token con la clave privada y que cualquiera pueda verificarla con la clave pública.

RSA

Si la clave pública está disponible, un atacante puede falsificar tokens:

  • Cambiando el algoritmo del token por HMAC.

  • Manipulando el resultado con el payload.

  • Firmar el token con la clave pública detectada en la aplicación o en el JWT.

  • Enviar el JWT de vuelta a la aplicación.

HMAC

La fortaleza de la firma vendrá determinada por la fortaleza de la contraseña. Si la aplicación utiliza una contraseña débil, se podrá llegar a obtener por medio de fuerza bruta.

COMPROBACIÓN DEL ALGORITMO

Normalmente las librerías comprueban el algoritmo de la siguiente manera:

  • HMAC. verify(token, secret).

  • RSA o similar. verify(token, secret).

Ataques contra JWT

Inyección en parámetro kid

Si la cabecera contiene el parámetro kid, este normalmente es usado para obtener la clave de una base de datos o sistema de ficheros. La aplicación verifica la firma utilizando la clave obtenida en este parámetro.

Si el parámetro es inyectable, este podría ser utilizado para saltarse la firma o realizar ataques de RCE, SQLi o LFI.

EJEMPLO

JWT Original

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "key"
}.
{
  "name": "Jennifer Torres",
  "user_name": "jennifer.torres",
  "is_admin": false
}

JWT con inyección

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "key|/usr/bin/uname"
}.
{
  "name": "Jennifer Torres",
  "user_name": "jennifer.torres",
  "is_admin": false
}

Bypass de la firma

INYECCIÓN EN PARÁMETRO KID + DIRECTORY TRAVERSAL

Si la aplicación utiliza el parámetro kid como valor de la clave utilizada para validar la firma, un atacante podría modificarlo por un fichero con el valor de la clave, por ejemplo, /dev/null y con ello saltársela.

Ejemplo

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "../../../../../../dev/null"
}.
{
  "name": "Jennifer Torres",
  "user_name": "jennifer.torres",
  "is_admin": true
}

INYECCIÓN EN PARÁMETRO KID + INYECCIÓN SQL

Si la aplicación utiliza el parámtro kid para obtener la información de la base de datos como la clave de la firma, se podría intentar inyectar código SQL.

Ejemplo de consulta SQL

SELECT key FROM keys WHERE key='key'

Ejemplo de inyección SQL en kid

{
  "alg": "HS256",
  "typ": "JWT",
  "kid": "test' UNION SELECT 'aaaa"
}.
{
  "name": "Jennifer Torres",
  "user_name": "jennifer.torres",
  "is_admin": true
}

Consulta con inyección en kid

SELECT key FROM keys WHERE key='test' UNION SELECT 'aaaa'

La inyección ' UNION SELECT 'aaaa permite a un atacante sustituir la clave por aaaa y con ello firmar el token con dicha clave para saltarse la firma.

Manipulación del parámetro jku

Este parámetro se utiliza para especificar el JSON Web Key Set URL (RFC7515), que indica donde se encuentra el JSON Web Key utilizado para verificar la firma.

Ejemplo de JWT con jku

{
  "alg": "RS256",
  "typ": "JWT",
  "jku":"https://example.com/key.json"
}.
{
  "name": "Jennifer Torres",
  "user_name": "jennifer.torres",
  "is_admin": false
}

Ejemplo de JSON Web Key (fichero key.json)

{
  "kty": "RSA",
  "n": "-4KIwb83vQMH0YrzE44HppWvyNYmyuznuZPKWFt3e0xmdi-WcgiQZ1TC...RMxYC9lr4ZDp-M0",
  "e": "AQAB"
}

FORMAS DE SALTARSE LAS LIMITACIONES

  • Utilizando https://xtormin si la aplicación solo comprueba el inicio de la URL (ej: https://xtormin@attacker.com/key.json).

  • Utilizando fragmentos con el caracter #.

  • Utilizando la jerarquía de nombres DNS.

  • Concatenándolo con open redirect, header injection o ssrf.

### Prevención

Para prevenirlo, se debe:

  • Filtrar la URL correctamente.

  • Añadir los hosts permitidos en whitelist.

  • Prevenir otros ataques que puedan ser concatenados para saltarse las restricciones.

Herramientas

JWT Tool

https://github.com/ticarpi/jwt_tool.git

Extensión de Burp

JSON Web Token Attacker - JOSEPH

Securización

Recursos

Vídeos

Última actualización