A Dead Blog Held Four Backdoors Hiding Behind a Cape Verde Link
At the 2026 World Cup, Cape Verde caused a sensation.
First appearance ever. Population 550,000. The second-least populous country to reach the World Cup, after Iceland. They held Spain to a 0-0 draw in Group H, then went to extra time against defending champions Argentina in the round of 32 before falling 2-3.
Watching this, I was restless.
Because 13 years ago, I wrote about Cape Verde on this blog.
September 2013. Back then, almost nobody in Japan had heard of the country’s name.
I wanted to link to it with a smug look on my face. I really wanted to.
But I couldn’t. The blog was dead with a 500 error.
The last time I’d properly touched it was 2018. Before that, the last update was 2013. In other words, I’d left it unattended for over ten years. I hadn’t even been aware I’d left it unattended. That’s how thoroughly I’d forgotten about it.
So I opened the lid just to post one link, and found strangers living inside. Four of them.
This article isn’t about the recovery procedure. It’s about what came out when I opened the lid.
Just to be clear up front: my background is in electrical circuits, and running a web server is more of a side task for me. What follows isn’t an expert’s investigation report — it’s the scream of a person who opened his own storage shed for the first time in ten years.
The first sign of trouble was a file nobody could read
I tried to run tar to take a backup, and one file couldn’t be read.
---------- 1 user group 66108 Aug 20 2013 wp-content/plugins/search.php
Change: 2023-06-03
Permissions 0000.
Not even I, the owner, could read it. My own file on my own server.
A file created in 2013 had had only its permissions changed in 2023. Left unattended for ten years, then one day its access rights were suddenly stripped.
This wasn’t corruption — it was the trace of quarantine. My hosting provider’s tamper-detection system had made it unreadable and sealed it off. Thank you.
I restored the permissions and looked inside. A WSO web shell — a backdoor complete with a file manager, command execution, and a full SQL client.
What made me angry was how carefully it was implemented. It checked the User-Agent to detect search engine crawlers and returned a 404, hiding its existence. It disabled error_log so no logs remained.
It was built more conscientiously than my own site.
There wasn’t just one of them
The one thing I did right that day was not stopping at “if it’s quarantined, we’re fine.” I scanned everything.
| File | Move-in date | Status |
|---|---|---|
wp-content/plugins/search.php | 2013-08-20 | Quarantined |
wp-includes/pomo/admin.php | 2013-07-10 | Running free |
wp-includes/pomo/feeds.php | 2013-07-10 | Running free |
php5.php (directly under the public directory) | 2014-04-12 | Running free |
Four of them.
The detection tool had only caught one.
A .250 batting average. In pro baseball that’s minor-league territory.
And the move-in dates: two on July 10, 2013, one 40 days later, and one more the following year. Regular additions. Like an extension being built on.
Skilled at hiding
wp-includes/pomo/ is a WordPress translation library, and its legitimate structure is fixed at six files.
entry.php mo.php plural-forms.php po.php streams.php translations.php
Mixed in here were admin.php and feeds.php.
Deep in the core, a place nobody opens for ten years. The filenames were chosen to look plausible too. It felt like someone had been living in my attic.
The contents of feeds.php were obfuscated as well — an elaborate construction that wrote eval in hex and invoked it through the regex /e modifier.
But this modifier was removed in PHP 7.
So this one, just like me, had been killed off by a PHP version upgrade. A comrade.
The scariest thing was the “backup”
There was a directory called backup/. A snapshot from 2013.
And in the handover notes I’d written myself, it said:
The only asset. Do not delete.
In bold.
I opened it, and the same backdoor, three copies of it, were sitting there untouched.
And it was in the public directory.
/backup/php5.php → HTTP 200
/backup/wp-includes/pomo/feeds.php → HTTP 200
/backup/wp-includes/pomo/admin.php → HTTP 200
All of them were alive. Exposed to anyone, for 13 years.
Thinking about it, it’s obvious. A backup taken after infection preserves the infection along with everything else.
And the tricky part is that backups carry a label saying “important, do not delete.” So nobody looks inside. It’s the thing that survives longest. The place that looked safest was the place left most unattended.
The moment I fixed it, it started serving eight-year-old malware
I swapped out the core, and the site finally displayed. My relief was short-lived — before upgrading to the latest version, I checked the version number, and this is what came up:
name="generator" content="WordPress 4.5.13"
What I’d installed was the 5.9 series.
There’s no way the core could roll backward. I checked three times, thinking I must be misreading something.
The answer was written at the end of the HTML.
</html><!-- Cache file was created on 29-03-18 22:48:26 -->
It was serving static HTML baked on March 29, 2018.
I’d restored wp-content/ wholesale, and the 2018 cache had come along with it. Cache plugins, if HTML exists, return it immediately without routing through PHP — so it was faithfully replaying eight years ago. A time capsule. Nobody asked for it to be dug up.
The problem was its contents. Stuck at the end of that same HTML was an <script> tag loading jquery-update.php from two completely unrelated external sites. A classic sign of tampering, and filenames chosen to look harmless — also classic.
I counted 40 files.
Meaning from the very moment the site came back online, it had been serving this to visitors.
I thought I’d fixed it, and instead I’d faithfully reproduced 2018’s contamination. It made me question what “recovery” even means.
Incidentally, the injection source wasn’t the cache — it was header.php and footer.php in the currently active theme. Clear the cache, and next time it comes right back out from there. A two-layer setup. Nobody asked for that either.
I even falsely accused something
While scanning, pharmaceutical spam keywords turned up in a WordPress core file.
For a moment, the color drained from my face. If even the core was contaminated, that would change everything.
I looked inside.
general-template.php:13 * specialised header will be included.
spe-cialis-ed.
An English word.
By the same logic, a well-known plugin’s bot-detection code got flagged en masse as “crawler-evasion implementation.” That too was legitimate functionality.
To a mind gripped by paranoia, everything looks suspicious.
And there was something that had been failing continuously for ten years
This is the part that hit me hardest.
After the recovery, I checked every external script the theme was loading. Of 8, 3 either pointed to destinations that no longer existed, or didn’t support https.
The breakdown: an ad delivery service that had long since shut down, and an access counter that the hosting provider used to offer.
Parts from 2013 had sat written into the HTML for over ten years, unnoticed by anyone, silently failing the whole time.
On top of that, 53MB of debris from an unrelated CMS turned up too. 798 files. Permissions 777. Wide open. I have no memory whatsoever of when I put that there.
I’d assumed the site I’d left unattended was simply “stopped.” That nobody was watching, and nothing was happening.
That wasn’t true at all.
Four things that shouldn’t have been running were running, and three things that should have been running were dead.
Neglect isn’t stasis. Where nobody’s watching, things that multiply on their own and things that break on their own both pile up.
The culprit is unknown
I’ll write this honestly.
The point of entry is unknown. WordPress back in 2013 was in the 3.x series, and every plugin was old across the board. There are too many possibilities to narrow down. The worst-case situation: too many suspects.
What was done over those ten years is also unknown. WSO can manipulate files, manipulate the database, and execute commands. Fortunately there were only the two legitimate admin accounts, and no unauthorized user had been added. But no access logs remain, so what was actually done can’t be traced.
Even the cause of this latest 500 error is unknown. The root .htaccess had been rewritten into a template for a completely different framework, not meant for WordPress.
Was it the intruder’s doing, or an operational mistake by my past self?
The most pathetic part is that either is plausible.
I preserved the specimens without deleting them, just dropping their permissions. Keeping the evidence. Not that I know what it’s evidence of.
So, how did the actual Cape Verde article hold up?
Let’s remember the original goal here: posting the link with a smug look on my face.
I reread my own article for the first time in 13 years.
The 2013 me had cited the FIFA rankings, pointing out they were “one spot above Japan, which was 37th at the time,” cited their run to the quarterfinals in their first-ever Africa Cup of Nations appearance, and introduced a 23-year-old player playing for Lille in France as “a promising player.”
So far so good. A fine piece of work. Something to be smug about.
And then I’d written this:
They might actually be a country that’s been building up some real strength lately, I guess.
“I guess.”
Hedging. About a country that, 13 years later, would stop Spain from converting on 27 shots in the actual tournament — “I guess.”
What’s worse comes after that. In the latter half of the same article, after going on at length criticizing the sloppy organizational management of African football, I closed with this:
A team that repeatedly threatens the world’s powerhouses will suddenly pop up out of Africa, but these don’t tend to last long, and they sink again quickly.
Cape Verde spent the following 13 years not sinking, and made it to the World Cup finals.
13 years later, I’d been refuted by myself.
What’s more, the subject of the 2013 article was the incident where Cape Verde was disqualified from World Cup qualifying for fielding a suspended player. A country that had shut its own door to the finals through a procedural failure went on, 13 years later, to earn its first-ever appearance on its own merits.
Looked at that way, the article’s choice of subject wasn’t bad. Only the writer’s own judgment was wrong.
Results
4 active backdoors → all neutralized (200 → 403)
Injected scripts in the theme → removed (0 deliveries)
2018 cache containing malware → quarantined and regenerated
6 sets of old WordPress in public dir → moved to private dir (771MB)
Zero data loss. All 294 articles matched under a full line-by-line hash check. What needed protecting was protected.
And now, finally, I can link to the article from 13 years ago.
I exterminated 4 backdoors, evacuated 771MB, migrated the database server, and burned an entire day. All to link to one article about football.
The lesson fits in one line. Don’t leave things unattended.
That said, if I hadn’t left it unattended, I never would have discovered the four cohabitants, nor would I have discovered that 13-years-ago me had written something so hedged and timid. That part is complicated.
The recovery work itself involved plenty of stumbling around too, but I’ll save that for the next article.
References
- My own article: Cape Verde / September 16, 2013 (the article in question)
- FIFA: Argentina vs Cape Verde Round of 32 match report
- WordPress: What to do if your site has been hacked (official documentation)
- WordPress: the legitimate structure of
wp-includes/pomo/(source repository) - WordPress: about drop-ins and the cache directory
This piece was drafted and directed by Yuichiro Kuzuryu, with the writing by AI.
Originally published in Japanese at https://clazytech.com/2026/08/1759/. Translated with LLM assistance and reviewed before publication.