Most compromise stories start with a break-in: an unpatched plugin, a brute-forced password, a leaked key. This one didn't. When I finished tracing it, the uncomfortable answer was that nobody broke in at all. The backdoor had been sitting on the server for two years, installed on purpose, by the site owner, as part of a theme they downloaded for free. The attack that finally made the phone ring was just someone walking through a door that had been standing open since 2024.
This is a story about nulled software — pirated copies of paid plugins and themes — and about a simple forensic technique, timestamp clustering, that can prove where a backdoor really came from. Everything here is sanitized; the point is the method and the lesson, not the victim.
The call: a defacement, and files that came back
The visible problem was ordinary. A business site had been defaced — its homepage replaced with a spam gambling page, along with a rewritten robots.txt and a fake sitemap.xml to drag search rankings along with it. There was an active persistence mechanism keeping those files alive, which we removed, and there was a credential-stealing snippet planted the week before. That part of the cleanup was routine, and it is not what this article is about.
What made this case worth writing up came after the live infection was gone. Scattered across the site's theme and plugin folders were roughly a dozen tiny PHP files — a few hundred bytes each — that did one thing: fetch code from a remote server and run it. Small, generic, remote-execution backdoors. The obvious assumption is that whoever defaced the site last week dropped them. That assumption was wrong, and the file timestamps were about to prove it.
The question that changes everything: how old is it?
Before you can fix a compromise, you have to answer one question honestly: how did the first foothold get here? Clean the symptom without answering it and you will be back next month. The fastest way to start is almost embarrassingly simple — ask the filesystem when each suspicious file was last modified, and look at the shape of the answer.
$ find WEBROOT -name '*.php' -printf '%TY-%Tm-%Td %TH:%TM:%.2TS %s %p\n' | sort
When you bucket suspicious files by date, the distribution tells a story. Files an attacker uploads during a single session cluster around that session — last Tuesday, within a few minutes of each other. But these backdoors did not land near the recent defacement at all. They fell into a much older cluster, and that cluster is the whole case:
count date
18 2024-07-30 <- theme & plugin files, AND the backdoors
2 2026-09-07 <- a couple of later copies
6 2026-09-29 <- last week's defacement + credential stealer
Timestamp clustering: proving files shipped together
Here is the technique in one sentence: files that were extracted or copied in a single operation share a modification time, often down to the same second. When you unzip an archive, the extractor stamps every file it writes within the same short window. So if a backdoor carries the exact same timestamp as the legitimate files around it, the backdoor was in the archive too.
That is precisely what I found. The dozen backdoors did not just fall on the same day as the theme's legitimate files — several shared the same timestamp down to the second:
2024-07-30 11:31:42 THEME/framework/loader.php (legit)
2024-07-30 11:31:42 THEME/framework/class-loader-ext.php (backdoor)
2024-07-30 11:31:42 THEME/framework/widgets.php (legit)
2025-05-10 10:35:18 PLUGIN/core/config.php (legit)
2025-05-10 10:35:18 PLUGIN/core/cache-config.php (backdoor)
2025-05-10 10:35:19 PLUGIN/core/loader.php (legit)
A backdoor and a legitimate framework file, written to disk in the same second, two years ago. That is not something an attacker did after the fact by logging in and uploading a file — a later upload would carry a later, isolated timestamp (which is exactly what the 2026-09 files show). The only way the backdoor and the theme's own code share an extraction second is if they came out of the same ZIP. The theme was backdoored before the site owner ever downloaded it.
Timestamps can be forged, so this is evidence, not proof beyond doubt — but attackers rarely bother to backdate individual files inside a bundle they are already trojanizing, and the clustering across a dozen files is very hard to fake convincingly. When the mtimes line up like this, provenance is usually settled.
What "nulled" actually means
A premium theme or plugin that normally costs money gets re-hosted for free on a "nulled", "GPL club", or "free download" site — often with the license check ("nulling") removed so it runs without a key. That is the pitch: the paid thing, for free. The catch is the part nobody advertises. Whoever went to the trouble of cracking and re-hosting a paid product did not do it out of generosity. They did it to distribute their code alongside yours.
In this case the injected files were small remote-execution stubs, buried inside the theme's framework and a bundled page-builder plugin — directories a site owner never opens and a busy administrator never audits. They sat dormant. No spam, no defacement, no CPU spike. Just a quiet, remotely-triggerable way back in, on every page load, waiting.
Two years later, someone came to collect
Dormant is not the same as harmless. Roughly two years after installation, the pre-installed backdoors were used exactly as designed: to gain an authenticated foothold, plant a credential stealer, and stand up the gambling defacement and its self-healing persistence. (The mechanics of that self-healing layer — a disguised watcher process and cron jobs that rebuild each other — are a rabbit hole of their own, and one I have written about separately. Here it is just the payload; the story is how the door got there.)
The sequence is what matters for defenders. The dramatic event — the defacement — was the last step, not the first. The first step was a download, two years and one decision earlier. Any timeline that starts at "the site got hacked last week" is starting the story two years too late.
Why no amount of downstream security fixes this
This is the part worth sitting with. Every standard hardening measure — a web application firewall, two-factor authentication, aggressive patching, a malware scanner — is designed to keep bad code out. None of them help when the bad code was invited in, carried by a bundle the owner installed deliberately, signed by the same trust they extended to the theme itself. A scanner may eventually flag a known-bad stub, but nulled packages are re-trojanized constantly and can pull fresh payloads on demand; you are playing signature whack-a-mole against a threat that lives inside software you chose to run.
There is only one control that actually closes this channel, and it is upstream of every tool: do not run pirated software. Install premium themes and plugins only from the original vendor, with a valid license, or use their genuinely-free tiers. "Free download of the paid version" is not a bargain — it is the delivery mechanism. If budget is the constraint, the honest options are the vendor's free edition, a real GPL redistributor that sells support, or a different product you can afford. A €59 plugin obtained for free routinely costs a full incident response, and sometimes a rebuild.
Takeaways for defenders
- Let timestamps tell the origin story. Bucket suspicious files by mtime. A backdoor sharing an extraction second with legitimate bundle files came in that bundle — that single observation can rewrite your entire timeline.
- Date the foothold, not just the symptom. The loudest artifact (a defacement, a spam page) is usually the last thing to happen. Find the oldest malicious file and start the story there.
- Treat nulled plugins and themes as pre-compromised. Not "risky" — compromised, by default, until proven otherwise. The business model of free premium software is the payload.
- Audit the folders owners never open. Theme framework directories and bundled plugins are ideal hiding places precisely because nobody looks there.
- Provenance is a control. "Where did this software come from?" belongs on your security checklist next to "is it patched?" — because for supply-chain infections, it is the only question that matters.
The most memorable part of this case wasn't a clever payload or novel obfuscation. It was the timeline. An infection that looked days old turned out to be two years old, and it arrived not through a firewall gap but through a checkout the owner chose to skip. Sometimes the vulnerability isn't in the code at all — it's in where you got the code from.