Cloud Detection Engineering with CloudTrail and GuardDuty

Cloud Detection Engineering with CloudTrail and GuardDuty - article cover image Cloud Security
Time it takes to read this article 7 minutes.

Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.

Introduction

CloudTrail is the record of truth for what happened in an AWS account: every management-plane API call (creating a role, changing a security group, assuming a role via STS) and, when enabled, every data-plane call against supported resources (S3 object reads, Lambda invocations, DynamoDB item access) is logged as a structured JSON event with the calling principal, source IP, user agent, and request/response parameters. GuardDuty sits on top of that evidence stream — plus VPC Flow Logs, DNS query logs, S3 data events, EKS audit logs, and Route 53 resolver logs — and runs managed threat detection against it, correlating anomalous API usage, known-bad IP reputation, and behavioral baselining into discrete findings.

Neither service is a complete detection program on its own. CloudTrail without a downstream pipeline is just an S3 bucket full of JSON nobody reads until an incident forces someone to. GuardDuty without tuning and response automation is a findings list that quietly fills up and gets ignored. Cloud detection engineering is the discipline of wiring these two together — plus EventBridge, Security Hub, and a SIEM — so that specific attacker actions, especially attempts to blind the logging itself, generate a page within minutes rather than being discovered in a post-incident forensic review weeks later.

Attack Prerequisites

From the defender’s side, a working detection capability requires several pieces of infrastructure to actually be turned on before any of it is useful. From the attacker’s side, operating with reduced visibility requires the inverse — gaps in that same infrastructure:

  • An organization or multi-region trail that logs management events in every region, including ones the account rarely uses (attackers deliberately pivot to quiet regions).
  • S3/Lambda data events enabled on the trail — off by default, and the single most common visibility gap in real environments because of the added cost.
  • GuardDuty enabled account-wide with a delegated administrator at the AWS Organizations level, not opted in ad hoc per account.
  • Log file validation turned on for the trail, and the destination S3 bucket locked down with a restrictive bucket policy (ideally in a separate log-archive account) so a compromised workload account cannot tamper with historical logs.
  • A SIEM or CloudTrail Lake / Athena pipeline actually ingesting and being queried against — logs that exist but are never queried provide no detection value.
  • From the attacker’s perspective: an IAM principal with cloudtrail:StopLogging, cloudtrail:DeleteTrail, or guardduty:DeleteDetector/UpdateDetector — the prerequisite for successfully operating blind.

How It Works

CloudTrail records three event categories. Management events cover control-plane operations (IAM, EC2, VPC, and similar) and are logged by default once a trail exists. Data events cover object- and record-level operations on S3, Lambda, DynamoDB, and other supported services, and must be explicitly selected via PutEventSelectors or advanced event selectors because of volume and cost. Insights events are a separate, opt-in feature that uses CloudTrail’s own math to flag unusual levels of API activity (a spike in IAM:CreateUser calls, for instance) without needing a downstream rule. Trails deliver compressed JSON log files to an S3 bucket roughly every five minutes, each accompanied by a SHA-256 digest file when log file validation is enabled — the digest chain lets an investigator cryptographically prove a log file was not altered after delivery. CloudTrail Lake offers a managed, SQL-queryable event data store as an alternative to hand-rolling an Athena table over the S3 bucket.

GuardDuty does not receive raw logs from the customer; it consumes CloudTrail management/S3-data events, VPC Flow Logs, DNS logs, and (if enabled) EKS audit logs and Runtime Monitoring telemetry directly from the underlying AWS infrastructure, independent of whether the account owner has a trail configured — which is deliberate, so that an attacker stopping CloudTrail does not also blind GuardDuty. Findings follow a Type:ResourceType/ThreatPurpose naming convention, for example UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B (impossible-travel logins), Recon:IAMUser/UserPermissions (systematic List/Describe/Get enumeration consistent with privilege discovery), Persistence:IAMUser/NetworkPermissions, and CredentialAccess:IAMUser/AnomalousBehavior. Each finding carries a severity score from 1.0 to 8.9 (Low/Medium/High) driving triage priority.

The finding most relevant to detection engineering itself is Stealth:IAMUser/CloudTrailLoggingDisabled — GuardDuty explicitly watches for the StopLogging and DeleteTrail API calls and raises a High-severity finding the moment a principal disables or removes a trail, precisely because disabling logging is such a common precursor to further, unlogged, malicious activity. This finding fires from GuardDuty’s independent event stream even if the trail being killed was the account’s only visibility into CloudTrail itself.

Vulnerable Code / Configuration

The gap that makes blinding detection possible is almost always an over-broad IAM policy attached to an automation role, CI/CD deployment role, or an individual administrator that was never scoped down after initial setup. A policy such as the following, commonly seen attached to a “DevOpsFullAccess”-style role, grants unrestricted logging-control actions with no resource constraint and no conditions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OverPermissiveTrailAdmin",
      "Effect": "Allow",
      "Action": [
        "cloudtrail:StopLogging",
        "cloudtrail:DeleteTrail",
        "cloudtrail:UpdateTrail",
        "cloudtrail:PutEventSelectors"
      ],
      "Resource": "*"
    },
    {
      "Sid": "OverPermissiveGuardDutyAdmin",
      "Effect": "Allow",
      "Action": [
        "guardduty:DeleteDetector",
        "guardduty:UpdateDetector",
        "guardduty:CreateIPSet",
        "guardduty:DisassociateFromMasterAccount"
      ],
      "Resource": "*"
    }
  ]
}
JSON

