|8 min read|Yvann Lièvre

Fake GitHub Repos: IOC Feed for the Typosquatting Infostealer Campaign (292+ Repos)

292+ typosquatted GitHub repos push infostealers disguised as security tools and crypto wallets. Building an IOC feed (hashes, C2) to detect them.

IOCSupply ChainGitHubInfostealer
Fake GitHub Repos: IOC Feed for the Typosquatting Infostealer Campaign (292+ Repos)

A developer is looking for a vulnerability scanner, a command-line crypto wallet, or an obscure macOS utility. A repository shows up with a plausible name, a clean README, a handful of stars, a bundled installer. They clone it, they run it. What they just installed is not the tool they were looking for. It is an infostealer.

This is the pattern behind a large-scale GitHub typosquatting campaign tracked over the past several months: 292+ repositories identified so far, impersonating security tools, cryptocurrency wallets, and popular macOS utilities. What makes this campaign hard to handle is not technical sophistication, it is fairly ordinary, but its regeneration speed: a repository taken down after a report reappears under a near-identical name within hours. This is a distinct developer-targeted vector from the legitimate software supply chain problem we cover elsewhere (vulnerable open source components inside an SBOM): this is not a compromised dependency, it is a deliberate impersonation of a project's identity.

Why the repo name is never enough

The obvious trap is building detection on the repository name or on typosquatting patterns (a swapped character, an added hyphen, a -tool or -cli suffix). That is a dead end, for a simple reason: thousands of legitimate repositories have names that structurally resemble the malicious ones. An honest fork of a security scanner, a wallet under active development, a macOS utility maintained by a single developer, all of these look, from a distance, like typosquatting targets.

The anti-false-positive key to this campaign fits in one sentence: it is never the repository name that proves malice, it is the hash of the delivered installer and the command-and-control domain it contacts. Two repos with an identical name can carry entirely different content; two repos with different names can ship the same malicious binary, recompiled for the occasion. The repo is the distribution vector, not the indicator.

The IOC pack: what it contains, and why it has to run continuously

The pack built for this campaign brings together three categories of indicators:

  • C2 domains used by the fake installers for exfiltration and configuration persistence.
  • Hashes of the fake installers (fingerprints of the binaries or install scripts observed in the wild, not filename heuristics).
  • Identified GitHub repositories, with status tracking (active, taken down, reappeared under a new name) to follow a single campaign's reincarnations.

The ingestion logic cannot be a one-off. Where a typical vulnerability feed refreshes on a daily cadence, this one needs an hourly cycle: the speed at which repositories get recreated after takedown is measured in hours, not days. A pack generated once a week would be stale before it ever shipped.

feed:
  name: github-typosquat-infostealer
  refresh_interval: 1h
  sources:
    - github_repo_registry     # flagged repos, status tracked
    - installer_hash_corpus    # SHA-256 hashes of observed installers
    - c2_domain_list           # domains observed in post-execution callbacks
  consumers:
    - proxy_web
    - edr_endpoint
    - yara_scanner

Catching the clone and the download upstream

Before execution even happens, the earliest observation point sits at the proxy and EDR layer, on raw content retrieval requests. The fake installers are overwhelmingly delivered through raw.githubusercontent.com URLs pointing at the flagged repositories, bypassing GitHub's rendered pages entirely.

Simplified proxy detection rule:

detection:
  title: Download from a flagged GitHub repo (infostealer campaign)
  logsource:
    category: proxy
  detection:
    selection:
      url|contains: "raw.githubusercontent.com"
      url|contains|any:
        - "<repo-path-1>"
        - "<repo-path-2>"
    condition: selection
  level: high

The url|contains|any field is populated dynamically from the pack's flagged repo path list, updated on the hourly cycle. A match here does not yet prove infection: it flags a download to correlate with the next step.

Before (naive approach on repo name) :

detection:
  selection:
    url|contains: "security-scanner"
  condition: selection

This rule floods on every developer legitimately looking for an open source security scanner, the vast majority of these hits will be benign.

After (flagged repo name correlated with confirmed hash on the endpoint) :

detection:
  title: Flagged GitHub clone followed by known-hash installer execution
  logsource:
    product: edr
  detection:
    selection_download:
      url|contains: "raw.githubusercontent.com"
      url|contains|any: "<repo-path-list>"
    selection_execution:
      file_hash|in: "<installer-hash-list>"
    condition: selection_download and selection_execution near 1h
  level: critical

The combined condition narrows the signal to what actually matters: a download from a monitored repository followed, within a short window, by the execution of a binary with a known hash.

Spotting the dropped installer on the endpoint

On the endpoint, the goal is to catch the post-clone behavior: an executable file or install script appearing in a characteristic path (downloads directory, temp directory, or directly inside a Git clone folder), then running under standard user privileges, bypassing any recognized package manager.

Elements to correlate on the EDR:

  • The hash of the executed file, checked against the pack's known installer corpus.
  • The post-clone execution path (typically inside or adjacent to a git clone directory).
  • Immediate outbound connections to one of the pack's C2 domains.

A complementary YARA rule, applied to the installer itself rather than its filename, covers recompiled variants of the same family even before their exact hash is added to the pack.

Raising awareness without assigning blame

The entry vector is almost always the same: a developer is looking for a legitimate tool (security scanner, wallet, system utility) and picks, among the search results, a repository that appears to match. This is not isolated carelessness, it is a structural blind spot: GitHub enforces no project identity verification, and the usual trust signals (stars, forks, commit activity) are trivially forgeable at small scale.

Two habits reduce exposure without slowing anyone down: verifying the organization or maintainer behind an unfamiliar repository before cloning it, and cross-referencing this risk with the dependency monitoring already in place in the pipeline (SBOM analysis). The two problems are adjacent but distinct: one concerns a declared dependency integrated into the build, the other a repository manually cloned by an individual outside any dependency management process.

In summary

This campaign is not handled with a list of suspicious repo names, it is handled with a feed that follows the installer hash and the command-and-control domain, refreshed fast enough to keep up with a recreation cycle measured in hours, and consumed at the same time by the web proxy, the EDR, and a rule engine on the binary itself. The repo name stays a weak signal to correlate, never proof on its own.

That is exactly the discipline applied in the ThreatClaw IOC feed: installer hashes, C2 domains, and flagged repositories refreshed on an hourly cycle, ready to be consumed by your proxy, your EDR, and your detection engine before your developers become the entry point for the next intrusion.

Related articles