You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/pentesting-cloud/aws-security/aws-post-exploitation/aws-ec2-ebs-ssm-and-vpc-post-exploitation/README.md
+40Lines changed: 40 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -183,6 +183,44 @@ It's possible to run an EC2 instance an register it to be used to run ECS instan
183
183
184
184
For [**more information check this**](../../aws-privilege-escalation/aws-ec2-privesc/README.md#privesc-to-ecs).
A compromise inside any ECS task running on an EC2 container instance is typically enough to pivot into the host role and the IAM roles associated with all the other tasks in that node. Because there is **no task isolation for ECS-on-EC2**, every task can query the EC2 Instance Metadata Service (IMDS) by default, steal the container instance profile, and then talk the same WebSocket protocol that the ECS agent uses to the control plane (the **ECScape** primitive) to request the credentials for every task currently scheduled on that host. Latacora documented this workflow in their [ECS-on-EC2 IMDS research](https://www.latacora.com/blog/2025/10/02/ecs-on-ec2-covering-gaps-in-imds-hardening/), which the following offensive summary condenses.
189
+
190
+
#### Attack chain
191
+
192
+
1.**Steal the instance profile from inside the container.** Assume IMDSv2 is required, so request a token and then fetch the profile.
193
+
194
+
```bash
195
+
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
2.**Use the container instance role to impersonate the ECS agent.** With those credentials you can speak the undocumented WebSocket channel the ECS agent uses; the control plane trusts you as the real agent and delivers **all task IAM credentials** to your process. You can now run higher-privileged tasks locally, dump task environment secrets, or update services/tasks to redeploy workloads you can fully inspect.
199
+
200
+
#### IMDS reachability with IMDSv2 + hop limit 1
201
+
202
+
Setting IMDSv2 with `HttpTokens=required` and `HttpPutResponseHopLimit=1` only blocks tasks that live behind an extra hop (Docker bridge). Other networking modes stay within one hop of the Nitro controller and still receive responses:
203
+
204
+
| ECS network mode | IMDS reachable? | Reason |
205
+
| --- | --- | --- |
206
+
|`awsvpc`| ✅ | Each task gets its own ENI that is still one hop away from IMDS, so tokens and metadata responses arrive successfully. |
207
+
|`host`| ✅ | Tasks share the host namespace, so they see the same hop distance as the EC2 instance. |
208
+
|`bridge`| ❌ | Responses die on the Docker bridge because that extra hop exhausts the hop limit. |
209
+
210
+
Therefore, **never assume hop limit 1 protects awsvpc or host-mode workloads**—always test from inside your containers.
211
+
212
+
#### Detecting IMDS blocks per network mode
213
+
214
+
-**awsvpc tasks:** Security groups, NACLs, or routing tweaks cannot block the link-local 169.254.169.254 address because Nitro injects it on-host. Check `/etc/ecs/ecs.config` for `ECS_AWSVPC_BLOCK_IMDS=true`. If the flag is missing (default) you can curl IMDS directly from the task. If it is set, pivot into the host/agent namespace to flip it back or execute your tooling outside awsvpc.
215
+
216
+
-**bridge mode:** When metadata requests fail even though hop limit 1 is configured, defenders probably inserted a `DOCKER-USER` drop rule such as `--in-interface docker+ --destination 169.254.169.254/32 --jump DROP`. Listing `iptables -S DOCKER-USER` exposes it, and root access lets you delete or reorder the rule before querying IMDS.
217
+
218
+
-**host mode:** Inspect the agent configuration for `ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST=false`. That setting removes task IAM roles entirely, so you must either re-enable it, move to awsvpc tasks, or steal credentials through another process on the host. When the value is `true` (default), every host-mode process—including compromised containers—can hit IMDS unless bespoke eBPF/cgroup filters target `169.254.169.254`; look for tc/eBPF programs or iptables rules referencing that address.
219
+
220
+
Latacora even released [Terraform validation code](https://github.com/latacora/ecs-on-ec2-gaps-in-imds-hardening) you can drop into a target account to enumerate which network modes still expose metadata and plan your next hop accordingly.
221
+
222
+
Once you understand which modes expose IMDS you can plan your post-exploitation path: target any ECS task, request the instance profile, impersonate the agent, and harvest every other task role for lateral movement or persistence inside the cluster.
223
+
186
224
### Remove VPC flow logs
187
225
188
226
```bash
@@ -618,6 +656,8 @@ if __name__ == "__main__":
618
656
619
657
## References
620
658
659
+
- [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/)
> Note that if the EC2 instance is enforcing IMDSv2, [**according to the docs**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html), 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.
23
+
> IMDSv2 with a hop limit of 1 **does not** block awsvpc or host-networked tasks—only Docker bridge tasks sit far enough away for the responses to die. See [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) for the full attack workflow and bypass notes. Recent [Latacora research](https://www.latacora.com/blog/2025/10/02/ecs-on-ec2-covering-gaps-in-imds-hardening/) shows that awsvpc and host tasks still fetch host credentials even when IMDSv2+h=1 is enforced.
24
24
25
25
### Privesc to node to steal other containers creds & secrets
0 commit comments