This is dangerous for a specific reason: any principal that assumes this role or has this policy attached can unilaterally turn off the exact telemetry that would otherwise catch whatever they do next. StopLogging/DeleteTrail kill the CloudTrail evidence trail; UpdateDetector with Enable: false or DeleteDetector kills GuardDuty outright; and CreateIPSet lets an attacker add their own C2 infrastructure to GuardDuty’s trusted IP allowlist so that traffic to it is never flagged. GuardDuty’s Stealth:IAMUser/CloudTrailLoggingDisabled finding exists precisely because this combination of permissions is common in real IAM policies and is routinely the first move in a cloud intrusion once initial access is achieved.

Walkthrough / Exploitation

A detection engineer starts by establishing ground truth on what is actually deployed — an audit step that is equally useful for confirming the gap above exists:

# Enumerate all trails and their multi-region / logging status
aws cloudtrail describe-trails --query 'trailList[].{Name:Name,MultiRegion:IsMultiRegionTrail,LogValidation:LogFileValidationEnabled}'
aws cloudtrail get-trail-status --name org-trail
aws cloudtrail get-event-selectors --trail-name org-trail

# Enumerate GuardDuty detectors and their status per region
aws guardduty list-detectors
aws guardduty get-detector --detector-id <DETECTOR_ID>
Bash

In a non-production test account, the attacker action being defended against can be reproduced directly to validate the detection pipeline end to end:

# Simulate the attacker move in an isolated test account only
aws cloudtrail stop-logging --name org-trail
# ...confirm a GuardDuty Stealth:IAMUser/CloudTrailLoggingDisabled finding appears...
aws guardduty list-findings --detector-id <DETECTOR_ID> \
  --finding-criteria '{"Criterion":{"type":{"Eq":["Stealth:IAMUser/CloudTrailLoggingDisabled"]}}}'

# Re-enable logging immediately after validating
aws cloudtrail start-logging --name org-trail
Bash

The detection itself is built as an EventBridge rule matching the raw CloudTrail event, feeding an SNS topic or Lambda for immediate paging — this fires faster than waiting on GuardDuty’s finding pipeline:

{
  "source": ["aws.cloudtrail"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["cloudtrail.amazonaws.com"],
    "eventName": ["StopLogging", "DeleteTrail", "UpdateTrail", "PutEventSelectors"]
  }
}
JSON

A second EventBridge rule with source: ["aws.guardduty"] and detail.type: ["Stealth:IAMUser/CloudTrailLoggingDisabled", "Persistence:IAMUser/NetworkPermissions"] provides a corroborating, independent trigger. For retrospective hunting, the same event is queryable directly against the CloudTrail S3 logs via Athena:

SELECT eventtime, useridentity.arn, eventname, sourceipaddress, requestparameters
FROM cloudtrail_logs
WHERE eventsource = 'cloudtrail.amazonaws.com'
  AND eventname IN ('StopLogging','DeleteTrail','UpdateTrail','PutEventSelectors')
ORDER BY eventtime DESC;
SQL

Note: GuardDuty pulls from AWS’s own internal collection of the underlying signals rather than from the customer’s trail, so disabling a customer trail does not disable GuardDuty. Do not assume the reverse is also safe: guardduty:UpdateDetector with Enable: false or DeleteDetector does fully blind GuardDuty regardless of CloudTrail state, which is why both action sets need independent monitoring.

Opsec: When testing this in a lab, expect a short gap between the API call and the GuardDuty finding — Stealth findings are not always instantaneous, typically landing within several minutes. Rely on the EventBridge rule against the raw CloudTrail event for time-critical alerting rather than waiting on GuardDuty for this specific scenario.

Detection and Defense

The layered defense here treats CloudTrail/GuardDuty tampering as itself a Tier-0 event, not just another API call:

  • Enable GuardDuty account-wide through AWS Organizations with a delegated administrator account so new member accounts inherit protection automatically.
  • Use an organization trail with log file validation, delivering to a dedicated log-archive account whose bucket policy denies delete/overwrite even to the source account’s administrators.
  • Alert on StopLogging, DeleteTrail, UpdateTrail, and PutEventSelectors via EventBridge → SNS/Lambda, independent of GuardDuty’s own pipeline latency.
  • **Alert on GuardDuty Stealth:IAMUser/CloudTrailLoggingDisabled and Persistence:IAMUser/* findings** at High/Critical priority with automated paging, not a dashboard nobody watches.
  • Use Service Control Policies to deny cloudtrail:StopLogging, cloudtrail:DeleteTrail, and guardduty:DeleteDetector/UpdateDetector actions org-wide, including for account-level administrators, so no single compromised principal can disable logging unilaterally.
  • Aggregate through Security Hub so GuardDuty findings, Config rule evaluations, and other detective controls land in one place for correlation and case management.

Real-World Impact

Disabling or gutting CloudTrail and GuardDuty is a well-documented step in cloud intrusions and is consistently cited by AWS and incident-response vendors as an early action taken by attackers who obtain credentials with broad IAM permissions, since it buys time for follow-on activity such as data exfiltration or persistent-access creation before a human notices. It is common enough as a technique that AWS built a dedicated GuardDuty finding type for the single case of CloudTrail being disabled, which is a strong signal of how frequently this specific action is observed across real customer environments.

Conclusion

CloudTrail and GuardDuty are complementary, not redundant: CloudTrail is the raw evidence and GuardDuty is managed correlation on top of a broader signal set that survives CloudTrail being disabled. Effective cloud detection engineering treats attempts to weaken either one as a high-priority event in its own right, backs that with IAM policies and SCPs that make tampering hard even for privileged principals, and validates the whole pipeline periodically in a test account rather than trusting it works until an incident proves otherwise.

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL