AWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure
editAWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure
editDetects 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.
Rule type: esql
Rule indices: None
Severity: high
Risk score: 73
Runs every: 1h
Searches indices from: now-7d (Date Math format, see also Additional look-back time)
Maximum alerts per execution: 100
References:
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
Version: 1
Rule authors:
- Elastic
Rule license: Elastic License v2
Investigation guide
editTriage 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:GetCallerIdentityfollowed 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-cliorboto3from 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-credentialswithrole-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
AssumeRoleWithWebIdentityto known GitHub runner IP ranges. - Enable GitHub’s secret scanning and push protection to detect accidental credential exposure in code or logs.
Rule query
editfrom 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.*
Framework: MITRE ATT&CKTM
-
Tactic:
- Name: Initial Access
- ID: TA0001
- Reference URL: https://attack.mitre.org/tactics/TA0001/
-
Technique:
- Name: Valid Accounts
- ID: T1078
- Reference URL: https://attack.mitre.org/techniques/T1078/
-
Sub-technique:
- Name: Cloud Accounts
- ID: T1078.004
- Reference URL: https://attack.mitre.org/techniques/T1078/004/
-
Tactic:
- Name: Lateral Movement
- ID: TA0008
- Reference URL: https://attack.mitre.org/tactics/TA0008/
-
Technique:
- Name: Use Alternate Authentication Material
- ID: T1550
- Reference URL: https://attack.mitre.org/techniques/T1550/
-
Sub-technique:
- Name: Application Access Token
- ID: T1550.001
- Reference URL: https://attack.mitre.org/techniques/T1550/001/