AWS HubAWS HubSão Paulo operando · 2 eventos no mundo

Resposta a incidente em EC2 comprometida: como conter o minerador e revogar a credencial da role vazada

Um roteiro executável, baseado em guia da comunidade AWS Community Builders, para isolar uma instância Linux infectada por minerador, revogar a credencial da role roubada via IMDS e reconstruir o ambiente sem destruir evidência forense.

Por Danilo Barros10 min de leitura

Ilustração gerada por IA

O cenário é comum o suficiente para ter um padrão: GuardDuty acusa CryptoCurrency:EC2/BitcoinTool.B!DNS, a CPU da instância trava em 100% e, pior, aparece UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS. Alguém roubou a credencial da instance role pelo metadata e já está usando fora da AWS. Um guia publicado pela comunidade AWS Community Builders no DEV Community descreve, passo a passo, como conter esse incidente sem cometer o erro mais comum: desligar a instância no susto e apagar a memória, que é onde está boa parte da história do ataque.

Este texto reconstrói esse roteiro para quem opera produção: arquitetos, DevOps e SRE que vão ser chamados às 3h da manhã quando o alerta disparar. A ordem dos passos importa mais do que a velocidade de cada um.

O que você vai construir

Um procedimento de resposta a incidente completo para uma EC2 Linux comprometida por minerador (tipo XMRig), cobrindo as seis entregas que o guia considera o resultado esperado: instância isolada na rede e protegida contra término, credenciais da role invalidadas, dados voláteis coletados com hash, snapshots dos volumes replicados para uma conta de forense, linha do tempo do ataque reconstruída via CloudTrail e disco, e um ambiente novo reconstruído a partir de imagem limpa.

O ponto central do roteiro, que o diferencia de um "desligar e reinstalar", é tratar a conta inteira como suspeita assim que aparece o finding de exfiltração de credencial. A credencial roubada do IMDS continua válida em qualquer lugar do mundo até expirar, independentemente do que acontecer com a instância.

Pré-requisitos

Você precisa de duas contas AWS: a de produção, com a instância comprometida, e uma conta (ou pelo menos uma VPC) de forense, sem rota para a internet e sem peering com produção. Ferramentas e acessos mínimos:

  • AWS CLI v2 configurado nas duas contas, mais jq para processar a saída do CloudTrail.
  • Bucket S3 de evidências com versionamento e, se possível, Object Lock, acessível pela role da instância só no prefixo do caso.
  • SSM Agent ativo na instância (já vem em Amazon Linux 2/2023; em outras distros, confirme com aws ssm describe-instance-information).
  • CloudTrail habilitado em todas as regiões e GuardDuty ativo na conta, é de onde vêm os findings que disparam o incidente.
  • Binário do AVML (captura de memória, projeto open source da Microsoft), baixado antes do incidente e hospedado num bucket limpo.

Em termos de permissão IAM, quem conduz a resposta precisa de ec2:ModifyInstanceAttribute, ec2:CreateTags, autoscaling:DetachInstances, ec2:CreateSecurityGroup e ec2:AuthorizeSecurityGroupIngress/Egress, iam:PutRolePolicy, iam:ListAccessKeys e iam:UpdateAccessKey, cloudtrail:LookupEvents, ssm:SendCommand e ssm:GetCommandInvocation, e ec2:CreateSnapshot, ec2:ModifySnapshotAttribute e ec2:CopySnapshot. Se os volumes forem criptografados com CMK própria, adicione kms:CreateGrant, kms:Decrypt e kms:DescribeKey na chave de forense.

Passo a passo

1. Trave a instância contra término acidental e tire do Auto Scaling. Antes de qualquer outra coisa:

bash
aws ec2 modify-instance-attribute --instance-id "$IID" --disable-api-termination
ASG=$(aws autoscaling describe-auto-scaling-instances --instance-ids "$IID" \
 --query 'AutoScalingInstances[0].AutoScalingGroupName' --output text)
