Trzy rodziny backdoorów w jednej instalacji WordPress
The client's website was unavailable for several days. Underneath: three independent backdoor families, a Chinese C&C server, a Ukrainian Telegram bot and a phishing kit impersonating a Japanese store. All in one WordPress installation that looked healthy from the outside.
When the customer contacted us, there was not a single symptom of „something not working”. There was a specific, measurable problem: the company website was unresponsive. Behind this failure there were several small signals that individually meant nothing, but together they created a picture of systematic, long-term access by third parties to the server. We finished the story in one day. We left the mechanism that made us sure that the problem had really disappeared to the client permanently. If you're looking for exactly this type of care, check out ours website maintenance and development offers.
In the case of a company website that serves as a website and a source of leads, this is a direct path to a measurable loss:
- zero inquiries during the entire period of unavailability
- search engine positions to be rebuilt for weeks after returning (Google is removed from the index in a few hours, restored in a few weeks)
- loss of trust of customers who have received an error message in the meantime
- risk of permanently marking the domain as a „deceptive site” by Google Safe Browsing and Microsoft SmartScreen, which blocks the website in Chrome, Edge, Firefox and anti-virus warnings even after removing the code
Our work started with recovery from failure: restoring the availability of the website to regular traffic was the first step, and the diagnosis and cleanup took place in parallel, only after the website was launched.
Industry and context
The client runs a company landing + offer website, based on WordPress with Elementor Pro and the Jet plug-in family. Shared hosting. A standard stack that serves hundreds of thousands of companies in Poland. That's why this case is interesting: it doesn't stand out in any way. It could have been any company's website. For comparison, We made a similar WordPress case for a numerology school: different industry, same level of complexity under the hood.
Starting point
Small, difficult to relate things started happening on the website:
- new files appeared periodically
index.phpin random plugin subfolders - file
license.txtin the main directory it had zero size and was regularly „moved” (changing the timestamp without changing the content) every several days - Google Search Console showed pages indexed in Japanese that no one was publishing
- next to WordPress, under
/quiz/, the „calculate the cost of a photovoltaic installation” quiz in Polish worked, although the client does not deal with photovoltaics
Each of these signals individually can be ignored. Together this is a textbook example of long-term disgrace.
Diagnosis: three backdoor families + phishing kit
The investigation showed that the server had been attacked several times over the years over three years, by different groups, with different goals. Below is a short description of each family, and under each description there is an expanded section with technical details for those interested.
Family 1: Japanese SEO cloak + Chinese RCE
Main index.php website that normally runs WordPress was modified to hold three overlapping layers of malicious code. An ordinary user from Poland saw a normal page. Googlebot from Japan saw a fake store. An attacker with the right parameter in the URL received remote code execution.
▸Technical deep-dive: three layers in one file
- Cloak SEO in Japanese: if the request came from Google.co.jp, Yahoo.co.jp, Bing or Baidu (checked after
User Agent+HTTP referer), the server responded to othersLocationthan normal and redirected to a fake store masquerading as Askul. - „2DUAN” (Chinese family): separate block with markers
2DUAN_STARTi2DUAN_end, with stitched URL to C&C server encoded in ROT13, symmetric key in constantKKand instance identifier in the variableNN. In addition, remote code execution by parameters?pwd=X&gv=Y(password after MD5). - At the end: a normal WordPress bootstrap so that the site does not crash for normal traffic.
Three layers in one file strongly suggest that the server passed through successive groups of attackers, and each added its own piece.
Family 2: dropper with random hashes
The second family is remote loader, which from time to time created a new folder with a random hash and threw PHP into it, which through cURL retrieved a text response from the C&C server and executed it through eval. Three generations of dropper lived in the server in parallel, at different ages. The oldest one from December 2022.
▸Technical deep-dive: three generations, one launcher
/f1dec/of December 2022 (the oldest, probably the moment of the first burglary)/wp-content/ad1b029e/of August 2026/wp-content/x9f8ea6/of September 2026 (regeneration during our diagnostic session, caught live)
In each instance, an identical launcher with the same SHA hash. Zero license.txt in the root was their „canary”: a simple marker after which the attacker knew that persistence was still alive.
Family 3: magic-URL admin creator with auto-heal
It was the most dangerous family because it could rebuild itself. One HTTP request with the appropriate parameter in the URL caused the server to create a WordPress administrator account itself and log the attacker immediately. The second file, in a hidden location, rebuilt the whole thing if someone deleted the first one.
▸Technical deep-dive: two files, three locations, zero chance of manual cleanup
firewall.php: responded to a specific GET parameter in the URL. One hit caused the server to create an administrator account (login and email sewn in the code) and immediately logged the attacker without the form.fixer.php: HTTP-open regenerator. If someone removedfirewall.php, one HTTP request was enough tofixer.php, so that he decodes the backup copies from base64 and puts them back on the disk, in three locations at the same time: in the root, inmu-plugins, in the theme folder.
This meant that manually deleting files on the client side did nothing. As long as he lived on the server fixer.php, the other files were coming back.
Add-on: phishing kit /quiz/
Regardless of the backdoors, in the subfolder /quiz/ a quiz based on SaaS Marquiz was running. Setting up the quiz: Polish scam lead gene for photovoltaics. Leads from the form were sent in two channels: to the client's company mailbox (the attacker impersonated him as the sender) and on Telegram's Ukrainian bot (chat ID + bot token sewn in the code).
Attack timeline (reconstructed)
| Date | Event |
|---|---|
| 2022-12-10 | First loader (/f1dec/index.php). Probably the moment of the original compromise by the old CVE. |
| 2025-08-12 | Deploy phishing kit /quiz/ + Telegram bot. |
| 2026-08-22 | Second-generation dropper (/wp-content/ad1b029e/). |
| 2026-08-30 14:16 | Batch drop: a hidden folder with a name containing quotes (antiNext trick) + wp2shell-batch-guard. |
| 2026-08-31 01:31 | Massive deploy: firewall.php + fixer.php + wp2shell-batch-guard.php in three locations. On the same day, the attacker replaced the WordPress core and overwritten wp-config.php, probably to regain control after some automatic cleanup. |
| 2026-09-03 11:33 | During our session: third generation dropper (/wp-content/x9f8ea6/) appears live. We catch her on the fly. |
| 2026-09-03 11:52 | Full cleanup of all three families + phishing putty. |
Three years of persistence, including a whole series of active surgeries in the last month. The customer didn't know anything.
What we have done
Step 1: Restore availability
The first hours went to make the site work at all, no matter what we find underneath. Shutting down traffic from the outside, restoring a clean entry point (index.php), removing the obvious 500s. Only then did the correct diagnosis begin.
Step 2: Enumerate all droppers
Full webroot scanning after characteristic markers (goto Base64, 2DUAN, eval(curl(...)), unusual quotation marks in folder names as an anti-lstat trick). Each file found: backup off-site + deletion.
Step 3: Cutting the regeneration chain
Family 3 regenerated for fixer.php. While fixer.php he was alive, the other files were coming back. We had to establish that fixer.php exists in three locations at the same time and remove all three in one pass. After the first attempt, in which we deleted only a copy in the theme, the files returned within a few minutes.
Step 4: Phishing kit + Telegram
Deleting an entire directory /quiz/ and the related Marquiz account (we gave the client a list of steps to carry out at their home). Blocking shipments from own domain as a sender (SPF + DMARC ===reject).
Step 5: Watcher mu-plugin (stays permanently)
We have built a dedicated mu-plugin, let's call it a „forensic watcher” that lives in wp-content/mu-plugins/ and boots up with every PHP request. It is part of the package that we attach to each contract as part of website maintenance and development.
▸What exactly does the watcher do (list of functions)
- Logs all, What is changing
.PHPon the server: new file, modification, deletion. Separate log with full path + timestamp. - Logs each attack attempt: request with known signatures of the three backdoor families. Separate log, separate file.
- Blocks 403: before the attacker gets a response, the server returns 403 Forbidden to known signatures. So even if in the future some miracle appears on the server
firewall.php, the magic URL won't work anyway. - Diagnostic endpoints token-protected: status, baseline, change list, user list, cron list, grep by file content, autoremediation. One HTTP request and we know if anything on the server has changed in the last 24 hours.
- Logs outside webroot: log files go to the directory higher than the publicly available webroot, additionally from
.htaccess deny. Even if the attacker gets FTP access again, he will not see what exactly we know.
Step 6: Database Audit
In addition, we checked through the watcher that there is no trash from attackers in the base: wp_users (the only admin is a customer account, no accounts created by attackers), wp_options.autoload (clean), WP-Cron (clean, no registered hooks that cannot be assigned to standard plugins).
Why it happened in the first place
Three independent vectors are rare, but the specific reasons attackers enter are one of three things in 95% cases:
- Outdated plugins with public CVE. The server had several plugins from the Jet family version from a dozen or so months ago. One of them in the period 2022-2023 had a publicly described gap allowing for authenticated file upload.
- Weak admin password or leak from external service (data breach). The original compromise may not have been „technical”: it is enough that someone used the same password in several places, and one of them leaked.
- Shared Hosting. Some documented attacks on shared hosting involved passing from one compromised side of a neighbor to the other through a shared
TMPor the default directory rights.
Rezultaty w liczbach
Trzy lata ciszy, dziesięć dni erupcji, jedna minuta cleanupu
Skumulowana liczba złośliwych artefaktów na serwerze w czasie. Każdy punkt to osobny deploy atakującego. Zielony punkt na końcu to nasza interwencja.
Co znaleźliśmy
Podział szkodliwych artefaktów
- Japanese cloak + 2DUAN
- Dropper (3 generacje)
- magic-URL admin
- Phishing kit /quiz/
Czas: atak vs reakcja
Porównanie skal
Paski proporcjonalne. Watcher kompresuje czas detekcji o ok. tysiąc razy względem stanu pierwotnego.
Czego nauczył nas ten case
1. Backdoory potrafią współistnieć
Przyzwyczajenie „jeden atak = jeden rodzaj szkodnika” jest mylące. Jeśli strona była wystawiona i podatna przez lata, mogła być skompromitowana kilka razy, przez różne grupy, z różnymi celami. Każda z nich zostawia własny mechanizm persistencji i każdy z nich trzeba znaleźć osobno. Zatrzymanie się po usunięciu pierwszego pliku to typowy błąd.
2. Auto-heal to główny mechanizm przeżycia nowoczesnego malware
Pliki w widocznych miejscach to przynęta. Prawdziwym mechanizmem przeżycia jest regenerator w nieoczywistej lokalizacji, który odbudowuje widoczne pliki za każdym razem, gdy ktoś je usunie. Jeśli po „wyczyszczeniu” strony pliki wracają w ciągu kilku godzin, znaczy to, że czyszczono tylko objawy.
3. Twoja domena może być narzędziem, nawet jeśli Twoja strona wygląda OK
Phishing kit z Telegram botem nie zrobił nic widocznego z perspektywy odwiedzającego stronę. Nie pokazywał reklam, nie przekierowywał, nie zmieniał SEO. Po prostu wykorzystywał domenę jako nadawcę. Dla Google, Microsoftu, Barracudy i reszty operatorów antyspamowych wygląda to tak, jakby to Ty wysyłałeś phishing. Konsekwencje (SPAMhaus blacklist, utrata deliverability) są poważniejsze niż defacement.
4. Monitoring plików to nie luksus
Większość klientów rozpoznaje atak, dopiero gdy coś wizualnie się zepsuje: strona nie ładuje się, pojawia się ostrzeżenie Chrome „deceptive site”, Google wywala z indeksu. W tym momencie szkodliwa aktywność na serwerze trwa często miesiącami. Prosty watcher, który loguje zmiany .PHP, daje czas reakcji liczony w godzinach, nie w miesiącach. To standardowy element każdej naszej umowy utrzymaniowej.
5. Backups before the X date contain backdoors
The standard reflex after detecting a compromise („I will restore the backup from a week ago”) would not work in this case. The backup from a week ago contained a complete set of backdoors. Backup from a year ago, too. The only way is a full forensic cleanup + fresh backups from scratch.
WordPress backdoor: how to detect it and remove it so it doesn't come back
This guide is for owners of business websites on WordPress who suspect a hack or have already “cleaned” the site once, only for the strange files to come back. We answer the question of how to remove a WordPress backdoor in a way that closes the case, not just treats the symptoms.
What a WordPress backdoor is and why it stays invisible for so long
A WordPress backdoor is a piece of PHP code that gives an attacker persistent access to the server regardless of the passwords in the admin panel. It can create administrator accounts, download and execute code from an external server, or swap content for selected visitors.
The key trait: a well-written backdoor doesn't break the site. Ordinary users see a normal website, because the malicious code launches WordPress at the end anyway. In the case described above, the main index.php file contained three layers of foreign code, and for more than three years the site looked healthy from the outside.
Owners usually find out about the problem only when the browser shows a dangerous site warning, Google starts dropping pages from its index, or the site stops responding.
Hacked WordPress site: warning signs you shouldn't ignore
A single symptom is easy to dismiss. Together, they form a pattern of long-term compromise. It’s worth acting if you see even a few of the following:
- new index.php files in random plugin subfolders or directories with hash-like names (e.g., a string of letters and digits in wp-content),
- zero-byte files whose modification date changes regularly without any change in content,
- pages in a foreign language in Google Search Console that nobody published,
- subdirectories with content unrelated to the company, e.g. forms, quizzes, landing pages,
- administrator accounts nobody created, or unknown WP-Cron jobs,
- reports from customers about emails sent “from your domain” that you didn't send.
In the case described, all these signs were visible long before the outage. An empty license.txt file in the root directory served attackers as a marker that their access was still working.
How to remove a backdoor from WordPress: the order of steps
The most common mistake is deleting suspicious files one by one as you find them. An effective process looks different:
- Restoring availability. A clean entry point (index.php), traffic limiting, and eliminating 500 errors. Diagnosis runs in parallel, but the site has to work first.
- Preserving a copy of the evidence. Every file found is copied off the server before it's deleted. Without that, you won't be able to reconstruct what happened and since when.
- Full enumeration. Scanning the entire webroot for characteristic patterns, rather than browsing folders “by eye.”
- Breaking the regeneration chain. First, you find every copy of the mechanism that restores the other files and remove them all in a single pass.
- Cleanup beyond the files. Database, email, DNS, and third-party service accounts.
- Continuous monitoring. A mechanism that will alert you if anything comes back.
Scanning PHP files: what to look for in the webroot
Security plugins catch known signatures, but backdoors are often obfuscated. It's worth searching .php files for a few patterns: eval constructs with content fetched via curl, long base64 strings combined with goto, ROT13-encoded text, and unusual characters (e.g. quotation marks) in directory names that make them harder to list.
The second avenue is comparing the WordPress core with the official version. In the implementation described, the attacker replaced core files and overwrote wp-config.php. Such changes are visible only when compared against a clean copy, not by browsing the admin panel.
The third area is the mu-plugins directory and the theme folder. Files in mu-plugins always load and don't appear in the regular plugin list, which makes them a convenient hiding place for an attacker.
Malware with auto-heal: why files come back after deletion
If files come back a few hours after the site was “cleaned,” the symptoms were removed, not the cause. Modern backdoors have a regenerator: a separate file in a non-obvious location that, with a single HTTP request, restores everything that was deleted from a backup copy (e.g. base64-encoded).
In the case described, the regenerator sat in three locations at once: in the root directory, in mu-plugins, and in the theme folder. After the first attempt, in which only the copy in the theme was removed, the files came back within minutes. Only removing all three at once closed the loop.
The second trap is several malware families coexisting. A site that's been vulnerable for years often gets taken over several times, by different groups. Here there were three independent backdoor families and a separate phishing kit. Stopping after finding the first family would have meant the others kept running.
Database, email, and backups after a hack
Files aren't everything. After the cleanup, you need to check the wp_users table (for unknown administrators), autoloaded options in wp_options, and registered WP-Cron jobs. In the case described, the database turned out to be clean, but without this verification there would have been no way to confirm it.
Email is a separate issue. A phishing kit hidden in a subdirectory was sending emails with the client’s domain address as the sender. To anti-spam operators, this looks as if the company itself were sending phishing, and the result can be the domain getting blacklisted. The protection is a correct SPF record and a DMARC policy set to reject.
The last thing is backups. The reflex “I'll restore last week's backup” doesn't work if the breach is several years old. Here, both the backup from a week ago and the one from a year ago contained the full set of backdoors. After a complete cleanup, you need to start a fresh series of backups from scratch.
WordPress file change monitoring instead of yet another cleanup
The most common causes of a breach are outdated plugins with a published vulnerability, weak or reused administrator passwords, or shared hosting neighbors. Updates and passwords are the foundation, but they don't tell you that something has already happened.
That’s why, after the cleanup, the client received an mu-plugin that logs every change to .php files, blocks requests with known signatures of these backdoor families with a 403 code, and keeps the logs outside the public directory. Detecting a returning dropper takes minutes, not years. Since this mechanism was deployed, there has been no recurrence.
If you have a company website on WordPress and nobody is monitoring the server from the inside, a good first step is a review of all .php files in the webroot and the list of administrator accounts. Such a review shows whether any WordPress backdoor is running on the server before the site stops responding.
We provide ongoing monitoring, updates, and incident response as part of website maintenance and development; you can check the state of your website by ordering website audit, and if you suspect an active breach, the fastest route is through direct contact.
Who this case is important for
- Owners of company websites on WordPress who „have hosting from an agency” and do not know who is responsible for its security
- Companies that have stopped updating plugins because „it works, do not move”
- Anyone who has seen strange files on their site
index.phpin unexpected locations and removed them manually, assuming it was the end of the matter
If this sounds familiar, a good first step is audit of all files .PHP in server webroot + list of administrative accounts in WordPress. Such an audit usually takes us a few hours, and it shows whether the problem exists at all. Even more cases you will find in the resources, the full offer is in covered by the offer.
You don't want to know you have a backdoor
...when the page is already lying
This case describes the page we got in the crash. Almost everything could be avoided if someone guarded the server from the inside: monitored file changes, updated plugins, kept backups, watched attempts of attacks. This is our service.
See the offer: maintenance and development of the parties →
or write to us directly, we'll do the audit without obligation.
Check before we talk
Services associated with this deployment. Take a look at the offer, see how we approach similar problems in other customers.
Podobał Ci się ten artykuł?
Jeśli po lekturze czujesz, że w Twojej firmie też są procesy warte przebudowy, umów bezpłatną rozmowę. Sprawdzimy razem, gdzie realnie wycieka sprzedaż i co ma sens wdrożyć w pierwszej kolejności.
Schedule a call