TL;DR: awslabs.cloudtrail-mcp-server exposes five CloudTrail tools to Claude Code. Point it at a read-only IAM profile. Run five hunt prompts covering the MITRE T1078–T1562 credential-access cluster. Claude flags anomalies; you triage them.

Eat a slice of pizza while Claude Code churns through 90 days of CloudTrail history and tells you what looks wrong.

The Problem

DevOps environments are a wide attack surface. CI/CD pipelines, OIDC federation, long-lived access keys, and container registries all produce AWS credentials. When one leaks, attackers move fast: leaked keys are probed within minutes of appearing in a public repo.

Manual CloudTrail review does not keep pace. A busy AWS account generates thousands of API events per day - cross-account calls, assumed roles, and service-linked roles - and the signal-to-noise ratio stays low throughout.

These five hunt patterns target the actions that matter:

MITRE Technique

What to Hunt

T1078.004 - Valid Cloud Accounts

GetCallerIdentity from anomalous IPs or ASNs

T1087.004 - Account Discovery

IAM enumeration: ListUsers, ListRoles, GetAccountSummary

T1098 - Account Manipulation

Privilege escalation: AttachUserPolicy, CreateAccessKey

T1550.001 - Application Access Token

OIDC / web identity tokens used outside CI/CD ASNs

T1562.008 - Disable Cloud Logging

StopLogging, DeleteTrail, PutEventSelectors

Prerequisites

  • AWS account with CloudTrail enabled (management events, all regions)

  • IAM profile with these six read-only actions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudtrail:LookupEvents",
        "cloudtrail:ListEventDataStores",
        "cloudtrail:GetEventDataStore",
        "cloudtrail:StartQuery",
        "cloudtrail:DescribeQuery",
        "cloudtrail:GetQueryResults"
      ],
      "Resource": "*"
    }
  ]
}

Use a read-only IAM profile scoped to these six actions for threat hunting. Analyst activity must never touch production credentials or write-capable policies.

  • Claude Code installed: npm install -g @anthropic/claude-code

  • uvx available: pip install uv

  • CloudTrail Lake event data store (optional - needed for Hunts 3–5 which use SQL)

Step 1: Install the MCP Server

Command: Send below command to terminal

uvx awslabs.cloudtrail-mcp-server --help

This confirms uvx can pull and run the package. No separate install step is needed - uvx handles the Python environment on each run.

Step 2: Configure Claude Code

Add this to .claude/settings.json in your project directory:

{
  "mcpServers": {
    "cloudtrail": {
      "command": "uvx",
      "args": ["awslabs.cloudtrail-mcp-server"],
      "env": {
        "AWS_PROFILE": "threat-hunt-readonly",
        "FASTMCP_LOG_LEVEL": "ERROR"
      },
      "transportType": "stdio"
    }
  }
}

Replace threat-hunt-readonly with your read-only profile name. FASTMCP_LOG_LEVEL=ERROR suppresses server noise in the Claude Code terminal.

Step 3: Verify the Connection

Command: Send below command to terminal

claude

Once inside Claude Code, run:

Prompt: Send below prompt to Claude Code

/mcp

Step 4: Hunt 1 - Credential Validation (T1078.004)

Every stolen-key attack starts with sts:GetCallerIdentity: the attacker confirms the key is valid and maps the account before doing anything else.

Prompt: Send below prompt to Claude Code

You are a cloud threat hunter. Use the CloudTrail MCP server to search for GetCallerIdentity 
calls in the past 30 days.

For each result, check:
1. Is the source IP in an expected range for this account (CI/CD systems, developer offices)?
2. Does the user agent match expected tooling (aws-cli, boto3, Terraform)?
3. Is the call timing outside business hours?

Flag any call where source IP is unexpected OR user agent is unusual.
Output a table: timestamp | principal | source IP | user agent | flag reason

Do not modify any file outside hunt-results/.

A clean account returns only known IPs and tool names. A call from 45.129.14.x (a common cloud-scanning ASN) with user agent python-requests/2.28.0 is worth escalating.

Step 5: Hunt 2 - IAM Enumeration (T1087.004)

Attackers map permissions after validating credentials. ListUsers, ListRoles, and GetAccountSummary together in a short window from a single principal is the pattern.

Prompt: Send below prompt to Claude Code

You are a cloud threat hunter. Use the CloudTrail MCP server to search for IAM enumeration 
events in the past 7 days.

Target events:
- iam:ListUsers
- iam:ListRoles  
- iam:GetAccountSummary
- iam:ListAttachedUserPolicies
- iam:ListAttachedRolePolicies

Flag any principal that called 3+ of these actions within a 10-minute window.
Output: principal | events called | window start | window end | source IP

Do not modify any file outside hunt-results/.

A developer running aws iam list-users once is normal. Five enumeration calls in 90 seconds from an EC2 instance role is not.

Step 6: Hunt 3 - CI/CD Token Theft (T1550.001)

OIDC tokens issued to GitHub Actions or GitLab CI are scoped to specific ASNs. When the same assumed-role session appears from an ASN outside CI/CD infrastructure, the token was likely exfiltrated.

This hunt requires CloudTrail Lake (Trino SQL, up to 10 years of retention).

Prompt: Send below prompt to Claude Code

