Overview

This tutorial shows you how to hunt for IAM privilege escalation activity in an AWS environment using Claude Code and the Elastic MCP server. You query Elasticsearch indexes that contain CloudTrail logs, analyze suspicious IAM API calls, and investigate privilege escalation chains, all through conversational Claude Code sessions.

By the end of this tutorial, you can:

  • Configure Claude Code with the Elastic MCP server to query Elasticsearch directly

  • Write Elasticsearch Query DSL expressions targeting high-fidelity IAM privilege escalation patterns

  • Identify common privilege escalation techniques mapped to MITRE ATT&CK T1098 and T1548

  • Pivot from an initial indicator to a full principal activity timeline

  • Encode validated hunting queries as persistent Kibana detection rules

Prerequisites

Before you begin, make sure you have the following:

Software

  • Claude Code 1.0 or later installed and licensed

  • Node.js 18 or later (required by the Elastic MCP server)

  • npm 9 or later

  • Elasticsearch 8.x cluster with the Elastic AWS integration configured

AWS configuration

  • AWS CloudTrail enabled and delivering logs to your Elasticsearch cluster

    • An organization-level trail covers all regions; account-level trails are region-specific

    • CloudTrail logs typically land in the index pattern logs-aws.cloudtrail-* when ingested by the Elastic AWS integration

  • Read access to the CloudTrail indexes in Elasticsearch (the monitor and read cluster privileges, plus read on the target index pattern)

Knowledge

  • Basic understanding of AWS IAM concepts: users, roles, policies, and the AWS Security Token Service (STS)

Understanding IAM privilege escalation

IAM privilege escalation occurs when a principal with limited permissions manipulates IAM resources to gain elevated access (often administrative). The table below summarizes the most common techniques, the IAM actions they abuse, and their MITRE ATT&CK mappings.

Technique

Abused IAM actions

MITRE ATT&CK

Policy manipulation

iam:CreatePolicyVersion, iam:SetDefaultPolicyVersion, iam:AttachUserPolicy, iam:PutUserPolicy

T1098

Access key creation for another user

iam:CreateAccessKey

T1098.001

Role assumption chaining

sts:AssumeRole (repeated hops)

T1550.001

Login profile takeover

iam:UpdateLoginProfile

T1098.001

PassRole to compute service

iam:PassRole + ec2:RunInstances or lambda:CreateFunction

T1548

Admin user creation

iam:CreateUser + iam:AttachUserPolicy

T1136.003

These techniques share a pattern: a low-privilege principal calls one or more IAM or STS API operations that grant a principal, itself or another, the permissions it was not supposed to have. CloudTrail records every IAM and STS API call, making it your primary data source for hunting these patterns.

Anomalies to look for:

  • IAM write operations (Create, Attach, Put, Update) from principals that do not normally call them

  • IAM write operations targeting a different principal than the caller

  • AssumeRole calls where the source identity is itself an assumed-role session

  • A burst of AccessDenied errors on IAM actions immediately before a successful privilege escalation

Setting up the Elastic MCP server with Claude Code

Installing the MCP server

  1. Install the Elastic MCP server package globally:

npm install -g @elastic/mcp-server-elasticsearch
  1. Verify the installation:

elastic-mcp-server --version

Expected output:

elastic-mcp-server 1.2.0

Configuring Claude Code

Add the Elastic MCP server to your Claude Code configuration file at ~/.claude/claude.json:

{
  "mcpServers": {
    "elasticsearch": {
      "command": "elastic-mcp-server",
      "args": [],
      "env": {
        "ES_URL": "https://your-cluster:9200",
        "ES_API_KEY": "your-api-key-here",
        "ES_CA_CERT": "/path/to/ca.crt"
      }
    }
  }
}

Replace the following placeholder values:

  • your-cluster:9200: your Elasticsearch cluster endpoint

  • your-api-key-here: your Elasticsearch API key (base64-encoded id:key string)

  • /path/to/ca.crt: path to your CA certificate; omit this field entirely if your cluster uses a public certificate authority

Verifying the connection

  1. Start a Claude Code session:

claude
  1. Check that the Elasticsearch MCP server is connected:

/mcp

Expected output includes elasticsearch in the list of connected servers:

Connected MCP servers:
  elasticsearch  (6 tools available)

If the server doesn't appear, check ~/.claude/mcp.log for connection errors. The most common causes are a malformed claude.json file, an unreachable cluster endpoint, or an API key with insufficient permissions.

Tip: To confirm your API key has the right permissions, run:

curl -H "Authorization: ApiKey your-api-key-here" \
  https://your-cluster:9200/_security/authenticate

The response shows the roles and privileges associated with the key.

Establishing baseline IAM activity

Before hunting anomalies, establish what normal IAM API activity looks like in your environment. Run the following aggregation in your Claude Code session:

Using the Elasticsearch MCP, query logs-aws.cloudtrail-* for the past
30 days and summarize IAM API activity. I need:

1. The top 25 IAM API calls by volume; flag any associated with privilege
   escalation (CreatePolicyVersion, AttachUserPolicy, PutUserPolicy,
   CreateAccessKey, UpdateLoginProfile, AssumeRole).
2. The top 10 principals by IAM call volume.
3. A callout for any IAM write operations (Create*, Put*, Attach*, Update*)
   in the top 25, with a breakdown of which principals are making them.

Example Claude Code output:

✦ Queried logs-aws.cloudtrail-* · 47,832 IAM events · 2026-09-13 to 2026-10-13

TOP IAM API CALLS
──────────────────────────────────────────────────────────
 #   Event name                    Count    Share
──────────────────────────────────────────────────────────
 1   ListRoles                    21,450   44.8%
 2   GetRole                      12,210   25.5%
 3   ListPolicies                  5,640   11.8%
 4   AttachRolePolicy              1,820    3.8%  ← WRITE
 5   ListAttachedRolePolicies      1,340    2.8%
 ...
18   CreatePolicyVersion              48    0.1%  ← WRITE
22   PutRolePolicy                    31    0.1%  ← WRITE

TOP CALLERS
──────────────────────────────────────────────────────────
 TerraformProvisioner (role)      31,200   65.2%
 ci-pipeline (user)                9,440   19.7%
 john.doe (user)                   4,100    8.6%
 svc-monitoring (role)             1,890    3.9%

