Skip to main content
 /  Around 10 minutes to read

The Code You Didn't Write

Malicious code ran on my machine before I knew it was there.

I pulled down a codebase for some work, like I normally do. I cloned the repo, installed the dependencies, spun it up locally, and started poking around to get my bearings. Then things started looking strange.

The Code You Didn't Write

This is the same thing I've done a few hundred times over the years, the same thing most developers do without a second thought when they inherit a project.

You clone, you boot, you look around.

Then I noticed something odd at the end of one of the backend files.

Right after the last real line of code, sitting just past a module.exports, there was a wall of obfuscated JavaScript wrapped in a self-decoding string-shuffle routine.

Committed right into the repo, in a file I'd already loaded several times.

Because it sat after module.exports in a CommonJS module, it self-executed the moment that file got required, which meant it ran the instant the app booted.

Nothing to import or trigger or click. It just went.

And by the time I understood what I was looking at, I'd already started the app a few times to watch it run. So this thing had quietly executed on my machine, several times, before I even knew it existed.

The rest of that day involved a lot less contract work and a lot more scanning my machine and rotating everything I could think of.

What It Was Actually Doing

I pulled it apart with static analysis, reading it carefully without ever running it again. What it was doing was clever, in the way that makes your stomach drop a little.

Here's what it did, on a loop throttled to roughly every thirty seconds with a global flag so it wouldn't hammer anything or spike CPU in a way someone might notice.

  1. It queried the latest transaction from a couple of attacker-controlled wallets on a public blockchain. In my sample that was Tron, with a fallback node configured for resilience.
  2. It read a pointer out of that transaction, which turned out to be the hash of a second transaction on another chain.
  3. It fetched that second transaction and pulled a payload out of the raw data attached to it.
  4. It decrypted the payload with a hardcoded key.
  5. It executed the result.

The throttling and the fallback node are the details that tell you this was built by someone who cared about staying alive.

A payload that phones home every few seconds gets noticed. But, one that checks in twice a minute and quietly does nothing most of the time can sit undisturbed for a very long while.

Which is exactly what happened here.

The code committed into the repo was only a loader. It carried no malicious logic of its own that a casual reader would recognize as an attack. All it did was fetch whatever the attacker had most recently posted to the chain, decrypt it, and run it.

The actual commands lived on-chain, which means the attacker could push a brand new payload whenever they felt like it, just by broadcasting a new transaction for a dollar or two in fees.

There was no server to seize and no domain to take down, because the blockchain itself was the command channel.

The loader in the repo never had to change, and it never did.

This is the piece that makes the technique so freakin' hard to kill.

Traditional malware phones home to a server somewhere, and that server has an address, which means it can be blocklisted or seized or sinkholed. Security researchers spend a lot of their time doing exactly that.

A public blockchain has none of those weak points.

It's decentralized, replicated across thousands of nodes, and it's usually built from the ground up to be impossible to censor or take down. The same properties that make people excited about blockchains are the properties that make this an almost perfect place to hide a command channel.

This technique has a name.

It's called EtherHiding, and it was first documented by Guardio Labs in October 2023.

Since then it has gone from a curiosity to a genuine trend. Google's Threat Intelligence Group has tracked it across roughly 14,000 compromised web pages, and in late 2025 they documented a North Korean state group adopting it, the first time a nation-state actor was seen using the method.

Security researchers have taken to calling the pattern a dead-drop resolver, borrowing the old espionage term for a hidden spot where a handler leaves instructions for an agent to collect later.

The blockchain is the drop, the loader is the agent, and neither side ever has to meet.

The escalation hasn't slowed down. By the end of 2025 the technique had been packaged up and put on sale, with at least one operator advertising a blockchain-based command-and-control botnet on underground forums, complete with a web panel for pushing new payloads.

What started as a clever trick has become a product other criminals can rent.

Because the loader runs at boot, it has the entire process environment in scope. Every database credential and API key and token sitting in process.env is right there for the taking.

The payload in my case was built around crypto-wallet theft, which lines up with what the security researchers keep finding, since these campaigns tend to go straight for wallets and credentials.

Why This One Was Different

Most documented EtherHiding lives in the browser.

The typical chain starts with a compromised WordPress site or a fake job interview, tricks someone into running code, and pulls the real payload from the chain. The victim is usually an end user who got socially engineered into clicking the wrong thing.

Mine was kinda different.

This one was server-side, committed straight into source, and aimed at whoever inherited the repo and booted it.

There was no phishing and no fake recruiter, just a payload sitting in the code itself. The delivery surface was the codebase, and the victim was a developer doing completely ordinary work (me! yay!)

I didn't get tricked into anything. I did the most normal thing in the world, which was run a project I'd been hired to work on.

It was unsettling, to say the least. The whole attack relied on nothing more than a developer behaving exactly the way developers are trained to behave.

What Actually Matters

Cloning a repo is an act of trust, and almost nobody thinks of it that way.

We treat git clone as neutral, like picking up a book. And it mostly is, since cloning by itself doesn't execute anything.

The next couple of commands are a different story.

  • npm install runs code, through install hooks that fire automatically before you've done anything at all.
  • npm start runs code, the entire application entry point, written by whoever touched it last.

The moment you boot an inherited project, you are executing thousands of lines you have never read, written by people you have never met. And the worst part is that you're doing it with your own credentials on your own machine.

That was always true.

It was true ten years ago and nobody got hurt most of the time, because the population of people running unfamiliar code was smaller and more hardened, and the attacks were rarer.

