# EKS and ECS Cluster Enumeration

> Techniques for enumerating AWS EKS and ECS clusters, tasks, services, and container metadata to map containerised workloads and identify attack surface.

- **URL:** https://hackindex.io/platforms/aws/enumeration/eks-ecs/eks-ecs-cluster-enumeration
- **Path:** AWS > Enumeration > EKS / ECS
- **Up:** [EKS / ECS](https://hackindex.io/platforms/aws/enumeration/eks-ecs.md)
- **Published:** 2026-05-24
- **Updated:** 2026-05-24

EKS clusters expose a Kubernetes API endpoint backed by AWS IAM authentication. Enumerating clusters tells you what is deployed and where identity boundaries may be weak.

- EKS clusters with public API endpoints and unrestricted CIDR ranges
- Node group IAM roles with overly broad permissions (S3, ECR, Secrets Manager)
- OIDC trust policies using `StringLike` wildcards instead of `StringEquals`
- ECS task definitions with plaintext credentials in environment variables
- ECS Exec enabled on clusters, allowing direct shell access to running containers

## EKS

### List Clusters

Output is just cluster names for the specified region. Run this across every region your access covers.

```bash
aws eks list-clusters --profile $PROFILE --region $REGION
```

### Describe Cluster

The describe call returns the full cluster config. Focus on the Kubernetes version, the API endpoint, the cluster IAM role, and the public access setting.

```bash
aws eks describe-cluster --name $CLUSTER_NAME --profile $PROFILE --region $REGION --query 'cluster.{Name:name,Version:version,Endpoint:endpoint,RoleARN:roleArn,VPC:resourcesVpcConfig.vpcId,Public:resourcesVpcConfig.endpointPublicAccess}'
```

`endpointPublicAccess: true` means the Kubernetes API server is reachable from the internet. If public CIDR ranges are not restricted, valid credentials can reach the API from anywhere. Check `publicAccessCidrs` in the full describe output. A value of `0.0.0.0/0` means no IP restriction is in place. If you have valid AWS credentials, try generating a kubeconfig with `aws eks update-kubeconfig`. Even read-only Kubernetes API access can reveal running workloads, mounted secrets, and service account tokens. Also note the Kubernetes version. Older versions may expose known API vulnerabilities or run deprecated admission controllers that weaken pod security.

### Node Groups

Node groups define the EC2 instances that run cluster workloads. The node IAM role is the critical field.

```bash
aws eks list-nodegroups --cluster-name $CLUSTER_NAME --profile $PROFILE --region $REGION
aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name $NODEGROUP --profile $PROFILE --region $REGION --query 'nodegroup.{NodeRole:nodeRole,InstanceType:instanceTypes,AmiType:amiType}'
```

The `nodeRole` ARN reveals the IAM role attached to all EC2 nodes in the group. Any pod that escapes to the host inherits this role. Look at this role's policies using IAM enumeration. Common misconfigurations attach broad S3 or ECR access at the node level. The correct pattern is pod-level access via IRSA. With `hostPID` or privileged mode enabled, the node role is reachable via the instance metadata at `169.254.169.254`. Also note the AMI type. Amazon Linux 2 nodes run older kernels that may be vulnerable to container escape techniques.

### OIDC Provider

EKS uses OIDC for pod-level IAM roles (IRSA). IRSA lets individual pods assume IAM roles without sharing the node role. This is the correct isolation pattern, but misconfigured trust policies break it. List the OIDC provider:

```bash
aws iam list-open-id-connect-providers --profile $PROFILE
```

OIDC-trusted roles have trust policies that bind to a specific namespace and service account. The `StringEquals` condition is the secure form:

```json
{
  "Condition": {
    "StringEquals": {
      "oidc.eks.$REGION.amazonaws.com/id/$CLUSTER_ID:sub": "system:serviceaccount:$NAMESPACE:$SA_NAME"
    }
  }
}
```

Overly broad conditions (e.g. `StringLike` with wildcards) allow any pod in the namespace to assume the role. A condition like `system:serviceaccount:prod:*` means any service account in the `prod` namespace can assume the role. Control any pod in that namespace and you can request the projected token to assume the role directly. Look for roles with high-value permissions such as `s3:GetObject`, `secretsmanager:GetSecretValue`, or `iam:PassRole`. Cross-reference OIDC-trusted roles with their policies to find the most valuable targets.

ECS clusters group EC2 or Fargate capacity. The cluster ARN is required for all subsequent calls. Check every region and account in scope.

## ECS

### List Clusters

```bash
aws ecs list-clusters --profile $PROFILE --region $REGION
```

### List Services

Services keep a desired count of tasks running. The task definition revision shows which container config version is active. A stopped service may still have task definitions worth inspecting.

```bash
aws ecs list-services --cluster $CLUSTER_ARN --profile $PROFILE --region $REGION
aws ecs describe-services --cluster $CLUSTER_ARN --services $SERVICE_ARN --profile $PROFILE --region $REGION --query 'services[*].{Name:serviceName,Task:taskDefinition,Status:status,Running:runningCount}'
```

### Task Definitions

Task definitions are versioned. Always check the active revision and at least one previous revision. Secrets and credentials are often left in plain environment variables after initial setup and never removed. The `secrets` field references Parameter Store or Secrets Manager values by ARN. Finding those ARNs tells you what secrets are in use and whether your current role can read them.

```bash
aws ecs list-task-definitions --profile $PROFILE --region $REGION
aws ecs describe-task-definition --task-definition $TASK_DEF --profile $PROFILE --region $REGION
```

**Extract environment variables**

```bash
aws ecs describe-task-definition --task-definition $TASK_DEF --profile $PROFILE --region $REGION | jq '.taskDefinition.containerDefinitions[].environment'
```

**Check secrets references**

```bash
aws ecs describe-task-definition --task-definition $TASK_DEF --profile $PROFILE --region $REGION | jq '.taskDefinition.containerDefinitions[].secrets'
```

### Running Tasks

Running tasks show what is live right now. The task ARN is your handle for ECS Exec if it is enabled on the cluster. Check `lastStatus` to confirm the task is running before attempting to exec. If ECS Exec is enabled, the SSM agent runs inside the container. Open a shell via `aws ecs execute-command` without touching the task definition.

```bash
aws ecs list-tasks --cluster $CLUSTER_ARN --profile $PROFILE --region $REGION
aws ecs describe-tasks --cluster $CLUSTER_ARN --tasks $TASK_ARN --profile $PROFILE --region $REGION --query 'tasks[*].{Task:taskArn,Role:overrides.taskRoleArn,Status:lastStatus}'
```

### Task Role

The task role is distinct from the task execution role. The execution role only covers pulling images and writing logs to CloudWatch. The task role is what the application code runs as. Enumerate what it can do:

```bash
aws ecs describe-task-definition --task-definition $TASK_DEF --profile $PROFILE --region $REGION | jq -r '.taskDefinition.taskRoleArn'
```

Then enumerate that role's policies as per [IAM Authenticated Enumeration](https://hackindex.io/platforms/aws/enumeration/iam/iam-authenticated-enumeration). An ECS task role with broad S3 or SSM access is a high-value lateral movement target. If the task role has `iam:PassRole` or `ecs:RegisterTaskDefinition`, you can register a new task definition with a different role. Specify a higher-privileged role in the new definition and launch a task to pivot.
