{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "keep-as-periodic-hunt",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "AWS Console access is the primary control plane for cloud resources; a successful takeover can lead to full organizational compromise. Triaging this chain centrally ensures uniform response for decentralized teams."
      },
      "name": "AWS Cloud Identity Takeover Chain",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1110.001",
        "attack.t1098",
        "attack.t1078.004"
      ],
      "related": [
        {
          "hunt": "aws-suspicious-iam-policy-modification",
          "reason": "This hunt focuses on the takeover chain behavior, while the alternative would focus directly on the IAM management events if they were ingested.",
          "relation": "out-of-scope-alternative"
        }
      ],
      "targets": {
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "network": {
          "name": "Network telemetry",
          "category": "network",
          "telemetry": [
            "network"
          ]
        },
        "identity": {
          "name": "Identity / sign-in telemetry",
          "category": "identity",
          "telemetry": [
            "identity"
          ]
        }
      },
      "analysis": "A single rule can detect a brute force; this hunt correlates that brute force with password resets and successful logons to different accounts across 100 projects, then follows the session to identify post-compromise network activity\u2014a cross-surface correlation no single rule can perform.",
      "coverage": [
        {
          "stage": "failed-aws-console-logins",
          "steps": [
            "console-brute-force",
            "evaluate-early-auth"
          ],
          "status": "covered"
        },
        {
          "stage": "account-password-reset",
          "steps": [
            "suspected-password-resets",
            "evaluate-early-auth"
          ],
          "status": "covered"
        },
        {
          "stage": "cloud-privilege-escalation",
          "reason": "Requires management-plane events like PutUserPolicy which are not visible in the provided normalized surfaces.",
          "status": "not_visible",
          "blind_spot": "no-iam-management-visibility"
        },
        {
          "stage": "access-key-creation",
          "reason": "Requires management-plane visibility into access key lifecycle events.",
          "status": "not_visible",
          "blind_spot": "no-long-term-credential-visibility"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "Failed AWS console logins",
            "slug": "failed-aws-console-logins",
            "tactic": "initial-access",
            "techniques": [
              "T1110.001"
            ],
            "observables": [
              "user.name",
              "event.category: authentication",
              "event.action: failure",
              "source.ip",
              "host.name"
            ]
          },
          {
            "name": "Cloud account password reset",
            "slug": "account-password-reset",
            "tactic": "persistence",
            "techniques": [
              "T1098"
            ],
            "observables": [
              "event.action: PasswordReset",
              "user.name"
            ]
          },
          {
            "name": "AWS privilege escalation",
            "slug": "cloud-privilege-escalation",
            "tactic": "privilege-escalation",
            "techniques": [
              "T1078.004"
            ],
            "observables": [
              "event.action: PutUserPolicy",
              "event.action: AttachUserPolicy"
            ]
          },
          {
            "name": "AWS access key creation",
            "slug": "access-key-creation",
            "tactic": "persistence",
            "techniques": [
              "T1098"
            ],
            "observables": [
              "event.action: CreateAccessKey",
              "user.name"
            ]
          }
        ],
        "summary": "The campaign involves an AWS account takeover sequence beginning with credential-stuffing or brute-force attempts on the AWS Management Console. Once an account is compromised, the actor performs a password reset, escalates privileges within the cloud environment, and generates long-term persistence through the creation of new AWS access keys."
      },
      "severity": "high",
      "rationale": "Scope to the central origin project. The initial queries cover all linked projects by default; use the scope_hosts parameter to isolate specific AWS accounts identified as targets. Known admin IPs are excluded to focus on user-driven activity.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An adversary has gained initial access to a cloud account by brute-forcing the console and performing a password reset, then used that access to establish a presence across multiple projects in the organization.",
      "parameters": {
        "admin_ips": {
          "from": {
            "ref": "https://www.elastic.co/security-labs/blog/centralized-alert-triage-cross-project-search",
            "kind": "article",
            "observed": "2026-09-18"
          },
          "type": "list[ip]",
          "default": [],
          "description": "Known administrative or automation IPs to exclude from scoping counts."
        },
        "scope_hosts": {
          "type": "list[host]",
          "default": [],
          "description": "The AWS Account IDs (dst_endpoint_name) to focus on; leave empty to hunt across all 100 linked projects."
        },
        "lookback_days": {
          "type": "number",
          "default": "14",
          "description": "Days of history to examine for brute force and follow-on activity."
        },
        "suspicious_ips": {
          "from": {
            "ref": "https://www.elastic.co/security-labs/blog/centralized-alert-triage-cross-project-search",
            "kind": "article",
            "observed": "2026-09-18"
          },
          "type": "list[ip]",
          "default": [],
          "description": "IPs identified in the first agent read to pivot on in the follow-on stage."
        },
        "reset_indicators": {
          "type": "list[string]",
          "default": [
            "PasswordReset",
            "ChangePassword",
            "UpdateAccount",
            "ResetPassword"
          ],
          "description": "Specific activity_name values in authentication logs that indicate account modification."
        },
        "failed_logon_threshold": {
          "type": "number",
          "default": "10",
          "description": "Number of failed attempts from a single IP before it is considered a brute-force lead."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://www.elastic.co/security-labs/blog/centralized-alert-triage-cross-project-search",
          "gates": [
            "dry-run",
            "lint"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://www.elastic.co/security-labs/blog/centralized-alert-triage-cross-project-search",
          "name": "One SOC, 100 projects: running centralized alert triage on Elastic Security Serverless"
        }
      ],
      "blind_spots": [
        {
          "id": "no-iam-management-visibility",
          "risk": "The normalized auth surface only shows the logon result; internal policy changes are invisible and require manual auditing of raw logs.",
          "stage": "cloud-privilege-escalation",
          "question": "Did the attacker elevate their own permissions after logging in?",
          "requires": "Raw CloudTrail Management Events (PutUserPolicy, AttachUserPolicy)",
          "remediation": "Ingest CloudTrail Management Events into a dedicated security surface."
        },
        {
          "id": "no-long-term-credential-visibility",
          "risk": "An attacker may create a key and leave the console session, maintaining access even after the console session is revoked.",
          "stage": "access-key-creation",
          "question": "Did the attacker create access keys for permanent backdoor access?",
          "requires": "CloudTrail CreateAccessKey event"
        }
      ]
    },
    "name": "AWS Cloud Identity Takeover Chain",
    "description": "This hunt triages AWS identity takeover attempts across a centralized SOC managing 100 projects. It starts by identifying brute-force patterns and password-reset markers on the authentication surface, then pivots to successful logons and subsequent network traffic to confirm the transition from attempt to takeover. By acting centrally on a single origin but validating across linked projects, analysts can contain compromised cloud identities across the entire organization."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "coverage": [
          {
            "stage": "failed-aws-console-logins",
            "steps": [
              "console-brute-force",
              "evaluate-early-auth"
            ],
            "status": "covered"
          },
          {
            "stage": "account-password-reset",
            "steps": [
              "suspected-password-resets",
              "evaluate-early-auth"
            ],
            "status": "covered"
          },
          {
            "stage": "cloud-privilege-escalation",
            "reason": "Requires management-plane events like PutUserPolicy which are not visible in the provided normalized surfaces.",
            "status": "not_visible",
            "blind_spot": "no-iam-management-visibility"
          },
          {
            "stage": "access-key-creation",
            "reason": "Requires management-plane visibility into access key lifecycle events.",
            "status": "not_visible",
            "blind_spot": "no-long-term-credential-visibility"
          }
        ],
        "rationale": "An adversary has gained initial access to a cloud account by brute-forcing the console and performing a password reset, then used that access to establish a presence across multiple projects in the organization.",
        "blind_spots": [
          {
            "id": "no-iam-management-visibility",
            "risk": "The normalized auth surface only shows the logon result; internal policy changes are invisible and require manual auditing of raw logs.",
            "stage": "cloud-privilege-escalation",
            "question": "Did the attacker elevate their own permissions after logging in?",
            "requires": "Raw CloudTrail Management Events (PutUserPolicy, AttachUserPolicy)",
            "remediation": "Ingest CloudTrail Management Events into a dedicated security surface."
          },
          {
            "id": "no-long-term-credential-visibility",
            "risk": "An attacker may create a key and leave the console session, maintaining access even after the console session is revoked.",
            "stage": "access-key-creation",
            "question": "Did the attacker create access keys for permanent backdoor access?",
            "requires": "CloudTrail CreateAccessKey event"
          }
        ],
        "scoping_notes": "Scope to the central origin project. The initial queries cover all linked projects by default; use the scope_hosts parameter to isolate specific AWS accounts identified as targets. Known admin IPs are excluded to focus on user-driven activity.",
        "beyond_detection": "A single rule can detect a brute force; this hunt correlates that brute force with password resets and successful logons to different accounts across 100 projects, then follows the session to identify post-compromise network activity\u2014a cross-surface correlation no single rule can perform."
      }
    },
    {
      "id": "inventory-aws-accounts",
      "type": "query",
      "label": "Inventory AWS Accounts",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "identity",
        "content": "SELECT DISTINCT dst_endpoint_name AS aws_account, COUNT(*) AS event_count FROM hb_auth_signin WHERE provider = 'aws' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{admin_ips}}' = '' OR NOT (instr(',' || '{{admin_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0)) GROUP BY dst_endpoint_name",
        "surface": "hb_auth_signin",
        "description": "Find every AWS account recording activity to scope the hunt across the organization projects while excluding administrative noise.",
        "expected_signal": "A list of AWS account names and their event volume excluding known admin IPs. Silence means no AWS authentication events were recorded."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Inventory AWS Accounts",
        "reads": [
          "dst_endpoint_name",
          "provider",
          "src_endpoint_ip",
          "time"
        ],
        "source": "hb_auth_signin",
        "target": "identity",
        "content": "SELECT DISTINCT dst_endpoint_name AS aws_account, COUNT(*) AS event_count FROM hb_auth_signin WHERE provider = 'aws' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{admin_ips}}' = '' OR NOT (instr(',' || '{{admin_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0)) GROUP BY dst_endpoint_name",
        "silence": "not_evidence_of_absence",
        "expected": "A list of AWS account names and their event volume excluding known admin IPs. Silence means no AWS authentication events were recorded.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "console-brute-force",
      "type": "query",
      "label": "AWS Console Brute Force",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "identity",
        "content": "SELECT src_endpoint_ip, COUNT(DISTINCT actor_user_name) AS targeted_users, COUNT(*) AS failures, MIN(time) AS first_failure, MAX(time) AS last_failure FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 5 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY src_endpoint_ip HAVING failures >= {{failed_logon_threshold}} ORDER BY failures DESC",
        "surface": "hb_auth_signin",
        "description": "Identify source IPs targeting one or more users with high failure volumes.",
        "expected_signal": "Source IPs with failure counts exceeding the threshold. High failure counts across many users indicate a spray; high counts for one user indicate brute force."
      },
      "parents": [
        {
          "id": "inventory-aws-accounts"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "AWS Console Brute Force",
        "reads": [
          "src_endpoint_ip",
          "actor_user_name",
          "activity_id",
          "time"
        ],
        "source": "hb_auth_signin",
        "target": "identity",
        "content": "SELECT src_endpoint_ip, COUNT(DISTINCT actor_user_name) AS targeted_users, COUNT(*) AS failures, MIN(time) AS first_failure, MAX(time) AS last_failure FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 5 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY src_endpoint_ip HAVING failures >= {{failed_logon_threshold}} ORDER BY failures DESC",
        "silence": "evidence_of_absence",
        "baseline": {
          "window": "{{lookback_days}}d",
          "compare": "first_seen"
        },
        "expected": "Source IPs with failure counts exceeding the threshold. High failure counts across many users indicate a spray; high counts for one user indicate brute force.",
        "verified": "dry-run",
        "prevalence": {
          "by": "actor_user_name",
          "key": [
            "src_endpoint_ip"
          ],
          "rare_below": 5
        },
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "suspected-password-resets",
      "type": "query",
      "label": "Suspected Password Modification",
      "config": {
        "dsl": "sqlite",
        "role": "baseline",
        "source": "identity",
        "content": "SELECT src_endpoint_ip, actor_user_name, dst_endpoint_name, activity_name, time FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 99 AND instr(',' || '{{reset_indicators}}' || ',', ',' || activity_name || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_auth_signin",
        "description": "Identify management-related authentication activity from the same time window.",
        "expected_signal": "Authentication metadata events matching the reset keywords. These indicate the persistence stage of the takeover."
      },
      "parents": [
        {
          "id": "inventory-aws-accounts"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "baseline",
        "label": "Suspected Password Modification",
        "reads": [
          "src_endpoint_ip",
          "actor_user_name",
          "activity_name",
          "activity_id",
          "time"
        ],
        "source": "hb_auth_signin",
        "target": "identity",
        "content": "SELECT src_endpoint_ip, actor_user_name, dst_endpoint_name, activity_name, time FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 99 AND instr(',' || '{{reset_indicators}}' || ',', ',' || activity_name || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Authentication metadata events matching the reset keywords. These indicate the persistence stage of the takeover.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "evaluate-early-auth",
      "type": "analytic",
      "label": "Evaluate Early Auth Anomalies",
      "config": {
        "cite": "required",
        "tools": [
          "identity",
          "network"
        ],
        "context": [
          "console-brute-force",
          "suspected-password-resets"
        ],
        "objective": "Identify the source IPs that performed brute-force attempts and then reached a password reset event. Prioritize IPs that appear in both console-brute-force and suspected-password-resets results as they represent the highest risk.",
        "description": "Determine if the brute-force attempts and modification events originate from the same sources.",
        "max_iterations": 4,
        "expected_signal": "A list of high-confidence suspicious source IPs that pivot into the follow-on stage.",
        "success_criteria": "A verdict per IP citing specific rows that show the progression from failure to modification."
      },
      "parents": [
        {
          "id": "console-brute-force",
          "kind": "merge"
        },
        {
          "id": "suspected-password-resets",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "successful-logons-pivot",
      "type": "query",
      "label": "Successful Logons from Suspicious IPs",
      "config": {
        "dsl": "sqlite",
        "role": "triage",
        "source": "identity",
        "content": "SELECT src_endpoint_ip, actor_user_name, COUNT(DISTINCT dst_endpoint_name) AS account_count, GROUP_CONCAT(DISTINCT dst_endpoint_name) AS accounts, MIN(time) AS first_success, MAX(time) AS last_success FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 1 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || dst_endpoint_name || ',') > 0) AND ('{{suspicious_ips}}' = '' OR instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0) GROUP BY src_endpoint_ip, actor_user_name",
        "surface": "hb_auth_signin",
        "description": "Confirm if the identified IPs successfully logged in to any account and check if they reached multiple distinct accounts.",
        "expected_signal": "Rows naming a successful logon from a source IP previously seen brute-forcing. Multiple accounts indicate organization-wide compromise."
      },
      "parents": [
        {
          "id": "evaluate-early-auth"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "triage",
        "label": "Successful Logons from Suspicious IPs",
        "reads": [
          "src_endpoint_ip",
          "actor_user_name",
          "dst_endpoint_name",
          "activity_id",
          "time"
        ],
        "source": "hb_auth_signin",
        "target": "identity",
        "content": "SELECT src_endpoint_ip, actor_user_name, COUNT(DISTINCT dst_endpoint_name) AS account_count, GROUP_CONCAT(DISTINCT dst_endpoint_name) AS accounts, MIN(time) AS first_success, MAX(time) AS last_success FROM hb_auth_signin WHERE provider = 'aws' AND activity_id = 1 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || dst_endpoint_name || ',') > 0) AND ('{{suspicious_ips}}' = '' OR instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0) GROUP BY src_endpoint_ip, actor_user_name",
        "silence": "evidence_of_absence",
        "expected": "Rows naming a successful logon from a source IP previously seen brute-forcing. Multiple accounts indicate organization-wide compromise.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "suspicious-network-outbound",
      "type": "query",
      "label": "Outbound Traffic from Suspicious IPs",
      "config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "source": "network",
        "content": "SELECT src_endpoint_ip, dst_endpoint_ip, dst_endpoint_port, protocol, COUNT(*) AS connections FROM hb_network_connection WHERE time >= datetime('now', '-{{lookback_days}} days') AND ('{{suspicious_ips}}' = '' OR instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0) GROUP BY src_endpoint_ip, dst_endpoint_ip, dst_endpoint_port, protocol",
        "surface": "hb_network_connection",
        "description": "Determine if the attacker IPs are interacting with other infrastructure after the logon.",
        "expected_signal": "Connections from the brute-force IPs to unexpected destinations. This establishes the post-logon behavior."
      },
      "parents": [
        {
          "id": "evaluate-early-auth"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "label": "Outbound Traffic from Suspicious IPs",
        "reads": [
          "src_endpoint_ip",
          "dst_endpoint_ip",
          "dst_endpoint_port",
          "protocol",
          "time"
        ],
        "source": "hb_network_connection",
        "target": "network",
        "content": "SELECT src_endpoint_ip, dst_endpoint_ip, dst_endpoint_port, protocol, COUNT(*) AS connections FROM hb_network_connection WHERE time >= datetime('now', '-{{lookback_days}} days') AND ('{{suspicious_ips}}' = '' OR instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0) GROUP BY src_endpoint_ip, dst_endpoint_ip, dst_endpoint_port, protocol",
        "silence": "not_evidence_of_absence",
        "expected": "Connections from the brute-force IPs to unexpected destinations. This establishes the post-logon behavior.",
        "verified": "dry-run",
        "verified_at": "2026-09-20"
      }
    },
    {
      "id": "confirm-account-takeover",
      "type": "analytic",
      "label": "Confirm Account Takeover",
      "config": {
        "cite": "required",
        "tools": [
          "identity",
          "network"
        ],
        "context": [
          "evaluate-early-auth",
          "successful-logons-pivot",
          "suspicious-network-outbound"
        ],
        "objective": "Determine if the evidence supports a continuous chain from brute-force attempt to successful account takeover and operational usage. Factor in the early auth findings and follow-on pivots.",
        "description": "Synthesize the auth success and network markers to confirm active exploitation of a cloud identity.",
        "max_iterations": 5,
        "expected_signal": "A final malicious/suspicious/benign verdict per identity.",
        "success_criteria": "A per-identity verdict citing the transition from failures to success and any subsequent network traffic."
      },
      "parents": [
        {
          "id": "successful-logons-pivot",
          "kind": "merge"
        },
        {
          "id": "suspicious-network-outbound",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-takeover",
      "type": "checkpoint",
      "label": "Route on Takeover Verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the confirm-account-takeover verdict is malicious for at least one cloud identity",
        "condition": "the confirm-account-takeover verdict is malicious for at least one cloud identity",
        "blind_spot": "no-iam-management-visibility",
        "confidence": "high",
        "description": "Direct response actions based on the confirmed takeover status.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "confirm-account-takeover"
        }
      ]
    },
    {
      "id": "isolate-identity",
      "type": "action",
      "label": "Isolate Identity",
      "config": {
        "target": "identity",
        "description": "Revoke sessions and deactivate credentials to stop the takeover.",
        "instructions": "Revoke all active IAM sessions and deactivate all existing access keys for the actor_user_name identified in the malicious verdict.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-takeover",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "manual-iam-audit",
      "type": "task",
      "label": "Manual IAM Management Audit",
      "config": {
        "assignee": "analyst",
        "description": "Analyze management-plane events that are not visible in normalized surfaces to find privilege escalation.",
        "instructions": "Review the raw CloudTrail logs for the compromised user to find evidence of PutUserPolicy, AttachUserPolicy, or CreateAccessKey events that occurred after the suspicious logon."
      },
      "parents": [
        {
          "id": "route-on-takeover",
          "branch": "default"
        },
        {
          "id": "route-on-takeover",
          "branch": "on_unavailable"
        },
        {
          "id": "route-on-takeover",
          "branch": "on_refutes"
        },
        {
          "id": "isolate-identity"
        }
      ]
    },
    {
      "id": "remediation-summary",
      "type": "task",
      "label": "Remediation and Close-Out",
      "config": {
        "assignee": "analyst",
        "description": "Document findings across the 100 projects and recommend architectural hardening.",
        "instructions": "Record the full takeover chain; recommend enabling MFA and IP-based conditional access for all console users across all organizational projects."
      },
      "parents": [
        {
          "id": "manual-iam-audit"
        }
      ]
    }
  ]
}