The threat stayed the same, but it's the number of people standing in front of it that has exploded.

More people are entering development through no-code tools and AI-assisted paths that never taught them to treat inherited code as hostile. And hey, even if you've been at it for over 20 years like I have, you still get into your own habits, like I did.

More code is generated by AI and merged by people who never read it line by line. More repos get forked and handed off and inherited than ever before.

The crowd exposed to it simply exploded, and a huge share of that crowd has never once been told that booting someone else's project is the moment they're most exposed (in all honesty, I should've known better.)

I've written before about how reviewing code you can't fully evaluate is quietly becoming everyone's default state. This is the security face of the same problem.

When you don't read what you run, and increasingly nobody does, you're trusting authors you can't see and can't vet.

The New People At The Door

What makes this moment specifically dangerous goes beyond the general truth that inherited code is a risk.

For most of the history of this craft, the people running unfamiliar codebases were developers who had come up through some version of the hard path. They'd been burned before. They'd seen a rogue postinstall script, or watched a dependency go bad, or at least absorbed the folklore from people who had.

The instinct to be a little suspicious of someone else's code was baked in through experience, even if nobody could quite articulate it.

That filter is gone now, or at least it's a much smaller share of the room.

The people entering development today are arriving through paths that never taught the suspicion.

Someone who builds their first app by prompting an AI and copying the output has never been burned, because the tool did the part where you'd normally learn to be careful.

Someone who clones a template from a tutorial, or forks a repo a stranger posted, or inherits a codebase that was itself half-generated by a model, is running enormous amounts of code that no human ever read with an adversarial eye.

And they're doing it with the same casual confidence you'd bring to opening a document.

AI makes this worse in a second way, beyond just growing the crowd.

A model will happily generate code that pulls in a package, and it has no real sense of whether that package is trustworthy. It's pattern-matching against a training set, not vetting a supply chain.

When the code looks right and runs clean, the person prompting it has no reason to look closer, and the tool that wrote it can't warn them that the dependency it just added has a name one typo away from something malicious.

The whole loop optimizes for output that works, and works and safe are very different tests.

None of this is a reason to gatekeep who gets to build things. More people making software is good. I truly believe that.

But the on-ramp skipped a lesson that used to come standard, and the lesson is that the code you didn't write is a stranger, and you don't hand a stranger your keys just because they showed up looking useful.

How It Hid For Over A Year

The thing that really got me is that this payload had been sitting in the repo for more than a year before I found it.

It slipped in through an innocent-looking merge commit under a boring title. Something forgettable, the kind of message you scroll past a hundred times without a flicker of suspicion.

And that's exactly why it worked.

Merge commits and dull messages make excellent camouflage. A normal git log and a pickaxe search across the history tend to skip right over merge commits, so the malicious diff lived in a blind spot that most people never think to check.

The attacker didn't need to hide the code well. They just needed to hide it somewhere nobody looks, wrapped in a commit message nobody reads.

I still don't know exactly how it got in. It could have been a compromised contributor account, a malicious pull request that got waved through in a busy week, or a dependency that pulled it in further upstream.

Any of those is plausible, and for the person who inherits the repo later, it almost doesn't matter.

By the time it reached me it was just another commit in the history, indistinguishable from the honest ones, carrying the same implied trust as everything around it.

A repository's history looks authoritative, but it's only ever as trustworthy as the least careful moment in its past.

What To Actually Do

None of this means you should be afraid to take on inherited work. It means treating a few habits as non-negotiable. Here's what I've pulled from the whole experience, laid out as a quick reference.

Signal or moment What to do about it
You just cloned an unfamiliar repo Treat it as untrusted code until you've read it. Cloning is safe. Booting is the risk.
You see obfuscated code in a source tree Red flag, every single time. Real code has no reason to be scrambled, so when it is, assume the worst until proven otherwise.
You're about to run npm install or npm start Both execute code you didn't write. Skim the package scripts and entry points first, and run unknown projects in a container or VM.
You're reviewing the git history Actually read it, merge commits included. Boring titles hide interesting diffs.
Something ran that shouldn't have Assume every secret in that environment is compromised. Rotate all of it, and scan the machine.

The container point is the one I'd underline though.

If I had spun this project up inside a throwaway virtual machine instead of straight on my working laptop, the entire incident would have been a shrug instead of a scramble.

The malware would have run, stolen nothing worth having, and died with the VM. Gone.

That single habit turns most of these attacks from a crisis into a non-event, and it costs almost nothing once it's part of your routine.

The obfuscation red flag is worth internalizing too because, really, there's essentially no legitimate reason for application source to be scrambled into a self-decoding string blob.

Minified build output is one thing, but hand-obfuscated logic sitting in a source file you're meant to edit is a warning. The moment you see it, stop and read before you run anything.

Read Before You Run

I've been doing this for twenty years, and it still got me.

Not because the attack was sophisticated on my end, since I wasn't targeted personally and I wasn't careless in some dramatic way.

It got me because I extended the same casual trust to inherited code that all of us extend a dozen times a year or more. The trust that says this project is just a project and running it is just running it. That trust was never really earned though.

The uncomfortable takeaway has little to do with blockchain malware coming for you specifically. It's that the industry just onboarded a few million people who were never taught to treat the code they inherit as a stranger, right as the tooling made inheriting and generating unread code the normal way to work.

So read before you run. The gap between git clone and the first time you boot the thing is the last moment you're truly safe.

Be careful out there. It's easy to assume the code you inherit is clean, and sometimes it really fucking isn't.