Shai-Hulud 2.0: How I Uncovered Secrets From Major Organizations During the Largest npm Supply Chain Attack

Shai-Hulud 2.0 had dropped.

If you're in the security space, you already know what Shai-Hulud is. The original campaign hit the npm ecosystem back in September 2025 — a self-replicating worm that compromised hundreds of packages, harvested developer credentials, and exfiltrated them to attacker-controlled GitHub repositories. It was a big deal. And I missed it. By the time I caught wind of v1.0, it was already mitigated. Packages were cleaned up, repos were nuked, and the window had closed.

I told myself: next time, I won't be late.

What changed in v2

"The Second Coming" — that's literally what the attackers called it. The repository description on every exfiltration repo read: "Sha1-Hulud: The Second Coming."

This time around, the campaign was significantly more aggressive. Instead of exfiltrating to a single endpoint that could be rate-limited (like they did in v1), the attackers used stolen GitHub tokens to create public repositories directly under compromised users' accounts. The malware ran TruffleHog on victims' machines to scan for secrets, then dumped the results into JSON files — environment.json, contents.json, and truffleSecrets.json — with secrets double base64-encoded inside those files.

Every affected account had a repo with a Shai-Hulud-style naming pattern. Over 20,000 repositories. Hundreds of thousands of secrets. All sitting in plain sight on GitHub.

As soon as I read the first few blog posts breaking down the technical details, I knew exactly what I needed to do.

I already had the tooling

Here's the thing — I already had the tooling.

I've been building iscan.today, a security scanning platform that supports org scanning, repo scanning, and dork-based scanning. It's built for exactly this kind of moment. The secret scanning engine was already there. What I didn't have was support for this specific attack pattern: identifying Shai-Hulud exfiltration repos and decoding the double base64-encoded secrets inside.

So I opened Cursor and prompted it:

Add support for detecting Shai-Hulud affected repositories. Check if the repo name matches the attack pattern. If it does, pull the JSON files, double-decode the base64 content, and run secret scanning on the decoded output.

The code was done in seconds. Literally seconds. I plugged it into my existing dork scanning pipeline and hit run.

Secrets everywhere

The moment the scan started, my screen lit up.

Hundreds of live secrets. Across hundreds of repositories. GitHub tokens. npm tokens. AWS keys. GCP credentials. Azure secrets. API keys for services I'd never even heard of. All valid. All exposed. All belonging to real organizations.

I started analyzing every secret — tracing which company owned it, which bug bounty program (if any) they had, and what the potential impact was. I was primarily focused on org-owned secrets rather than individual developer credentials, because those represented the most critical exposure.

I also prompted Cursor to write a report generation script. When you're looking at this volume of findings, you can't afford to craft each report by hand. I needed a generic template that I could populate with the specifics — the exposed secret type, the affected organization, the repository where it was found, and remediation steps. That script saved me hours.

I went to sleep and regretted it

I spent the entire evening scanning, validating, and reporting. By around 1:00 AM, I was exhausted. I had a decision to make: keep going, or get some sleep and pick it up in the morning.

I chose sleep. Left the scan running overnight.

That was a mistake.

When I woke up, I had thousands of new findings sitting in the results. The scan had kept churning while I slept. But here's the gut punch — many of my reports started coming back as duplicates.

When I checked the timestamps on the duplicate reports, most of them were filed around 2:00–3:00 AM. Right after I went to bed. Someone else — probably multiple people — had been running the same playbook, scanning through the night while I was sleeping. The window between "first reporter" and "duplicate" was measured in hours.

If I had just pushed through that one night, the payout could have been significantly larger. Lesson learned the hard way.

Next morning: too many secrets, not enough time

The morning brought a new problem: sheer volume. Thousands of secrets to validate is too much for one person. I needed to prioritize.

I decided to focus exclusively on GitHub tokens and npm tokens. Why? They were the easiest to validate. A quick API call tells you if a GitHub token is still active and what scopes it has. Same with npm tokens. No ambiguity, no guesswork — either the token works or it doesn't.

I spent the entire day validating and reporting. The reports went everywhere:

  • 10+ bug bounty programs across HackerOne, Bugcrowd, YesWeHack, and Intigriti
  • 50+ reports sent directly to companies that didn't have formal bug bounty programs

It was a full day of work. Just me, my tooling, and an inbox full of draft reports.

How companies responded

This is the part that always fascinates me about bug bounty hunting. You submit the same type of finding — critical secret exposure from a supply chain attack — and the responses are wildly different:

The Good: Some companies awarded maximum bounty. No questions asked. They understood the severity, rotated their secrets immediately, and paid out quickly. A few companies that didn't even have a bug bounty program still sent payments as a thank-you. That's class.

The Silent Rotators: Some companies quietly rotated their secrets without ever responding to my report. I'd check back a few days later, the token would be dead, but my inbox? Empty. No acknowledgment, no thanks. Just silence.

The Promisers: A few companies initially responded positively, promised a reward, and then… ghosted. Emails stopped. Tickets went unanswered. The corporate version of "the check is in the mail."

The Deleters: Some companies deleted the exposed repository but never rotated the actual secret. The repo is gone, sure, but if an attacker already grabbed the token before deletion — which they almost certainly did — it's still valid. As of writing, some of those secrets are still active. Deleting the evidence isn't the same as fixing the problem.

The Duplicates: And then there were the duplicates. Many of my reports from that morning batch came back marked as duplicates, with the original reporter having submitted during the 1–4 AM window while I was sleeping.

Results

Despite the duplicates and the sleep tax, I managed to collect bounties from around 10 companies across all my reports. Individual payouts ranged from $100 to $3,500, depending on the program and the severity of the exposure.

After 2–3 days, GitHub took action and started automatically removing the exfiltration repositories. The immediate attack surface was cleaned up. Things cooled down.

Then Trust Wallet got hacked

But the story didn't end there.

A few weeks later, in late December 2025, news broke that Trust Wallet's Chrome browser extension had been compromised. Attackers had pushed a malicious version (v2.68) to the Chrome Web Store on Christmas Eve, draining approximately $8.5 million from over 2,500 wallets.

When Trust Wallet published their post-mortem, the root cause was clear: their developer GitHub secrets had been exposed during the Shai-Hulud 2.0 campaign. Those leaked secrets gave the attackers access to Trust Wallet's source code and their Chrome Web Store API key, which they used to publish the backdoored extension.

I had actually found Trust Wallet's exposure in my scans and reported it. Duplicated.

The Shai-Hulud 2.0 attack wasn't just a credential leak. It was the seed for a multi-million dollar heist that wouldn't materialize until weeks later. That's the terrifying thing about supply chain attacks — the initial compromise is just the beginning. The real damage comes downstream, when stolen credentials are weaponized in ways nobody anticipated.

The Shai-Hulud 2.0 campaign exposed over 294,000 unique secrets across more than 20,000 repositories. It compromised packages from organizations like Zapier, PostHog, and Postman. It was, by most measures, one of the largest supply chain attacks in npm history.

And for one long night and day, I was racing through it — scanning, decoding, validating, reporting. It wasn't glamorous. It was exhausting, repetitive, and occasionally frustrating. But it's exactly the kind of moment that makes bug bounty hunting worth it: a real attack, real exposure, and a real opportunity to make things a little safer.

Even if you do lose a few bounties to sleep.