EXPLORE
← Back to Explore
elastichighTTP

AWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure

Detects AWS access keys that are used from both GitHub Actions CI/CD infrastructure and non-CI/CD infrastructure. This pattern indicates potential credential theft where an attacker who has stolen AWS credentials configured as GitHub Actions secrets and is using them from their own infrastructure.

MITRE ATT&CK

initial-accesslateral-movement

Detection Query

from logs-aws.cloudtrail-* metadata _id, _version, _index

| WHERE event.dataset == "aws.cloudtrail"
  AND aws.cloudtrail.user_identity.access_key_id IS NOT NULL
  AND @timestamp >= NOW() - 7 days
  AND source.as.organization.name IS NOT NULL

// AWS API key used from github actions 
| EVAL is_aws_github = user_agent.original LIKE "*aws-credentials-for-github-actions"

// non CI/CD related ASN 
| EVAL is_not_cicd_infra = not source.as.organization.name IN ("Microsoft Corporation", "Amazon.com, Inc.", "Amazon Technologies Inc.", "Google LLC")

| STATS Esql.is_github_aws_key = MAX(CASE(is_aws_github, 1, 0)),
        Esql.has_suspicious_asn = MAX(CASE(is_not_cicd_infra, 1, 0)),
        Esql.last_seen_suspicious_asn = MAX(CASE(is_not_cicd_infra, @timestamp, NULL)),
        Esql.source_ip_values = VALUES(source.address), 
        Esql.source_asn_values = VALUES(source.as.organization.name) BY aws.cloudtrail.user_identity.access_key_id, user.name, cloud.account.id

// AWS API key tied to a GH action used from unusual ASN (non CI/CD infra)
| WHERE Esql.is_github_aws_key == 1 AND  Esql.has_suspicious_asn == 1 

        // avoid alert duplicates within 1h interval
        AND Esql.last_seen_suspicious_asn >= NOW() - 1 hour

| KEEP user.name, aws.cloudtrail.user_identity.access_key_id, Esql.*

Author

Elastic

Created

2026/04/21

Data Sources

AWSAmazon Web ServicesAWS CloudTrailAWS IAM

Tags

Domain: CloudData Source: AWSData Source: Amazon Web ServicesData Source: AWS CloudTrailData Source: AWS IAMUse Case: Threat DetectionTactic: Initial AccessTactic: Lateral MovementResources: Investigation Guide
Raw Content
[metadata]
creation_date = "2026/04/21"
integration = ["aws"]
maturity = "production"
updated_date = "2026/04/21"

