From a Defender Alert to 43,448 GitHub Repositories Linked to Malicious Activity
43,448 public GitHub repositories under 4,250 accounts hold malicious code or attacker-made artifacts, each proven with a rule fixed before the count. Eleven analyses, one verification method, and a clear line between what is proven and wha
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
Static analysis of public GitHub repositories proved 43,448 GitHub repositories linked to malicious activity under 4,250 accounts across nine activities, from PolinRider to Contagious Interview.
An antivirus alert on a harmless Markdown file led me to fake job-interview coding tests, and from there to 43,448 public GitHub repositories under 4,250 accounts that hold malicious code or attacker-made artifacts. Every one of them was proven again with a rule decided before the count: at a fixed commit, except the 1,995 Shai-Hulud repositories, which are proven from the worm's own description marker without opening their contents.
- Most of it is PolinRider, a publicly known supply-chain loader: 40,136 repositories under 3,334 accounts. The largest public count I found was 1,951.
- The fake coding tests (Contagious Interview) led me there. Their operators keep the source code of their own staging and payload servers on public GitHub. In the first fifteen of those repositories, 375 commits date every change of hostname and deployment.
- Seven more activities sit in the proven total:
- the Shai-Hulud npm worm, which publishes stolen secrets;
- a loader that hides its address behind an
AUTH_API_KEYvariable; - Discord token stealers and copied loaders;
- cryptomining planted in other people's GitHub Actions;
- fake exploits for real CVEs;
- cheat and crack lures that link to stealer archives.
- Two lures are described but not counted, because their final payload was not retrieved:
- trading-bot pages;
- fake security alerts.
- Almost all of it was still public when I last checked.
I did not execute anything, open any victim's data, or contact attacker servers. Downloads and decoding ran in an isolated virtual machine. Owners of infected repositories are treated as victims and are not named. Each dedicated article says why the code is malicious, what could make it harmless, and what is not proven.
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 total: 43,448 repositories under 4,250 accounts, 40,136 of them PolinRider.
- Still live on 1 November: the Shai-Hulud worm was still creating repositories (newest at 16:44 UTC); two Contagious Interview staging-server repositories received new commits on 31 October; the phishing issues were still open.
- Removed so far: a daily random sample of 600 PolinRider repositories found 5 cleaned and 2 deleted on 31 October, an estimated 1% of the set.
How it started
#It began with an antivirus alert, not a plan to hunt North Korean actors. From 16 October 2026,
Microsoft Defender Antivirus repeatedly quarantined docs/disclosed-reports/hunt-lfi.md, a Markdown
file from an open-source collection of bug-bounty notes installed as a coding-assistant plugin. It
did so twelve times between 16 and 27 October, with the detection Trojan:Script/Wacatac.H!ml.
My initial hypothesis was that Defender had flagged exploit-related strings quoted in the write-up. That would make it a false positive in the context of a security document, but the alert alone could not establish the cause or confirm that assessment. It also provided no evidence of a connection between that repository and the operation described in the dedicated analysis.
Still, the alert raised a question: how much genuinely malicious code sits in plain sight on GitHub, alongside harmless security notes? I started looking for malicious behavior in public repositories, without a particular actor or country in mind. One focus was code that retrieved JavaScript from a remote endpoint and then executed it. Downloading code is not inherently malicious. The surrounding application, the execution path and the content retrieved are what make the distinction.
The first sweep recorded in my research log, on 26 October, used GitHub code search. It returned 100 hits for the loader's function names, and static inspection of each matched file showed that 98 contained the same remote-code loading pattern: an encoded endpoint in the environment configuration, a network request, and dynamic execution of the returned code. That figure describes the initial search results, not the final count of distinct repositories. The tooling and lures match the Contagious Interview campaign, which Microsoft, Unit 42 and other vendors attribute to North Korean operators (public reporting collected in the Contagious Interview analysis).
The same evening (26 October, 20:28 UTC), Defender flagged the most common loader file as
Backdoor:NPM/Obfuse!AMTB when my tooling wrote it to disk. Unlike the original Markdown alert, this
detection concerned a file whose remote-code loading behavior I had also identified through static
inspection. The detection itself did not establish actor attribution. From that point, suspicious
loader content was handled in memory, and sample analysis moved to an isolated virtual machine. Nothing
in the malware chain was intentionally executed; the method and its deobfuscation exception are
described in the dedicated analysis.
That was the first thread. While mapping the fake coding tests, I found repositories that carried a second, unrelated loader. I followed every thread the same way: search, read the exact file, decide by a fixed rule, then prove it again at a pinned commit. The dedicated articles in this series document what that produced.
What I uncovered
#Repositories in the proven set
43,448
Unique by repository id; frozen on 1 November 2026
Accounts that own them
4,250
Unique by owner id; most are victims
Independent re-check
555 of 555
Random sample re-read through a second API
PolinRider alone is 40,136 of these repositories, so the chart below leaves it out; on the same axis every other activity would be invisible.
Frozen set of 1 November 2026. Each repository read at its default-branch commit; Shai-Hulud proven from repository metadata; cheat lures from the README and the linked release archive. PolinRider (40,136) is shown separately above.
Table view
| Item | Value | Note |
|---|---|---|
| Shai-Hulud exfiltration repos | 1,995 repository | 61 accounts |
| AUTH_API_KEY loader | 649 repository | 379 accounts |
| Contagious Interview | 436 repository | 374 accounts |
| Discord token stealers | 202 repository | 187 accounts; published tools, not victims |
| Copied errorHandler loaders | 70 repository | 68 accounts |
| D1337 cryptomining | 55 repository | 28 accounts |
| Cheat and crack lures | 12 repository | 11 accounts; 1,422 more suspected |
| Fake CVE exploits (SmartLoader) | 10 repository | 10 accounts |
| Python Fernet loader | 1 repository | 1 account |
Full data table · 9 rows
| Activity | Proven repos | Accounts | What it does | Already public? |
|---|---|---|---|---|
| Contagious Interview | 436 | 374 | Coding tests that run a loader on install, start or checkout | The operation is; its server source and most hosts were not |
| PolinRider | 40,136 | 3,334 | Loader in fake fonts and padded build configs; next stage from blockchains | The campaign is; this scale was not |
AUTH_API_KEY loader |
649 | 379 | Server or build file decodes a variable from .env and evaluates code |
The pattern is (2025); this set and its hosts were not |
| Older families | 273 | 256 | Discord token stealers, copied errorHandler loaders and one Python loader |
Yes |
| Shai-Hulud | 1,995 | 61 | npm worm publishes stolen secrets as repositories in the victim's account | The worm is; an October 2026 burst was not |
| D1337 | 55 | 28 | A "CI Build" workflow mines cryptocurrency in other people's accounts | No |
| Fake CVE exploits | 10 | 10 | Proof-of-concept repositories that ship SmartLoader archives | Partly |
| Cheat and crack lures | 12 | 11 | README lures that link a release archive named NWHStealer or detected as a trojan; 1,422 more only suspected | The lure pattern is; these 12 were not checked |
| All proven | 43,448 | 4,250 | 203 accounts appear in two activities |
Activity rows are not mutually exclusive: 118 repositories carry two activities (mostly PolinRider with the AUTH_API_KEY loader), so the rows add up to 43,566. The total counts each repository id once. Counting rules.
Described, not counted
| Activity | Size | Why it is not in the total |
|---|---|---|
| Trading-bot pages | 2,439 repositories, 15 accounts | Redirect and cloaking proven; the downloaded file was never retrieved |
| Fake GitHub security alerts | 5 issues, 1 account | Links not followed |
Sixteen days in one timeline
#What happened in each activity, on one axis. Times are UTC; dates come from the platform, not from commits.
Start
Antivirus alert on a security note
Defender quarantines a bug-bounty notes file, twelve times by 27 October; the question it raised starts the search.
Shai-Hulud
First repository of the burst
10:13:59 UTC.
Fake security alerts
Issue repository created
Contagious Interview
First recorded sweep
100 code-search hits for the loader's function names, 98 with the remote-code loading pattern; Defender flags the most common loader file at 20:28 UTC.
Contagious Interview
Payload build pushed
Pushes to four payload repositories 02:50–04:06 UTC.
Fake security alerts
Five phishing issues opened
11:51–12:40 UTC.
Shai-Hulud
Repository creation moves to a second account
Contagious Interview
Operators update three staging servers and one payload server
00:02–02:54 UTC.
All activities
First lists compiled
7,332 active PolinRider infections; the Shai-Hulud burst; 58 D1337 mining repositories; ten fake-CVE repositories; the trading-bot farm (15 accounts, 2,439 repositories); the cheat and crack hubs; the five phishing issues.
All activities
Proof pass at pinned commits
Every candidate re-read under fixed rules; 555-repository independent re-check, 555 matches; rule tightened after the sample. PolinRider at 39,995 repositories; cheat and crack lures at 12 proven, 1,422 suspected.
All activities
Extended searches and daily tracker
Wider read of lure and tool repositories; 138 more PolinRider repositories; discovery cut-off 31 October 2026, 17:59 UTC.
All activities
Set frozen
Tracker finds (up to the cut-off) pinned and added; the 162 tracked repositories, 73 accounts and 12 issues re-read at 1 November 2026, 18:23 UTC; 43,448 repositories under 4,250 accounts.
All activities
State at the freeze
Changed since 29 October: Shai-Hulud still publishing from four accounts, the phishing issues still open.
What the activities have in common
#Developer accounts are the distribution channel. PolinRider rewrites history in compromised accounts and force-pushes it. Shai-Hulud creates repositories in the victim's own account. D1337 plants a workflow in unrelated accounts. A clean-looking account with years of history is no proof that its code is safe.
Developers are the target. The lures are things developers run without a second thought:
- a coding test from a recruiter;
- a proof-of-concept for a fresh CVE;
- a build config that loads on
npm run dev; - a VS Code folder that starts a task when opened.
Hiding from scanners is part of the design. Each family hides from one kind of check:
- PolinRider puts JavaScript in files named
.woffand pushes its payload off-screen. - The cheat hubs pad their archives to 80–120 MB.
- The trading-bot pages refuse to build their link for crawlers.
- D1337 downloads the genuine XMRig release, so a file-hash check sees nothing wrong.
Two loaders share their victims. 193 of the 379 accounts with the AUTH_API_KEY loader also own a
proven PolinRider repository. That shows shared victims, not shared operators.
Very little has been cleaned up. On 29 and 30 October:
| Activity | Still public |
|---|---|
| PolinRider | 99% on the default branch; 22,870 infected branches |
| D1337 | 58 of 58 |
| SmartLoader | 10 of 10 |
The Shai-Hulud burst was still adding repositories while I checked.
Read the investigation
#Each activity has its own article with its full analysis, evidence register, indicators where available, and limitations. The activities are not attributed to a single operator. The trading-bot and fake-alert lures, and the 1,422 suspected cheat lures, remain outside the proven total.
See the shared methodology and proof rules for the pinned-commit procedure and independent re-check.
Evidence for the whole investigation
#One row per repository with repository id, owner id, pinned commit and commit permalink; for code-based activities also file path, git object id, size, rule and task-target checks; for Shai-Hulud the description marker. No file contents. Kept private because permalinks name victims' accounts.
Source: GitHub GraphQL and REST, read from an isolated virtual machine · Collected 30 October 2026
555 proven repositories in a stratified random sample with a recorded seed, re-read through the REST contents API at the pinned commit and compared by object id and, for task targets, by size and file type.
Source: GitHub REST · Collected 30 October 2026
Limitations of the whole investigation
#- Totals are a floor. Code search covers default branches only. Seven accounts holding 7,500–89,800 repositories each, where every infected repository is a fork, were recorded but not scanned.
- Proof covers code, not execution. Whether a given loader ran on someone's machine is not known.
- Accounts are counted by owner id. One person can hold several accounts.
- Attribution is not mine. Where an activity is linked to North Korea, that link comes from public reporting and is labelled as such.
- Only 12 cheat-lure repositories are in the proven total. The 1,422 suspected lures, the trading-bot pages and the fake alerts are not; each chapter says what is established.
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.
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.