JWT
Estrutura
O JWT (JSON Web Token) é divido em 3 partes, separadas por um .
(ponto). A primeira parte é o Header
(configurações do JWT), a segunda parte é o Payload
(infomações particulares do usuário) e o último é a Assignature
(chave de acesso). Tanto o Header, quanto o Payload podem ser descriptografados e manipulados, pois estão em base64
.
No Header, geralmente vamos temos ter uma configuração do tipo {"alg":"HS256","typ":"JWT"}
, onde podemos selecionar o tipo de algorítmo, como HS256
(chave secreta para assinar e verificar cada mensagem) ou RS256
(usa a chave privada para assinar a mensagem e a chave pública para autenticação). Como a chave pública às vezes pode ser obtida pelo invasor, ele pode modificar o algorítmo no cabeçalho para HS256 e, em seguida, usar a chave pública RSA para assinar os dados. O código de back-end usa a chave pública RSA + algorítmo HS256 para verificação de assinatura.
Na segunda parte (payload) contém informações do user, como login, groups, etc. Também vemos alguns valores não obrigatórios, mostrados abaixo:
INDEX
DESCRIÇÃO
sub
(subject)
Entidade à quem o token pertence, normalmente o ID do usuário
iss
(issuer)
Emissor do token
exp
(expiration)
Timestamp de quando o token irá expirar
iat
(issued at)
Timestamp de quando o token foi criado
nbf (Not Before)
Identifica o tempo antes do qual o token não deve ser aceito para processamento
aud
(audience)
Destinatário do token, representa a aplicação que irá usá-lo
A última parte (assignature) é feita com o seguinte padrão (exemplo feito em Python):
Gerando Chave Privada e Pública
Manipulando Token
Isso irá alterar os valores em tempo real.
JsonWebTokenError (Modo Debug Ativo)
Nada melhor do que procurar dados sensíveis em um dos princípios básicos da segurança: Mensagens de erro ou debug!!
No caso do JWT é até melhor do que isso, pois podemos ter na resposta do erro, a assinatura (última parte do token). E o melhor de tudo, para fazer isso basta enviar um token inválido. Mas não envie um abcd
da vida no token, mas sim um token inválido que tenha pelo menos a estrutura de um token válido, por exemplo, podemos pegar um token válido e alterar o payload.
Provavelmente ao enviar o token manipulado, a aplicação irá nos retornar uma mensagem como no exemplo abaixo, então basta pegar o hash desse resultado (em negrito), colocar na última parte do token (assinatura) e enviar novamente ao endpoint.
JsonWebTokenError: Invalid Signature. Expected
cASN_7WEv-nr_Z7XZFT0hVGOFPeeo7Qlvp7-QrZtdw4
got 7zobEz6bncpk7OjQpTfvh-a3ElW1HK3L4Wyypgxa_dw
None Algorithms
Pegue o token JWT, divida-o em 3 partes separadas por pontos (<header>.<payload>.<assignature>
). Faça um base64 decode da primeira parte e altere o valor de alg
para None
. Agora na segunda parte, manipule algum valor do user, por exemplo, podemos mudar o tipo do user para um perfil de administrador ou até mesmo ter acesso ao usuário admin (alterando o username/login). A última parte é a assinatura, então não iremos mexer em nada.
DICA: Já presenciei alguns valores diferente do alg
dando certo de maneiras diferentes e em diferentes lugares, então tente algumas variantes, como None
, none
, "None"
e "none"
.
Feito isso, codifique essas duas partes em base64 e monte o token novamente, utilizando o .
(ponto) para dividir as 3 partes. Como o alg
foi definido como None
, então não precisa colocar a última parte do token, ficando assim no seguinte padrão: <header>.<payload>.
(Deixe o ponto no final). Agora coloque o token no header e veja se o resultado.
NOTA: Ao fazer um base64 decode, pode ser que o json não fiquei no seu formato correto (geralmente faltando um }
no final). Arrume isso colocando um sinal de =
no final do encode original, mas não use-o no Request Header.
TokenBreaker
Tudo o que vimos como fazer um ataque None Algorithms de forma manual, vamos utilizar uma ferramenta para automatizar o processo.
Convertendo RSA para HMAC
Podemos modificar o header para ler um HMAC (HS256) ao invés de RSA (RS256). Porém, para realizarmos essa operação, precisamos ter um rsa public key
em mãos, ou caso contrário, não irá ter êxito. O RsaToMac.py
faz parte do pacote TokenBreaker
.
KID (Key ID)
Em alguns casos (principalmente nas aplicações mais modernas do JWT), vemos o campo kid
(Key ID) no header, seguido de um hash. O servidor usa isso para evitar que alguém possar alterar os valores do header. Mas como nada é perfeito, veremos que o kid
também possui falhas.
Podemos utilizar um token de outro usuário, alterando o valor do kid
para ../../../../../../../../../../dev/null
, e utilizando a key (para gerar o token com valor vazio). Feito isso, basta alterar no payload as devidas informações para utilziar a conta de outro usuário.
Também podemos citar a CVE-2017-17405 que surgiu a partir do kid
, onde podemos conseguir um RCE, aproveitando de uma falha escrita em Ruby. Para isso, precisamos apenas trocar o valor do kid
para |<comando>
.
jwt_tool
Brute Force
jwtcat
Utilizando incremental
Utilizando wordlist
jwt_tool
Selecione a opção 7 depois de executar o comando abaixo:
c-jwt-cracker
Brute force em uma senha que possui somente 6 dígitos:
jwt-pwn
jwtcrack
Hashcat
John the Ripper
Forjando JWT com Certificado Digital
Primeiro vamos extrair o certificado (https), que pode ser pêgo através do navegador web, ou utilizando o comando abaixo e verificando sua saída.
Agora devemos extrair a nossa chave pública a partir do certificado que acabamos de capturar.
Abaixo está um script desenvolvido em python para gerar um novo token JWT válido a partir da chave pública gerada. Não se esqueça de alterar o valor da variável encoded_payload
no código abaixo.
Por fim, execute o script para ter um JWT válido em mãos
Buscando por Diversas Vulnerabilidades
jwt tools
jwtcat
Sites
Last updated