JWT Lab 5
Skills
- JWT authentication bypass via jku header injection
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Este laboratorio
utiliza un mecanismo basado en JWT
para manejar las sesiones
. El servidor
admite el parámetro jku
en el encabezado del JWT
. Sin embargo, no verifica si la URL
proporcionada pertenece a un dominio de confianza
antes de obtener la clave
. Para resolver
el laboratorio, debemos forjar un JWT
utilizando una URL maliciosa
en el parámetro jku
. Esta URL debe apuntar a un servidor bajo nuestro control
, donde alojaremos una clave pública
diseñada para verificar
nuestro JWT modificado
. Una vez que el servidor acepte nuestro JWT
, obtendremos acceso al panel de administración
en /admin
y eliminaremos al usuario
carlos
. Podemos iniciar sesión
en nuestra propia cuenta utilizando las credenciales wiener:peter
Resolución
Al acceder
a la web
nos sale esto
Pulsamos en My account
y nos logueamos
con las credenciales wiener:peter
Recargamos
con F5
y capturamos
la petición
con Burpsuite
Este es el JWT
, lo vemos así gracias a la extensión JWT Editor
Vemos que el algoritmo
usado en un RS256
, nos dirigimos a la ventana JWT Editor
y nos creamos
una clave privada RSA
pulsando en New RSA Key
Nos dirigimos al Exploit server
y pegamos este JSON
, que es la clave privada
en el body
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"keys": [
{
"p": "5uY4PlzLzwgO4g3LtAxzC5SUOtBaq4HhxeW5YPNKDBL34NxUufvOjHeUz16LQBFGWlBgobX-yYGDw4nj_MDD5Qv1XxjLEQ19nrAfTaFfsysK48JP1d-6h6abYsBm_fESj54HumkncvkUIx85OoCXz0jj679t_MbutKUGdocxvx8",
"kty": "RSA",
"q": "v44iZ9OE1u1bR8AOiXdDIRcAksofJgsWDRkPemEukn_krCitsXpyvUPksCOLY1BrlLUdtL1ODRoPW72sWBWsJ4fecb3oHmPt2qpgX0KWdg0Sx7tXrRPd49mUrX6TLFl_0zvvaFW_kR_xIOJZbgbFIGe0ngUmy-ofzUTdmSV3C1M",
"d": "EwlX1HlcaUIzcWZIpcM5XwfLilRURz2Kc2ATTDuKGx0xjz9PwwbVV0LkMnZCxWmDX0ZEHTccxPsrM9BoC7bjx7rPhqE16bhc_pGOH_ty_Uqq1Ezk-bwHSjimfOvFNsiv2IVUmOfW2In_o7JrfmYgn1VqJXG626EE0VMFwkFT7U4nYcXhEljiIxmeJTcdHDwocALRXgphVQtDo5NG7CdjsxtKnncv9_Ke4QmoPeeRGSe7l-pMyNsnwryr1VL72fzVfOVfkBaxsV0XKwmr3EcWL0pEYdDmjrsv7Nq9hIMbRE71OYQVa5kLuLexp3TxmhYOjXaZc9p17TE--FkbP243",
"e": "AQAB",
"kid": "1413e558-09db-4e90-a475-fc2b41665c6a",
"qi": "vJqiRzmZT73cbdMjtkJdm3wxHk0qTH1-yvucm8vzR_sx1HuJdYjM52-NVuOutfFiuDQ1P--I5ZyqtyfLidTpMUZMMVVL8Sj8iNIV6XEmO07hXzRmQwaMYzkQ5a_vpE8YG-QZ2fPx9u3XX2iQ-A55WUC8ET1lVXHfWAM_bcmxzTM",
"dp": "Cs8kOuclM1_xMmvq6Vbb2-LsvYQbeoPuCdGjSAKbqM0KhaBK5xn-pVzDvqFya2Fjpb61h1x5vP0vKew-nAp6ITLaHcWPXBEBnHgqPvwa-sw9CkSCcES0-ry18X2IVMMAV3DSc8uQaTV3190SVMbIVI_Y-6ROhdLaQeKn1syla4k",
"dq": "a5ie4ssA9ujkOO08PSLsZg3RmKJH1MqtXDYTZ55m1otYGwvOZL5u7LjSH84XYXWuuBzzzq32xY05y0JDKEckKCA9Zo5Rb6CZ3hWNhHSWp0C4p9Anc2SRYoa46KsMaADtt-0WkPh5Xj8e5mW97fuHhqg53_jszeV4b4ibIIbW01k",
"n": "rMX2sfK0AtkwMN0A3FH2OyKL0FclVpdf2Df95mlZ3q4PMG0vSFoluzOBHHCaUod6wxSP8OM3J34ulPzEi3zT9arkzzCQ0buTy6TL4TXMLa-YlQXnVaBsQ3k6qoOo5TUt65JSAJTxWEQD0RJuD9BIDFXkGV5YWvN241lV_cesNsXyLQyUehvPCG1mvC4Zn3HLh686A_X7JPMQI7230OkNnyOD99mdGUFYhRHcSFI8Gvz3WQKWQyzYgXN9mCnEweNS4VLqxHaQr6c-8DLz90w_ZN4bb9dl-EKZv_sGPZGP56v7eWvJLDdKsFnwLVBnRtyDQnP_BIzT_V0hYDWDSsBMDQ"
}
]
}
Debido a que estamos tramitando
un JSON
, debemos cambiar
el Content-Type
en el Head
a application/json
1
Content-Type: application/json
Cambiamos
el nombre
de usuario
a administrator
Actualizamos
el kid
, el cual debe ser el mismo que el de la clave privada
que hemos generado y añadimos
el parámetro jku
que apunta al servidor
desde donde cargaremos
la clave RSA
Firmamos
el JSON
con la clave privada
Hacemos una petición
a /admin
para comprobar que nos hemos convertido
en administrador
En el navegador pulsamos Ctrl + Shift+ i
y pegamos la cookie
Refrescamos
la web
con F5
y ya podemos eliminar
al usuario carlos
Hemos podido vulnerar
este laboratorio
debido a que, en lugar de incorporar claves públicas
directamente mediante el parámetro
de encabezado jwk
, algunos servidores
permiten utilizar el parámetro de encabezado jku
(URL del conjunto JWK
) para hacer referencia a un conjunto JWK
que contiene la clave
. Durante el proceso de verificación de la firma
, el servidor
obtiene la clave relevante desde esta URL
. Este es un ejemplo de un conjunto de claves JWK
al cual podemos acceder
mediante el parámetro jku
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
"n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
},
{
"kty": "RSA",
"e": "AQAB",
"kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
"n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
}
]
}
Conjuntos JWK
como este a veces se exponen públicamente a través de un endpoint estándar
, como /.well-known/jwks.json
. Los sitios web
más seguros
solo obtienen claves de dominios confiables
, pero a veces puedes aprovechar
las discrepancias
en el análisis
de la URL
para evitar
este tipo
de filtrado
. En este laboratorio
se está confiando
en dominios externos
, esto se solucionaría teniendo una whitelist
de dominios confiables
. Hay algunos ejemplos
en este laboratorio
de PortSwigger
https://portswigger.net/web-security/ssrf#ssrf-with-whitelist-based-input-filters sobre SSRF