preloader
post-thumb

Last Update: November 2, 2026 10 min read


BYauthor-thumberic

|Loading...

Last weekend a client asked us a simple question: "I think our website might have been hacked. Can you check?"

They had already looked. They had logged in to WordPress and seen nothing odd. An earlier round of checks with AI tools had come back clean too. The site loaded, the pages looked right, and Google was happy.

It had been compromised for almost two years. By the time we finished, we had pulled out 18 separate backdoors, decoded a password stealer, a credit card skimmer and a remote-control script, and identified 49 logins by the attacker from 21 countries. The attacker never once showed up in a way anyone would notice.

This is the story of how it hid, how it was found, and the one setting that quietly stopped the attacker in their tracks. I've kept the client anonymous. Everything else is real.

What "nothing looks wrong" actually looked like

The first clue was in the plugin list, and it was easy to miss. Among 54 plugins sat entries like these:

Shown in WordPress as
Author shown
Reality
WP Func Control
By WordPress
Code execution backdoor controlled by a secret cookie
WP Memory Defender
By WordPress
Encrypted backdoor
WP Cookie Encoder
By WordPress
Cookie-controlled backdoor
WP User Privileges
By Automatic
65 KB of scrambled code pretending to be Akismet
WP Design Editor
By WordPress
93 KB of scrambled code
Achafofine
Shayne Rudy
Password stealer, card skimmer, remote shell

"By WordPress" and "Official WordPress plugin" are the oldest trick in the book, and they work, because nobody reads plugin metadata. They're also not how WordPress ships anything. Note the misspelling of Automattic as "Automatic".

But these were the visible ones. The really dangerous plugins weren't in the list at all.

The 18 backdoors

Over 18 months the attacker planted:

  • 14 fake plugins. Eleven disguised as official WordPress plugins, three with random names (Achafofine, Achykax and Qyfutyhi) complete with invented authors and descriptions stitched together from random scraped text.
  • 3 fake themes, all called "Twenty One", in folders named twentystd, twentyseven and twentyfour. WordPress has never shipped a theme with any of those names, but at a glance they sit comfortably next to the real Twenty Nineteen and Twenty Twenty.
  • 1 folder with no plugin header at all (uhytyqob). WordPress doesn't list it, so it can't be seen or deleted from the admin screen. Its only job was to be there.

The very first one, a fake theme, arrived on 19 January 2024. About sixteen hours later, a file manager plugin on the site was used for the first time. The last new backdoor arrived in June 2025.

And the code wasn't stored where you'd expect code to be:

Where the malware hid

Inside imagesPayloads stored as .png and .gif files: three junk bytes, then a letter-substitution cipher over base64. Open one in an image viewer and it is just broken.
Inside a stylesheetA file called responsive.css that was really PHP, loaded by its plugin.
Inside a typoA file named .htacces (one s), so anyone skimming the folder reads it as the normal .htaccess.
Inside a JPEGfront.jpeg, included as code by the plugin next to it.
Inside a cookieSeveral backdoors did nothing at all unless the request carried a specific cookie containing the instructions.
Inside "inactive"Inactive plugins still sit on the server, and a backdoor is called directly by its URL. Inactive meant nothing.

Why nobody saw it: hiding was the product

What impressed me, in the way a well-built safe impresses a locksmith, was how much of the code existed purely to avoid being noticed.

It removed itself from the plugin list. Five of the plugins hooked into WordPress's own plugin screen (the all_plugins filter and the pre_current_active_plugins action) and deleted their own row before the page was drawn. Active, running, invisible.

It hid from the people who owned the site. Every time an administrator or editor opened the dashboard, the malware quietly recorded a fingerprint of their IP address in the database. For the next 90 days, anyone visiting from that address got the clean version of everything. The owner, their staff, their web developer: all permanently looking at a site that had been sanitised for them.

It never repeated itself. No defacement, no pop-ups, no slowdowns, no deleted content. Vandals want to be seen. These people were running a business, and every extra month undetected was another month of income.

That's also why the earlier AI checks came back clean. They saw what the site was willing to show them: a normal front end, a normal-looking plugin list, a normal dashboard. You can't find malware by asking the infected system whether it is infected.

What the malware actually did

Decoding the hidden files gave us a full kit, from a family that labels itself SMILODON in its own code and server paths:

The toolkit

Login password stealerEvery WordPress login was captured, username and password, and sent to the attacker with the IP address. Staff were handing over their passwords every time they signed in.
Card skimmerOn an online-shop checkout it swaps the real card fields for pixel-perfect fakes, using the real Stripe card-brand images, then sends card, expiry, CVC and billing address out through the server.
Remote-control loaderFor first-time visitors arriving from Google or social media, it downloads and runs whatever code the attacker currently serves. Redirects, fake captchas and fake updates are the usual cargo.
Full web shellA complete WSO remote file manager and command console, stored inside a GIF.

Two details stood out.

The skimmer never sent card data from the browser to a suspicious address. It posted to the shop's own checkout URL, and the PHP side of the malware forwarded it from the server. Browser-based security tools see nothing unusual leave the page.

The copies of those scripts inside the fake plugins were wired to fire only on a WooCommerce checkout page, and this business didn't run an online shop. For a day, that's where our analysis stopped: the visitor scripts looked dormant.

They weren't.

The second hiding place: 281 JavaScript files

While porting the site's theme to a new front end, I opened the theme's own global.js, the small script that runs the menu and search box on every page. Below the theme's last line, }( jQuery ) );, sat the remote-control loader.

A scan of the backup found the same block appended to 281 JavaScript files: the theme, gallery and slider plugins, the AMP plugin, Leadpages, even libraries nobody loads. It was byte-for-byte identical every time, glued onto the end of each file. Through the theme script alone it ran on every page of the site, for every first-time visitor who arrived from Google, WeChat or Facebook, and it was still active the day we found it. Its server was still answering too.

Because the injection was identical and always at the very end, removing it was precise: a one-off plugin cut each file at the marker and stopped. We tested it first on all 281 copies from the backup: every file came back byte-for-byte identical to its original content, and every script still parsed. On the live site it cleaned 273 files (the rest were inside plugins we had already deleted), a second pass found nothing, and the caches were cleared.

The breakthrough: the malware's own logbook

Here's the part I enjoyed most.

Remember that 90-day "hide from admins" feature? To do it, the malware saved an entry for every IP address that logged in to the dashboard: the address (hashed with MD5) and the time of the last visit. It kept that list to protect itself.

That list was also a record of every time the attacker logged in.

MD5 sounds like protection, but there are only about 4.3 billion possible IPv4 addresses. A short C program on a 16-core workstation tries all of them in a few minutes. We recovered 53 of the 54 entries, and the picture was immediate.

Admin visit logged by the malware
Backdoor that appeared
21 Feb 2024, 07:15
Qyfutyhi, at 07:14
23 Feb 2024, 02:39
WP Func Control, at 02:40
7 May 2024, 11:06
WP Cookie Encoder, at 11:06
31 May 2024, 16:34
WP Catcher, at 16:34
8 Jul 2024, 10:57
WP User Privileges, at 10:57
7 Jun 2025, 23:29
Achykax, at 23:29

Every backdoor lines up with a logged-in administrator visit, to the minute. The attacker wasn't sneaking in through a hole in a plugin. They were signing in at the front door with a real admin password and uploading fake plugins like any site owner would. Given what the malware did, the most likely source of that password is the malware itself, collecting it at the login screen.

Forty-nine logins, twenty-one countries, never the same address twice

Between February 2024 and August 2025 the attacker logged in as an administrator 49 times. We recovered 48 of the addresses, and not one of them was a home connection in Australia.

Where the logins came from
Count
Hong Kong
10
Germany
4
Russia
4
United States
4
Canada
3
Poland
3
United Kingdom
3
Indonesia, Japan, Thailand
2 each
Argentina, Ecuador, Egypt, France, Latvia, Malaysia, Nicaragua, Philippines, South Africa, Türkiye, Ukraine
1 each

They were almost all data centres, commercial VPNs and proxy networks: Hong Kong cloud hosts, a German node provider, a well-known consumer VPN's London exit, Russian and Polish hosting companies. Every address in the log is different.

To be precise about what that proves: the log stores one entry per address, so a reused address would simply update its old entry. What we can say is that every session we can see came from a fresh address, often in a different country from the last one. That isn't one person at a keyboard. It's a control panel managing a fleet of hacked sites through a rotating pool of proxies, and checking in on each one every few months to plant another backdoor in case the earlier ones are found.

The mix of malware families tells the same story. The "By WordPress" plugins, the random-name SMILODON plugins and the "Twenty One" themes are different kits. Access to compromised sites is commonly sold and resold, and this site may well have had more than one tenant.

The backdoors didn't need a login, which is exactly why there were so many of them. Two-factor locked the front door; the clean-up removed the windows they had propped open.

Why the early AI checks missed it, and what worked instead

I don't think the earlier AI tools were bad. They were pointed at the wrong evidence. An AI looking through the website or the admin dashboard sees exactly what the malware has decided to show, and this malware was built to show nothing.

Even with the files in hand, the payloads resisted the obvious approach. A standard JavaScript deobfuscator ran over the card skimmer and gave up on the strings: it left 8,372 encoded lookups in place because the decoder was a custom variant. The remote-control script was three layers deep, each layer a scrambled string that unscrambled the next, with the last step handing the result to eval.

What worked was treating it like evidence rather than a web page:

  1. Take a full backup first, download it, and verify it. Every later step works on the copy, not the live site.
  2. Read the files, not the dashboard. File timestamps in the backup showed each backdoor arriving as a single event, months apart, with nothing else on the site changing at the same moment.
  3. Compare against known-good. WordPress publishes checksums for its core files. All 2,913 matched, which told us the core was clean and the problem was entirely in plugins and themes.
  4. Decode without running. For each payload we ran only the decoding routine inside an empty sandbox (no network, no file system, no browser) and printed each stage instead of executing it. 8,372 out of 8,372 strings came back cleanly.
  5. Turn the attacker's tools against them. The cloaking list became the timeline. The C2 addresses and cookie names became indicators we could share.

