INC-2026-0827highGCP 服務帳戶金鑰外洩(教學個案)

教學個案:一把 Google Service KEY JSON, 從外洩到停權的 15 天全紀錄

一支 LINE Bot 用來讀寫試算表的 GCP:Service Account,金鑰放了六年沒換,而且帳戶本身是專案 Owner。金鑰外洩後,攻擊者先盜用 AI 模型,再開 VM 轉手給別人大量下載,直到 Google 偵測到濫用、停權整個專案,我們才發現。這篇是內部教學版:每一步都標出為什麼能成功、哪一道控制擋得下來,最後附鑑識指令與討論題。

事件期間 2026-08-28 → 09-12·整理於 2026-09-17·14 分鐘·3 可證明2 推論3 缺口
GCPIAM服務帳戶教學個案Vertex AICompute Engine
tl;dr
  1. 外洩的是一把 2020 年建立的 Google Service KEY JSON,被 commit 進至少兩個 repo,也放進部署平台的環境變數。平台 8/27 出事,36 小時內金鑰就被用。
  2. 攻擊者第一個動作不是開 VM,是測 Vertex AI 上哪些模型能用;9/3 才開防火牆與 3 台 e2-standard-4,前 2.5 天閒置,之後被拿去大量下載,收進 3.2 TB。
  3. 帳單 US$146.10 全部可歸因,稽核記錄 14 筆完整;但 VM 裡做了什麼、資料有沒有被讀、流量去哪,因為三種 log 都沒開,永遠答不出來。
◉ 攻擊者視角他要的不是你的資料,是一個有 Owner 權限、沒人看帳單的專案:先試 AI token 能不能轉賣,再備幾台空機器交給別人用。
◎ 防守者視角四道防線任一有做都擋得住:不發金鑰、Bot 不給 Owner、關掉用不到的 API、預算警示。這次四道都沒有。
本篇目錄
VMAISheetsIAMroles/owner · 只需要寫試算表

數字先講

15 天
從第一次被盜用(8/28)到 Google 停權(9/12)
roles/owner
GCP:Service Account 的權限;它的工作只需要試算表
US$146.10
盜用造成的費用;此專案平常是 $0
6 年
Google Service KEY JSON 從建立到外洩都沒輪替(2020-09 建立)

時間一律為台灣時間(UTC+8)。整理於 2026-09-15,這篇教學版於 2026-09-17 去識別化後發布。

這次外洩了什麼

leaked credentials這次外洩了什麼:服務、用途、存在形式
服務憑證類型用途存在形式放在哪權限範圍狀態
GCP IAM 服務帳戶(GCP:Service Account)使用者管理金鑰:Google Service KEY JSONLINE Bot 讀寫 Google 試算表、上傳陳列照片到 Drive 的排程JSON 檔(含 private_key 的 PEM 私鑰、client_email、private_key_id);在部署平台則是整段 JSON 塞進一個環境變數,或 base64 後再塞至少兩個 repo 的同一個目錄(commit 進 git 歷史)+部署平台服務的環境變數roles/owner(整個專案)2026-09-13 刪除金鑰、拿掉 Owner;金鑰 2020-09 建立,6 年未輪替
AWS IAM 使用者access key(AKIA… + secret)應用程式存取 S3/SES 等 AWS 服務明文環境變數(AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)部署平台服務的環境變數依各 IAM policy2026-08-29~30 已輪替(平台清單內)
MySQL/PostgreSQL 資料庫帳號密碼各服務連線資料庫明文環境變數或連線字串(DATABASE_URL)部署平台服務的環境變數該資料庫全部資料2026-08-29~30 已輪替(平台清單內)
其他符合憑證格式的環境變數不明(平台通知只說「符合憑證格式」)不明明文環境變數部署平台不明未逐一確認;平台未提供清單
憑證本身已遮蔽或失效;這裡只說明它是哪個服務、拿來做什麼、以什麼形式存放,讓你能對照自己的環境。

第一列是這篇的主角:同一把 KEY JSON 存在三個地方(兩個 repo 的 git 歷史、部署平台的環境變數),8/29 的輪替只照平台的環境變數清單做,前兩個地方沒人想到。憑證清冊要以「身分」為單位,不是以「存放位置」為單位。

攻擊路徑

外洩的 Google Service KEY JSON(repo 內 commit、部署平台環境變數)→ 攻擊者主機(93.123.109.230,AS48090 主機商,自動化工具)→ 用私鑰換 token,以 GCP:Service Account 的身分行動 → 因為它是 roles/owner,三條路同時打開:

| 路 | 起點 | 做了什麼 | 結果 | |---|---|---|---| | Vertex AI | 8/28 起 | 試呼叫 Claude 27 次(404/429)→ 接受 Model Garden 使用條款 → Gemini 成功 212 次 | US$2.79 | | Compute Engine | 9/3 起 | 防火牆對全網開 22、3000–9999 → 5 台 VM 同時開,3 台成功 → 開機腳本開啟 root 密碼 SSH → 收進 3.2 TB、送出 284 GiB | US$143.32 | | Sheets/Drive | 不明 | 可讀所有分享給這個帳戶的檔案 | 沒開資料存取記錄,無法排除被讀 |

關鍵在中間那一格:服務帳戶有 Owner 權限,所以一把原本只用來寫試算表的金鑰,能打開整個專案的所有服務。第三列是「有能力但沒有記錄可以確認」。

發生了什麼 · 推定 · T1552.001
Google Service KEY JSON 被 commit 進至少兩個 repo 的同一個目錄,也可能放進部署平台的環境變數。8/27 平台發生事件,攻擊者取走了符合憑證格式的環境變數。第一次盜用發生在隔天晚上。
當時留下的訊號
沒有。金鑰在 repo 裡放了好幾年都沒被濫用,卻在平台出事後 36 小時內開始被使用。
能擋下它的控制
不發長期金鑰:改用 Workload Identity 或不需私鑰的代理服務。非發不可就設組織政策讓金鑰自動過期。

通知後金鑰仍有效:17 天

  1. 8/27
    部署平台通知環境變數外洩;我方依平台清單輪替 AWS 金鑰與資料庫密碼
    平台通知信與輪替紀錄。
  2. 8/27(推定)
    Google Service KEY JSON 隨環境變數被取走
    無直接證據,只有時間相關性。
  3. 8/28 23:00
    攻擊者首次使用金鑰(Vertex AI RawPredict)
    Cloud Audit Logs:callerIp 93.123.109.230。
  4. 8/29 03:21
    攻擊者替專案接受 Model Garden 使用條款
    Audit Logs:AcceptPublisherModelEula。
  5. 9/3 12:38–12:40
    開防火牆、同一秒送出 5 台 VM,3 台成功
    Audit Logs:firewalls.insert、instances.insert ×5(2 台 ZONE_RESOURCE_POOL_EXHAUSTED)。
  6. 9/5 20:00
    VM 開始被大量使用,一直到 9/11
    Cloud Monitoring:每 6 小時流入從 3 GiB 暴增到 100 GiB 以上。
  7. 9/7 → 9/12
    Gemini GenerateContent 成功 212 次
    Monitoring api/request_count 依 credential_id。
  8. 9/12 20:27
    Google 停權整個專案
    服務帳戶回 invalid_grant;VM 狀態 TERMINATED。
  9. 9/13
    刪除專案內 7 把使用者金鑰、拿掉 5 個服務帳戶的 Owner/Editor
    Audit Logs:DeleteServiceAccountKey、SetIamPolicy。
  10. 9/14 → 9/15
    送出申訴,隔日解封
    申訴單與解封通知。
斜線區:我們已經知道平台出事,但這把金鑰還能用

8/29~8/30 的輪替只處理了環境變數清單上的 AWS 金鑰和資料庫密碼。以 Google Service KEY JSON 形式散落在 repo 裡的 GCP 服務帳戶金鑰,一直到 9/13 才被處理。從通知到金鑰失效,17 天。

逐步拆解

每一步標出 MITRE ATT&CK 技術編號、當時留下的證據、為什麼能成功,以及哪一個控制可以擋下來。

1. 取得金鑰(8/27 推定 · T1552.001)

這把金鑰 2020 年建立,之後以 Google Service KEY JSON 的形式被 commit 進至少兩個 repo 的同一個目錄,也可能被放進部署平台服務的環境變數。8/27 平台發生事件,攻擊者取走了符合憑證格式的環境變數。

第一次盜用發生在隔天晚上。這把金鑰在 repo 裡放了好幾年都沒被濫用,卻在平台出事後 36 小時內開始被使用,所以研判來源是平台外洩,但沒有直接證據。

為什麼能成功:長期有效的私鑰檔案被複製到多個地方,沒有人知道全部位置在哪。 可以怎麼擋:不發長期金鑰。改用 Workload Identity 或不需私鑰的代理服務;非發不可就設組織政策讓金鑰自動過期。

2. 用金鑰登入,測試能用哪些 AI 模型(8/28 23:00 · T1078.004)

攻擊者用私鑰簽 JWT 換成 access token,直接以 GCP:Service Account 的身分呼叫 API。第一件事不是開 VM,而是在 us-central1、us-east5、europe-west1/4 呼叫 Vertex AI 上的 Claude 模型(RawPredict),全部回 404 或 429。8/29 03:21 接著呼叫 AcceptPublisherModelEula,替我們的專案接受了模型使用條款。

Admin Activity 稽核記錄(節錄)READ-ONLY
2026-08-28T19:21:46Z  ModelGardenService.AcceptPublisherModelEula
principal  GCP:Service Account
callerIp   93.123.109.230

為什麼能成功:專案裡 Vertex AI API 是啟用的,服務帳戶又是 Owner,什麼都能呼叫。 可以怎麼擋:關掉用不到的 API;Bot 帳戶只給需要的角色。讀寫試算表其實不需要任何專案角色,檔案有分享給帳戶就夠了。

3. 探查環境,打開防火牆(9/3 12:38–12:40 · T1580 · T1562.007)

攻擊者沒有開通任何服務。400 天的稽核記錄裡沒有攻擊者呼叫 EnableService:Compute Engine API 是我方 2026-03 為了一次測試自己開的,Vertex AI API 更早就開著。不過 Owner 本來就有權限開任何 API,所以就算當時是關的,也擋不住。

攻擊者先列出區域和網路,嘗試建立 default 網路(已存在,失敗)。兩分鐘後建立防火牆規則 crusader-allow-ssh,對 0.0.0.0/0 開放 tcp:22 與 3000–9999。整個過程只有幾秒鐘,是寫好的腳本在跑。

為什麼能成功:專案保留了預設網路和預設防火牆規則,Compute Engine API 為了一次測試開了之後沒關。 可以怎麼擋:沒有 VM 需求的專案停用 Compute Engine API;用組織政策限制可用地區,並禁止對 0.0.0.0/0 開放。

4. 分散開 VM,植入後門(9/3 12:40 · T1578.002 · T1098)

攻擊者沒有用 gcloud 或主控台,而是直接打 Compute Engine REST API(user-agent 只有 gzip(gfe))。一個 instances.insert 請求就帶齊所有設定:

| 欄位 | 攻擊者的設定 | 用意 | |---|---|---| | zone | us-central1-a/b、us-east4-a、us-east1-b/c,同一秒送出 5 台 | 分散地區,某區資源不足或被封也有別台能用 | | machineType | e2-standard-4(4 vCPU、16GB),標準計費、非搶佔式 | 便宜又不會被中途回收 | | disks | Ubuntu 24.04 官方映像檔,200GB pd-balanced | 放得下大量下載的檔案 | | networkInterfaces | default 網路、外部 IP(Premium tier)、網路標記 crusader | 讓前一步開的防火牆規則套用上去 | | serviceAccounts | 不掛 | 不需要,也避免留下更多 GCP 記錄 | | metadata | 開機腳本;enable-oslogin=FALSE | 開機自動植入 root 密碼登入 |

us-east1 的兩台回 ZONE_RESOURCE_POOL_EXHAUSTED 失敗,另外 3 台成功。開機腳本(metadata.startup-script)只做三件事,這裡只講形狀不貼指令:

  • 替 root 設一組密碼並解鎖 root 帳號
  • 改 sshd 設定,允許密碼登入與 root 直接登入,然後重啟 ssh
  • 對外查自己的公網 IP,印一行「idle-vm ready」帶著 IP 回報

腳本名稱叫 idle-vm,沒有挖礦程式,只把機器變成「可以用 root 密碼 SSH 登入」並回報 IP,也就是備好一台空機器交給別人用。VM 上沒有掛服務帳戶,所以攻擊者無法從 VM 再往 GCP 內部擴散。

為什麼能成功:沒有配額上限、沒有預算警示,新開 3 台 VM 沒有觸發任何通知。 可以怎麼擋:把 CPU 配額調到 0;對帳單帳戶設預算警示(例如每月 $10);監控 instances.insert 並通知。

5. 待命兩天半,然後被拿去大量下載(9/3 → 9/12 · T1496)

3 台 VM 開好後,前 2.5 天幾乎閒置:CPU 2–3%,每 6 小時只有約 3 GiB 進出,符合腳本名稱 idle-vm,是備好的空機器在等人接手。9/5 20:00 起流量突然暴增,CPU 升到 10–30%,一直持續到 9/11;9/11 08–14 時單一時段收進 486 GiB。

三台合計收進 3,186 GiB、送出 284 GiB,收比送多 11 倍。單純當代理的話,進出量會差不多,所以這不像跳板,比較像是機器被交給別人拿去大量下載。下載的內容從 GCP 外部看不到。GCP 流入流量不收費,所以帳單上只看得到送出的費用。

VM-2 和 VM-5 在 9/11 20:00 左右停止活動,VM-1 一直用到 9/12 停權。9/8 起,攻擊者每小時列一次 VM 清單,確認機器還活著。同一段期間(9/7 00:00~9/12 21:00),攻擊者改用 Gemini:GenerateContent 成功 212 次,混用 2.5 Pro、3.x Pro、3.6/3.7 Flash,文字和圖片都有,主要在 us-west4。

為什麼能成功:專案平常沒有任何計費,每天突然多出 $12~21,但沒有人在看這個專案的帳單。 可以怎麼擋:開啟帳單異常偵測和預算通知;依憑證監控 API 用量。一個 Sheets Bot 帳戶出現 compute 或 aiplatform 呼叫,就是警訊。

6. 被 Google 攔下(9/12 20:27)

Google 偵測到濫用,停權整個專案,三台 VM 被強制關機。我們是從服務帳戶突然回 invalid_grant: account not found 才注意到,當時還以為帳戶被刪了。9/13 刪除專案內 7 把使用者金鑰、拿掉 5 個服務帳戶的 Owner/Editor;9/14 送出申訴,9/15 解封。

停權也造成了業務影響:所有用這把金鑰讀寫試算表、上傳陳列照片的排程一起停擺,必須緊急換成其他服務帳戶。

我們能還原到哪裡

攻擊者在 GCP 上的每一個「動作」都查得到;在 VM 裡面做了什麼、下載了什麼,目前查不到。唯一還能補救的來源是那 3 顆磁碟,刪之前要先做映像。

| 要回答的問題 | 還原程度 | 資料來源 | 保留期限 | |---|---|---|---| | 建了什麼、改了什麼權限 | 完整:時間、IP、金鑰、完整參數 | Admin Activity 稽核記錄 | 400 天 | | 呼叫了哪些 API、AI 模型 | 只有次數、方法、地區,沒有內容 | Monitoring api/request_count | 6 週 | | VM 用了多少資源 | 每台 CPU、收送流量曲線 | Monitoring VM 指標 | 6 週 | | 誰 SSH 進來、執行了什麼、下載了什麼 | 目前沒有;磁碟鑑識可能查到 | 3 顆 200GB 磁碟的 auth.log、journal、shell 歷史、殘留檔案 | 刪除即消失 | | 流量從哪裡來、傳到哪裡 | 無 | VPC Flow Logs 沒開 | — | | AI 提示詞和回應 | 無 | Vertex AI 請求記錄沒開 | — | | 試算表、Drive 有沒有被讀 | GCP 端無 | 資料存取稽核沒開;可再查 Workspace 管理控制台的 Drive 記錄 | — | | 金鑰怎麼外洩的 | 只能推定 | 需要部署平台提供事件細節 | — |

證據等級

損失:舉證到哪裡

