Security · WordPress

WordPress hacked? Spot a new administrator or a modified file

When a WordPress site is hacked, the attacker almost always leaves two traces: an administrator account they created for themselves, and files added or changed to keep a foothold. Here is how to spot them from the admin dashboard, then with WP-CLI by comparing your files with the WordPress originals, and what to do, in order, to take back control without destroying the evidence.

Illustration: the red Tocsin24 bell rings above a list of accounts whose last entry, highlighted in blue, has just appeared, next to files being examined with a magnifying glass.

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-checksums and wp plugin verify-checksums --all compare 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.

  1. Log in to the WordPress dashboard, Users > All Users menu.
  2. Above the list, click the Administrator filter.
  3. 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.
  4. 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/uploads folder should only contain images, documents and videos. A .php file there is almost always a backdoor;
  • the wp-content/mu-plugins folder, 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).
/var/www/site/wp-content/uploads/2026/10
NameSizeModified
..
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
Example. A PHP file among the month's media: it has no business being there.

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.

  1. 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.
  2. Cut off the attacker’s access: remove the unknown account’s rights (Subscriber role) instead of deleting it, and end its sessions.
  3. Change every password: WordPress administrators, FTP or SFTP, the hosting provider’s customer area, the database. Every access, not just yours.
  4. Renew the security keys in wp-config.php. Every logged-in account is logged out, including the attacker’s.
  5. Put back the original files, in the same version as the one installed: WordPress advises replacing wp-admin and wp-includes entirely, then dealing with plugins and themes one by one. Also check the .htaccess files.
  6. Find the way in (vulnerable plugin, stolen password), fix it, then update WordPress, the plugins and the themes.
  7. Change the passwords a second time, once the site is clean.
Message to your hosting provider or agency

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:

  1. make a full copy of the site and the database, to keep for analysis;
  2. run “wp core verify-checksums” and “wp plugin verify-checksums –all”, and send us the result;
  3. look for PHP files in wp-content/uploads;
  4. list the administrator accounts directly in the database;
  5. 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.

  1. The WordPress core. wp core verify-checksums downloads 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.

    Terminal
    sudo -u www-data wp core verify-checksums --path=/var/www/site

    On 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 site
    Warning: 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-root option adds unknown files at the site root. If your WordPress is in another language, add the matching --locale option (for example --locale=fr_FR) if in doubt.

  2. Plugins. wp plugin verify-checksums --all does the same for each plugin published on WordPress.org.

    Terminal
    sudo -u www-data wp plugin verify-checksums --all --path=/var/www/site
    Result on a test site
    Warning: 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.

  3. Administrator accounts. --skip-plugins --skip-themes avoids loading the plugins and the theme, and therefore any code that could hide an account. Note that plugins in mu-plugins still load.

    Terminal
    sudo -u www-data wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --skip-plugins --skip-themes --path=/var/www/site
    Result on a test site
    ID 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:11

    To get around any filter, read the database directly. This query lists the accounts whose capabilities contain administrator. Replace wp_ with your table prefix, given by wp 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.

  4. Executable files in uploads.

    Terminal
    find /var/www/site/wp-content/uploads -type f \( -name '*.php*' -o -name '*.phtml' -o -name '*.phar' \) -print

    The command should print nothing. Each line it prints is a file to copy for analysis, then remove.

    .htaccess files, 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:

    Terminal
    find /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, especially AddHandler, AddType, SetHandler or php_value, is there to get code executed in uploads: copy the file for analysis, then remove it.

  5. Plugins that always load.

    Terminal
    sudo -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.

Terminal
Remove the suspicious account's rights, without deleting it sudo -u www-data wp user set-role 2 subscriber --path=/var/www/site End all its sessions sudo -u www-data wp user session destroy 2 --all --path=/var/www/site Renew the keys in wp-config.php: everyone is logged out sudo -u www-data wp config shuffle-salts --path=/var/www/site Put back the original core, same version, without touching wp-content sudo -u www-data wp core download --force --skip-content --version=$(sudo -u www-data wp core version --path=/var/www/site) --path=/var/www/site Put back an original plugin, same version (here akismet 5.7.2) sudo -u www-data wp plugin install akismet --force --version=5.7.2 --path=/var/www/site Check again sudo -u www-data wp core verify-checksums --path=/var/www/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
E-mail
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.

Portrait of Cédric Michel

Cédric Michel

Cédric has been building Magento stores for 18 years and holds the Adobe Commerce Developer Expert certification. He created Tocsin24 to monitor the security and health of Magento 2 stores and WordPress sites.

Adobe Commerce Developer Expert badge Adobe Commerce Developer Expert
Verify the certification on Adobe’s website

Sources

  1. WordPress: FAQ My site was hacked
  2. WordPress: Hardening WordPress (file monitoring)
  3. WordPress: wp-config.php, security keys
  4. WordPress: roles and capabilities
  5. WordPress: pre_user_query filter
  6. CNIL (French data protection authority): notifying a personal data breach (in French)
  7. WP-CLI: wp core verify-checksums
  8. WP-CLI: wp plugin verify-checksums
  9. WP-CLI: wp user list
  10. WP-CLI: wp user set-role
  11. WP-CLI: wp user session destroy
  12. WP-CLI: wp config shuffle-salts
  13. WP-CLI: wp core download
  14. WP-CLI: wp plugin install
  15. WP-CLI: global parameters (--skip-plugins, --skip-themes)
  16. WP-CLI: common issues (do not run WP-CLI as root)
  17. WP-CLI: installing