RC RANDOM CHAOS

CISA contractor leaked GovCloud keys to GitHub

Technical analysis of a CISA contractor's leaked AWS GovCloud admin keys on GitHub - blast radius, IAM persistence paths, CloudTrail detections, supply-chain tail.

· 7 min read

A CISA contractor admin pushed AWS GovCloud access keys to a public GitHub repository. The keys were live. The principal was privileged. The blast radius reached infrastructure that the U.S. government uses precisely because the commercial AWS partitions are deemed insufficient. This is not a misconfiguration story. It is a credential exposure story with a regulated-cloud blast radius and a supply-chain tail that does not end at the contractor’s tenant.

The technical fact first. AWS GovCloud (US) is a physically and logically isolated AWS partition operating under the aws-us-gov ARN namespace. It has its own IAM, its own STS endpoint, its own KMS, its own root certificate trust. Access keys minted in GovCloud do not authenticate against the commercial aws partition, and vice versa. The endpoints are distinct - sts.us-gov-west-1.amazonaws.com and sts.us-gov-east-1.amazonaws.com. The compliance boundary is FedRAMP High and DoD SRG IL4/IL5 workloads. The point of GovCloud is that the credentials are supposed to live in a hardened operational envelope. Pushing them to a public GitHub repo collapses that envelope to the same threat surface as a Hello World project.

The bug class here is not memory corruption. It is credential lifecycle failure. Long-lived IAM user access keys - AKIA-prefixed identifiers paired with a forty-character secret - were committed to a Git history alongside what was almost certainly application code or Terraform. The exposure primitive is well documented. GitHub’s public event stream is consumed in near real time by automated harvesters. The interval between push and first credential test is measured in seconds, not hours. Truffleog, gitleaks, and bespoke tooling operated by both researchers and adversaries scrape every public commit and validate any matching pattern against sts:GetCallerIdentity. A successful identity probe returns the account ID, the IAM user ARN, and the partition. For a GovCloud key, the partition string aws-us-gov in the ARN response tells the finder exactly what they have.

What the attacker controls from that point depends on the IAM policy attached to the principal. The contractor admin context is the worrying part. Admin-equivalent policies in GovCloud typically include iam:*, ec2:*, s3:*, kms:*, and sts:AssumeRole against roles in linked accounts. The first action a competent operator takes is enumeration. aws sts get-caller-identity to confirm the principal. aws organizations describe-organization to map the account topology. aws iam list-attached-user-policies to read the permission boundary. aws iam list-access-keys to see if other keys exist. These calls produce CloudTrail events. They also tell the attacker whether persistence is trivial or whether session limits will force operational tempo.

Persistence is the second move. With iam:CreateAccessKey the attacker mints a second key under the same user - or creates a new IAM user, attaches AdministratorAccess, and rotates off the original credentials before the leak is rotated. With iam:CreateRole and a trust policy referencing an attacker-controlled account ID in the commercial partition, a federated assume-role path is established. With iam:UpdateAssumeRolePolicy against an existing privileged role, the attacker inserts their own principal into the trust document without creating new resources that stand out in inventory diffs. MITRE ATT&CK T1098.001 - additional cloud credentials. T1136.003 - create cloud account. These are the canonical persistence techniques and they map directly onto the GovCloud IAM surface.

The supply chain tail is where this stops being one contractor’s problem. CISA contractors do not operate in isolation. They build, deploy, and operate systems that ingest from and emit to federal civilian agencies. If the leaked credentials were scoped to a development account, the immediate damage is the development account. If they were scoped to a shared services account hosting CI/CD pipelines, container registries, Terraform state buckets, or signing infrastructure, the damage propagates. An attacker with write access to an ECR repository in a shared services account poisons every downstream consumer that pulls from it. An attacker with write access to a Terraform state bucket - typically an S3 bucket with versioning enabled - modifies infrastructure on the next apply run. An attacker with kms:Encrypt and kms:Decrypt on a CMK used by downstream systems forges or reads anything those systems trust the key to protect.

The code-signing pivot is the highest-impact variant. If any artifact produced inside the contractor’s GovCloud tenant is signed with a key the attacker can reach - whether via AWS Signer, a self-managed code-signing certificate stored in Secrets Manager, or a private CA in ACM PCA - the attacker emits signed artifacts that downstream agencies accept by policy. This is the SolarWinds class of trust-chain failure, reduced to a single leaked AKIA key.

Detection on the AWS side is mechanically simple and operationally hard. CloudTrail logs every API call. GuardDuty has finding types specifically for this scenario - UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS, Recon:IAMUser/MaliciousIPCaller, CredentialAccess:IAMUser/AnomalousBehavior. The finding fires when an access key is used from an IP that the principal has not historically used, or when enumeration patterns match known reconnaissance behaviour. AWS itself runs a credential scanning service against public GitHub and will quarantine exposed keys by attaching an AWSCompromisedKeyQuarantineV2 policy. The quarantine policy denies a curated list of high-risk actions but does not deny everything. It will not stop an attacker who races the quarantine - and on GovCloud the quarantine workflow is slower because the scanning integration with GitHub is partition-aware but operationally less mature than on the commercial side.

What fires in SIEM depends on what is forwarded. CloudTrail Lake or CloudTrail logs shipped to Splunk, Sentinel, or Chronicle produce the raw events. The detections that matter are not the obvious ones. ConsoleLogin from a new geography fires for everyone and is mostly noise. The high-fidelity detections are CreateAccessKey where the calling identity matches the affected identity - an attacker minting a second key under the leaked user. UpdateAssumeRolePolicy on any role with admin-equivalent permissions. PutRolePolicy adding new inline policies to existing roles. GetSecretValue against secrets the leaked principal has never touched before. Decrypt calls against KMS keys outside the principal’s historical usage profile. These are the indicators that separate enumeration from weaponisation.

The GitHub-side telemetry is the other half. GitHub’s audit log records the push event, the actor, the repo visibility state, and any subsequent force-pushes intended to scrub history. Force-pushing a commit out of main does not remove the blob from GitHub’s object store, and it does not remove it from the GitHub events firehose that harvesters have already consumed. The secret is exposed from the moment it lands in a pack file that GitHub serves, not from the moment it becomes reachable from a branch tip. Any incident response that treats git push --force as remediation is failing the basic mental model of how Git distributes objects.

The upstream control that should have prevented this is the boring one. Pre-commit secret scanning. Gitleaks, trufflehog, GitHub’s own push protection. Push protection on GitHub Enterprise Cloud will block a commit containing a recognised AKIA pattern at the server side before the ref update completes. It is opt-in per organisation, and it requires Advanced Security licensing. Many federal contractor organisations do not have it enabled across every repository. The second control is short-lived credentials. IAM Identity Center with SAML federation, or IAM Roles Anywhere with X.509 client certificates, eliminates the long-lived AKIA key as a primitive. STS session tokens expire. AKIA keys do not expire until someone deletes them. The fact that an admin user in a federal contractor tenant held a long-lived access key at all is the deeper control failure. The leak is the symptom.

The residual exposure after rotation is the part that gets undercounted. Rotating the leaked key terminates new authentications. It does not terminate sessions already established with STS tokens derived from that key - those tokens remain valid until their natural expiry, up to twelve hours for default sessions and up to thirty-six hours for some role chaining configurations. It does not remove backdoor IAM users created during the window. It does not remove trust policy modifications. It does not remove artifacts already signed, containers already pushed, or Terraform state already mutated. A full response requires session revocation via aws iam put-user-policy with an explicit deny on aws:TokenIssueTime predating the incident, plus a full IAM diff against a known-good baseline, plus an artifact integrity audit across every downstream consumer.

The technical reality. A leaked GovCloud AKIA key under a contractor admin principal is a federal supply-chain incident from the moment the commit is pushed. The compliance boundary that justified the GovCloud partition in the first place ceases to exist the instant the credential leaves the envelope. Patch boundary here is not a software version. It is the IAM state of every account the principal could reach, the artifact integrity of everything that account produced, and the trust posture of every downstream consumer who pulled from it. Rotate the key, then start the real work.

Share

Keep Reading

Stay in the loop

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