CISA's Leaked Admin GitHub Token Remained Live 2 Days After Krebs Reported It Leaked

On May 18, Krebs on Security reported that a CISA contractor had exposed AWS GovCloud keys, plaintext database passwords, and internal credentials in a public GitHub repository named "Private-CISA." The repo was taken down within hours. CISA told Krebs there was "no indication that any sensitive data was compromised."

We detected the live keys in the GitHub repository from our public monitoring. The AWS keys that Krebs and researcher Philippe Caturegli focused on were one part of the story. But buried in the Terraform configs was something worse:

A GitHub App private key granting write access to CISA's entire GitHub organization, still live on May 20, two full days after the Krebs article.

A GitHub App Key With Write Access to CISA's Org

In the runners-caws-tf/environments/dso-us-east-1/ directory sat a file called github-app.key, a 2048-bit RSA private key for a GitHub App called arc-runners-dso (App ID 3464825, found in the terraform.tfvars in the same directory).

On May 20, two days after Krebs published, we constructed a JWT from this key and authenticated to the GitHub API. HTTP 200. The key was fully active.

github-app.key RSA Private Key 2048-bit terraform.tfvars App ID: 3464825 (both files needed) Sign JWT iss=3464825, iat, exp GitHub API GET /app 200 OK

The app was owned by the cisa enterprise account and installed on the cisa-it GitHub organization with access to all repositories. Its permissions:

administration write modify repository settings, branch protection, webhooks, deploy keys
organization_self_hosted_runners write register and manage self-hosted CI/CD runners
contents read read source code from every repository in the organization
actions read read workflow run details and logs

An attacker with this key could register a malicious self-hosted runner in the cisa-it organization. Any GitHub Actions workflow configured to run on self-hosted runners would execute on the attacker's machine, giving them access to repository secrets, deployment credentials, and whatever else those workflows touch.

We reported the key to Brian Krebs who reported it to CISA.

When we rechecked on May 21, the GitHub App key returned 401. Someone had revoked it within 24 hours of our validation.

Other Credentials Still Live

The GitHub App key was the most critical finding, but it wasn't the only credential still active days after the Krebs report. We identified additional live tokens granting access to vendor-specific services. Lower impact than org-wide GitHub admin, but still unrevoked and still valid.

These were multi-layer encoded credentials embedded in Terraform configs that standard scanning tools either missed entirely or flagged as generic findings without verifying them. We've included the details in our disclosure to CISA.

What Else Was In There

Beyond the confirmed-active credentials, the repository contained dozens of additional secrets across multiple categories:

JFrog Artifactory tokens (6 unexpired)
Reference tokens for svc.artifactory, scattered across Dockerfiles, .npmrc, .credentials, and pip configs. Expiry dates range from Nov 2026 to Apr 2027.
JFrog join & master keys (3 pairs)
The master key encrypts all secrets stored in Artifactory. Three separate pairs across DSO Helm values, DSO tfvars, and SBX tfvars.
Database passwords (6, predictable)
Every RDS password follows [Env][Service]1234^%$#. DSO Artifactory: DSOArt!factory1234^%$#. Production is guessable.
TLS private keys (2, still valid)
RSA 2048-bit keys for sonarqube.dso.cisa.dhs.gov and sonarqube.sbx.cisa.dhs.gov. Internal CA, no CT logs, OCSP on internal DNS only.
Wiz FedRAMP API credentials
Client ID, secret, and ACR pull secret for wizfedramp.azurecr.us. Auth returned 401, likely rotated.
EC2 SSH keys, Splunk HEC, AWS key
All either behind firewalls (Splunk), on dynamic infrastructure (EC2), or in GovCloud (AWS IAM). Cannot validate externally.

Why Three Days to Revoke a GitHub Key

10+ distinct rotation procedures across at least 6 different vendors. That's what CISA has to execute from this single incident: AWS IAM keys, a CrowdStrike vendor token, a GitHub App key, six JFrog Artifactory tokens, three JFrog master key pairs, six database passwords, two TLS certificates, Wiz API credentials, a Splunk token, and EC2 SSH keys.

The GitHub App key is instructive. It's a file called github-app.key sitting alongside dozens of other .pem files in a Terraform directory. Standard secret scanners flag it as a generic "PrivateKey" finding, the same classification as every other RSA key in the repo. To understand what it actually grants access to, you need to pair it with the App ID from a separate terraform.tfvars file, construct a JWT, and call the GitHub API. (TruffleHog will soon do this automatically end to end.)

The CrowdStrike token was three layers deep. Decode a base64 Docker config JSON, extract the auth field, decode that from base64 to get the username and password, then authenticate against a vendor-specific registry API. TruffleHog does and has for some time now done recursive decoding.

Rotation isn't always easy or fast, and the first step is knowing what's still live. That's where TruffleHog helps.

CISA Is Not an Outlier

When we tracked 10,000 leaked secrets from public GitHub repositories over 31 days, 74% were still valid a month later. When keys were revoked, it almost always happened in the first three days, or not at all.

In more than half the cases where developers removed the file exposing a secret, they never actually rotated the key. Taking down the repo feels like remediation. It isn't.

Deleting the repo doesn't even remove the data. As we documented in our research on Cross Fork Object References, anyone can access commit data from deleted and even private repositories on GitHub, as long as a single fork exists. GitHub's public events API and the GitHub Archive preserve commit hashes and content indefinitely. That is exactly how we reconstructed this repository after it was taken down.

Secrets don't just leak on GitHub. A scan of 22 million projects across four cloud development environments turned up 8,792 verified secrets, including a GitHub employee token with write access to github/github. When we scanned 400 terabytes of Common Crawl data, the dataset used to train LLMs like DeepSeek, we found 11,908 live API keys. They leak everywhere code is written, shared, or archived.

The CISA incident also highlights why scanning encoded and archived data matters. The CrowdStrike credential in this repo was base64-encoded inside a JSON blob, itself base64-encoded inside a Terraform string. GitHub's built-in secret scanning doesn't catch base64-encoded secrets. TruffleHog's recursive decoder does. It's the reason we found the credential in the first place.

The pattern holds whether it's an individual developer, a Fortune 500 company, or a federal agency: the hard part of secret remediation isn't finding the leak. It's rotating every credential across every system that touches it, and knowing which credentials are even in scope.

The Bigger Picture

The Private-CISA repository also exposed a detailed map of CISA's GovCloud infrastructure that persists regardless of credential rotation:

An attacker who archived the repository before it was taken down now has a comprehensive map of CISA's internal architecture. That doesn't expire.

Disclosure Timeline

November 13, 2025
Private-CISA repository created on GitHub
May 15, 2026
Guillaume Valadon alerts Krebs on Security
May 18, 2026
Krebs publishes; repo taken offline; CISA acknowledges
May 20, 2026
We confirm GitHub App key and other tokens are active
May 21, 2026
GitHub App key revoked; other tokens still active

Recommendations

Secret rotation runbooks need to cover all credential types, not just AWS keys. A leak like this touches half a dozen vendor systems, each with its own revocation process. GitHub Archive preserves public repository contents indefinitely. Taking down a repo does not undo the exposure. And predictable password patterns are equivalent to no password at all once any single instance is exposed.