権限昇格の「見えざる階段」:IAM iam:PassRole 悪用を完全攻略する
現場のエンジニア諸君。クラウドのセキュリティを語る時、多くの者が「S3のパブリック公開」や「セキュリティグループの穴」ばかりに目を向ける。だが、本当のプロが恐れるのは、「設計者が意図せず放置した、権限のバトンタッチ」だ。
今回は、AWSにおける権限昇格の盲点、iam:PassRole の悪用について深掘りしよう。これは、攻撃者が「EC2の中には入れたが、権限が足りない」という状況から、一気に管理者権限を奪取するための定石だ。
—
1. なぜ iam:PassRole が危険なのか
この権限は、一言で言えば「IAMロールを他のAWSリソース(EC2やLambdaなど)に紐付ける権利」だ。
もし、Webアプリに脆弱性があり、攻撃者がEC2インスタンス内でコマンド実行権を得たとしよう。そのEC2に iam:PassRole が付与されていた場合、攻撃者は以下のことができてしまう。
1. 自身の制御下にあるリソースを作成: 攻撃者が用意した(あるいは既存の)EC2インスタンスに、高権限なIAMロール(例: AdministratorAccess を持つロール)を割り当てる。
2. メタデータから認証情報を抽出: 攻撃者が立てたインスタンスにSSHし、http://169.254.169.254/latest/meta-data/iam/security-credentials/ を叩く。
3. 管理者権限の奪取: 権限昇格完了。AWS環境全体が攻撃者の掌の上となる。
「そんなことは開発チームの権限管理で防いでいる」という声が聞こえてきそうだが、IAMポリシーの Resource 指定が甘いケースが後を絶たない。
—
2. 実践的攻撃手法(PoCの概念)
攻撃者がAWS CLIを叩ける状態になったと仮定する。彼らが狙うのは、既存のインスタンスを無理やり別ロールに書き換えること、あるいは新たな高権限インスタンスの生成だ。
# 1. 攻撃者はまず、自分が何ができるか確認する
aws iam get-account-authorization-details --filter Role
# 2. 権限昇格のための悪意あるインスタンス起動
# pass-role権限があれば、自分に都合の良いロールを割り当てて起動する
aws ec2 run-instances \
--image-id ami-xxxxxxxx \
--instance-type t3.micro \
--iam-instance-profile Name="AdministratorRole" \
--key-name my-key
このコマンドが通るということは、そのインスタンスが「AdministratorRole というロールを任意のインスタンスに渡す権利」を持っていることを意味する。これが、インシデントの引き金だ。
—
3. 防御の要:iam:PassRole を制限するベストプラクティス
防御の鉄則は、「誰に」「どのロールを」渡せるかを徹底的に絞ることだ。
不適切なポリシー(やってはいけない例)
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*"
}
Resource に * を指定するのは、鍵を誰にでも配るのと同じだ。
セキュアなポリシー(推奨設定)
特定のロールだけを、特定のインスタンスに適用できるように制限する。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": [
"arn:aws:iam::123456789012:role/AppExecutionRole"
],
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
}
]
}
- Resource: 具体的なIAMロールのARNを指定する。これだけで、管理者権限ロールの付与を禁止できる。
- Condition:
iam:PassedToServiceを使用し、どのサービスに対してロールを渡せるかを限定する。
—
4. 現場で使える「GuardDuty + IAM 検知」の自動化
ポリシーの静的解析だけでは心許ない。異常な PassRole 操作を検知するPythonスクリプトを、Lambdaで回しておくことを強く勧める。
import boto3
# 特定の監視用Lambda関数例
def lambda_handler(event, context):
# CloudTrailイベントからPassRoleを抽出
event_name = event['detail']['eventName']
if event_name == 'PassRole':
role_name = event['detail']['requestParameters']['roleName']
# 許可リスト以外のロールが渡されたらSlack等へ通知
allowed_roles = ['AllowedAppRole']
if role_name not in allowed_roles:
print(f"警告: 許可されていないロール {role_name} が付与されました!")
# ここで即座にIAMセッションを無効化する等のアクションをトリガーする
—
セキュリティチーフからの助言
脆弱性とは「システムが想定外の挙動をとる瞬間」に生まれる。PassRole のような強力な権限は、「最小権限の原則」という言葉以上に、「誰が何にロールを紐付けられるか」という依存関係の可視化が全てだ。
明日、君たちのAWS環境で、iam:PassRole に * が指定されているポリシーがないか、今すぐ確認してほしい。もしあれば、それが君たちの環境の最大の「急所」だ。
システムを堅牢にするのは、高価なWAFではない。こうした地味で、論理的なIAMの構成管理の積み重ねこそが、攻撃者を跳ね返す最強の盾になる。健闘を祈る。
コメント