⚠ WRITE OPERATIONS IN TOP 25
   AttachRolePolicy (#4):    1,820 calls, 98% from TerraformProvisioner
   CreatePolicyVersion (#18):   48 calls: john.doe (44), ci-pipeline (4)
   PutRolePolicy (#22):         31 calls, all from TerraformProvisioner

Observation: john.doe made 44 of 48 CreatePolicyVersion calls. This is
unusual; CreatePolicyVersion is an administrator-level action. Recommend
reviewing whether these calls set permissive policy versions (setAsDefault:
true). Continue with the detection queries below.

Claude Code returns a summary of IAM call volumes over time, top callers, and flags any write operations present. Record the principals that normally generate IAM write calls. These are your expected actors. Any other principal performing IAM writes is an immediate focus for the hunting queries that follow.

Detecting policy manipulation

Policy manipulation is the highest-impact privilege escalation path: a single AttachUserPolicy call granting AdministratorAccess can give an attacker full control of an AWS account. Run this query in your Claude Code session:

Using the Elasticsearch MCP, search logs-aws.cloudtrail-* for the past
7 days for any of these IAM policy manipulation events:
CreatePolicyVersion, SetDefaultPolicyVersion, AttachUserPolicy,
AttachGroupPolicy, AttachRolePolicy, PutUserPolicy, PutGroupPolicy,
PutRolePolicy. Return up to 100 results sorted newest first.

For each result:
1. Identify the caller (with ARN) and target principal.
2. Note whether the attached or modified policy grants admin-level access
   (AdministratorAccess, Action: "*", or iam:*).
3. Flag any caller that is not in the expected list:
   TerraformProvisioner, ci-pipeline.
Group by caller and highlight outliers.

Example Claude Code output:

✦ 23 policy manipulation events · 3 unique callers · 2026-10-06 to 2026-10-13

EXPECTED ACTIVITY
──────────────────────────────────────────────────────────────────
 TerraformProvisioner: 18× AttachRolePolicy on service roles
   Policies: AmazonS3ReadOnlyAccess, AmazonDynamoDBReadOnlyAccess
   → No admin-level grants. Consistent with provisioning.

 ci-pipeline: 3× AttachRolePolicy on CIDeployRole
   Policy: AWSCodeDeployDeployerAccess
   → No admin-level grants. Expected CI behavior.

⚠ SUSPICIOUS ACTIVITY: CALLER NOT IN EXPECTED LIST
──────────────────────────────────────────────────────────────────
 Caller:    arn:aws:iam::123456789012:user/dev-temp-user
 Source IP: 198.51.100.47 (not a known corporate IP range)

 2026-10-11 03:14:02 UTC · CreatePolicyVersion
   Policy:  arn:aws:iam::123456789012:policy/DevAccess
   Version: v3  (setAsDefault: true)
   Change:  Added "Action": "*", "Resource": "*" ← grants full admin ⚠

 2026-10-11 03:14:09 UTC · AttachUserPolicy
   Caller:  dev-temp-user
   Target:  dev-temp-user  (self-escalation)
   Policy:  arn:aws:iam::aws:policy/AdministratorAccess ⚠

dev-temp-user self-escalated to AdministratorAccess within 7 seconds.
Recommend immediate investigation → proceed to the investigation section.

Any result where the caller is not in your expected list, or where the attached policy grants Action: "*" or iam:*, needs immediate investigation. Proceed to the Investigating a suspected privilege escalation section for those principals.

High-confidence indicator: A CreatePolicyVersion call with setAsDefault: true in requestParameters means the caller is replacing an existing policy with a new, potentially more permissive version and activated it immediately, with no delay between the change and its effect.

Detecting access key creation abuse

An attacker with iam:CreateAccessKey can create programmatic credentials for any IAM user, including administrators, establishing persistent backdoor access. The signal is straightforward: the caller and target user differ.

Run this query in your Claude Code session:

Using the Elasticsearch MCP, search logs-aws.cloudtrail-* for the past
7 days for CreateAccessKey events where the caller and target user are
different principals. For each result:
- Show the caller identity (with ARN), target username, and source IP.
- Check whether the newly created access key ID subsequently appears in
  any CloudTrail events as the caller's access key.
- If the key was used, list those follow-on API calls with timestamps.

Example Claude Code output:

✦ 1 cross-principal CreateAccessKey event · 2026-10-06 to 2026-10-13

⚠ HIGH CONFIDENCE INDICATOR
──────────────────────────────────────────────────────────────────
 Timestamp:    2026-10-10 22:47:31 UTC
 Caller:       arn:aws:iam::123456789012:user/dev-temp-user
 Target user:  admin-backup
 New key ID:   AKIAIOSFODNN7EXAMPLE
 Source IP:    198.51.100.47

 Subsequent key usage (3 calls found using AKIAIOSFODNN7EXAMPLE):
   22:58:44 UTC  s3:ListBuckets                        198.51.100.47
   23:01:12 UTC  s3:GetObject ×14                      198.51.100.47
                  → s3:::finance-reports/Q3_2026.xlsx
   23:04:55 UTC  secretsmanager:GetSecretValue         198.51.100.47
                  → prod/db/credentials

The backdoor key was used 11 minutes after creation to access financial
reports and production database credentials. This is an active incident.
Recommend immediate revocation of AKIAIOSFODNN7EXAMPLE and escalation.

Every result where userIdentity.userName differs from requestParameters.userName is a direct indicator of one identity creating credentials for another. If the newly created key ID subsequently appears in CloudTrail userIdentity.accessKeyId, the attacker actively used the backdoor key; escalate immediately.

Hunting for role assumption chains

Privilege escalation via role chaining occurs when an attacker assumes a role, then uses that role's credentials to assume a second, more privileged, role. CloudTrail records each AssumeRole call, including the caller identity, so you can reconstruct multi-hop chains.

Run this query to surface AssumeRole calls made from within an existing assumed-role session:

Using the Elasticsearch MCP, find all AssumeRole events in
logs-aws.cloudtrail-* in the past 24 hours where the caller is itself
an assumed-role session. Group results by origin session to reconstruct
role assumption chains in chronological order. For each chain:
1. Show the full hop sequence (origin identity → each assumed role ARN).
2. Note the elapsed time between first and last hop.
3. Flag any chain that terminates at a role containing "Admin",
   "PowerUser", "FullAccess", or "OrganizationAccountAccessRole".

Example Claude Code output:

✦ 3 role assumption chains from assumed-role sessions · past 24 hours

CHAIN 1: Expected
  TerraformProvisioner → DeployRole (12:01 UTC)
  Permissions: AWSCloudFormationFullAccess, EC2:Describe*
  → Low-privilege deployment role. No escalation.

CHAIN 2: Expected
  svc-monitoring → ReadOnlyAccessRole (14:00 UTC)
  Cross-account to 987654321098 (security-audit account)
  → Routine scheduled monitoring.

⚠ CHAIN 3: Unexpected privilege escalation
  Source IP for all hops: 198.51.100.47
  ────────────────────────────────────────────────────────
  14:22:03 UTC  dev-temp-user
                 → assumed DataAnalystRole
                    arn:aws:iam::123456789012:role/DataAnalystRole
                    Expected scope: Athena + Glue read access

  14:31:47 UTC  DataAnalystRole
                 → assumed OrganizationAccountAccessRole  ⚠
                    arn:aws:iam::123456789012:role/OrganizationAccountAccessRole
                    Grants: AdministratorAccess in management account
  ────────────────────────────────────────────────────────
  DataAnalystRole should NOT have sts:AssumeRole permission on
  OrganizationAccountAccessRole. Chain elapsed: 9 min 44 sec.
  Recommend reviewing DataAnalystRole's trust policy immediately.

Claude Code groups events by origin session and reconstructs each chain. A sequence starting from a low-privilege service role and ending at an OrganizationAccountAccessRole or similar cross-account admin role is a critical finding.

Tip: Extend the look-back window to 7 days and widen the scope to all AssumeRole calls (remove the AssumedRole filter) to map all role relationships in your environment. This gives you a baseline assumption graph that makes future chains immediately recognizable as anomalies.

Detecting UpdateLoginProfile abuse

A principal with iam:UpdateLoginProfile can reset the console password of any IAM user, including administrators, to hijack their account. This technique does not require the attacker to know the target's current password and leaves no failed authentication trail.

Run this query in your Claude Code session:

Using the Elasticsearch MCP, find all UpdateLoginProfile events in
logs-aws.cloudtrail-* in the past 7 days where the caller and target
user are different. For each result:
1. Show the caller, target user, and source IP.
2. Check whether the target user is a member of any admin-level group
   (look for subsequent GetGroup or ListAttachedGroupPolicies calls
   that mention AdministratorAccess or PowerUserAccess).
3. Check for a ConsoleLogin event from the target user within 30 minutes
   of the password reset, and note whether the login IP differs from the
   reset IP.

Example Claude Code output:

✦ 1 cross-principal UpdateLoginProfile event · 2026-10-06 to 2026-10-13

⚠ ACCOUNT TAKEOVER CONFIRMED
──────────────────────────────────────────────────────────────────
 Timestamp:    2026-10-10 23:19:08 UTC
 Caller:       arn:aws:iam::123456789012:user/dev-temp-user
 Target user:  john.doe
 Source IP:    198.51.100.47

 john.doe group membership: AdminGroup
   → Attached policy: AdministratorAccess ⚠

 ConsoleLogin check (±30 min window):
   2026-10-10 23:23:41 UTC  ConsoleLogin for john.doe  ✓ found
   Login IP:   203.0.113.12  (different from reset IP; attacker switched)
   MFA used:   false
   Elapsed:    4 min 33 sec after password reset

The attacker reset john.doe's password and signed in as that user 4 minutes
later from a different source IP. john.doe carries AdministratorAccess via
AdminGroup. Full console control is now in attacker hands.

A ConsoleLogin event from the target user shortly after their UpdateLoginProfile call confirms that the attacker successfully used the reset password to sign in. The source IP for the ConsoleLogin will often differ from the IP that made the UpdateLoginProfile call, since the attacker switches to the target account.

Investigating a suspected privilege escalation

When a hunting query surfaces a suspicious principal, use Claude Code to pivot from the initial indicator to a full activity timeline.

Building a principal timeline

Replace <PRINCIPAL_ARN> with the ARN of the suspicious identity:

Using the Elasticsearch MCP, build a complete activity timeline for
<PRINCIPAL_ARN> in logs-aws.cloudtrail-* over the past 48 hours.
Include all events where this ARN appears as the direct caller or as
the session issuer for an assumed-role session. Then:

1. Identify the first IAM or STS action and what it changed.
2. Map each relevant action to MITRE ATT&CK (T1098, T1548, T1550, T1530).
3. List all AWS services accessed after the first privilege escalation event.
4. Flag data exfiltration indicators: bulk S3 GetObject, Secrets Manager
   GetSecretValue, SSM GetParameter on SecureString, or KMS Decrypt on
   keys this principal did not previously use.
5. Summarize the full attack chain in one paragraph.

Example Claude Code output:

✦ 127 events · arn:aws:iam::123456789012:user/dev-temp-user
  2026-10-09 18:00 UTC to 2026-10-11 18:00 UTC

TIMELINE (privilege-relevant events)
──────────────────────────────────────────────────────────────────
2026-10-09 18:12  iam:ListPolicies                  (reconnaissance)
2026-10-09 18:13  iam:ListAttachedUserPolicies       (reconnaissance)
2026-10-09 18:14  iam:GetPolicy                     (reconnaissance)

2026-10-10 22:47  iam:CreateAccessKey               T1098.001  ⚠
                   → Key created for admin-backup (cross-user)

2026-10-10 22:58  [admin-backup key] s3:ListBuckets
2026-10-10 23:01  [admin-backup key] s3:GetObject ×14         T1530  ⚠
                   → s3:::finance-reports/Q3_2026.xlsx
2026-10-10 23:04  [admin-backup key] secretsmanager:GetSecretValue  T1552  ⚠
                   → prod/db/credentials

2026-10-11 03:14  iam:CreatePolicyVersion           T1098      ⚠
                   → DevAccess v3: Action:"*", Resource:"*"
2026-10-11 03:14  iam:AttachUserPolicy              T1098      ⚠
                   → AdministratorAccess attached to self
2026-10-11 03:19  iam:UpdateLoginProfile            T1098.001  ⚠
                   → Password reset for john.doe (AdminGroup)

POST-ESCALATION SERVICES: s3 · secretsmanager · iam · sts · ec2 · ssm

MITRE ATT&CK
  T1078.004  Valid Cloud Accounts (initial access)
  T1098.001  Account Manipulation: Additional Cloud Credentials
  T1098      Account Manipulation: Policy modification
  T1530      Data from Cloud Storage
  T1552      Unsecured Credentials (Secrets Manager)

ATTACK SUMMARY
  dev-temp-user performed IAM reconnaissance on Oct 9, then on Oct 10 at
  22:47 created a backdoor access key for admin-backup and used it to
  exfiltrate financial data and production credentials. On Oct 11 at 03:14
  it self-escalated to AdministratorAccess by modifying its own policy,
  then took over john.doe's console account (AdminGroup) 5 minutes later.
  Total time from first IAM write to full admin control: ~4.5 hours.

Claude Code returns a narrative timeline with TTP labels and an impact summary, ready to paste into an incident report.

Correlating with failed attempts

Attackers often probe permissions before finding a working path. The failed attempts leave an AccessDenied trail immediately before the successful escalation. Run:

Using the Elasticsearch MCP, find all AccessDenied and
Client.UnauthorizedOperation errors for <PRINCIPAL_ARN> on IAM and
STS APIs in logs-aws.cloudtrail-* in the 6 hours before
<FIRST_SUSPICIOUS_TIMESTAMP>.

Summarize:
1. Which IAM and STS actions were denied, and in what order.
2. What the denials suggest about the attacker's initial privilege
   boundaries (for example, blocked by SCPs, resource-based conditions,
   or missing identity-based permissions).
3. What escalation path the attacker ultimately found to bypass those
   denials; connect the denied attempts to the successful actions that
   followed.

Example Claude Code output:

✦ 12 denied IAM/STS calls · 2026-10-10 21:14 to 2026-10-11 03:14 UTC

DENIED ACTIONS (chronological)
──────────────────────────────────────────────────────────────────
21:14 UTC  iam:AttachUserPolicy        AccessDenied  (SCP: deny IAM write)
21:14 UTC  iam:PutUserPolicy           AccessDenied  (SCP: deny IAM write)
21:15 UTC  iam:CreatePolicyVersion     AccessDenied  (target: AWS-managed policy)
21:15 UTC  iam:SetDefaultPolicyVersion AccessDenied  (target: AWS-managed policy)
22:31 UTC  iam:CreateUser              AccessDenied  (SCP: deny IAM write)
22:44 UTC  iam:CreateAccessKey         AccessDenied  (target: admin-user)
22:46 UTC  iam:CreateAccessKey         AccessDenied  (target: devops-lead)
22:47 UTC  iam:CreateAccessKey         SUCCEEDED     (target: admin-backup) ✓

BOUNDARY ANALYSIS
  The SCP blocks direct IAM write actions (AttachUserPolicy, PutUserPolicy,
  CreateUser) and attempts to modify AWS-managed policies are rejected by the
  API. CreateAccessKey is not in the SCP deny list; only individual user-level
  conditions governed it.

ESCALATION PATH RECONSTRUCTED
  1. Direct policy writes blocked by SCP (21:14) → pivoted to alternative paths.
  2. AWS-managed policy modification rejected (21:15) → shifted to customer-managed.
  3. New user creation blocked by SCP (22:31) → tried credential theft instead.
  4. CreateAccessKey probed on admin-user and devops-lead (22:44–22:46); both
     had IAM conditions restricting key creation to specific IPs or roles.
  5. admin-backup lacked those conditions → key creation succeeded (22:47).
  6. Returned at 03:14 to exploit CreatePolicyVersion on the customer-managed
     DevAccess policy, which the SCP did not cover.

Recommend: Add an explicit SCP deny on iam:CreatePolicyVersion and
iam:SetDefaultPolicyVersion for all non-admin principals.

A dense cluster of AccessDenied errors on IAM write actions followed by a successful escalation is strong evidence of intentional permission probing rather than a misconfiguration or accidental call.

Running autonomous hunts with /goal

For IAM privilege escalation hunting, interactive prompts are better for investigation and triage where you need judgment calls as findings emerge. The pattern-scanning phase (running all four detection queries and writing results to files) is mechanical and exactly what /goal handles.

When to use /goal vs interactive prompts

Scenario

Better choice

Scan all four escalation patterns and log to files

/goal

Pivot on a specific principal after a hit

Interactive prompts

Nightly sweep of the past 24 hours

/goal + scheduled task

Decide whether a finding is a false positive

Interactive prompts

Writing a /goal for IAM hunting

A well-formed security goal has three parts: a role, a task with explicit constraints, and a verifiable success condition. Claude Code reads the goal, takes an action, checks whether the success condition is met, and loops until it is.

/goal Role: Threat hunter checking for IAM privilege escalation in AWS.

Task: Using the Elasticsearch MCP, run these four queries against
logs-aws.cloudtrail-* for the past 7 days:

1. Policy manipulation (CreatePolicyVersion, SetDefaultPolicyVersion,
   AttachUserPolicy, AttachGroupPolicy, AttachRolePolicy, PutUserPolicy,
   PutGroupPolicy, PutRolePolicy). Flag callers not in expected list:
   TerraformProvisioner, ci-pipeline.

2. Cross-user CreateAccessKey events (caller differs from target user).

3. AssumeRole chains where the caller is an AssumedRole session and the
   destination role ARN contains "Admin", "PowerUser", or "FullAccess".

4. Cross-user UpdateLoginProfile followed by a ConsoleLogin from the
   target user within 30 minutes.

For each query:
- Write results to hunt/findings/<query_name>.md
- Include caller ARN, target, source IP, and timestamp per finding
- Write "No findings." if the query returns zero results

Constraints:
- Read-only queries; do not modify any Elasticsearch index
- Write only to hunt/findings/

Success condition: hunt/findings/ contains four .md files. Each contains
structured results or "No findings." Any confirmed escalation pattern
carries a recommended next step.

Claude Code works through each query, writes the four files, and stops when all success conditions are met. Open the files and triage any findings manually.

Setting standing rules with CLAUDE.md

For recurring hunts, put a CLAUDE.md in your project directory. Claude Code reads it before every session in that directory, so you do not need to repeat constraints in each /goal invocation:

# IAM Threat Hunt Rules

## Target
Index pattern: logs-aws.cloudtrail-*
Expected IAM admins: TerraformProvisioner, ci-pipeline

## Constraints
- Read-only Elasticsearch queries
- Write findings to hunt/findings/ only
- Do not take direct action on any AWS resource

## Stop conditions
- Stop and flag if any query returns more than 50 results
  (likely a noisy rule that needs human review before continuing)
- Stop if Elasticsearch returns connection errors on three consecutive queries

For more /goal patterns, including multi-phase vulnerability discovery and penetration testing loops, see the SecEngAI guide on autonomous security engineering workflows.

What's next

You can extend this threat hunting workflow in several directions:

  • Expand coverage to compute-based escalation paths. Add hunting queries for iam:PassRole combined with ec2:RunInstances or lambda:CreateFunction.

  • Add cross-account coverage. If your AWS organization uses AWS Organizations, add queries for AssumeRole calls targeting roles in other accounts (the destination roleArn contains a different account ID than the caller's account).

Related resources: