AWS CloudTrail SES Check Send Quota


Description

Detect when someone checks how many emails can be delivered via SES. Excludes automated checks from AWS Trusted Advisor to reduce false positives.

Query · python

from panther_aws_helpers import aws_rule_context
from panther_core import PantherEvent


def rule(event: PantherEvent) -> bool:
    if event.get("eventName") == "GetSendQuota":
        # Exclude AWS Trusted Advisor automated checks
        role_name = event.deep_get("userIdentity", "sessionContext", "sessionIssuer", "userName")
        if role_name == "AWSServiceRoleForTrustedAdvisor":
            return False
        return True
    return False


def alert_context(event: PantherEvent) -> dict:
    context = aws_rule_context(event)
    context["accountRegion"] = f"{event.get('recipientAccountId')}_{event.get('eventRegion')}"
    return context
Raw source AWS CloudTrail SES Check Send Quota · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: aws_cloudtrail_ses_check_send_quota.py
RuleID: "AWS.CloudTrail.SES.CheckSendQuota"
DisplayName: AWS CloudTrail SES Check Send Quota
Enabled: true
LogTypes:
  - AWS.CloudTrail
Severity: Info
CreateAlert: false
Description: >
  Detect when someone checks how many emails can be delivered via SES.
  Excludes automated checks from AWS Trusted Advisor to reduce false positives. 
Reference: >
  https://stratus-red-team.cloud/attack-techniques/AWS/aws.discovery.ses-enumerate/
Tags:
  - AWS CloudTrail
  - SES
Tests:
  - Name: GetSendQuota Event
    ExpectedResult: true
    Log:
      {
        "p_event_time": "2025-01-20 16:52:14.000000000",
        "p_log_type": "AWS.CloudTrail",
        "p_parse_time": "2025-01-20 17:00:54.217261818",
        "additionalEventData": {
          "SignatureVersion": "4"
        },
        "awsRegion": "us-west-2",
        "eventCategory": "Management",
        "eventID": "141c7b0f-3ec3-40bd-b551-5a33d1a794b4",
        "eventName": "GetSendQuota",
        "eventSource": "ses.amazonaws.com",
        "eventTime": "2025-01-20 16:52:14.000000000",
        "eventType": "AwsApiCall",
        "eventVersion": "1.08",
        "managementEvent": true,
        "readOnly": true,
        "recipientAccountId": "111122223333",
        "requestID": "6495a102-3900-47fc-a8b4-88e4b4e56442",
        "sourceIPAddress": "1.2.3.4",
        "tlsDetails": {
          "cipherSuite": "TLS_AES_128_GCM_SHA256",
          "clientProvidedHostHeader": "email.us-west-2.amazonaws.com",
          "tlsVersion": "TLSv1.3"
        },
        "userAgent": "example-user-agent",
        "userIdentity": {
          "accessKeyId": "SAMPLE_ACCESS_KEY",
          "accountId": "111122223333",
          "arn": "arn:aws:sts::111122223333:assumed-role/SampleRole/bobson.dugnutt",
          "principalId": "SAMPLE_PRINCIPAL_ID:bobson.dugnutt",
          "sessionContext": {
            "attributes": {
              "creationDate": "2025-01-20T15:58:59Z",
              "mfaAuthenticated": "false"
            },
            "sessionIssuer": {
              "accountId": "111122223333",
              "arn": "arn:aws:iam::111122223333:role/aws-reserved/sso.amazonaws.com/us-west-2/SampleRole",
              "principalId": "SAMPLE_PRINCIPAL_ID",
              "type": "Role",
              "userName": "SampleRole"
            }
          },
          "type": "AssumedRole"
        }
      }
  - Name: GetSendQuota from Trusted Advisor (Should Not Alert)
    ExpectedResult: false
    Log:
      {
        "p_event_time": "2026-01-11 22:54:56.000000000",
        "p_log_type": "AWS.CloudTrail",
        "awsRegion": "us-east-1",
        "eventCategory": "Management",
        "eventID": "test-event-id",
        "eventName": "GetSendQuota",
        "eventSource": "ses.amazonaws.com",
        "eventTime": "2026-01-11 22:54:56.000000000",
        "eventType": "AwsApiCall",
        "eventVersion": "1.08",
        "managementEvent": true,
        "readOnly": true,
        "recipientAccountId": "187901811700",
        "sourceIPAddress": "trustedadvisor.amazonaws.com",
        "userAgent": "trustedadvisor.amazonaws.com",
        "userIdentity": {
          "type": "AssumedRole",
          "principalId": "AROASXP6SDP2MIDOVDS5K:TrustedAdvisor_187901811700_7472a099-2df6-4080-b339-9681aa0a066a",
          "arn": "arn:aws:sts::187901811700:assumed-role/AWSServiceRoleForTrustedAdvisor/TrustedAdvisor_187901811700_7472a099-2df6-4080-b339-9681aa0a066a",
          "accountId": "187901811700",
          "sessionContext": {
            "sessionIssuer": {
              "type": "Role",
              "principalId": "AROASXP6SDP2MIDOVDS5K",
              "arn": "arn:aws:iam::187901811700:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
              "accountId": "187901811700",
              "userName": "AWSServiceRoleForTrustedAdvisor"
            },
            "attributes": {
              "creationDate": "2026-01-11T22:54:56Z",
              "mfaAuthenticated": "false"
            }
          }
        }
      }

# ------ paired body: aws_cloudtrail_ses_check_send_quota.py ------

from panther_aws_helpers import aws_rule_context
from panther_core import PantherEvent


def rule(event: PantherEvent) -> bool:
    if event.get("eventName") == "GetSendQuota":
        # Exclude AWS Trusted Advisor automated checks
        role_name = event.deep_get("userIdentity", "sessionContext", "sessionIssuer", "userName")
        if role_name == "AWSServiceRoleForTrustedAdvisor":
            return False
        return True
    return False


def alert_context(event: PantherEvent) -> dict:
    context = aws_rule_context(event)
    context["accountRegion"] = f"{event.get('recipientAccountId')}_{event.get('eventRegion')}"
    return context

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.