AI Security

Scan Your Own Repos, Images and APKs for Leaked AI API Keys

Dark cyberpunk illustration of a night factory floor where thousands of identical sealed crates ride a conveyor through a glowing amber scanning arch, and the few that light up from inside are diverted down a cyan-lit chute into an opening in the floor.

Anthropic published its September threat intelligence report on Thursday, covering misuse it detected and shut down between December 2025 and August 2026. The cases that drew the coverage are the loud ones: a Russian state-nexus group whose agents rebuilt their own malware in a loop until it stopped tripping security products, and a Chinese-speaking group that ran unattended vulnerability research against network appliances and produced more than a dozen candidate zero-days in a single month.

The paragraph worth your afternoon is quieter. A financially motivated operator ran ten AWS EC2 workers that mass-downloaded 1.8 million distinct Android APKs from several app-store sources, decompiled them, scanned the output with TruffleHog, and routed every verified credential into a Telegram group sorted into more than a hundred source types. A second pipeline collected GitHub organisation email addresses and worked them into personal access tokens. Nothing in that description required a model at all. What the model added was the glue: the iteration, the triage, the follow-on use of each key that came back live.

TruffleHog is not attacker tooling. It is an open-source secret scanner with over 700 credential detectors that verify against the provider's own API, and it installs in one line. The operation that swept 1.8 million apps ran the same binary you can point at your own artifacts before lunch. That symmetry is the only genuinely actionable thing in a forty-page threat report.

You are in scope if your organisation ships a mobile app, publishes container images, has any public repository, or holds an API key for a model provider. You can put this down if every credential you hold is issued by your cloud provider as a short-lived role, you distribute nothing as a binary, and push protection is already on across your repositories. In that case, read the provider-ledger section and skip the rest of the page. Everyone in between has a scan to run.

The harvesting is indiscriminate, and that is the point

Nobody selected your app. It was in the list because it was in the store. The operator Anthropic tracks as GTG-50014, linked to suspected ShinyHunters affiliates, ran an industrial process across a fleet and let the output sort itself. Target selection never entered into it, which is why company size, sector and security budget made no difference to who ended up in the Telegram channel. The report describes the same posture elsewhere: a separate actor wrote a Rust scanner that swept public container registries for exposed API keys, and another injected instructions into an AI vendor's evaluation sandbox, extracted production keys belonging to that vendor's customers, and spent them against roughly thirty AI companies over about four days.

What a stolen model-provider key is worth has changed. It used to be a billing nuisance. It is now three things at once. It resells, because there is a market for discounted API access. It is compute, so the attacker's own agent workloads run without a paper trail on their side. And it is cover, because the requests leave your account, under your rate limit, attributed to your organisation. Anthropic states the consequence plainly: actors treat the AI supply chain as both a target and a resource.

The speed numbers in the same case are the argument for scanning now rather than scheduling it. One stolen developer token reached full cloud administrative control in about three hours. One breached software provider yielded more than 2,100 sets of Azure AD authentication data across more than forty corporate tenants in roughly thirty-four hours. Credential-to-consequence used to be measured in a change window. It now fits inside a working day, which means the window where finding your own leaked key still helps is short.

Point the same scanner at your own artifacts

Four surfaces, in the order they usually pay off. Run them against what you have already shipped, not only against what you are about to ship. A key removed from the working tree three years ago is still sitting in the git objects, and a key removed in the final Docker layer is still readable in the layer that added it.

# Install (Linux/macOS). Pin a release tag in CI rather than tracking main.
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \
  | sh -s -- -b /usr/local/bin

# 1. Full git history of a local clone, not just the checked-out tree.
trufflehog git file://. --results=verified,unknown --json > th-repo.json

# 2. Every repository in the org, archived ones included.
trufflehog github --org=your-org --results=verified,unknown --json > th-org.json

# 3. Container images, layer by layer.
trufflehog docker --image=registry.example.com/app:latest --results=verified,unknown

# 4. A build you already published: decompile, then scan the tree.
apktool d app-release.apk -o apk-out
trufflehog filesystem ./apk-out --results=verified,unknown

# 5. Object storage, where backups and build artifacts accumulate.
trufflehog s3 --bucket=your-build-artifacts --results=verified,unknown

