Skip to content

Prerequisites

  • projectx-prod-vpc has been created with subnets configured.
  • CloudTrail has been configured and is delivering logs to your S3 security datalake (see CloudTrail & Integration).
  • Wazuh is configured to ingest CloudTrail logs from S3.
  • project-x-sec-box configured with Wazuh.

Important

Enable S3 Data Events for CloudTrail to detect GetObject and ListBucket calls. In your trail, under Event typeData events, add S3 bucket-level data events (read/write) for the buckets you want to monitor. Without this, S3 object access will not appear in CloudTrail.

Important

Enable API Gateway Data Events for CloudTrail to detect Invoke calls to your APIs. In your trail, under Event typeData events, add execute-api.amazonaws.com data events. Without this, unauthenticated API invocations will not appear in CloudTrail.

Note

This guide builds on these attack scenarios: - Misconfigured S3 Bucket — public bucket access - Insecure API Gateway — public API access without authentication

Network Topology

Base Layout
(Click to zoom)

Overview

CloudTrail records API calls across your AWS account such as who made the call, when, from where, and what action was performed.

By creating Wazuh detection rules, you can identify patterns consistent with the Misconfigured S3 Bucket and Insecure API Gateway attacks—such as anonymous access to S3 objects, public API invocations without credentials, and configuration changes that grant overly permissive access.

This guide provides custom Wazuh detection monitors to deploy via the Alerting capability.

Detection Strategy

How would we detect malicious activity?

We have the visibility through our logs via AWS CloudTrail. This is one of the primary use cases for this AWS service.

We can start with the attack and work backward...

Attackers discover public S3 buckets through reconnaissance (s3scanner, bucket enumeration), then list and download objects with --no-sign-request.

Similarly, attackers discover exposed API Gateways and invoke them with curl, if opened, API keys, IAM auth will be allowed.

In CloudTrail, we look for events where eventSource (which service), eventName (what action), and userIdentity (who—and crucially, whether they're authenticated) tell the story.

Legitimate access uses IAM users, roles, or root. Anonymous or unauthenticated principals accessing S3 objects or invoking APIs is a strong signal of misconfiguration.

Configuration changes that grant public access, such as PutBucketPolicy, PutBucketAcl, or overly permissive API Gateway resource policies are also indicators we can detect.

So here's what we can come up with...

Attack Phase CloudTrail Indicator Detection Approach
Misconfigured S3 Bucket S3 GetObject/ListBucket from Anonymous principal Anonymous S3 access
Misconfigured S3 Bucket PutBucketPolicy/PutBucketAcl that grants public access Bucket policy misconfiguration
Insecure API Gateway API Gateway Invoke from Anonymous principal Anonymous API invocation
Insecure API Gateway PutRestApi/UpdateRestApiPolicy with permissive resource policy API Gateway policy misconfiguration

CloudTrail fields used in our rules include:

  • eventSource: The service that received the request (e.g., s3.amazonaws.com, execute-api.amazonaws.com)
  • eventName: The API action (e.g., GetObject, ListBucket, Invoke)
  • userIdentity.type / userIdentity.principalId: Who made the request—Anonymous indicates unauthenticated access
  • sourceIPAddress: Where the request originated

Create Wazuh Rule Extractors

<group name="aws,cloudtrail,s3,data_events">

  <rule id="119500" level="5">
    <decoded_as>json</decoded_as>

    <!-- This matches the JSON field, not the metadata location -->
    <field name="location">Wazuh-AWS</field>

    <field name="data.integration">aws</field>
    <field name="data.aws.source">cloudtrail</field>

    <field name="data.aws.eventSource">s3.amazonaws.com</field>
    <field name="data.aws.eventCategory">Data</field>

    <description>AWS CloudTrail S3 Data Event detected</description>
  </rule>

</group>

Create Wazuh Detection Rule

Anonymous S3 Access (Misconfigured S3 Bucket)

We want to detect when someone accesses S3 without credentials.

The Misconfigured S3 Bucket scenario allows public read access—attackers use aws s3 cp ... --no-sign-request or s3scanner to enumerate and exfiltrate data.

CloudTrail records these as GetObject and ListBucket/ListObjectsV2 with userIdentity.type of Anonymous. Let's build the rule step by step.

Rule Description In Layman Terms

Our condition is: "Alert when we see S3 GetObject or ListBucket from an Anonymous principal." We express this with three requirements:

  1. The event must be a CloudTrail event (we query the index that contains CloudTrail alerts).
  2. The event source must be S3 and the event name must be GetObject, ListBucket, or ListObjectsV2.
  3. The user identity must indicate Anonymous (unauthenticated) access.

Wazuh Query Syntax Walkthrough

Each monitor uses an Query DSL. Here's what each part does:

Element Purpose Example
term on data.aws.eventSource Match the AWS service. s3.amazonaws.com = S3
term on data.aws.eventName Match the API action. GetObject = object download
match_phrase / term on data.aws.userIdentity Match the identity. Anonymous = no credentials
bool + filter Combine conditions (all must match). AND logic

Create a Monitor

If you can recall from Enterprise 101, we used the "Alerting" capability inside Wazuh. Under Alerting, we can create a Monitor, which will run a pre-defined query against all incoming logs. If the log matches the query content, an alert is generated.

We will use the Monitoring and Alerting capability to capture anonymous S3 and API Gateway access.

Navigate to AlertingMonitors.

Monitor Details:

  • Monitor name: Name your monitor. Select "Per query monitor".

  • Monitor type: Choose "Extraction query editor".

Click To Zoom
(Click to zoom)

Schedule:

  • By interval.

  • Run every 1 Minute(s).

Click To Zoom
(Click to zoom)

Select Data:

  • Indexes: wazuh-alerts-4.x-*
Click To Zoom
(Click to zoom)

Query: Paste in Query.

Click To Zoom
(Click to zoom)

Triggers:

  • Trigger Name: Name Trigger.

  • Severity level: Choose severity level.

Click To Zoom
(Click to zoom)

S3 GetObject from Anonymous Principal

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "s3.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "term": {
                        "data.aws.eventName": {
                            "value": "GetObject",
                            "boost": 1
                        }
                    }
                },
                {
                    "query_string": {
                        "query": "data.aws.userIdentity.type:Anonymous OR data.aws.userIdentity.principalId:Anonymous",
                        "boost": 1
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

S3 ListBucket / ListObjectsV2 from Anonymous Principal

Use the same structure as above, but change the event name filter to capture list operations:

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "s3.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "terms": {
                        "data.aws.eventName": ["ListBucket", "ListObjectsV2", "ListObjects"],
                        "boost": 1
                    }
                },
                {
                    "query_string": {
                        "query": "data.aws.userIdentity.type:Anonymous OR data.aws.userIdentity.principalId:Anonymous",
                        "boost": 1
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

Anonymous API Gateway Invocation (Insecure API Gateway)

When an attacker invokes an API Gateway endpoint with curl—no API key, no IAM auth—CloudTrail logs the request with eventSource of execute-api.amazonaws.com and userIdentity.type of Anonymous. This indicates someone is calling your API without authentication, consistent with the Insecure API Gateway scenario.

Rule Description In Layman Terms

"Alert when we see an API Gateway Invoke from an Anonymous principal."

  1. The event source must be execute-api.amazonaws.com (data plane API invocation).
  2. The event name must be Invoke (the actual HTTP request to your API).
  3. The user identity must indicate Anonymous access.

Create a Monitor

Follow the same workflow as above (Navigate to AlertingMonitors, configure Monitor Details, Schedule, Select Data, Query, Triggers).

API Gateway Invoke from Anonymous Principal

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "execute-api.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "term": {
                        "data.aws.eventName": {
                            "value": "Invoke",
                            "boost": 1
                        }
                    }
                },
                {
                    "query_string": {
                        "query": "data.aws.userIdentity.type:Anonymous OR data.aws.userIdentity.principalId:Anonymous",
                        "boost": 1
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

Note

CloudTrail must have API Gateway data events enabled for execute-api.amazonaws.com to log Invoke operations. In your trail, under Event typeData events, add a data event type for API Gateway and select the appropriate logging template.

Bucket Policy / ACL Misconfiguration (Prevention Signal)

Detect configuration changes that could expose S3 buckets to the public. These are management events and are logged by default.

Rule Description In Layman Terms

"Alert when we see PutBucketPolicy or PutBucketAcl—especially if the policy grants public access."

These events indicate someone changed bucket permissions. A follow-up review should verify whether public access was granted.

S3 PutBucketPolicy

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "s3.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "term": {
                        "data.aws.eventName": {
                            "value": "PutBucketPolicy",
                            "boost": 1
                        }
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

S3 PutBucketAcl with Public Access

To detect PutBucketAcl that includes public-read or AllUsers, we use a query string to match policy text:

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "s3.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "term": {
                        "data.aws.eventName": {
                            "value": "PutBucketAcl",
                            "boost": 1
                        }
                    }
                },
                {
                    "query_string": {
                        "query": "AllUsers OR public-read OR PublicRead",
                        "boost": 1
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

API Gateway Resource Policy Changes (Prevention Signal)

Detect API Gateway configuration changes that could expose APIs to the public—such as PutRestApi or UpdateRestApiPolicy with a resource policy allowing Principal: '*'.

Rule Description In Layman Terms

"Alert when we see PutRestApi or UpdateRestApiPolicy."

These events indicate the API Gateway resource policy was modified. A follow-up review should verify whether the policy allows unauthenticated access.

API Gateway PutRestApi / UpdateRestApiPolicy

{
    "size": 0,
    "query": {
        "bool": {
            "filter": [
                {
                    "term": {
                        "data.aws.eventSource": {
                            "value": "apigateway.amazonaws.com",
                            "boost": 1
                        }
                    }
                },
                {
                    "terms": {
                        "data.aws.eventName": ["PutRestApi", "UpdateRestApiPolicy"],
                        "boost": 1
                    }
                }
            ],
            "adjust_pure_negative": true,
            "boost": 1
        }
    }
}

Verify Detections

Test Misconfigured S3 Detection

  1. Deploy the Misconfigured S3 Bucket CloudFormation stack.
  2. Populate the bucket with sample data (see the attack guide).
  3. From any machine (no AWS credentials), run:
aws s3 ls s3://delete-me-projectx-leaky-bucket --no-sign-request
aws s3 cp s3://delete-me-projectx-leaky-bucket/config/database-credentials.json . --no-sign-request
  1. Wait 5–15 minutes for CloudTrail to deliver logs and Wazuh to ingest them.
  2. Navigate to Alerting in Wazuh—you should see alerts for anonymous S3 GetObject and ListBucket.
Click To Zoom
(Click to zoom)

Test Insecure API Gateway Detection

  1. Deploy the Insecure API Gateway CloudFormation stack.
  2. Get the API Gateway invoke URL from CloudFormation outputs.
  3. From any machine (no AWS credentials), run:
curl https://<api-id>.execute-api.<region>.amazonaws.com/prod/test
curl https://<api-id>.execute-api.<region>.amazonaws.com/prod/users
  1. Wait 5–15 minutes for CloudTrail to deliver logs and Wazuh to ingest them.
  2. Navigate to Alerting in Wazuh—you should see alerts for anonymous API Gateway Invoke.

View Alerts

Navigate to Alerting.

You should see new alerts generated when anonymous S3 access or anonymous API Gateway invocations are detected.

CloudTrail Configuration Reminders

Requirement Purpose
S3 Data Events Log GetObject, ListBucket, etc. for anonymous S3 access detection
API Gateway Data Events (execute-api) Log Invoke operations for anonymous API access detection
Management Events (Read + Write) Log PutBucketPolicy, PutBucketAcl, PutRestApi, etc.
Multi-Region Capture activity across all regions