ELMBoukhriss/Automated-IAM-Incident-Response-on-AWS

GitHub: ELMBoukhriss/Automated-IAM-Incident-Response-on-AWS

该项目通过 AWS GuardDuty、Lambda 与 EventBridge 实现凭证泄露后的自动化 IAM 遏制,将事件响应时间从数小时压缩至约一分钟。

Stars: 0 | Forks: 0

# AWS 上的自动化 IAM 事件响应 ## 背景与业务影响 iCorp 是一家虚构的 B2B SaaS 公司,负责为企业客户处理付款。其 AWS 环境(S3 存储桶)保存着客户 PII、交易记录和合同文档,所有这些都可以通过 IAM 服务账户凭证进行访问。

最近,一名威胁行为者通过网络钓鱼活动获取了有效的访问密钥。该密钥从一个已知的恶意 IP 地址被用于枚举 S3 存储桶并开始数据外泄。由于 SOC 没有实施任何自动化,警报在 GuardDuty 中一直未被注意到,直到第二天早上一名分析师打开控制台,导致暴露窗口长达 10 到 16 个小时。

该项目的目标是实现响应流程自动化,要求在遏制阶段零人工干预,并将平均响应时间 (MTTR) 缩短至大约一分钟。这将使暴露窗口减少约 99%。请注意,虽然整体响应几乎是瞬时的,但总检测时间仍然取决于 GuardDuty 识别并发布检测结果的速度。

一旦通过 GuardDuty 检测到受损的 IAM 凭证,自动化工作流程将立即撤销该访问密钥,通过 AWS Lambda 函数使用明确的“拒绝所有” (Deny-All) 策略锁定身份,在 DynamoDB 中记录结构化的事件案例,并通过电子邮件向安全团队发出警报。所有这些编排工作都在 GuardDuty 发布检测结果的同一分钟内完成。 ## 架构 image

## 威胁建模 (STRIDE) 为了构建安全设计结构,我使用了 STRIDE 框架——这是一种在云环境中思考威胁的简单而强大的方法。

| 类别 | 威胁 | 控制 | |---|---|---| | **欺骗** | 从恶意 IP 使用被盗的长期访问密钥来冒充 `icorp-app-service-account` | GuardDuty 的 `MaliciousIPCaller` 检测会在发布检测结果后的 60 秒内触发自动密钥撤销 | | **篡改** | 攻击者在被遏制之前通过 `iam:AttachUserPolicy` 提升权限以获取更广泛的访问权限 | 将 `IREmergencyDenyAll` 内联 Deny-All 策略应用于身份——无论附加了什么权限,所有后续的 API 调用都会被阻止 | | **否认** | 攻击者禁用 CloudTrail 以抹除 API 活动的证据并掩盖横向移动 | 启用 CloudTrail 日志文件验证 + 在日志存储桶上启用 S3 MFA 删除,防止篡改审计跟踪 | | **信息泄露** | 攻击者在 GuardDuty 发布检测结果之前枚举 S3 存储桶并外泄客户 PII | 启用了 GuardDuty S3 Protection;约 5-10 分钟的检测窗口限制了数据外泄量 | | **拒绝服务** | 使用被盗密钥大规模终止 EC2 实例或删除 RDS 快照,导致生产环境停机 | 密钥撤销和 Deny-All 策略可在 Lambda 执行后的几秒钟内阻止所有破坏性的 API 调用 | | **权限提升** | 调用 `sts:AssumeRole` 从受损的服务账户跳转到权限更高的管理员角色 | Deny-All 策略会在遏制后阻止 `AssumeRole`;5-10 分钟的 GuardDuty 检测窗口是主要的风险面 | ## 技术步骤 ### 1. 实验环境设置 对于本实验,我将使用一个免费套餐 AWS 账户,并配置一个用于 CLI 和控制台的 IAM 用户,在区域方面我选择了 eu-west-1(爱尔兰),因为它在摩洛哥的延迟较低。 /!\ 如果您想重现此实验,请务必设置预算警报并在完成后销毁实验环境,以避免产生任何意外费用。 ``` # 验证 CLI 已配置 aws sts get-caller-identity # 为整个会话设置 region 和 account ID export AWS_DEFAULT_REGION=eu-west-1 export ACCOUNT_ID=123456789 ``` image