[rule]
author = ["Elastic"]
description = """
Detects AWS access keys that are used from both GitHub Actions CI/CD infrastructure and non-CI/CD infrastructure.
This pattern indicates potential credential theft where an attacker who has stolen AWS credentials configured as GitHub
Actions secrets and is using them from their own infrastructure.
"""
false_positives = [
    """
    AWS credentials legitimately shared between GitHub Actions and another Microsoft/Azure service
    may trigger this rule. Verify whether the non-CI/CD source IP is expected for the workload.
    """,
    """
    GitHub Actions self-hosted runners running on non-Microsoft/Amazon/Google infrastructure will
    appear as suspicious. Add the ASN of your self-hosted runner infrastructure to the is_cicd_infra
    allowlist.
    """,
]
from = "now-7d"
interval = "1h"
language = "esql"
license = "Elastic License v2"
name = "AWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure"
note = """## Triage and analysis

### Investigating AWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure

This rule detects when an AWS access key appears in CloudTrail from both GitHub Actions runners
(identified by Microsoft ASN or the `github-actions` user agent string) and from infrastructure
outside the expected CI/CD provider ASNs. This is a strong indicator that AWS credentials stored
as GitHub repository or organization secrets have been exfiltrated and are being used by an
attacker from their own infrastructure.

### Possible investigation steps

- Identify which GitHub repository owns the credential by cross-referencing the access key ID with
  your GitHub Actions workflow configurations and AWS IAM user/role assignments.
- Review the suspicious source IPs and ASNs — residential ISPs, VPN providers, or budget hosting
  providers are high-confidence indicators of credential theft.
- Check the actions performed from the suspicious source — `sts:GetCallerIdentity` followed by
  enumeration calls (`ListBuckets`, `DescribeInstances`, `ListUsers`) is a common attacker recon
  pattern after credential theft.
- Review the user agent strings from the suspicious source — `aws-cli` or `boto3` from a non-runner
  IP confirms manual/scripted usage outside CI/CD.
- Check GitHub audit logs for recent workflow changes, new collaborators, or secret access events
  that could indicate how the credential was stolen.
- Determine if the credential is a long-lived IAM user key or a temporary STS session — temporary
  credentials from `AssumeRoleWithWebIdentity` (OIDC) are less likely to be exfiltrated but still
  possible.

### Response and remediation

- Immediately rotate the compromised AWS access key in IAM and update the GitHub repository/org secret.
- Review and revoke any resources created or modified by the suspicious source IP using CloudTrail
  event history filtered by the access key ID.
- Audit the GitHub repository for signs of compromise — check for unauthorized workflow modifications,
  new secrets, or suspicious pull requests that could have exfiltrated the credential.
- Implement OIDC-based authentication (`aws-actions/configure-aws-credentials` with `role-to-assume`)
  instead of long-lived access keys to eliminate the credential theft vector entirely.
- If using OIDC, add IP condition policies to the IAM role trust policy to restrict
  `AssumeRoleWithWebIdentity` to known GitHub runner IP ranges.
- Enable GitHub's secret scanning and push protection to detect accidental credential exposure in
  code or logs.
"""
references = [
    "https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services",
    "https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html",
]
risk_score = 73
rule_id = "b8c7d6e5-f4a3-4b2c-9d8e-7f6a5b4c3d2e"
severity = "high"
tags = [
    "Domain: Cloud",
    "Data Source: AWS",
    "Data Source: Amazon Web Services",
    "Data Source: AWS CloudTrail",
    "Data Source: AWS IAM",
    "Use Case: Threat Detection",
    "Tactic: Initial Access",
    "Tactic: Lateral Movement",
    "Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
from logs-aws.cloudtrail-* metadata _id, _version, _index

| WHERE event.dataset == "aws.cloudtrail"
  AND aws.cloudtrail.user_identity.access_key_id IS NOT NULL
  AND @timestamp >= NOW() - 7 days
  AND source.as.organization.name IS NOT NULL

// AWS API key used from github actions 
| EVAL is_aws_github = user_agent.original LIKE "*aws-credentials-for-github-actions"

// non CI/CD related ASN 
| EVAL is_not_cicd_infra = not source.as.organization.name IN ("Microsoft Corporation", "Amazon.com, Inc.", "Amazon Technologies Inc.", "Google LLC")

| STATS Esql.is_github_aws_key = MAX(CASE(is_aws_github, 1, 0)),
        Esql.has_suspicious_asn = MAX(CASE(is_not_cicd_infra, 1, 0)),
        Esql.last_seen_suspicious_asn = MAX(CASE(is_not_cicd_infra, @timestamp, NULL)),
        Esql.source_ip_values = VALUES(source.address), 
        Esql.source_asn_values = VALUES(source.as.organization.name) BY aws.cloudtrail.user_identity.access_key_id, user.name, cloud.account.id

// AWS API key tied to a GH action used from unusual ASN (non CI/CD infra)
| WHERE Esql.is_github_aws_key == 1 AND  Esql.has_suspicious_asn == 1 

        // avoid alert duplicates within 1h interval
        AND Esql.last_seen_suspicious_asn >= NOW() - 1 hour

| KEEP user.name, aws.cloudtrail.user_identity.access_key_id, Esql.*
'''

[rule.investigation_fields]
field_names = [
    "aws.cloudtrail.user_identity.access_key_id",
    "user.name"
]

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1078"
name = "Valid Accounts"
reference = "https://attack.mitre.org/techniques/T1078/"

[[rule.threat.technique.subtechnique]]
id = "T1078.004"
name = "Cloud Accounts"
reference = "https://attack.mitre.org/techniques/T1078/004/"

[rule.threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"


[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1550"
name = "Use Alternate Authentication Material"
reference = "https://attack.mitre.org/techniques/T1550/"

[[rule.threat.technique.subtechnique]]
id = "T1550.001"
name = "Application Access Token"
reference = "https://attack.mitre.org/techniques/T1550/001/"

[rule.threat.tactic]
id = "TA0008"
name = "Lateral Movement"
reference = "https://attack.mitre.org/tactics/TA0008/"