cost這次盜用的帳:用量、單價、期間成本
項目用量單價期間US$NT$舉證
E2 vCPU(Americas)1,790.19 vCPU·小時≈ $0.0218/vCPU·小時9/3 → 9/1239.051,250帳單 SKU:E2 Instance Core running in Americas
E2 vCPU(Virginia)895.1 vCPU·小時≈ $0.0246/vCPU·小時9/3 → 9/1221.99704帳單 SKU:E2 Instance Core running in Virginia
E2 記憶體(Americas)7,160.77 GiB·小時≈ $0.0029/GiB·小時9/3 → 9/1220.93670帳單 SKU:E2 Instance Ram running in Americas
E2 記憶體(Virginia)3,580.4 GiB·小時≈ $0.0033/GiB·小時9/3 → 9/1211.79377帳單 SKU:E2 Instance Ram running in Virginia
pd-balanced 磁碟 200GB × 3124.05 + 62.16 GiB·月≈ $0.10/GiB·月9/3 → 9/14(未刪,持續計費)19.89636帳單 SKU:Balanced PD Capacity ×2 區
對外流量(4 個 SKU)229.9 GiB 送出;流入 3.2 TB 不計費$0.08–0.12/GiB9/5 → 9/1224.80794帳單 SKU:Internet Data Transfer Out ×3、Carrier Peering ×1
Compute Engine 其餘 212 個 SKU外部 IP、其他區流量等9/3 → 9/124.87156帳單依服務小計 143.32 減前 10 名 SKU
Vertex AI/Gemini(27 個 SKU)
原價 3.62,優惠折抵 0.83
212 次成功呼叫;文字+圖片輸入輸出依模型 token 計價9/7 → 9/122.7989帳單依 SKU 分組;Monitoring api/request_count 依 credential_id
Networking(負載平衡/位址)
原價 2.02,優惠全額折抵
9/3 → 9/120.000帳單依服務分組
磁碟未刪前的後續費用
估;GCP 到 9/15 的預估總額為 192.84
600 GB pd-balanced≈ $0.10/GB·月9/15 起每月60.001,920牌價推算;VM 已 TERMINATED 但磁碟仍在
鑑識與申訴人力
約 1 人日顧問日費 NT$20k–35k9/13 → 9/156251094.00200,035,008工時紀錄
專案停權 3 天的營運中斷所有靠這把金鑰的排程停擺9/12 → 9/15無法量化,僅有排程停擺紀錄
試算表/Drive 被讀取的資料不明8/28 → 9/12資料存取稽核沒開,無法舉證
VM 收進的 3.2 TB 內容與去向3,186 GiB9/5 → 9/12VPC Flow Logs 與 OS 日誌皆無;只剩磁碟
合計(僅可證明列)146.114,676
匯率假設 1 USD ≈ NT$32。單價為公開牌價或帳單實付,來源:GCP 帳單報表(依服務/依 SKU 分組)截圖 2026-09-15;Cloud Monitoring;Cloud Audit Logs。「舉證」欄標明每筆損失的證據來源與等級( 可證明 推論 缺口)。

金額不大,是因為攻擊者只開了 3 台中型 VM。同樣的權限換成 GPU 機型或更多地區,15 天可以燒掉幾萬美元。這次是運氣好,不是防禦好。

其實有訊號,只是沒人看

| 最早可發現 | 訊號 | 在哪裡看 | |---|---|---| | 8/28 23:00 | 陌生 IP 以 Bot 帳戶呼叫 Vertex AI | Cloud Audit Logs:callerIp 不是我們的 | | 8/29 03:21 | 有人替專案接受 AI 模型使用條款 | Cloud Audit Logs:AcceptPublisherModelEula | | 9/3 12:40 | 新防火牆規則對全網開放、同時開 5 台 VM | Cloud Audit Logs;Security Command Center | | 9/4 | 平常 $0 的專案一天花 $13 | 帳單報表、異常狀況頁、預算警示 | | 9/5 20:00 | 三台 VM 收進的流量從每 6 小時 3 GiB 暴增到 100 GiB 以上 | Monitoring:VM 網路流量 | | 9/7 | Bot 帳戶的 API 用量出現 aiplatform、compute | Monitoring:api/request_count 依 credential_id |

攻擊者 vs 防守者

他在意的不是你賣什麼,是你能被換成什麼。
  • 先測 AI 模型:Claude token 有現成黑市,比開 VM 更快變現;不行才退而求其次。
  • 開 VM 不掛服務帳戶、不用 gcloud、user-agent 只有 gzip(gfe):留最少的指紋。
  • 同一秒開 5 台分散 3 區:某區配額不足或被封,還有別台。
  • 腳本叫 idle-vm:他不是要用這台機器,是要把它交給下一個人。
  • 每小時列一次 VM:確認資產還活著,這是在經營,不是在搗亂。

四道防線怎麼設

