AWS CloudTrail CloudWatch Logs


Description

CloudTrail supports sending data and management events to CloudWatch Logs. This setup can be used for real-time processing of all CloudTrail data events.

Query · python

import datetime

from panther_base_helpers import deep_get, resolve_timestamp_string

MAX_TIME_BETWEEN_LOGS = datetime.timedelta(hours=24)


def policy(resource):
    # Check if a CloudWatch Logs Group has been set, and received at least one log
    if not (
        resource.get("CloudWatchLogsLogGroupArn")
        and deep_get(resource, "Status", "LatestCloudWatchLogsDeliveryTime")
    ):
        return False

    # Check if the last log sent is within the allowable timeframe
    last_log_time = resolve_timestamp_string(
        deep_get(resource, "Status", "LatestCloudWatchLogsDeliveryTime")
    )

    if not last_log_time:
        return True
    return (datetime.datetime.utcnow() - last_log_time) <= MAX_TIME_BETWEEN_LOGS

Analyst notes

https://docs.runpanther.io/alert-runbooks/built-in-policies/aws-cloudtrail-trails-integrated-with-cloudwatch-logs

Raw source AWS CloudTrail CloudWatch Logs · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: policy
Filename: aws_cloudtrail_cloudwatch_logs.py
PolicyID: "AWS.CloudTrail.CloudWatchLogs"
DisplayName: "AWS CloudTrail CloudWatch Logs"
Enabled: false
ResourceTypes:
  - AWS.CloudTrail
Tags:
  - AWS
  - Security Control
  - Defense Evasion:Impair Defenses
Reports:
  CIS:
    - 2.4
  MITRE ATT&CK:
    - TA0005:T1562
Severity: Low
Description: >
  CloudTrail supports sending data and management events to CloudWatch Logs. This setup can be
  used for real-time processing of all CloudTrail data events.
Runbook: >
  https://docs.runpanther.io/alert-runbooks/built-in-policies/aws-cloudtrail-trails-integrated-with-cloudwatch-logs
Reference: >
  https://docs.aws.amazon.com/awscloudtrail/latest/userguide/send-cloudtrail-events-to-cloudwatch-logs.html
