Skip to content
PublishedThreat Intelligence · v1.0 · 7 min

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.

Technical detail:Long tables, code and the evidence register are folded; nothing is left out.

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_KEY variable;
    • 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.

Proven repositories per activity, PolinRider (40,136, off this scale) excludedone unit = one repository

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
Proven repositories per activity, PolinRider (40,136, off this scale) excluded
ItemValueNote
Shai-Hulud exfiltration repos1,995 repository61 accounts
AUTH_API_KEY loader649 repository379 accounts
Contagious Interview436 repository374 accounts
Discord token stealers202 repository187 accounts; published tools, not victims
Copied errorHandler loaders70 repository68 accounts
D1337 cryptomining55 repository28 accounts
Cheat and crack lures12 repository11 accounts; 1,422 more suspected
Fake CVE exploits (SmartLoader)10 repository10 accounts
Python Fernet loader1 repository1 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.

16 October – 1 November 2026: activities
  1. 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.

  2. Shai-Hulud

    First repository of the burst

    10:13:59 UTC.

  3. Fake security alerts

    Issue repository created

  4. 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.

  5. Contagious Interview

    Payload build pushed

    Pushes to four payload repositories 02:50–04:06 UTC.

  6. Fake security alerts

    Five phishing issues opened

    11:51–12:40 UTC.

  7. Shai-Hulud

    Repository creation moves to a second account

  8. Contagious Interview

    Operators update three staging servers and one payload server

    00:02–02:54 UTC.

  9. 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.

  10. 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.

  11. 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.

  12. 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.

  13. 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 .woff and 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

#
E-ALL-PROOFProof rows for every proven repository

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

E-ALL-SPOTIndependent spot check

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.

← All research