| 防線 | 做法 | 設定方式 | |---|---|---| | 1. 金鑰根本不存在 | 不發服務帳戶私鑰;應用程式改走代理,只拿可撤銷的呼叫 token;repo 掃描並清除歷史裡的 KEY JSON | 組織政策 iam.disableServiceAccountKeyCreation;例外專案加 iam.serviceAccountKeyExpiryHours | | 2. 外洩了也沒權限 | 服務帳戶一律不給 Owner/Editor | 每月跑一次 asset search-all-iam-policies 找 roles/owner 或 roles/editor 的 serviceAccount | | 3. 有權限也做不了大事 | 關掉用不到的 API;限制可用地區和 AI 模型;不用 VM 的專案把 CPU 配額設 0 | gcloud services disable compute.googleapis.com;組織政策 gcp.resourceLocations、compute.vmExternalIpAccess、vertexai.allowedModels | | 4. 出事馬上知道 | 每個專案設預算警示;高風險動作設記錄型警示;有在跑服務的專案開資料存取稽核與 VPC Flow Logs | gcloud billing budgets create;警示條件 methodName 比對 instances.insert、firewalls.insert、SetIamPolicy、CreateServiceAccountKey、AcceptPublisherModelEula |

改善進度

  • 刪除專案內全部使用者金鑰,拿掉服務帳戶的 Owner/Editor(9/13 完成)
  • 建立不需私鑰的試算表代理服務,讓應用程式不必持有 KEY JSON(9/13~14 進行中)
  • 先為 3 顆磁碟建快照做鑑識,再刪除攻擊者留下的 VM、磁碟與 crusader-allow-ssh 防火牆規則
  • 停用該專案的 Compute Engine 與 Vertex AI API
  • 所有專案開啟資料存取稽核記錄(至少 Sheets、Drive、Storage)
  • 帳單帳戶設預算警示,每個專案一條,門檻用平常費用的 2 倍
  • 另一個仍是 Owner、金鑰仍有效的服務帳戶,比照處理
  • 組織政策:禁止建立服務帳戶金鑰,例外要審核
  • 憑證清冊以「身分」為單位,而不是以「存放位置」為單位;這次漏掉的就是散落在 repo 裡的 KEY JSON
  • 不要刪除 GCP:Service Account 本身:它擁有大量 Drive 照片,刪除會連檔案一起消失

討論題

  1. 8/27 收到平台通知後,我們依環境變數清單逐一輪替。為什麼這把 GCP 金鑰沒有被列進去?如果重來一次,清冊要怎麼建?
  2. 一個只讀寫試算表的 Bot 帳戶,為什麼會是 Owner?回想你負責的專案,有沒有「當初為了方便先給 Editor」的帳戶?
  3. 攻擊者第一個動作是找 AI 模型,不是開 VM。這對我們「哪些 API 應該開著」的判斷有什麼影響?
  4. 如果攻擊者開的是 GPU 機型,要多久會被 Google 停權?在那之前,我們的哪一個設定會先響?
  5. VM 從我們的專案收進 3.2 TB。如果下載的是盜版或違法內容,來源 IP 會追到我們。我們手上的證據足以證明不是自己下載的嗎?還缺什麼?

附錄:鑑識怎麼查

以下指令全部唯讀。PROJECT_ID、ATTACKER_IP 換成你的。

誰開了 VM、用哪把金鑰、從哪個 IPREAD-ONLY
gcloud logging read 'protoPayload.methodName="v1.compute.instances.insert"' \
--project=PROJECT_ID --freshness=45d \
--format="table(timestamp,protoPayload.authenticationInfo.serviceAccountKeyName,protoPayload.requestMetadata.callerIp)"
攻擊者 IP 的所有動作(Admin Activity 保留 400 天)READ-ONLY
gcloud logging read 'protoPayload.requestMetadata.callerIp="ATTACKER_IP"' \
--project=PROJECT_ID --freshness=400d
攻擊者有沒有自己開 API;權限被怎麼改過READ-ONLY
gcloud logging read 'protoPayload.methodName=~"EnableService"' --project=PROJECT_ID --freshness=400d
gcloud logging read 'protoPayload.methodName="SetIamPolicy"'   --project=PROJECT_ID --freshness=10d --format=json

