RC RANDOM CHAOS

CISA pushed GovCloud keys to GitHub

Technical analysis of a CISA admin leaking AWS GovCloud keys on GitHub - exposure mechanics, CloudTrail detection paths, and residual session risk post-rotation.

· 7 min read

A CISA administrator account pushed AWS GovCloud access keys to a public GitHub repository. The credentials sat in commit history for an unconfirmed dwell window before takedown. The repository’s status, the exact key scope, and the rotation timeline have not been published. The class of failure is known. The exposure mechanics are predictable. The detection surface around public GitHub commits is well-mapped, and the lag between push and adversary acquisition is measured in seconds, not hours.

AWS GovCloud is not a region. It is a separate partition - aws-us-gov - with its own root account namespace, its own IAM principal universe, and its own STS endpoint. Credentials issued in commercial AWS (aws partition) do not authenticate against GovCloud. That separation is the point. GovCloud sits behind ITAR and FedRAMP High controls. Workloads landing there carry CUI, law enforcement sensitive data, and material covered under export regimes. The threat model assumes the partition itself is a higher-value target than the commercial side because of what the workloads imply, not the technology underneath.

Leaked key material follows a known format. access keys begin with AKIA. Session credentials begin with ASIA. GovCloud keys carry the same prefix structure but resolve against sts.us-gov-west-1.amazonaws.com or sts.us-gov-east-1.amazonaws.com. Detection tools - GitHub secret scanning, GitGuardian, TruffleHog, gitleaks - match on the prefix, not the partition. AWS itself ingests the GitHub firehose and revokes keys it identifies as leaked. That control is partition-aware on the AWS side. The substring AKIA is enough for third-party harvesters to flag and act on the candidate before AWS reaches it.

The exposure window opens at git push. GitHub’s public events API streams commit metadata in near-real-time. The public timeline is monitored by commercial scanners, by researchers running TruffleHog at scale against the firehose, and by adversaries running the same tooling for different reasons. Published research on commit-to-acquisition time consistently shows successful credential capture inside two minutes of push for keys committed to public repositories with no further obfuscation. That window does not depend on the repository being indexed, starred, or discovered organically. The firehose makes discovery passive.

The first action taken against a captured key is identity enumeration. sts:GetCallerIdentity returns the account ID, the IAM ARN, and whether the credential is a user or a role session. The call does not require any specific IAM permission. CloudTrail logs it. In GovCloud, the entry appears in the us-gov-* regional trail and, if configured, the management-account trail. The event name is GetCallerIdentity. The source IP will not be a known administrative range. The user agent will commonly be Boto3/<version> or aws-cli/<version> from a residential or VPS netblock. That is the first detection opportunity. Most organisations miss it because GetCallerIdentity runs constantly from legitimate workloads and is filtered out of high-priority alert paths.

Once the principal is identified, the adversary moves to permission enumeration. iam:GetUser, iam:ListAttachedUserPolicies, iam:ListGroupsForUser, iam:ListUserPolicies, iam:SimulatePrincipalPolicy if reachable. T1087.004 - Account Discovery: Cloud Account. The objective is to determine what the key can reach without burning the credential on a failed high-value call. If IAM read is denied, the fallback is direct service probing - s3:ListAllMyBuckets, ec2:DescribeInstances, dynamodb:ListTables, secretsmanager:ListSecrets. T1580 - Cloud Infrastructure Discovery. Each call produces a CloudTrail event. Each event is a detection opportunity. The signal density is highest in the first ten minutes after a credential lands in adversary hands.

GuardDuty in GovCloud generates the same finding families as commercial. UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS fires when credentials harvested from an EC2 instance role are used from outside AWS IP space. Recon:IAMUser/MaliciousIPCaller fires when known-bad IPs invoke recon APIs. UnauthorizedAccess:IAMUser/AnomalousBehavior fires on behavioural deviation from the principal’s baseline. None of these fire reliably on a long-term key that was never associated with a baseline. A freshly-leaked AKIA credential operated from a clean VPS does not trigger AnomalousBehavior because there is no prior behaviour to deviate from. This is a known coverage gap. The detection is post-hoc - built from CloudTrail anomaly correlation, not native GuardDuty output.

