In short
- A hacked WordPress site almost always keeps an administrator created by the attacker or added or modified files, often both.
- From the dashboard, Users > All Users, Administrator filter: every account should have a name and a real person behind it.
- With WP-CLI,
wp core verify-checksumsandwp plugin verify-checksums --allcompare your files with the originals from WordPress.org. - Do not delete anything straight away: remove rights, make a copy, then clean up. A reinstall does not remove added files.
Contents
The two most common traces of a hacked WordPress site
An attacker who gets into a WordPress site wants to be able to come back, even if the hole they used is fixed tomorrow. To do that, they usually leave one of these two doors open, or both:
- an administrator account: they then log in like any other manager of the site;
- an added or modified file: a backdoor in the WordPress core, in a plugin, in a theme, or a PHP file slipped into the media folder.
The WordPress documentation lists the creation of new accounts among the clear signs of a hack, and points out that an attack always leaves traces in the logs or in the files.
The visible signs (redirects, added pages, a warning from Google or the hosting provider) often come later. These two traces, on the other hand, can be checked right now.
Spot an unknown administrator in WordPress
No terminal needed for this first check.
- Log in to the WordPress dashboard, Users > All Users menu.
- Above the list, click the Administrator filter.
- For each account, ask yourself: who is this, and who created it? A generic username (“admin2”, “support”, “wpadmin”), an email address nobody recognises or an account belonging to a former contractor need dealing with.
- Do the same for the Editor role and, on a shop, Shop manager (a role added by WooCommerce): they can change the site’s content.
The Administrator role gives access to everything: plugins, themes, accounts and settings. One account too many at this level is enough to take over the whole site.
Spot a modified or added file in WordPress
Each WordPress version, and each version of a plugin published on WordPress.org, has an official list of checksums: a kind of unique fingerprint for each file. If a single character in a file changes, its checksum changes. Comparing your files with this list tells you exactly which ones have been modified, and which ones have no business being there.
This comparison is run with WP-CLI, the WordPress command-line tool (details below). Without a terminal, you can already make two checks with your FTP client:
- the
wp-content/uploadsfolder should only contain images, documents and videos. A.phpfile there is almost always a backdoor; - the
wp-content/mu-pluginsfolder, if it exists: these plugins always load and do not appear in the usual plugin list. Each file should have an explanation (your hosting provider or agency sometimes installs some).
| Name | Size | Modified |
|---|---|---|
| .. | ||
| sale-poster.jpg | 412 KB | 06/10/2026 |
| autumn-catalogue.pdf | 2.1 MB | 09/10/2026 |
| image-cache.php | 3 KB | 21/10/2026 |
| logo-white.png | 18 KB | 06/10/2026 |
A file’s modification date proves nothing: an attacker can change it. That is why comparing checksums is the reference check.
Your WordPress site is hacked: what to do, in order
The WordPress documentation insists on one point: stay calm and write everything down before acting. What you saw, at what time, what changed recently (new plugin, update, new contractor). It is the start of the incident report, useful for your insurer and your contractor. If customer data is involved, the GDPR requires you to notify your data protection authority (the CNIL in France) without undue delay, within 72 hours of discovery where feasible, even if you complete the notification later.
- Do not delete anything. An erased account, file or log is lost evidence. Have a full copy made of the site and the database, even infected.
- Cut off the attacker’s access: remove the unknown account’s rights (Subscriber role) instead of deleting it, and end its sessions.
- Change every password: WordPress administrators, FTP or SFTP, the hosting provider’s customer area, the database. Every access, not just yours.
- Renew the security keys in
wp-config.php. Every logged-in account is logged out, including the attacker’s. - Put back the original files, in the same version as the one installed: WordPress advises replacing
wp-adminandwp-includesentirely, then dealing with plugins and themes one by one. Also check the.htaccessfiles. - Find the way in (vulnerable plugin, stolen password), fix it, then update WordPress, the plugins and the themes.
- Change the passwords a second time, once the site is clean.
Hello,
We think our WordPress site has been hacked: [describe what you saw, with the date and time].
Without deleting anything for now, could you:
- make a full copy of the site and the database, to keep for analysis;
- run “wp core verify-checksums” and “wp plugin verify-checksums –all”, and send us the result;
- look for PHP files in wp-content/uploads;
- list the administrator accounts directly in the database;
- send us the web server access logs for the last 30 days.
Please also let us know whether other sites hosted on the same server are affected.
Thank you.
You manage the server: check WordPress integrity with WP-CLI
WP-CLI installs as a single file (see its installation guide). Always run it as the user who owns the site’s files, here www-data, never as root: the WP-CLI project points out that all the site’s code, plugins included, would then have full power over the server. On a hacked site, that code is precisely what is suspect.
The examples assume a site in /var/www/site; replace the path with yours.
-
The WordPress core.
wp core verify-checksumsdownloads the official checksums for your version and compares them with your files. It does not load WordPress to do so, so no code from the site runs.Terminalsudo -u www-data wp core verify-checksums --path=/var/www/siteOn a healthy site:
Success: WordPress installation verifies against checksums.On our test site, after modifying one core file and adding another:Result on a test siteWarning: File doesn't verify against checksum: wp-includes/functions.php Warning: File should not exist: wp-includes/class-wp-cache-helper.php Error: WordPress installation doesn't verify against checksums.doesn't verify: modified file.should not exist: file added to a core folder. The--include-rootoption adds unknown files at the site root. If your WordPress is in another language, add the matching--localeoption (for example--locale=fr_FR) if in doubt. -
Plugins.
wp plugin verify-checksums --alldoes the same for each plugin published on WordPress.org.Terminalsudo -u www-data wp plugin verify-checksums --all --path=/var/www/siteResult on a test siteWarning: Could not retrieve the checksums for version 1.0 of plugin my-custom-plugin, skipping. plugin_name file message akismet akismet.php Checksum does not match Error: Only verified 1 of 3 plugins (1 failed, 1 skipped).A plugin that was bought or custom-built has no checksums on WordPress.org: it is skipped (
skipped). Compare it with the archive provided by its vendor. WP-CLI has no equivalent command for themes. -
Administrator accounts.
--skip-plugins --skip-themesavoids loading the plugins and the theme, and therefore any code that could hide an account. Note that plugins inmu-pluginsstill load.Terminalsudo -u www-data wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --skip-plugins --skip-themes --path=/var/www/siteResult on a test siteID user_login user_email user_registered 2 helpdesk helpdesk@example.net 2026-10-23 00:58:28 1 manager manager@example.com 2026-10-23 00:58:11To get around any filter, read the database directly. This query lists the accounts whose capabilities contain
administrator. Replacewp_with your table prefix, given bywp db prefix:MySQL or MariaDB client, or “wp db query”SELECT u.ID, u.user_login, u.user_email, u.user_registered FROM wp_users u JOIN wp_usermeta m ON m.user_id = u.ID WHERE m.meta_key = 'wp_capabilities' AND m.meta_value LIKE '%administrator%';An account that appears here but not in Users > All Users is hidden: that is an intrusion.
-
Executable files in
uploads.Terminalfind /var/www/site/wp-content/uploads -type f \( -name '*.php*' -o -name '*.phtml' -o -name '*.phar' \) -printThe command should print nothing. Each line it prints is a file to copy for analysis, then remove.
.htaccessfiles, on the other hand, are often legitimate: Contact Form 7 and WooCommerce place one in their folders (wpcf7_uploads,woocommerce_uploads,wc-logs,wc-imports) precisely to block access to them. Display them with their content:Terminalfind /var/www/site/wp-content/uploads -type f -name '.htaccess' -exec sh -c 'echo "== $1"; cat "$1"' _ {} \;A legitimate file only blocks access: “Deny from all” or “Require all denied”, sometimes wrapped in
<IfModule>lines. Any other directive, especiallyAddHandler,AddType,SetHandlerorphp_value, is there to get code executed inuploads: copy the file for analysis, then remove it. -
Plugins that always load.
Terminalsudo -u www-data wp plugin list --status=must-use --skip-plugins --skip-themes --path=/var/www/site sudo -u www-data wp plugin list --status=dropin --skip-plugins --skip-themes --path=/var/www/site
Restore WordPress with WP-CLI
Once the backup copy is made, these commands carry out the steps above. The example deals with account no. 2 on our test site.
For developers
wp core verify-checksums runs before WordPress loads: it is the only one of these commands that runs none of the site’s code. wp plugin verify-checksums, wp user list and the restore commands load WordPress. On a badly compromised site, run these checks on an isolated copy of the site, or read the database with the SQL query rather than with WP-CLI.
These checks have their equivalent on Magento: see spot a suspicious admin account and what to do when Magento 2 is hacked.
Tocsin24 watches your WordPress site between two checks
WP-CLI gives you a snapshot at a given moment. With the Tocsin24 plugin installed, monitoring is continuous, independently of your security plugins: the plugin records the state of the site, and Tocsin24 analyses it on its own servers.
Accounts. Tocsin24 flags any new administrator account, and any account with sensitive rights that is present in the database but hidden from the list of accounts (“Hidden administrator account”).
Files. The plugin records the files of the core, plugins, must-use plugins and themes every hour. On the first full pass, Tocsin24 keeps this record as a baseline. It then flags any file modified, added or deleted outside a recognised update, as well as known malicious files. It also watches for unexpected files in uploads, with a critical alert for a PHP file or a script.
Plugins. A new plugin, a new theme, or a plugin that has been removed from WordPress.org or abandoned is flagged.
Tocsin24 blocks nothing and cleans nothing: it complements your security plugin, as explained on the WordPress page. Since the baseline is taken when the plugin is installed, run wp core verify-checksums once at that point to start from a healthy site.
Critique Ouvert #318
Nouveau — compte administrateur : assistance
Sécurité · détecté le 23 octobre 2026 à 01:04 · 1 occurrence
Traiter l'incident
Compte concerné Relevé du 23 octobre 2026 à 01:04
Compte administrateur inconnu du référentiel, apparu sur l'instance. Vérifiez qui l'a créé avant de le valider.
- Identifiant
- assistance
- Nom
- Assistance technique
- assistance@example.net
- Rôles
- administrator
- Liste des utilisateurs
- présent
- Inscrit le
- 23 oct. 00:58
Critique Ouvert #319
Nouveau — fichier uploads : 2026/10/image-cache.php
Sécurité · détecté le 21 octobre 2026 à 14:37 · 1 occurrence
Traiter l'incident
Fichier concerné Relevé du 21 octobre 2026 à 14:37
Fichier PHP ou script présent dans un dossier de téléversement : webshell probable.
- Chemin
- wp-content/uploads/2026/10/image-cache.php
- Extension
- php
- Taille
- 3 Ko
- Modifié le
- 21 oct. 14:32
- Empreinte
- 9f2c…41d7
Preuve détaillée dans « Historique et preuves ».
Application screens with example values.
Frequently asked questions
How can I tell whether my WordPress site has been hacked?
The visible signs: redirects to other sites, added content, a warning from Google or your hosting provider. The quiet signs: an administrator nobody knows, and files that no longer match the originals. wp core verify-checksums and wp plugin verify-checksums --all spot them in a few seconds.
Should I delete an unknown administrator straight away?
First remove their rights (Subscriber role) and end their sessions: they can no longer do anything, and you keep the record of when the account was created for the investigation. Delete it once the intrusion is understood.
Is a PHP file in wp-content/uploads serious?
Yes. This folder should only contain media. A PHP file there is almost always a backdoor planted by an attacker. Make a copy for analysis before removing it, and treat the site as compromised.
Is reinstalling WordPress enough to clean a hacked site?
No. A reinstall replaces existing files but leaves added files in place: check again with wp core verify-checksums. You also need to change every password and the keys in wp-config.php, and find the hole that was used, otherwise the attacker comes back.
My security plugin did not notice anything. Is that normal?
It happens. A plugin runs inside WordPress, just like the attacker's code, which can get around it. A check done from the outside (WP-CLI, comparison with the originals, separate monitoring) is a useful complement to the plugin.
Sources
- WordPress: FAQ My site was hacked
- WordPress: Hardening WordPress (file monitoring)
- WordPress: wp-config.php, security keys
- WordPress: roles and capabilities
- WordPress: pre_user_query filter
- CNIL (French data protection authority): notifying a personal data breach (in French)
- WP-CLI: wp core verify-checksums
- WP-CLI: wp plugin verify-checksums
- WP-CLI: wp user list
- WP-CLI: wp user set-role
- WP-CLI: wp user session destroy
- WP-CLI: wp config shuffle-salts
- WP-CLI: wp core download
- WP-CLI: wp plugin install
- WP-CLI: global parameters (--skip-plugins, --skip-themes)
- WP-CLI: common issues (do not run WP-CLI as root)
- WP-CLI: installing