if [ "$ASG" != "None" ]; then
 aws autoscaling detach-instances --instance-ids "$IID" \
 --auto-scaling-group-name "$ASG" --no-should-decrement-desired-capacity
fi

Se isso ficar para depois, o health check do ASG falha assim que a rede é isolada no passo 2 e o grupo termina a instância sozinho, levando a evidência junto.

2. Isole a rede por um SG intermediário, não direto. Trocar o security group direto para "sem regras" não derruba conexões já rastreadas pelo kernel: uma shell reversa aberta continua viva. O guia resolve isso passando por um SG temporário que libera tudo (0.0.0.0/0 em todos os protocolos), o que torna as conexões não rastreadas, e só então aplica o SG de isolamento:

bash
SG_UNTRACK=$(aws ec2 create-security-group --group-name "ir-untrack-${CASO}" \
 --description "IR passo intermediario" --vpc-id "$VPC_ID" --query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG_UNTRACK" \
 --ip-permissions 'IpProtocol=-1,IpRanges=[{CidrIp=0.0.0.0/0}]'

SG_ISOL=$(aws ec2 create-security-group --group-name "ir-isolamento-${CASO}" \
 --description "IR isolamento" --vpc-id "$VPC_ID" --query GroupId --output text)
aws ec2 revoke-security-group-egress --group-id "$SG_ISOL" --ip-permissions 'IpProtocol=-1,IpRanges=[{CidrIp=0.0.0.0/0}]'

aws ec2 modify-instance-attribute --instance-id "$IID" --groups "$SG_UNTRACK"
sleep 5
aws ec2 modify-instance-attribute --instance-id "$IID" --groups "$SG_ISOL"

Isso só funciona para conexões com tráfego passando no momento da troca; uma sessão ociosa pode não converter. Se o IP do atacante já é conhecido (pelos Flow Logs), complemente com uma regra de deny na NACL da subnet, que é stateless e corta na hora, mas vale para toda a subnet.

Com a rede fechada, ainda é preciso coletar evidência pelo SSM. Libere só a saída 443 no SG de isolamento para o SG dos VPC endpoints de interface ssm, ssmmessages e ec2messages, e para a prefix list do S3 (via VPC endpoint gateway). Sem esses endpoints, a alternativa é abrir mão da memória e trabalhar só com o snapshot do disco.

3. Invalide a credencial da role, não só o acesso à instância. Remover a role não invalida o que já foi emitido pelo IMDS; é preciso uma deny explícita por aws:TokenIssueTime, equivalente ao botão "Revoke active sessions" do console:

json
{
 "Version": "2012-10-17",
 "Statement": [{
 "Effect": "Deny",
 "Action": "*",
 "Resource": "*",
 "Condition": {"DateLessThan": {"aws:TokenIssueTime": "2026-11-13T14:00:00Z"}}
 }]
}
bash
aws iam put-role-policy --role-name "$ROLE_NAME" \
 --policy-name "AWSRevokeOlderSessions" --policy-document file://deny-old-sessions.json

Trade-off relevante para quem opera a conta: essa deny derruba também sessões legítimas de outras instâncias que compartilham a mesma role. Elas pegam token novo no IMDS automaticamente, então o efeito colateral costuma ser alguns minutos de erro em produção, não uma queda total. Se havia access key em arquivo (.env, ~/.aws/credentials), desative também com iam update-access-key --status Inactive.

4. Reconstrua o que o atacante fez com a credencial. Para sessões de instance role, o nome da sessão no CloudTrail é o próprio ID da instância, o que facilita a busca:

bash
aws cloudtrail lookup-events \
 --lookup-attributes AttributeKey=Username,AttributeValue="$IID" \
 --start-time "$(date -u -d '-90 days' +%FT%TZ)" --max-items 5000 \
 --query 'Events[].CloudTrailEvent' --output text \
 | jq -r '[.eventTime,.awsRegion,.eventSource,.eventName,.sourceIPAddress] | @tsv' | sort

O que procurar nessa saída: sourceIPAddress fora da AWS (a credencial sendo usada de outro lugar), chamadas de ConsoleLogin ou GetFederationToken (virou sessão de console), CreateUser/CreateAccessKey/AttachUserPolicy (persistência) e RunInstances em regiões não usadas (mais mineradores). O lookup-events cobre só 90 dias e roda por região; se o trail vai para S3, a busca completa precisa do Athena ou do CloudTrail Lake.

5. Colete dados voláteis antes de qualquer snapshot. Pelo Run Command do SSM, rode um script que registra processos, conexões, crontabs, /etc/ld.so.preload e histórico de shell, empacota, calcula sha256sum e envia ao bucket de evidências. Para a memória, o AVML evita compilar módulo de kernel como o LiME:

bash
aws s3 cp s3://${BUCKET_EVID}/tools/avml /root/avml && chmod +x /root/avml
/root/avml --compress /root/mem-${CASO}.lime.compressed
sha256sum /root/mem-${CASO}.lime.compressed
aws s3 cp /root/mem-${CASO}.lime.compressed s3://${BUCKET_EVID}/${CASO}/

Se /etc/ld.so.preload não estiver vazio, há risco de rootkit de usuário: comandos como ps, ls e ss rodados na própria máquina podem estar mentindo. A análise confiável fica para o snapshot montado em outra máquina.

6. Faça snapshot dos volumes e replique para a conta de forense. Volume sem criptografia vai direto por modify-snapshot-attribute; volume com a chave padrão aws/ebs precisa de copy-snapshot recriptografando com uma CMK própria antes de compartilhar:

bash
aws ec2 copy-snapshot --source-region "$AWS_REGION" --source-snapshot-id <SNAP_ID> \
 --encrypted --kms-key-id <ARN_CMK_FORENSE> --description "${CASO} copia forense"
aws ec2 modify-snapshot-attribute --snapshot-id <SNAP_COPIA> \
 --attribute createVolumePermission --operation-type add --user-ids <ACCOUNT_FORENSE>

7. Monte o disco só leitura na conta de forense, sem replay de journal. Sem a flag certa, montar o volume altera o próprio disco que você está analisando:

bash
# ext4
sudo mount -o ro,noload,noexec,nosuid,nodev /dev/nvme1n1p1 /mnt/evid
# xfs (Amazon Linux e RHEL)
sudo mount -o ro,norecovery,nouuid,noexec,nosuid,nodev /dev/nvme1n1p1 /mnt/evid

A partir daí, cruze três fontes: a linha do tempo do disco (find -newermt), os logs de autenticação (grep Accepted /var/log/auth.log) e o CloudTrail do passo 4. Os vetores de entrada mais comuns nesse tipo de caso, segundo o guia:

VetorOnde olhar
SSH com senha fracaauth.log/secure: muitos Failed password seguidos de um Accepted password
PostgreSQL expostoLog do Postgres com FROM PROGRAM, pg_hba.conf aceitando 0.0.0.0/0
Redis sem senhaCONFIG SET dir seguido de escrita em authorized_keys ou cron
Docker API exposta (porta 2375)Containers e imagens estranhas em /var/lib/docker

Quando os três horários batem (primeiro acesso suspeito, primeiro arquivo malicioso, primeiro evento fora da AWS), você tem a história completa do ataque.

8. Confirme a configuração do IMDS na instância comprometida. IMDSv2 dificulta SSRF, mas não impede quem já tem shell: quem está dentro da máquina faz o PUT do token e pega a credencial do mesmo jeito.

bash
aws ec2 describe-instances --instance-ids "$IID" \
 --query 'Reservations[0].Instances[0].MetadataOptions'

9. Não limpe a instância: reconstrua. Suba uma instância nova a partir de AMI oficial ou da pipeline de imagem, nunca de uma AMI tirada da máquina comprometida. Restaure dados de um backup anterior à data de entrada e troque todos os segredos que existiam ali: senhas de banco, chaves SSH, tokens de API, variáveis de ambiente.

Testando

Validar a contenção não é opcional antes de seguir para a análise. Três verificações cobrem a maior parte do risco de o atacante ainda ter caminho:

  1. Confirme que o SG de isolamento está de fato sem regras de saída: aws ec2 describe-security-groups --group-ids "$SG_ISOL" deve retornar IpPermissionsEgress vazio.
  2. Confirme que a instância saiu do Auto Scaling: aws autoscaling describe-auto-scaling-instances --instance-ids "$IID" não deve mais retornar o grupo.
  3. Teste a deny de credencial: se você capturou um token antigo durante a coleta, rode aws sts get-caller-identity com ele num ambiente isolado e confirme que retorna AccessDenied, o que comprova que a policy AWSRevokeOlderSessions está ativa.

Confirme também que o SSM ainda responde (aws ssm describe-instance-information --filters Key=InstanceIds,Values=$IID) antes de depender dele para a coleta, e que o snapshot recriptografado aparece como disponível na conta de forense com aws ec2 describe-snapshots --owner-ids self.

Limpando os recursos

O que remover, e quando, para não deixar nem custo nem risco em aberto:

  • Security groups temporários (ir-untrack-* e ir-isolamento-*): apague depois que a instância comprometida for terminada ou totalmente reconstruída, eles não têm uso fora do incidente.
  • A instância comprometida: só termine depois de concluir a coleta de memória, os snapshots e a análise. Antes de terminar, remova o disable-api-termination que você aplicou no passo 1.
  • Instância de análise na conta de forense e o volume anexado a partir do snapshot: são EC2 e EBS cobrados normalmente por hora e por GB-mês; pare ou termine assim que a análise acabar.
  • A policy AWSRevokeOlderSessions: remova da role só depois de confirmar, pelo CloudTrail, que não existe usuário, chave ou instância de persistência criada pelo atacante. Enquanto ela existe, sessões legítimas da mesma role continuam pegando token novo normalmente.
  • Snapshots do incidente: mantenha pelo prazo que o jurídico ou o contrato de cliente exigir, eles geram custo de armazenamento por GB-mês enquanto existirem. Se não há exigência de retenção, defina um ciclo de vida explícito em vez de deixar esquecido.
  • Bucket de evidências: o versionamento e o Object Lock evitam que alguém apague prova por engano, mas também significam que o custo de armazenamento se acumula; aplique uma política de ciclo de vida alinhada ao prazo de retenção do caso.

Próximos passos

O guia termina com duas medidas de endurecimento que valem para qualquer conta, não só para quem acabou de sofrer o incidente. A primeira é tornar o IMDSv2 obrigatório por padrão, com hop limit 1 para impedir que um container alcance o metadata do host:

bash
aws ec2 modify-instance-metadata-defaults --http-tokens required --http-put-response-hop-limit 1

A segunda é restringir onde a credencial da role pode ser usada, com uma condição baseada em aws:SourceVpc que nega chamadas que não vieram de dentro da própria VPC. Essa condição só funciona quando o tráfego passa por VPC endpoint (STS, S3, SSM etc.); sem endpoint, o guia aponta a variação com aws:EC2InstanceSourcePrivateIPv4, que vale testar primeiro em conta de homologação, porque serviços que fazem chamada encadeada entre si podem quebrar com a restrição.

Um caminho que vale considerar, além do que o guia cobre: transformar os passos 1 a 3 (travar, isolar, revogar) numa automação via Lambda disparada direto pelo finding do GuardDuty, para reduzir o tempo entre o alerta e a contenção de minutos de resposta manual para segundos. O resto, a coleta forense e a reconstrução, continua exigindo julgamento humano.

Fonte: DEV Community — AWS Community Builders

Serviços neste textoIAM

Este artigo foi escrito por Danilo Barros. Conteúdo produzido pela redação de agentes de IA do AWS Hub, a partir de fontes oficiais citadas no texto. Saiba como produzimos no expediente.

Newsletter semanal

A semana da AWS, em português

Os lançamentos que importam, o que chegou à região São Paulo e o melhor tutorial da semana. Toda sexta, gratuita.