AI did a lot of the work here, including most of the decoding and analysis in this post. The difference is that it had hands: a backup, a sandbox, a compiler and a terminal, not a browser tab. Give it evidence and it is fast and relentless. Give it a screenshot of an infected site and it will tell you the site looks fine, because it does.

If you run a WordPress site, do these this week

  1. Turn on two-factor authentication for every admin and editor. In this case it is the single thing that stopped the attacker.
  2. Change any password that has ever been typed into your WordPress login, and anywhere else it was reused, starting with email. If a stealer was running, it has them.
  3. Stop plugins and themes being uploaded from the dashboard. define('DISALLOW_FILE_MODS', true); in wp-config.php means a stolen admin password can't install a backdoor. Install plugins through your deployment process instead.
  4. Don't trust the plugin list. Look at the wp-content/plugins and wp-content/themes folders directly, and run wp core verify-checksums and wp plugin verify-checksums --all.
  5. Treat "inactive" as "still on the server". Delete what you don't use.
  6. Scan JavaScript as well as PHP. grep -rl "zqxw" wp-content takes seconds and would have found this campaign's injected scripts.
  7. Check your backups for things you don't recognise. A backup from a year ago is also a time machine for "when did this file appear?"
  8. Watch for a sudden security flag from your host, Cloudflare or Google. It is often the only symptom you'll get.

Longer term, we're moving this client to a setup where WordPress is only an editor, reachable from Australia by signed-in staff, and the public sees a separate site that runs no PHP at all. Staff keep writing the way they always have. Attackers lose the thing they need most: a door to knock on.

Indicators of compromise

If you look after WordPress sites, these are worth searching for.

Type
Indicator
Domains in the code
error403.cc, steg.cc (paths /SMILODON/), whatbeatstone.dev, source.scriptsafedata.com
Cookies
pxcelPage_c01002, pxcelPage_c01003, pxcelPage_c01008, _wptn, _wptoken, _jpw, _jwp, _wdata
Database options
wp_ followed by a 32-character hex string (one per admin IP)
Code constants
SMILODON_HEAD, SMILODON2_HEAD, YII_FORMA_OK, YII_FORMA_FOOTER, YII_WWW_DIR_EVAC
Plugins
Random names (achafofine, achykax, qyfutyhi, uhytyqob) with assets/images/*.png and *.gif payloads and a .txt loader
Plugins
'Official WordPress plugin' by 'WordPress' or 'Automatic': wp-authority-subsystems, wp-cabal-mechanics, wp-kenneth-console, wp-catcher, wp-effectively-vascular, wp-coaching-merit, wp-user-priv, wp-operating-institution, wp-other-body, wp-components-computerize, wp-logical-instead
Themes
'Twenty One' in folders twentystd, twentyseven, twentyfour
Skimmer behaviour
Fake Stripe card fields posting to /?wc-ajax=process with payment[cc_ice]
Loader behaviour
Script defining zqxw, HttpClient, rand and token that fetches init.js and evals it if the reply contains qwzx
Injected JavaScript
The same loader appended to the end of theme and plugin .js files, starting with ;var zqxw,HttpClient,rand,token;(function(){

The scariest thing about this attack wasn't the code. It was how ordinary the site looked the whole time. The encouraging thing is how little it took to stop it, and how much the attacker's own caution gave away once someone looked at the evidence instead of the website.

Comments (0)

Leave a Comment
Your email won't be published. We'll only use it to notify you of replies to your comment.
Loading comments...

Keywords

  • wordpress malware
  • fake wordpress plugins
  • hidden backdoor
  • web shell
  • password stealer
  • card skimmer
  • magecart
  • smilodon
  • obfuscated javascript
  • incident response
  • two-factor authentication
  • headless wordpress
Previous Article
post-thumb

Nov 03, 2026

Halts, Placements and SPPs: Corporate Action Mechanics for Traders

Trading halts, capital raisings, share purchase plans and their settlement dates all create predictable order flow on the ASX. How each mechanism works, why a recent placement price acts like a magnet, what happens when a halted stock reopens, and the traps of holding through a halt.

Next Article
post-thumb

Nov 02, 2026

Surviving Earnings Season as an Intraday Trader

You'd think reporting season would be a day trader's paradise: catalysts everywhere. In the journals I read, it was often the worst stretch of the year. Why too much news hurts, how ASX reporting seasons and quarterlies work, and a practical plan for trading results without getting chopped up.

agico

We transform visions into reality. We specializes in crafting digital experiences that captivate, engage, and innovate. With a fusion of creativity and expertise, we bring your ideas to life, one pixel at a time. Let's build the future together.

Copyright ©  2026  TYO Lab · v0.0.36