### 2. 启用 GuardDuty 并生成示例检测结果 ``` # 我已经启用了 GuardDuty,首次使用请运行以下命令 aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES # 保存 detector ID export DETECTOR_ID=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text) # 确认已启用 aws guardduty get-detector --detector-id $DETECTOR_ID --query '{Status: Status}' ``` image

GuardDuty 提供了生成安全示例检测结果的功能,这些结果类似于真实攻击,我们可以用它们来测试事件响应流程。 对于这种情况,我选择使用 "UnauthorizedAccess:IAMUser/MaliciousIPCaller" 检测结果来模拟用户访问密钥泄露以及被已知恶意 IP 使用的情况。 ``` # 生成示例 findings aws guardduty create-sample-findings --detector-id $DETECTOR_ID --finding-types "UnauthorizedAccess:IAMUser/MaliciousIPCaller" # 列出 MaliciousIPCaller finding ID aws guardduty list-findings --detector-id $DETECTOR_ID --finding-criteria '{"Criterion":{"type":{"Eq":["UnauthorizedAccess:IAMUser/MaliciousIPCaller"]}}}' --query 'FindingIds' # 保存 finding ID export FINDING_ID={Replace_with_FINDING_ID} # 可视化查看 finding JSON 详情 aws guardduty get-findings --detector-id $DETECTOR_ID --finding-ids $FINDING_ID --query 'Findings[0]' ```

image

我们还可以通过 GUI 控制台查看该检测结果。 image

### 3. 创建 IAM 受害者用户账户 该受害者用户将是一个有权访问 S3 存储桶的应用程序服务账户。 ``` # 创建受害服务账户 aws iam create-user --user-name icorp-app-service-account # 附加 S3 scoped policy aws iam attach-user-policy --user-name icorp-app-service-account --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess # 创建 access key(这是在遏制期间将被撤销的内容) aws iam create-access-key --user-name icorp-app-service-account > victim_key.json cat victim_key.json # 保存 key ID 以供整个实验使用 export VICTIM_KEY_ID={Replace_by_VICTIM_KEY_ID} ``` image

### 4. 创建 Lambda 执行角色 为了使 Lambda 函数能够自动化执行 IR 操作,我们需要为其授予 IAM 用户遏制(密钥撤销和附加 Deny-All 策略)、SNS 和 DynamoDB 所需的权限。 ``` # Trust policy(此 policy 将允许 lambda service assume 该 role) cat > lambda-trust-policy.json << 'EOF' { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }] } EOF # 创建 role aws iam create-role --role-name icorp-IR-lambda-role --assume-role-policy-document file://lambda-trust-policy.json --description "Execution role for iCorp IR containment lambda" # 保存 role ARN export LAMBDA_ROLE_ARN={Replace_with_ROLE_ARN} # 附加 managed policies for policy in \ "IAMFullAccess" \ "AmazonDynamoDBFullAccess" \ "AmazonSNSFullAccess" \ "service-role/AWSLambdaBasicExecutionRole"; do aws iam attach-role-policy \ --role-name icorp-IR-lambda-role \ --policy-arn arn:aws:iam::aws:policy/$policy echo "Attached: $policy" done # 确认已附加全部四个 aws iam list-attached-role-policies --role-name icorp-IR-lambda-role --query 'AttachedPolicies[].PolicyName' ``` image

image

### 5. 创建 DynamoDB 案例记录表 该 DynamoDB 表将作为案例记录,供 SOC 分析师/安全工程师查看受影响的用户和密钥。 ``` # 创建 table aws dynamodb create-table --table-name icorp-IR-cases \ --attribute-definitions AttributeName=case_id,AttributeType=S \ --key-schema AttributeName=case_id,KeyType=HASH --billing-mode PAY_PER_REQUEST # 等待 active 状态 aws dynamodb wait table-exists --table-name icorp-IR-cases # 确认 table 创建 aws dynamodb describe-table --table-name icorp-IR-cases --query 'Table.{Name:TableName, Status:TableStatus}' ``` image image

### 6. 创建 SNS 电子邮件并订阅邮件 为了在触发自动化 IR 时能够接收即时电子邮件,我们将创建一个 SNS 主题并使用我们的电子邮件地址进行订阅。 ``` # 创建 topic aws sns create-topic --name icorp-IR-alets # 保存 ARN export SNS_TOPIC_ARN={Replace_with_SNS_TOPIC_ARN} # Subscribe aws sns subscribe --topic-arn $SNS_TOPIC_ARN --protocol email --notification-endpoint {email address} ``` image

image

We will receive an email from aws to confirm our subscription to the topic. image

``` # 确认后 — 验证 subscription 是否已生效 aws sns list-subscriptions-by-topic --topic-arn $SNS_TOPIC_ARN \ --query 'Subscriptions[0].{Protocol:Protocol,Endpoint:Endpoint,Status:SubscriptionArn}' ``` image

### 7. 打包并部署 Lambda Lambda 函数将负责在接收到 GuardDuty 的触发后执行遏制流程。 完整函数代码 -> [handler.py](https://github.com/ELMBoukhriss/Automated-IAM-Incident-Response-on-AWS/blob/main/lambda/handler.py) ``` # 创建 lambda handler.py 并将其 zip nano handler.py zip -r icorp-ir-containment.zip # 部署 Lambda aws lambda create-function --function-name icorp-IR-containment --runtime python3.12 --role $LAMBDA_ROLE_ARN --handler handler.lambda_handler \ --zip-file fileb://icorp-ir-containment.zip --timeout 30 --memory-size 128 --environment "Variables={DYNAMODB_TABLE=icorp-IR-cases,SNS_TOPIC_ARN=$SNS_TOPIC_ARN}" \ --description "iCorp IR: automated IAM containment on GuardDuty MaliciousIPCaller finding" # 等待 active 状态 aws lambda wait function-active --function-name icorp-IR-containment # 确认已使用 env vars 部署 aws lambda get-function-configuration --function-name icorp-IR-containment --query '{Name:FunctionName, State:State, Runtime:Runtime, Env:Environment.Variables}' ``` image image

### 8. 创建 EventBridge 规则并连接到 Lambda EventBridge 是将 GuardDuty 检测结果连接到 Lambda 函数的中间件。 ``` # 编写 event pattern 文件(如果你注意到的话,我将 "aws.guardduty" 替换为了 "icorp.guardduty",我将在下一节 Testing 中解释原因。 cat > ir-event-pattern.json << 'EOF'{ "source": ["icorp.guardduty"], "detail-type": ["GuardDuty Finding"], "detail": { "type": [ "UnauthorizedAccess:IAMUser/MaliciousIPCaller" ] } } EOF # 创建 Rule aws events put-rule --name icorp-IR-iam-compromise --event-pattern file://ir-event-pattern.json \--state ENABLED --description "iCorp IR: route Guardduty IAM findings to containment Lambda" # 保存 rule ARN export RULE_ARN={Replace_with_Rule_ARN} # 保存 Lambda ARN export LAMBDA_ARN={Replace_with_Rule_ARN} # 将 Lambda 添加为 target aws events put-targets --rule icorp-IR-iam-compromise --targets "Id=IRContainmentLambda,Arn=$LAMBDA_ARN" # 授予 EventBridge 调用 Lambda 的权限 aws lambda add-permission --function-name icorp-IR-containment --statement-id EventBridgeIRInvoke --action lambda:InvokeFunction --principal events.amazonaws.com --source-arn $RULE_ARN # 验证完整设置 aws events describe-rule --name icorp-IR-iam-compromise --query '{Name:Name, State:State}' aws lambda get-policy --function-name icorp-IR-containment --query 'Policy' --output text | python3 -m json.tool ``` image image

### 9. 通过 EventBridge put-events 进行测试 GuardDuty 的 "create-sample-findings" 总是注入 "GeneratedFindingAccessKeyId" 作为一个硬编码的占位符,您无法用真实的密钥 ID 覆盖它。"put-events" 完全绕过了这一限制,允许您将一个完全定制的、符合 GuardDuty 格式的事件(包含真实的受害者密钥 ID)直接注入到 EventBridge 中。这样 Lambda 就能够通过密钥 ID 对受害者用户执行遏制步骤,这就是为什么在上一步中我将 "aws.guardduty" 替换为了自定义的 "icorp.guardduty"。 ``` # 在测试前确认受害 key 仍然为 Active aws iam list-access-keys --user-name icorp-app-service-account --query 'AccessKeyMetadata[0].[AccessKeyId,Status]' # 构建经过构造的 GuardDuty 形状 event payload cat > ir-putevents-payload.json << EOF [ { "Source": "icorp.guardduty", "DetailType": "GuardDuty Finding", "EventBusName": "default", "Detail": "{\"type\": \"UnauthorizedAccess:IAMUser/MaliciousIPCaller\", \"severity\": 7, \"accountId\": \"$ACCOUNT_ID\", \"region\": \"$AWS_DEFAULT_REGION\", \"id\": \"ir-test-$(date +%s)\", \"resource\": {\"resourceType\": \"AccessKey\", \"accessKeyDetails\": {\"accessKeyId\": \"$VICTIM_KEY_ID\", \"userName\": \"$PROJECT-app-service-account\", \"userType\": \"IAMUser\", \"principalId\": \"AIDACKCEVSQ6C2EXAMPLE\"}}, \"service\": {\"action\": {\"actionType\": \"AWS_API_CALL\", \"awsApiCallAction\": {\"api\": \"ListBuckets\", \"serviceName\": \"s3.amazonaws.com\", \"remoteIpDetails\": {\"ipAddressV4\": \"203.0.113.42\", \"organization\": {\"asnOrg\": \"Malicious Actor ISP\"}, \"country\": {\"countryName\": \"Unknown\"}, \"city\": {\"cityName\": \"Unknown\"}}}}}}" } ] EOF # 触发 EVENT aws events put-events --entries file:///tmp/ir-putevents-payload.json ``` image

The alert email was sent near realtime after triggering the event image

### 10. 验证完整遏制 ``` # 验证 access key 为 Inactive aws iam list-access-keys --user-name icorp-app-service-account --query 'AccessKeyMetadata[0].{KeyId:AccessKeyId, Status:Status}' # 验证已附加 Deny-All inline policy aws iam list-user-policies --user-name icorp-app-service-account # 验证 DynamoDB case record aws iam get-user-policy --user-name icorp-app-service-account --policy-name IREmergencyDenyAll --query 'PolicyDocument' ``` image image 我们也可以通过控制台进行验证。 image

用户的遏制已经成功且在 60 秒内完成,现在 SOC 分析师/安全工程师可以查看 DynamoDB 案例记录并开始调查该事件。 ### 11. 销毁环境 最后一步是销毁实验环境,以避免产生任何经常性费用。 ``` #GuardDuty aws guardduty delete-detector --detector-id $DETECTOR_ID # Lambda aws lambda delete-function --function-name icorp-IR-containment # EventBridge aws events remove-targets --rule icorp-IR-iam-compromise --ids IRContainmentLambda aws events delete-rule --name icorp-IR-iam-compromise # SNS aws sns unsubscribe --subscription-arn $SUBSCRIPTION_ARN aws sns delete-topic --topic-arn $SNS_TOPIC_ARN # DynamoDB aws dynamodb delete-table --table-name icorp-IR-cases # IAM 受害用户 aws iam detach-user-policy \ --user-name icorp-app-service-account \ --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess # 删除 inline policy(如果仍然存在) aws iam delete-user-policy \ --user-name icorp-app-service-account \ --policy-name IREmergencyDenyAll 2>/dev/null || true aws iam delete-access-key \ --user-name icorp-app-service-account \ --access-key-id $VICTIM_KEY_ID aws iam delete-user --user-name icorp-app-service-account # IAM Lambda role for policy in \ "IAMFullAccess" \ "AmazonDynamoDBFullAccess" \ "AmazonSNSFullAccess" \ "service-role/AWSLambdaBasicExecutionRole"; do aws iam detach-role-policy \ --role-name icorp-IR-lambda-role \ --policy-arn arn:aws:iam::aws:policy/$policy done aws iam delete-role --role-name icorp-IR-lambda-role ``` ### 12. Terraform [TODO] : 稍后添加
标签:AWS, DPI, IAM访问控制, Serverless, StruQ, 安全编排与自动化响应(SOAR), 自动化事件响应, 逆向工具