Two flags carry the weight here. --json gives you something to diff between runs, so the second scan reports only what is new. --results=verified,unknown sets what comes back, and the difference between those two words decides your morning.

Verified means the key answered when the scanner called

TruffleHog does not stop at a regular expression. Each detector holds a pattern and a live verification step. The Anthropic detector matches the string shape sk-ant-api03- or sk-ant-admin01- followed by a fixed-length body, then calls https://api.anthropic.com/v1/models for a standard key, or the organisation key-listing endpoint for an admin key. A result marked verified is a credential that authenticated seconds ago. Sort the output on that field and work it in this order:

How to triage the three result states

  • verified - the key is live right now. Rotate today, then check the provider ledger for what it has been doing. This is the only queue that matters before the end of the day.
  • unknown - the scanner tried to verify and could not get an answer: a network failure, a rate limit, a provider hiccup. Treat these as verified until one of them proves otherwise. A quiet failure to check is not a clean result.
  • unverified - the string matched the shape and the provider rejected it. Usually a rotated or fake key, and usually safe. Read the surrounding lines anyway, because the pattern that put one key in a config file put the rest of them there too.

One result deserves its own escalation. An sk-ant-admin01- prefix is an administrative credential, not a workload key. It reads your organisation's full key inventory, its usage and its costs. If one of those verifies out of a repository, the incident is bigger than a leaked key and the rotation order starts there.

Read the provider's own ledger before you rotate

Rotating first feels decisive and destroys the evidence. Before the key changes, pull what the provider recorded against it. The Anthropic Admin API lists every key with the identity it acts as, its scope, and whether it expires; the Usage and Cost API breaks consumption down per key and per hour.

# Every active key, with its scope and whether it ever expires.
curl -sS "https://api.anthropic.com/v1/organizations/api_keys?limit=100&status=active" \
  -H "anthropic-version: 2023-06-01" \
  -H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
| jq -r '.data[] | [.id, .name, .created_at,
                    (.expires_at // "no-expiry"),
                    (.scope.type // "organization")] | @tsv'

# Hourly token consumption for the last day, split per key.
curl -sS "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:00:00Z)&\
bucket_width=1h&group_by[]=api_key_id" \
  -H "anthropic-version: 2023-06-01" \
  -H "x-api-key: $ANTHROPIC_ADMIN_KEY" | jq '.data'

Three shapes in that output are worth a phone call. Consumption on a key that belongs to a project nobody has touched in a year. A steady hourly band through the night in a company where everyone works one timezone and sleeps. And a key with no-expiry, organisation-wide scope, and a volume profile that does not match the service it was issued for. Usage from the Console playground is not attributed to any key, so an api_key_id of null is normal rather than suspicious.

If you are a one-person shop on an individual account, the Admin API is not available to you. The Usage and Cost pages in the Console carry the same numbers. Open them before you rotate and screenshot the week.

Make the next key cost an attacker less to lose

Rotation buys you back today. The part that holds is reducing what a single harvested string is worth, and none of it needs a budget.

  • Scope every key to one workspace, never to the organisation. A workspace-scoped key that leaks costs you one project, not the estate.
  • Give keys an expiry date. An unexpiring key in a 2023 commit is still a live key. One with a ninety-day life is an alert that fires on its own.
  • Turn on push protection at the organisation level so a supported secret blocks the push instead of generating an alert after the object is already in the history.
  • Keep provider keys out of client binaries entirely. A key compiled into a mobile app is published, not hidden. Proxy the call through a backend you control and keep the credential server-side.
  • Build images with secret mounts, not by copying an environment file into a layer you then delete. The deleted layer ships.

Run the scan against a build you already published before you change anything else. That output tells you which of these five you actually needed.

Rotate what verifies, then make the next key expire on its own

Take the four commands above and run them against one repository, one image, one shipped build and one bucket this week. Work the verified list first and the unknown list immediately after it. Pull the provider ledger before each rotation so you know whether the key was only leaked or actually used. Then set an expiry on every key you reissue, because the pipeline in that report is still running, it still costs almost nothing to operate, and it does not need to know your company's name to find your credential.

Want to try our open-source security tools?

We build open-source tools that automate security workflows, including scopecheck for mapping the external surface you did not know you published. Check them out on GitHub, or book a session to talk through secret hygiene and AI key governance in your environment.