You are a cloud threat hunter. Use the CloudTrail Lake MCP server to find AssumeRoleWithWebIdentity 
calls where the same role was assumed from two different network locations in the same day.

Query logic:
- Find all AssumeRoleWithWebIdentity events in the past 14 days
- Group by roleArn and date
- Flag roleArns that appear with more than one distinct source IP block (/24)
- Separate CI/CD ASNs (GitHub Actions: 140.82.0.0/16, GitLab: 34.74.0.0/16) from all others

Output: roleArn | ci_cd_ip | external_ip | date | verdict

Do not modify any file outside hunt-results/.

Finding the same role session from 140.82.112.x (GitHub) AND 185.220.101.x (Tor exit node) on the same day is high severity.

Step 7: Hunt 4 - Privilege Escalation (T1098)

Post-compromise, attackers attach admin policies or create new access keys to persist access.

Prompt: Send below prompt to Claude Code

You are a cloud threat hunter. Use the CloudTrail MCP server to find privilege escalation 
attempts in the past 14 days.

Target events:
- iam:AttachUserPolicy (especially AdministratorAccess or PowerUserAccess)
- iam:AttachRolePolicy
- iam:CreateAccessKey
- iam:PutUserPolicy (inline policies)
- iam:UpdateAssumeRolePolicy

For each hit:
1. What principal made the call?
2. What was the target principal?
3. Was this principal also seen in IAM enumeration events in the prior 24 hours?

Output: timestamp | actor | target | action | policy attached | prior enumeration?

Do not modify any file outside hunt-results/.

An iam:AttachUserPolicy for AdministratorAccess by a principal that called ListRoles 20 minutes earlier is a confirmed attack sequence.

Step 8: Hunt 5 - Log Tampering (T1562.008)

Attackers who want to hide their activity disable logging. StopLogging, DeleteTrail, and PutEventSelectors with reduced event scope are the three actions to watch.

Prompt: Send below prompt to Claude Code

You are a cloud threat hunter. Use the CloudTrail MCP server to search for any CloudTrail 
configuration changes in the past 30 days.

Target events:
- cloudtrail:StopLogging
- cloudtrail:DeleteTrail
- cloudtrail:PutEventSelectors (check if management events were narrowed or excluded)
- cloudtrail:UpdateTrail

For each hit:
1. What was changed?
2. What was the before/after configuration?
3. Was logging restored within 5 minutes (possible authorized change)?

Output: timestamp | principal | action | trail ARN | change summary | logging restored?

Do not modify any file outside hunt-results/.

Any StopLogging not followed by StartLogging within 5 minutes from an ops principal is an incident, not a mistake.

Step 9: Run All Five as an Autonomous Loop

The /goal command runs Claude Code in a loop until a verifiable condition is met. Use it to run all five hunts unattended and get a single verdict file. For a full breakdown of how /goal works in security workflows, see Using /goal in Claude Code for Autonomous Security Engineering Workflows.

Prompt: Send below prompt to Claude Code

/goal Run all five credential-access hunt patterns (GetCallerIdentity anomalies, IAM enumeration, 
OIDC token abuse, privilege escalation, log tampering) against the past 30 days of CloudTrail data.

Success condition: All five hunts complete with findings written to hunt-results/.

If any hunt returns a HIGH or CRITICAL finding, write ESCALATE.md listing the finding, 
affected principal, and recommended action, then stop.

If all five complete with zero findings, write CLEAN.md with a timestamp and summary.

Do not modify any file outside hunt-results/.

Interrupt at any time with Ctrl+C. Run headless (no interactive terminal) with:

Command: Send below command to terminal

claude --print "/goal Run all five credential-access hunt patterns..."

Step 10: Triage the Findings

Claude Code produces a structured findings table. Each row needs a human decision.

Triage process:

  1. Confirm the principal exists. Run aws iam get-user or aws sts get-caller-identity against the flagged ARN.

  2. Check the IP. Look up the ASN. Known CI/CD ranges, corporate egress, or VPN are usually safe. Cloud-hosting ASNs or anonymizing infrastructure are not.

  3. Check the time. Calls at 03:00 UTC from a developer principal warrant explanation.

  4. Look for sequences. GetCallerIdentity → ListRoles → AttachUserPolicy in the same hour from the same IP is a confirmed attack chain.

If the finding is valid:

  1. Disable the access key immediately: aws iam update-access-key --access-key-id <KEY_ID> --status Inactive

  2. Do not delete it yet; you need the key ID for forensics.

  3. Attach an explicit deny policy to the principal as a containment measure.

  4. Open an incident. Preserve the CloudTrail event JSON.

Running This on a Schedule

Add this to your project's CLAUDE.md so each new Claude Code session inherits the hunting context:

# Threat Hunt Context

AWS account: production
Hunt scope: credential access (T1078, T1087, T1098, T1550, T1562)
Output directory: hunt-results/
IAM profile: threat-hunt-readonly
Do not modify files outside hunt-results/.

Then schedule via cron or CI/CD:

Command: Send below command to terminal

# Run every Monday at 06:00 UTC
0 6 * * 1 cd /path/to/hunt && claude --print "/goal Run weekly credential-access hunt. Write ESCALATE.md or CLEAN.md in hunt-results/." >> hunt-results/weekly-$(date +%Y%m%d).log 2>&1

Further Reading

Subscribe to SecEngAI for Applied AI for Security Engineering