新しいAWS Security Hubで危険な公開Lambda関数を検出し可視化してみた
こんにちは、臼田です。
みなさん、新しいAWS Security Hub使ってますか?(挨拶
今回はプレビューで使えるAWS Security HubのExposure(露出)で、危険なLambda関数を検出して可視化してみました。
概要
先日のAWS re:Inforce 2025にて、新しいAWS Security Hubが登場し、統合セキュリティソリューションとして各種AWSセキュリティサービスから情報を収集しまとめて確認することができるようになりました。
その中でも特に、集めた情報からより危険な状態を発見し可視化する「露出(Exposure)」の機能がとても良く、私はお気に入りです。詳細は下記をご確認ください。
以前はEC2周りの検出を行ったのですが、今回はもう少し露出について深堀りしつつ、タイトル通り今度はLambda関数について検出して可視化してみたいと思います。
新しい検出、Exposure Findingsの仕組み
AWS Security HubではこれまでCSPMの機能やAmazon GuardDutyなどから収集してきた情報をFindings(検出)として記録していました。新しいAWS Security Hubでは露出が新しい機能として誕生しており、ここで扱うFindingsはExposure Findingsとなります。
新しいAWS Security HubではFindingsは従来のASFFからOCSFに変わっており、Exposure FindingsのフォーマットもOCSFとなっています。
以降、このExposure Findingsで扱う情報から、露出でどのようなことができるのか把握していきましょう。
Exposure Findings対応リソース
現在AWS Security Hubでは下記8種類のリソースタイプに対する露出の検出をサポートしています。
- AWS::DynamoDB::Table
- AWS::EC2::Instance
- AWS::ECS::Service
- AWS::EKS::Cluster
- AWS::IAM::User
- AWS::Lambda::Function
- AWS::RDS::DBInstance
- AWS::S3::Bucket
主要な攻撃に晒されやすいリソースがありますが、まだまだカバレッジは増やしてほしいところですね。
検出メカニズム
Exposure Findingsは従来の個別のセキュリティチェックとは異なり、複数の「特性(Trait)」を統合してリソースごとに最大1つのFindingを生成します。特性タイプは現在下記4つの種類があります。
- 設定ミス(Misconfiguration): 全リソース対象、CSPM検出
- 到達可能性(Reachability): EC2・Lambda対象、CSPM+Inspector検出
- 機密データ(Sensitive Data): S3対象、Macie検出
- 脆弱性(Vulnerability): EC2/ECS/EKS/Lambda対象、Inspector検出
1つのリソースに関する様々な特性を集計してまとめて確認できるExposure Findingsとなります。従来はバラバラに見ていく必要があったものを1つにまとめてくれるのは嬉しいですね。
Severity算出の詳細ロジック
単純にまとめるだけでなく、まとめた後にその脅威のレベルも下記5つの評価軸による総合判定を行います。
- 認識(Awareness): 公開エクスプロイトの存在
- 検出の容易さ(Ease of discovery): 自動ツールでの発見容易性
- 悪用の容易さ(Ease of exploit): エクスプロイトの実行容易性
- エクスプロイトの可能性(Likelihood of exploit): 30日以内攻撃可能性(EPSS連携)
- 影響(Impact): 攻撃成功時の影響範囲
このあたりはだいぶAmazon Inspectorによる評価が強く出るところですね。必ず連携して使っていきたいところです。
なおExposure Findingsは、AWS Security Hubが6時間ごとに検出を行う仕組みのため、すぐに検出されないことには注意が必要です。
どのようなExposureが検出されそうか?
対応リソースと特性タイプから下記のようなものは検出できそうかな?と考えられます。
- EC2インスタンスの検出しそうな例
- IMDSv1の使用
- 管理者権限IAMロール
- パブリックIPあり
- 0.0.0.0/0のセキュリティグループ
- 既知の脆弱性を持つパッケージ
- S3バケットの検出しそうな例
- パブリックアクセス許可
- バージョニング無効
- KMS暗号化未使用
- Macieが検出したPII/金融情報
- Lambda関数の検出しそうな例
- 廃止予定のランタイム使用
- パブリック実行許可
- 依存関係の脆弱性
他にも色々考えられますが、対象リソースタイプが主体であり、だいたい外部から利用できる状態にし、追加で1つ以上の強い権限や脆弱性や機密情報などと紐づいている、というのがうまくいきそうな気がします。もちろんこれは推測なので実際にはやってみないとわかりませんね。
Lambda関数でやってみた
というわけで実際にやってみましょう。今回は、Lambda関数でExposure Findingsを意図的に発生させる実験をしてみました。どのアーキテクチャ要素が検出のトリガーになるかを確認したかったんです。
実装したアーキテクチャ
CloudFormationテンプレートで以下のような構成を作成しました。
テンプレートはこちらです。
CloudFormationテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Lambda Exposure Findings Test - Security Hub検証用シンプルテンプレート'
Resources:
# Lambda実行用IAMロール(意図的に過剰権限)
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: 'exposure-test-lambda-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
# 意図的な過剰権限(Misconfiguration Trait用)
Policies:
- PolicyName: OverPrivilegedPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:*
- dynamodb:*
- rds:Describe*
Resource: '*'
Tags:
- Key: Purpose
Value: 'exposure-findings-test'
- Key: Environment
Value: 'test'
# Exposure Findings対象のLambda関数
ExposureLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: 'exposure-test-function'
Runtime: python3.8 # 意図的に古いランタイム(Misconfiguration)
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Timeout: 30
MemorySize: 128
# 同時実行数制限(コスト制御)
ReservedConcurrentExecutions: 3
# VPC設定なし(デフォルトでVPC外配置 = Misconfiguration)
Code:
ZipFile: |
import json
import time
import os
def lambda_handler(event, context):
"""
Exposure Findings テスト用Lambda関数
"""
# レート制限(コスト制御)
time.sleep(0.5)
# 簡易認証チェック
auth_token = event.get('queryStringParameters', {}).get('token') if event.get('queryStringParameters') else None
if not auth_token or auth_token != "test-token-2025":
return {
'statusCode': 401,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'error': 'Unauthorized access'})
}
# 正常レスポンス
response_data = {
'message': 'Hello from exposed Lambda function!',
'timestamp': int(time.time()),
'function_name': context.function_name,
'request_id': context.aws_request_id,
'status': 'success'
}
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps(response_data, indent=2)
}
Environment:
Variables:
ENVIRONMENT: 'test'
PURPOSE: 'exposure-findings-test'
Tags:
- Key: Purpose
Value: 'exposure-findings-test'
- Key: Environment
Value: 'test'
- Key: Security-Test
Value: 'true'
# Function URL実行許可(Reachability Traitの主要因)
LambdaUrlInvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref ExposureLambdaFunction
Action: lambda:InvokeFunctionUrl
Principal: '*'
FunctionUrlAuthType: NONE
# Function URL(認証なしパブリックアクセス)
LambdaFunctionUrl:
Type: AWS::Lambda::Url
Properties:
TargetFunctionArn: !GetAtt ExposureLambdaFunction.Arn
AuthType: NONE # 危険:認証なし
Cors:
AllowCredentials: false
AllowHeaders:
- 'Content-Type'
AllowMethods:
- GET
- POST
AllowOrigins:
- '*'
# CloudWatch Logs Group(短期保存)
LambdaLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: '/aws/lambda/exposure-test-function'
RetentionInDays: 3
Outputs:
FunctionUrl:
Description: 'Public Lambda Function URL'
Value: !GetAtt LambdaFunctionUrl.FunctionUrl
TestUrl:
Description: 'Test URL with authentication token'
Value: !Sub '${LambdaFunctionUrl.FunctionUrl}?token=test-token-2025'
SecurityNotice:
Description: 'SECURITY WARNING'
Value: 'This Lambda function is intentionally insecure for testing purposes. DELETE after testing!'
これでいい感じに検出してくれました。
想定する検出ポイント
このテンプレートで以下の特性タイプと内容により検出される想定です。
- 設定ミス
- 古いランタイム: python3.8という廃止予定のランタイムを使用
- 過剰な権限: S3やDynamoDBへの全権限を付与
- 到達可能性
- Function URL: 認証なし(AuthType: NONE)でインターネットから直接アクセス可能
- パブリック実行許可: Principal: '*'でどこからでも実行可能
- CORS設定: AllowOrigins: '*'で全てのオリジンからアクセス可能
実際の検出結果
しばらくしたら期待通りExposure Findingsが生成されました!Highのイベントとして検出されています。
詳細を開いてみてみましょう。
検出タイトルは「潜在的な資格情報の盗難: サービスレベルの管理実行ロールを持つ、パブリックに呼び出し可能なLambda | Potential Credential Stealing: Publicly invocable lambda with service-level administrative execution role」です。アタックパスも可視化されていますね!
実際のFinding jsonを一部抜粋したのがこちらです。
{
"finding_info": {
"title": "Potential Credential Stealing: Publicly invocable lambda with service-level administrative execution role",
"desc": "The Lambda function in question is publicly invocable, meaning it can be triggered by any user or service without proper authentication. Additionally, the function has been granted an AWS role with elevated access permissions, specifically a service-level administrative execution role. This combination creates a significant security vulnerability...",
"types": [
"Exposure/Potential Credential Access/Unsecured Credentials"
],
"related_events": [
{
"traits": [
{
"category": "Misconfiguration",
"name": "The IAM Role associated with the Lambda function has a policy with administrative access to an AWS Service",
"type": "Contributing Trait",
"uid": "OverPrivilegedPolicy",
"values": [
"s3:*",
"dynamodb:*"
]
}
]
},
{
"traits": [
{
"category": "Reachability",
"name": "The Lambda function can be invoked by anyone",
"type": "Contributing Trait"
}
]
},
{
"traits": [
{
"category": "Misconfiguration",
"name": "The Lambda function is running an unsupported runtime",
"type": "Contextual Trait"
}
]
}
]
},
"severity": "High",
"resources": [
{
"type": "AWS::Lambda::Function",
"uid": "exposure-test-function",
"resource_relationship": {
"name": "exposure-test-function Relationships",
"nodes": [
{
"name": "Node0",
"type": "AWS::Lambda::Function"
},
{
"name": "Node1",
"type": "AWS::IAM::Role"
},
{
"name": "Node2",
"type": "AWS::IAM::Policy"
}
],
"edges": [
{
"name": "AWS::Lambda::Function->AWS::IAM::Role",
"relation": "Is associated with",
"source": "Node0",
"target": "Node1"
}
]
}
}
]
}
だいたい想定通りの検出になっています。アタックパス部分では、インラインポリシーで強い権限を入れている場合、リソースとして別れているわけではないのでIAM Role側に赤いバッチ(関連する特性の数)がついているのは注意ポイントですね。
特性はこのようになっています。
設定ミス2つと到達可能性1つの検出です。
以前検証したときのEC2での到達可能性の検出はAmazon Inspectorのものでしたが、今回のAWS Lambdaの到達可能性の検出はLambda.1 Lambda function policies should prohibit public access
でCSPM由来のものでした。後から解釈すると納得ですが、上述した到達可能性の特性タイプの仕様通りAmazon Inspector以外にCSPMによる到達可能性の検出が確認できた形です。
まとめ
露出で検出されるExposure Findingsの仕組みをもう少し深く理解しながら、新たなLambdaの公開による検出を試してみました。
露出については単体の問題ではなく、複数の要素が組み合わさり現実的な脅威になると検出されるのは非常に良いところです。その分緊急度の高い検出になりますから内容を理解し、即時対応できるようにしていきましょう。
他にもいろんなパターンで検出できると思うので、是非試して行きましょう!