Tests:
  - Name: CloudWatch Logs Not Setup
    ExpectedResult: false
    Resource:
      {
        "EventSelectors":
          [
            {
              "DataResources": [{ "Type": "AWS::S3::Object", "Values": [] }],
              "IncludeManagementEvents": false,
              "ReadWriteType": "All",
            },
          ],
        "CloudWatchLogsLogGroupArn": null,
        "CloudWatchLogsRoleArn": null,
        "HasCustomEventSelectors": true,
        "HomeRegion": "us-west-2",
        "IncludeGlobalServiceEvents": true,
        "IsMultiRegionTrail": false,
        "IsOrganizationTrail": false,
        "KmsKeyId": null,
        "LogFileValidationEnabled": true,
        "Name": "cloudtrail-name",
        "S3BucketName": "bucket-name",
        "S3KeyPrefix": null,
        "SnsTopicARN": null,
        "SnsTopicName": null,
        "TrailARN": "arn:aws:cloudtrail:us-west-2:112233445566:trail/cloudtrail-name",
        "Status":
          {
            "IsLogging": true,
            "LatestCloudWatchLogsDeliveryError": null,
            "LatestCloudWatchLogsDeliveryTime": null,
            "LatestDeliveryAttemptSucceeded": "2019-01-01T00:00:00Z",
            "LatestDeliveryAttemptTime": "2019-01-01T00:00:00Z",
            "LatestDeliveryError": null,
            "LatestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestDigestDeliveryError": null,
            "LatestDigestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestNotificationAttemptSucceeded": "",
            "LatestNotificationAttemptTime": "",
            "LatestNotificationError": null,
            "LatestNotificationTime": null,
            "StartLoggingTime": "2019-01-01T00:00:00Z",
            "StopLoggingTime": null,
            "TimeLoggingStarted": "2019-01-01T00:00:00Z",
            "TimeLoggingStopped": "",
          },
      }
  - Name: CloudTrail Set But No Logs Delivered
    ExpectedResult: false
    Resource:
      {
        "EventSelectors":
          [
            {
              "DataResources": [{ "Type": "AWS::S3::Object", "Values": [] }],
              "IncludeManagementEvents": false,
              "ReadWriteType": "All",
            },
          ],
        "CloudWatchLogsLogGroupArn": "arn:aws:cloudwatch:us-west-2:123456789012:watch/cloudwatch-name",
        "CloudWatchLogsRoleArn": null,
        "HasCustomEventSelectors": true,
        "HomeRegion": "us-west-2",
        "IncludeGlobalServiceEvents": true,
        "IsMultiRegionTrail": false,
        "IsOrganizationTrail": false,
        "KmsKeyId": null,
        "LogFileValidationEnabled": true,
        "Name": "cloudtrail-name",
        "S3BucketName": "bucket-name",
        "S3KeyPrefix": null,
        "SnsTopicARN": null,
        "SnsTopicName": null,
        "TrailARN": "arn:aws:cloudtrail:us-west-2:112233445566:trail/cloudtrail-name",
        "Status":
          {
            "IsLogging": true,
            "LatestCloudWatchLogsDeliveryError": null,
            "LatestCloudWatchLogsDeliveryTime": null,
            "LatestDeliveryAttemptSucceeded": "2019-01-01T00:00:00Z",
            "LatestDeliveryAttemptTime": "2019-01-01T00:00:00Z",
            "LatestDeliveryError": null,
            "LatestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestDigestDeliveryError": null,
            "LatestDigestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestNotificationAttemptSucceeded": "",
            "LatestNotificationAttemptTime": "",
            "LatestNotificationError": null,
            "LatestNotificationTime": null,
            "StartLoggingTime": "2019-01-01T00:00:00Z",
            "StopLoggingTime": null,
            "TimeLoggingStarted": "2019-01-01T00:00:00Z",
            "TimeLoggingStopped": "",
          },
      }
  - Name: CloudTrail Set But Logs Delivered Too Long Ago
    ExpectedResult: false
    Resource:
      {
        "EventSelectors":
          [
            {
              "DataResources": [{ "Type": "AWS::S3::Object", "Values": [] }],
              "IncludeManagementEvents": false,
              "ReadWriteType": "All",
            },
          ],
        "CloudWatchLogsLogGroupArn": "arn:aws:cloudwatch:us-west-2:123456789012:watch/cloudwatch-name",
        "CloudWatchLogsRoleArn": null,
        "HasCustomEventSelectors": true,
        "HomeRegion": "us-west-2",
        "IncludeGlobalServiceEvents": true,
        "IsMultiRegionTrail": false,
        "IsOrganizationTrail": false,
        "KmsKeyId": null,
        "LogFileValidationEnabled": true,
        "Name": "cloudtrail-name",
        "S3BucketName": "bucket-name",
        "S3KeyPrefix": null,
        "SnsTopicARN": null,
        "SnsTopicName": null,
        "TrailARN": "arn:aws:cloudtrail:us-west-2:112233445566:trail/cloudtrail-name",
        "Status":
          {
            "IsLogging": true,
            "LatestCloudWatchLogsDeliveryError": null,
            "LatestCloudWatchLogsDeliveryTime": "1019-01-01T00:00:00Z",
            "LatestDeliveryAttemptSucceeded": "2019-01-01T00:00:00Z",
            "LatestDeliveryAttemptTime": "2019-01-01T00:00:00Z",
            "LatestDeliveryError": null,
            "LatestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestDigestDeliveryError": null,
            "LatestDigestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestNotificationAttemptSucceeded": "",
            "LatestNotificationAttemptTime": "",
            "LatestNotificationError": null,
            "LatestNotificationTime": null,
            "StartLoggingTime": "2019-01-01T00:00:00Z",
            "StopLoggingTime": null,
            "TimeLoggingStarted": "2019-01-01T00:00:00Z",
            "TimeLoggingStopped": "",
          },
      }
  - Name: CloudTrail Set And Logs Delivered Recently
    ExpectedResult: true
    Resource:
      {
        "EventSelectors":
          [
            {
              "DataResources": [{ "Type": "AWS::S3::Object", "Values": [] }],
              "IncludeManagementEvents": false,
              "ReadWriteType": "All",
            },
          ],
        "CloudWatchLogsLogGroupArn": "arn:aws:cloudwatch:us-west-2:123456789012:watch/cloudwatch-name",
        "CloudWatchLogsRoleArn": null,
        "HasCustomEventSelectors": true,
        "HomeRegion": "us-west-2",
        "IncludeGlobalServiceEvents": true,
        "IsMultiRegionTrail": false,
        "IsOrganizationTrail": false,
        "KmsKeyId": null,
        "LogFileValidationEnabled": true,
        "Name": "cloudtrail-name",
        "S3BucketName": "bucket-name",
        "S3KeyPrefix": null,
        "SnsTopicARN": null,
        "SnsTopicName": null,
        "TrailARN": "arn:aws:cloudtrail:us-west-2:112233445566:trail/cloudtrail-name",
        "Status":
          {
            "IsLogging": true,
            "LatestCloudWatchLogsDeliveryError": null,
            "LatestCloudWatchLogsDeliveryTime": "3019-01-01T00:00:00Z",
            "LatestDeliveryAttemptSucceeded": "2019-01-01T00:00:00Z",
            "LatestDeliveryAttemptTime": "2019-01-01T00:00:00Z",
            "LatestDeliveryError": null,
            "LatestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestDigestDeliveryError": null,
            "LatestDigestDeliveryTime": "2019-01-01T00:00:00Z",
            "LatestNotificationAttemptSucceeded": "",
            "LatestNotificationAttemptTime": "",
            "LatestNotificationError": null,
            "LatestNotificationTime": null,
            "StartLoggingTime": "2019-01-01T00:00:00Z",
            "StopLoggingTime": null,
            "TimeLoggingStarted": "2019-01-01T00:00:00Z",
            "TimeLoggingStopped": "",
          },
      }


# ------ paired body: aws_cloudtrail_cloudwatch_logs.py ------

import datetime

from panther_base_helpers import deep_get, resolve_timestamp_string

MAX_TIME_BETWEEN_LOGS = datetime.timedelta(hours=24)


def policy(resource):
    # Check if a CloudWatch Logs Group has been set, and received at least one log
    if not (
        resource.get("CloudWatchLogsLogGroupArn")
        and deep_get(resource, "Status", "LatestCloudWatchLogsDeliveryTime")
    ):
        return False

    # Check if the last log sent is within the allowable timeframe
    last_log_time = resolve_timestamp_string(
        deep_get(resource, "Status", "LatestCloudWatchLogsDeliveryTime")
    )

    if not last_log_time:
        return True
    return (datetime.datetime.utcnow() - last_log_time) <= MAX_TIME_BETWEEN_LOGS

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.