The AUTH_API_KEY Loader in 649 GitHub Repositories
An environment variable that conceals a URL, dynamic execution and overlapping affected accounts.
Cristian Chiriac
- Published:
- 2 November 2026
- Version:
- 1.0
- Last observed:
- 30 October 2026
- Dataset frozen:
- 1 November 2026
- Collection cut-off:
- 31 October 2026, 17:59 UTC
- Tracked items rechecked:
- 1 November 2026, 18:23 UTC
- Time zone
- All dates UTC.
Contents
Server and build files decode an environment variable and pass code to eval or Function.
The research in 30 seconds
- What I found
- Server and build files decode an environment variable and pass code to eval or Function.
- What this adds
- The pinned-commit set and decoded host inventory quantify a publicly described pattern; 193 owners also appear in the PolinRider set.
- Verified scope
- 649 proven repositories under 379 accounts on 30 October–1 November, after a paged search, a review with fixed rules and the 31 October and 1 November additions; the URL decode set was collected on 28–30 October.
- What remains unknown
- The final payload was not retrieved, and the fetch step was not confirmed file by file.
The numbers, with their boundaries
Current count
649
Proven repositories
30–31 Oct 2026 · UTC
- Counts:
- Pinned-commit decode-and-evaluate rule; 379 owner accounts.
Earlier snapshot or subset
571 / 609
Decoded URL set
28–30 Oct 2026 · UTC
- Counts:
- 571 URL-bearing .env values among 609 of the 614 proven repositories read.
Context · separate measure
193 / 379
Owners also in PolinRider
31 Oct 2026 · UTC
- Counts:
- Overlap of owner ids; shared affected accounts do not prove shared operators.
In 649 public repositories, an ordinary-looking server or build file decodes an environment variable
named AUTH_API_KEY and evaluates code with it. The variable sits in the repository's .env file. It is
not an API key: it is the base64 form of a web address.
- The 649 repositories belong to 379 accounts. None is a fork.
- 193 of those 379 accounts also own a proven PolinRider repository. The two loaders often hit the same victims.
- In 571 of the 609 repositories whose
.envI read, the variable pointed to one of 30 Vercel apps, most namedauth-confirm-<word>, or to a file on the catbox file host. - The pattern itself was described publicly in 2025, in a recruitment-scam write-up. I found no public count of affected repositories and none of these addresses.
I did not request any of these addresses. The variable was decoded as text inside an isolated virtual machine. The owners are treated as victims and are not named.
Where this stands (1 November 2026)
#The proven set was closed on 1 November 2026 with what had been found up to 31 October 2026, 17:59 UTC; repositories found after that are tracked separately and are not in these figures. The full proof pass at pinned commits ran on 30–31 October. On 1 November 2026, 18:23 UTC a read-only GitHub API recheck covered the 162 repositories, 73 accounts and 12 issues that are tracked one by one (operator repositories, Shai-Hulud accounts, phishing issues), not the whole set.
- Frozen: 649 repositories under 379 accounts; the last three were added on 1 November after being pushed on 31 October, so the loader is still being planted.
- Overlap: 193 of the owners also hold a proven PolinRider repository.
Key findings
#How it works
#- 01A normal-looking fileT1059.007server.ts, index.ts, vite.config.ts, next.config.ts or a similar file that runs on start or build
- 02A secret that is not a secretT1027.013AUTH_API_KEY in the committed .env holds a base64 string
- 03Decodeatob turns it into a URL on a Vercel app or a catbox file
- 04EvaluateT1105In the public 2025 example, the code fetches the URL and passes the response to eval, so the payload can change without a commit
Because the address lives in .env, a reviewer who reads only the source sees a call that looks like
authentication. The proof rule checks the decode-and-evaluate pattern; I did not
confirm the fetch step file by file, and I never requested the addresses.
How the count grew on 30 October
#The first proof pass found 231 repositories. They came from a single code search on 27 October that I did not page through. On 30 October the daily spread tracker ran the same search with full paging and found 447 more repository files that match the proof rule. I reviewed them with rules fixed before counting:
| Review result | Repositories | Decision |
|---|---|---|
Same file as a proven repository, or .env decodes to an address already in the proven set |
352 | Added |
Same code structure as a proven file, or an auth-* Vercel address |
32 | Added |
| Same pattern, but a different code structure and no decodable address | 42 | Not added |
| Not code: a scanner, an indicator list, a test or an audit note | 21 | Not added |
One repository in the first 231 matched only through a security audit note, not code. I removed the loader label from it; its separate PolinRider proof stays. That gives 231 − 1 + 384 = 614.
On 31 October 33 were added and one removed. Nine were found by the daily tracker and also carry
PolinRider. 24 are among the 42 not added above: their current .env gives no decodable address, but an
.env committed earlier in their history decodes to an address already in the proven set. One repository in
the 614 matched only in a coding-assistant notes file, with no loader in any code file, so I removed it.
That gives 614 + 33 − 1 = 646.
On 1 November I added three more, found by the daily tracker on the evening of 31 October. Each loader file was pushed on 31 October and was read again at a pinned commit on 1 November. Two of the three owners are new to the set. That gives 649.
Where it hides
#Full data table · 9 rows
| File | Repositories |
|---|---|
index.ts |
113 |
server.ts |
89 |
vite.config.ts |
82 |
main.ts |
47 |
app.ts |
25 |
next.config.ts |
7 |
nuxt.config.ts |
6 |
prisma.config.ts |
7 |
| Other | 163 |
The table covers the 539 repositories listed under their loader file. The other 110 also carry PolinRider
and are listed under that proof. 531 of the 539 files are TypeScript. Build configurations matter most
here: vite.config.ts or next.config.ts runs on every npm run dev, before any application code.
Who is affected
#| Measure | Value |
|---|---|
| Proven repositories | 649 |
| Owner accounts | 379 |
| …also owning a proven PolinRider repository | 193 |
| Repositories owned by user accounts / organizations (609 read) | 520 / 89 |
| Forks | 0 |
| Not proven at the current commit (first set) | 3 (no evidence), 1 deleted |
Repository creation years (609 read): 302 in 2025 and 226 in 2026. The other 81 were created between 2014 and 2024. Last pushes cluster in October 2026 (195), August (162) and September (86).
Sample at a date: 609 of the 614 repositories in the 30 October 2026 set, platform creation dates read on 28–30 October. The 35 repositories added later are not included. Most were created in 2025–2026.
Table view
| Item | Value | Note |
|---|---|---|
| 2014 | 1 | |
| 2015 | 0 | |
| 2016 | 0 | |
| 2017 | 0 | |
| 2018 | 4 | |
| 2019 | 0 | |
| 2020 | 3 | |
| 2021 | 6 | |
| 2022 | 9 | |
| 2023 | 14 | |
| 2024 | 44 | |
| 2025 | 302 | |
| 2026 | 226 |
379 owner accounts after the 1 November 2026 additions, compared by owner id with the proven PolinRider owners.
Table view
| Item | Value | Note |
|---|---|---|
| Also own a PolinRider repo | 193 | |
| AUTH_API_KEY only | 186 |
The addresses
#Full data table · 10 rows
| Address (defanged) | Repositories pointing to it |
|---|---|
auth-confirm-eight[.]vercel[.]app |
229 |
files[.]catbox[.]moe (various file names) |
182 |
auth-den-tdu[.]vercel[.]app |
26 |
fortesting-alpha[.]vercel[.]app |
21 |
auth-confirm-two[.]vercel[.]app |
21 |
auth-confirm-seven[.]vercel[.]app |
16 |
auth-rho-dun[.]vercel[.]app |
13 |
auth-con-firm[.]vercel[.]app |
10 |
data-kappa[.]vercel[.]app |
9 |
| 22 more Vercel apps | 1–7 each |
Each repository points to one address. Catbox and Vercel are public hosting services and are abused here; neither is attacker-owned.
How it relates to the other activities
#- PolinRider. 193 of the 379 owners overlap. Both loaders appear in the same developer accounts, and both evaluate code fetched at run time. The data shows shared victims. It does not show whether one operator runs both, or whether a machine infected by one later received the other.
- Contagious Interview. Its
errorHandlerloader also evaluates a remote response, and the public 2025 description of this pattern came from a fake-recruitment project. I count the two separately, because the code and the hosting differ.
No attribution is made here.
Evidence register
#Expand technical detail · complete record
649 repositories with repository id, owner id, commit, path, git object id and the matched rule (atob on an environment variable plus eval or Function).
Source: GitHub GraphQL, read from an isolated virtual machine · Collected 30 October 2026
447 repository files found by the paged search on 30 October, each compared with the proven files by git object id, normalised code structure and decoded .env host; tier rules fixed before counting; 384 added, 63 not added.
Source: GitHub GraphQL and code search, read from an isolated virtual machine · Collected 30 October 2026
609 of the 614 repositories read on 28–30 October; 583 commit an .env file, and in 571 the AUTH_API_KEY value decodes to a URL. Only host names and file paths were kept.
Source: GitHub GraphQL, decoded in an isolated virtual machine · Collected 30 October 2026
Owner ids of proven AUTH_API repositories compared with owner ids of proven PolinRider repositories in the same proof set.
Source: Proof-pass tables · Collected 30 October 2026
Data notes and reconciliation
#Expand technical detail · complete record
| Measure | Figures | Why they differ |
|---|---|---|
| Proven repositories | 231 → 614 → 646 → 649 | First unpaged search (one audit-note match later removed) → paged search, +384 (30 Oct) → +33 from the extended search, −1 notes-file match (31 Oct) → +3 from the daily tracker (1 Nov); frozen |
| Owner accounts | 363 → 377 → 379 | Same steps |
| Listed under the loader file vs also PolinRider | 539 / 110 | A repository carrying both loaders is listed under its PolinRider file; both are in the 649 |
| Overlap with PolinRider owners | 193 | Unchanged by the 1 November additions |
Defensive checks
#- Search your code for
atob(process.envnext toeval(,new Function(orFunction(. - Never commit
.env, and treat a base64 value that decodes to a URL as hostile. - Read build configurations (
vite.config.*,next.config.*,nuxt.config.*) before running a project you did not write. They execute onnpm run dev. - If you find it, also check for PolinRider. Look for
.vscode/tasks.jsonwithfolderOpen, and for build configs with long whitespace runs.
Indicators
#Expand technical detail · complete record
| Type | Value | Context | Lookups | Copy | |
|---|---|---|---|---|---|
| domain | auth-confirm-eight[.]vercel[.]app | Decoded from AUTH_API_KEY in 229 repositories Confidence: Confirmed malicious | |||
| domain | auth-den-tdu[.]vercel[.]app | Decoded from AUTH_API_KEY in 26 repositories Confidence: Confirmed malicious | |||
| domain | fortesting-alpha[.]vercel[.]app | Decoded from AUTH_API_KEY in 21 repositories Confidence: Confirmed malicious | |||
| domain | auth-confirm-two[.]vercel[.]app | Decoded from AUTH_API_KEY in 21 repositories Confidence: Confirmed malicious | |||
| domain | auth-confirm-seven[.]vercel[.]app | Decoded from AUTH_API_KEY in 16 repositories Confidence: Confirmed malicious | |||
| domain | auth-rho-dun[.]vercel[.]app | Decoded from AUTH_API_KEY in 13 repositories Confidence: Confirmed malicious | |||
| domain | files[.]catbox[.]moe | Public file host; payload paths decoded from AUTH_API_KEY in 182 repositories Confidence: High-confidence suspicious |
What was already public
#Expand technical detail · complete record
Sources were read on or before 30 October 2026. Each one also links to its Internet Archive record, which lists the captures that exist, in case the original moves or changes.
- happihacking, "Exposing the 'Multibank Crypto Poker' Recruitment Scam" (20 August 2025): a
fake-recruitment project whose
routes/api/auth.jsdecodesAUTH_API_KEYwithatob, fetches the decoded URL and passes the response toeval. Its payload host is a different Vercel app from those listed here. https://happihacking.com/blog/posts/2025/recruitment_scam/ (Internet Archive lookup)
Not found in the public sources I checked, as of 30 October 2026: a count of affected repositories, the Vercel app names above, and the overlap with PolinRider victims.
Limitations
#- The payloads were not downloaded, so what they do is not known from this work.
- Five repositories were not read for
.envand are missing from the address and year tables. - The proof rule requires
atobon an environment variable plusevalorFunction. A file that loads the code some other way would be missed. - 42 repositories with the same pattern but no decodable address are not counted.
- Code search covers default branches only, so this is a floor.
Investigation context
#This article is part of the GitHub threat investigation. Counts are a snapshot, not evidence that the code ran on a victim's machine. The shared methodology and proof rules explain inclusion, exclusions and the independent re-check.
Claims, evidence and limits
| Claim | Assessment | Evidence | Limit |
|---|---|---|---|
| 649 repositories carry the loader at a pinned commit | Confidence: Confirmed malicious | E-AA-PROOF, E-AA-REVIEW | Not stated in the finding |
| The payload address is kept in .env | Confidence: Confirmed malicious | E-AA-ENV | Not stated in the finding |
| Many victims are also PolinRider victims | Confidence: High-confidence suspicious | E-AA-PROOF, E-AA-OVERLAP | Not stated in the finding |
Terms used on this page
- pinned commit
- A specific commit, identified by its hash, at which a repository was read. Every counted repository was proven at the default-branch commit recorded in the proof pass, so a later change to the repository does not change what was proven.
- default branch
- The branch a repository shows by default. Counts are taken on default branches only; infected non-default branches were recorded separately and are not counted.
- proof pass
- The 30–31 October 2026 re-read of every candidate at a pinned commit under fixed inclusion rules, followed by an independent re-read of a random sample of 555 repositories through a different API.
- defanged
- Written so it cannot be clicked or resolved as written, for example example[.]com or hxxps://. Every network indicator on this site is defanged.
All terms: glossary.
Corrections & discussion
Found an error, or have evidence that supports or contradicts a finding? Send a correction. Name the section and what you observed.