The threat actor population scanning the GitHub firehose is broad. Commodity crypto-mining operations are the loudest. They want ec2:RunInstances in any region the key permits, with the largest GPU SKUs the account quota allows. The activity is noisy, immediate, and well-characterised in CloudTrail. For GovCloud specifically, the commodity actors are a smaller subset because the partition is harder to monetise - GovCloud account creation is gated, instance types are constrained, and the egress surface for converting compute into cryptocurrency payouts is narrower. The actors that remain interested in GovCloud keys are not running for crypto. The interest is data - what the account holds, what trust relationships it carries into federal tenants, what cross-account roles it can assume.

Cross-account assumption is the multiplier. An IAM user in one GovCloud account is rarely interesting in isolation. The value is in sts:AssumeRole against trust relationships into other accounts in the same organisation. CISA’s AWS footprint is not a single account. It is a constellation of accounts spanning operational, analytical, and partner-facing functions. Each role with a trust policy listing the leaked user’s account as a principal - or worse, a wildcard Principal: "*" with weak external ID handling - is a lateral path. T1078.004 - Valid Accounts: Cloud Accounts. The exposed key becomes a pivot, not a destination.

The CWE classifications cover this without ambiguity. CWE-798, Use of Hard-coded Credentials. CWE-540, Inclusion of Sensitive Information in Source Code. There is no CVE because there is no software defect. The mechanism is operational - a credential committed to a repository under an administrator’s control. The bug is in the process boundary between developer environment and version control. Pre-commit hooks running gitleaks or trufflehog catch this. Server-side push protection on GitHub Enterprise catches this. AWS IAM Roles Anywhere with short-lived X.509-bound credentials removes the long-term key from the workflow. Each control is widely available. The failure here is procedural, and the surface where procedure fails is operator workstations under time pressure, not architectural diagrams.

What CloudTrail produces in this scenario is well-defined. eventName: ConsoleLogin will not appear - raw access keys do not federate to the console without an additional GetFederationToken or SSO step. The first event will be GetCallerIdentity from sourceIPAddress outside the agency’s egress ranges. Subsequent events will cluster in the IAM and discovery API families. The userIdentity.type will be IAMUser. The userIdentity.accessKeyId will match the leaked key. Correlation on accessKeyId across sourceIPAddress changes is the highest-confidence signal - a long-term key that suddenly invokes from a new IP, with no SSO event preceding it, and no prior CloudTrail history from that IP, is a binary indicator. The detection rule is not subtle. The reason it commonly fails is alert volume tuning that suppresses IAM read events to manage noise.

Post-rotation, the residual exposure is what was retrieved before the key was revoked. Object reads from S3 do not modify the object. CloudTrail data events for S3 are off by default - the management trail does not log GetObject. If S3 data events were not enabled for the affected buckets, the exfiltration record does not exist on the AWS side. The reconstruction depends on whatever the adversary touched at the management plane - bucket listings, object metadata calls, lifecycle queries. The contents of what was read after ListObjects returned a target are unknowable from default logging. This is the gap that turns a credential leak into an indeterminate breach. T1213 - Data from Information Repositories - operates below the default audit surface.

The patch boundary here is rotation plus revocation plus session invalidation. Disabling the access key stops new STS calls. Existing session tokens minted before revocation remain valid until their TTL expires - typically up to 36 hours for long-duration sessions, up to 12 hours for default AssumeRole outputs. The compromised principal must be assumed to have minted sessions during the exposure window. Revoking active sessions requires attaching an inline deny policy with an aws:TokenIssueTime condition predating the revocation, or removing the user entirely. Until that step lands, the credential is revoked in name and still operational in effect. The window between key revocation and full session expiry is the last live exposure surface, and it is the one most commonly missed in incident response runbooks. For a GovCloud principal with cross-account trust into adjacent agency tenants, that window is where the actual impact assessment lives.

See also: NordVPN for tunneled traffic when operating outside controlled networks.


#ad Contains an affiliate link.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.