AWS IAM Access Key Compromise Detection


Description

This alert occurs when AWS has detected exposed credentials. It attaches a policy to deny certain actions, effectively quarantining those credentials, and is accompanied by a support case with instructions for detaching the policy.

Query · python

from panther_aws_helpers import aws_rule_context

EXPOSED_CRED_POLICIES = {
    "AWSExposedCredentialPolicy_DO_NOT_REMOVE",
    "AWSCompromisedKeyQuarantine",
    "AWSCompromisedKeyQuarantineV2",
    "AWSCompromisedKeyQuarantineV3",
}


def rule(event):
    if event.get("eventName") != "PutUserPolicy":
        return False

    request_params = event.get("requestParameters") or {}
    if request_params.get("policyName") not in EXPOSED_CRED_POLICIES:
        return False
    return True


def title(event):
    user_name = event.deep_get("userIdentity", "userName")
    access_key_id = event.deep_get("userIdentity", "accessKeyId")
    return (
        f"[{user_name}]'s AWS IAM Access Key ID [{access_key_id}]"
        f" was exposed and quarantined by AWS"
    )


def alert_context(event):
    return aws_rule_context(event)

Analyst notes

Determine the IAM user who owns the key, rotate/disable/delete the key, and then investigate which actions were taken by the compromised key to determine scope and if any remediation is needed.

Raw source AWS IAM Access Key Compromise Detection · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: aws_key_compromised.py
RuleID: "AWS.IAM.AccessKeyCompromised"
DisplayName: "AWS IAM Access Key Compromise Detection"
Enabled: true
LogTypes:
  - AWS.CloudTrail
Reports:
  MITRE ATT&CK:
    - TA0006:T1552
Tags:
  - AWS
  - Credential Access:Unsecured Credentials
Severity: High
Description: This alert occurs when AWS has detected exposed credentials. It attaches a policy to deny certain actions, effectively quarantining those credentials, and is accompanied by a support case with instructions for detaching the policy.
Runbook: Determine the IAM user who owns the key, rotate/disable/delete the key, and then investigate which actions were taken by the compromised key to determine scope and if any remediation is needed.
Reference: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
Tests:
  - Name: An AWS IAM Access Key was Compromised
    ExpectedResult: true
    Log:
      {
        "eventSource": "iam.amazonaws.com",
        "recipientAccountId": "123456789012",
        "responseElements": null,
        "userIdentity":
          {
            "type": "IAMUser",
            "userName": "compromised_user",
            "principalId": "XXXXXXXXXXXXXXXXXXX",
            "accessKeyId": "XXXXXXXXXXXXXXXXXXXXX",
            "arn": "arn:aws:iam::123456789012:user/compromised_user",
            "accountId": "123456789012",
          },
        "eventName": "PutUserPolicy",
        "eventVersion": "1.05",
        "userAgent": "aws-internal/3 aws-sdk-java/1.11.706 Linux/4.9.184-0.1.ac.235.83.329.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.242-b08 java/1.8.0_242 vendor/Oracle_Corporation",
        "requestParameters":
          {
            "policyDocument": '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1538161409","Effect":"Deny","Action":["lambda:CreateFunction","iam:AttachUserPolicy","iam:PutUserPolicy","organizations:InviteAccountToOrganization","ec2:RunInstances","iam:DetachUserPolicy","iam:CreateUser","lightsail:Create*","lightsail:Update*","ec2:StartInstances","ec2:RequestSpotInstances","iam:ChangePassword","iam:CreateLoginProfile","organizations:CreateOrganization","organizations:CreateAccount","lightsail:Delete*","iam:AttachGroupPolicy","iam:CreateAccessKey","iam:UpdateUser","iam:UpdateAccountPasswordPolicy","iam:DeleteUserPolicy","iam:PutUserPermissionsBoundary","iam:UpdateAccessKey","lightsail:DownloadDefaultKeyPair","iam:CreateInstanceProfile","lightsail:Start*","lightsail:GetInstanceAccessDetails","iam:CreateRole","iam:PutGroupPolicy","iam:AttachRolePolicy"],"Resource":["*"]}]}',
            "userName": "compromised_user",
            "policyName": "AWSCompromisedKeyQuarantineV3",
          },
        "eventID": "1c2a53d1-58cc-41b3-85b8-bd7565370e0d",
        "eventType": "AwsApiCall",
        "sourceIPAddress": "72.21.217.97",
        "awsRegion": "us-east-1",
        "requestID": "27ca92a5-61cc-44aa-b875-042a25310064",
        "eventTime": "2020-04-10T06:22:08Z",
      }
  - Name: Request Param is null
    ExpectedResult: false
    Log:
      {
        "eventSource": "iam.amazonaws.com",
        "recipientAccountId": "123456789012",
        "responseElements": null,
        "userIdentity":
          {
            "type": "IAMUser",
            "userName": "compromised_user",
            "principalId": "XXXXXXXXXXXXXXXXXXX",
            "accessKeyId": "XXXXXXXXXXXXXXXXXXXXX",
            "arn": "arn:aws:iam::123456789012:user/compromised_user",
            "accountId": "123456789012",
          },
        "eventName": "PutUserPolicy",
        "eventVersion": "1.05",
        "userAgent": "aws-internal/3 aws-sdk-java/1.11.706 Linux/4.9.184-0.1.ac.235.83.329.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.242-b08 java/1.8.0_242 vendor/Oracle_Corporation",
        "requestParameters": null,
        "eventID": "1c2a53d1-58cc-41b3-85b8-bd7565370e0d",
        "eventType": "AwsApiCall",
        "sourceIPAddress": "72.21.217.97",
        "awsRegion": "us-east-1",
        "requestID": "27ca92a5-61cc-44aa-b875-042a25310064",
        "eventTime": "2020-04-10T06:22:08Z",
      }


# ------ paired body: aws_key_compromised.py ------

from panther_aws_helpers import aws_rule_context

EXPOSED_CRED_POLICIES = {
    "AWSExposedCredentialPolicy_DO_NOT_REMOVE",
    "AWSCompromisedKeyQuarantine",
    "AWSCompromisedKeyQuarantineV2",
    "AWSCompromisedKeyQuarantineV3",
}


def rule(event):
    if event.get("eventName") != "PutUserPolicy":
        return False

    request_params = event.get("requestParameters") or {}
    if request_params.get("policyName") not in EXPOSED_CRED_POLICIES:
        return False
    return True


def title(event):
    user_name = event.deep_get("userIdentity", "userName")
    access_key_id = event.deep_get("userIdentity", "accessKeyId")
    return (
        f"[{user_name}]'s AWS IAM Access Key ID [{access_key_id}]"
        f" was exposed and quarantined by AWS"
    )


def alert_context(event):
    return aws_rule_context(event)

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.