嗯...今天要來聊聊 AWS 的 IAM 列舉 (Enumeration)。
說真的,這是一個蠻...tricky 的主題。簡單講,就是在你根本沒有登入對方 AWS 帳號的情況下,去「猜」或者說「探測」出裡面有哪些 IAM user、哪些 role 存在。聽起來有點像駭客行為,沒錯,這在滲透測試或紅隊演練裡是基本的偵查起手式。
所以這到底怎麼辦到的?
聽起來很玄,但背後的原理其實...說穿了有點蠢。就是 AWS 一個 policy 設定上的「特性」。
在 AWS 裡面,有些資源可以掛所謂的「資源導向策略」(Resource-Based Policies)。當你嘗試更新這個 policy,想把權限指給某個 Principal (例如一個 IAM User 或 Role) 的時候,奇妙的事情發生了:
- 如果那個 User 或 Role 真的存在,API 會回傳成功,policy 就更新上去了。
- 但如果那個 User 或 Role 是你亂掰的,不存在,API 就會直接噴一個 `InvalidPrincipal` 的錯誤訊息給你。
你看,這就成了一個破口。攻擊者可以寫個腳本,拿著一本字典檔(裡面是一大堆常見的名字組合),然後瘋狂去嘗試更新一個他自己帳號下的資源 policy。只要哪個名字沒報錯,就代表「Bingo!這個帳號裡真的有這個人」。
我自己是覺得,這有點像是在一個漆黑的房間裡,你不知道裡面有誰,但你可以大喊名字。如果有人回應「欸!」,你就知道那個人在房間裡了。嗯,差不多是這個感覺。
動手之前,先在自己家翻箱倒櫃
不過,在我們去別人家「喊名字」之前,有一個更基本也更常見的情境:你已經弄到一台主機的 shell 了。這時候,第一件事不是往外打,而是先看看這台機器上有沒有什麼 AWS 的憑證可以撿。
那要去哪裡找?跟著 AWS SDK 的思路走就對了。像是最多人用的 Python SDK,也就是 Boto3,它會依序去幾個固定的地方找 credential:
- 環境變數:最直接的,`AWS_ACCESS_KEY_ID` 和 `AWS_SECRET_ACCESS_KEY`。
- 共用憑證檔:這應該是最常見的,藏在 `~/.aws/credentials` 裡面。用 `aws configure` 設定的 user key 都會在這。
- AWS 設定檔:另一個是 `~/.aws/config`,有時候會用來設定 Assume Role 的一些組態。
- CLI 快取:如果有人用 AWS SSO 或 `assume-role`,CLI 會把拿到的 session token 暫存在 `~/.aws/cli/cache/` 下面。這個超容易被忽略,但裡面都是寶。
- Instance Metadata Service (IMDS):如果在 EC2 instance 上,可以直接 curl `169.254.169.254` 這個 IP,AWS 會把掛在 EC2 上的 IAM Role 的臨時憑證給你。ECS 也有類似的機制。
說到這個,就想到有些比較舊的系統可能還在用 Boto2... 那設定檔位置又不一樣。在台灣這邊的專案比較少見啦,但如果你接手的案子歷史超過十年,天曉得會不會踩到這種雷。總之,先搜刮一遍再說。
用工具來規模化這件事:Quiet Riot
好,回到主題。手動一個一個試太慢了,所以當然有工具可以用。今天主要看的是 `Quiet Riot` 這個工具。
安裝很簡單,就用 pip:
pip3 install quiet-riot
它的概念就跟我前面說的一樣,只是幫你自動化了整個流程。你需要準備:
- 一個目標 AWS Account ID。
- 一本字典檔。裡面是你想猜的 username 或 rolename 列表。
- 你自己要有一組有效的 AWS key。因為 Quiet Riot 需要在你自己的帳號下建立資源(例如一個 IAM Role)來做測試,所以你還是需要權限的。這就是為什麼它叫 "unauthenticated" enumeration,引號要特別標出來,因為你對目標是 unauthenticated,但對 AWS 本身還是要 authenticated。
假設我們懷疑目標公司有兩個員工叫 Adam 和 John,我們就可以寫個簡單的 python script 去產生各種常見的用戶名組合,像是 `adam.smith`、`asmith`、`adam_smith`... 然後存成一個 `usernames.txt` 的字典檔。
接著,跑 Quiet Riot:
quiet_riot --scan 5
它會問你要掃描 IAM Roles (1) 還是 IAM Users (2)。你就選一個,然後提供目標 Account ID 跟你的字典檔路徑。
然後就是等待... 根據字典檔大小,可能要跑個幾分鐘。如果運氣好,它就會告訴你哪些名字是 `VALID` 的。
原文的例子就真的找到了 `john.cervantes` 和 `adam.foreman`。你看,從完全的外部,我們就已經確認了這個 AWS 帳號裡面至少有這兩個 IAM User 存在。這就是很有價值的情報了。
不只找人,還能找服務
更有趣的是,這個方法不只對人有效。
AWS 很多服務在運作時,也需要權限,它們會使用一種叫做 "aws-service-role" 的東西。例如,你啟用 GuardDuty,AWS 就會在你帳號下自動建立一個叫 `AWSServiceRoleForGuardDuty` 的 role。
這些 service role 的名稱都是預先定義好的。所以,我們可以反過來利用這點。我們拿著一份已知的 service role 列表去跑一次列舉,只要哪個 role 存在,就代表目標帳號「很有可能」正在使用或曾經使用過那個服務。
用 Quiet Riot 跑這個就更容易了,連字典檔都不用自己準備。
quiet_riot --s 3
輸入目標 Account ID,它就會自己拿內建的列表去掃。掃出來的結果可能像這樣:
- AWSServiceRoleForGuardDuty
- AWSServiceRoleForOrganizations
- AWSServiceRoleForSupport
- AWSServiceRoleForTrustedAdvisor
看到這個列表,攻擊者馬上就知道:喔,這個帳號有開 GuardDuty,那我的攻擊行為就要更小心,避免觸發告警。它有用 Organizations,表示這可能是一個大公司的母帳號或子帳號。這對後續的攻擊策略擬定,幫助真的很大。
當然,這邊有個限制。就像前面說的,Role 存在不代表服務「當下」還在用,可能只是以前開過後來關掉了,但 Role 沒刪掉。不過,這仍然是個很好的參考指標。
各種 IAM 列舉手法的比較
整理一下,用這個 resource-based policy 的技巧,主要有幾種玩法。我把它們整理成一個表,比較好懂。
| 列舉目標 | 使用方法 | 我的看法 / 眉角 |
|---|---|---|
| IAM User / Role | 用 `Quiet Riot` 搭配字典檔,針對 `iam:UpdateAssumeRolePolicy` 等 API 去試錯。 | 這是最常見的用法。效果好不好,完全取決於你的字典檔品質。如果能針對目標公司員工的命名習慣去產生字典,成功率會高很多。 |
| Root User Email | 一樣用 `Quiet Riot`,但它背後用的是舊版 S3 ACL 的特性去試 Email。 | 這個...說真的,現在比較雞肋。因為 S3 預設都禁用 ACL 了。而且速度慢,很容易被 AWS 限速。但如果真的賽到一個 Root User email... 那就跟中樂透一樣,因為 Root User 的權限預設是無敵的,除非被 SCP (AWS Service Control Policy) 限制。 |
| 啟用的 AWS 服務 | 用 `Quiet Riot` 內建的 Service Role 列表去掃。 | 我自己是覺得這個最實用。CP 值超高,不用準備字典,跑一下馬上就能對目標的技術堆疊和防禦措施有個大概的輪廓。特別是看到 `GuardDuty` 或 `Macie` 這種資安服務,就要特別小心了。 |
總結一下
所以,AWS IAM 列舉這件事,就是一個利用 AWS API 設計上的「特性」來進行的資訊收集手段。它本身不算是個漏洞,AWS 似乎也認為這是預期行為,但對攻擊方來說,這無疑是個非常有用的資訊洩漏管道。
了解這個攻防手法,不只是為了攻擊。如果你是防守方(藍隊),你才會知道為什麼公司的 IAM 命名規則那麼重要,為什麼不該用可預測的員工姓名來當 IAM user name。你也才會知道,即便你的系統固若金湯,還是有可能在不知不覺中,洩漏了內部的人員或架構資訊。
對了,順便提一下,雖然在 TryHackMe 這種靶機環境玩這個很有趣,但在現實世界中,未經授權去掃描別人的 AWS 帳號是絕對的禁區。尤其在台灣,根據《資通安全管理法》,如果目標是政府或關鍵基礎設施,這絕對是違法的。所以...知道原理就好,不要手癢亂試啊。
你覺得,在設計 IAM 命名規則時,除了避免使用員工姓名,還有哪些方法可以增加這種列舉攻擊的難度? 在下面分享你的想法吧!