API 呼叫次數依憑證分組,用 Cloud Monitoring 的 serviceruntime.googleapis.com/api/request_count,以 resource.label.credential_id 與 resource.label.method 分組;主控台的 API 用量頁看不到依憑證的分組。VM 流量曲線用 compute.googleapis.com/instance/network/received_bytes_count 與 sent_bytes_count,每 6 小時 ALIGN_SUM。

重點學習

keywords重點學習:最需要學習的 10 個關鍵字10
  1. 01
    roles/owner
    一把只寫試算表的金鑰為什麼能開 VM、叫 AI:因為帳戶是 Owner。權限決定損害上限。
  2. 02
    Google Service KEY JSON
    長期有效的私鑰檔,複製到哪裡就在哪裡;外洩不是「會不會」,是「哪一份」。
  3. 03
    以身分為單位輪替
    照平台的環境變數清單輪替會漏掉 repo 裡的 KEY JSON;清冊要以「這個身分有幾份憑證」建。
  4. 04
    Workload Identity
    讓程式不需要持有私鑰就能叫 API,是「金鑰根本不存在」這道防線的實作。
  5. 05
    AcceptPublisherModelEula
    攻擊者第一個成功的動作:替你接受模型條款。這個方法名出現在 Bot 帳戶下就是警訊。
  6. 06
    instances.insert
    開 VM 的稽核事件名;一批多區同時、無服務帳戶、enable-oslogin=FALSE 是攻擊者的指紋。
  7. 07
    credential_id
    Monitoring 的 api/request_count 可依憑證分組;主控台看不到,這是唯一能看出「哪把金鑰在叫什麼」的方法。
  8. 08
    預算警示(budget alert)
    平常 $0 的專案一天花 $13,9/4 就該響。這是四道防線裡最便宜的一道。
  9. 09
    Admin Activity 400 天 vs 資料存取稽核
    前者預設開、保留 400 天,所以攻擊者每個動作都查得到;後者預設關,所以資料有沒有被讀永遠不知道。
  10. 10
    idle-vm
    備好空機器交給別人用。攻擊者不是在用你的機器,是在經營你的機器。

今天就可以來試試

try today今天就可以來試試15 分鐘

全部唯讀,不改任何設定;做完你會知道自己有沒有同樣的破口。

  1. 找出所有是 Owner 或 Editor 的服務帳戶
    gcloud asset search-all-iam-policies --scope=projects/PROJECT_ID \
      --query='policy:(roles/owner OR roles/editor) memberTypes:serviceAccount'
    看到什麼代表什麼:任何一筆都是紅燈。Bot 帳戶不該出現在這裡。
  2. 列出每個服務帳戶的使用者金鑰與建立日期
    gcloud iam service-accounts keys list --iam-account=SA_EMAIL --managed-by=user \
      --format='table(name,validAfterTime)'
    看到什麼代表什麼:validAfterTime 超過 90 天的,當成已外洩處理;這次那把是 6 年。
  3. 查 45 天內誰開過 VM、誰改過防火牆
    gcloud logging read 'protoPayload.methodName=~"instances.insert|firewalls.insert"' --freshness=45d \
      --format='table(timestamp,protoPayload.authenticationInfo.principalEmail,protoPayload.requestMetadata.callerIp)'
    看到什麼代表什麼:callerIp 不是你們辦公室或 CI 的 IP,或 principal 是 Bot 帳戶,就是這篇的第 3、4 步。
  4. 看帳單帳戶有沒有任何預算警示
    gcloud billing budgets list --billing-account=BILLING_ACCOUNT_ID
    看到什麼代表什麼:空的代表沒有人會在 9/4 收到通知。每個專案至少一條,門檻用平常費用的 2 倍。
  5. 在 repo 歷史裡找 KEY JSON
    git grep -l '"type": "service_account"'
    git log --all -p -S'"private_key"' --name-only | grep -E '\.json$' | sort -u
    看到什麼代表什麼:任何命中都當已外洩:金鑰進過 git 歷史就拿不回來,要輪替不是刪檔。
§
對應課程模組

M2 身分與金鑰 · M3 雲端層偵測 · M0 駭客經濟學 · 想在自己的環境做一次同樣的盤點,從 自我盤點 開始。

newsletter
下一篇拆解直接寄到信箱

每篇新文章一封:三點摘要、攻擊者/防守者視角、今天能做的一件事。或先 加入會員 記錄你完成的任務。