A fake WordPress admin user is an administrator-level account that appears in your site's user list without any legitimate admin creating it. The account is the symptom; the persistence mechanism that's creating it is the actual problem. Deleting the account without finding the mechanism just buys you time until the same account (or one with a slightly different name) reappears.
This article covers: the SQL that detects fake admins and the unusual patterns in their wp_users and wp_usermeta rows, the four mechanisms that create them automatically, how to clean both the accounts and the mechanism, and how to prevent the original entry vector from being re-exploited.
I've cleaned this pattern off sites since the 2017-era WP Statistics CVE (CVE-2017-9603) made it the entry vector du jour, and the basic shape has remained constant through every WordPress version since 4.x. The detection and cleanup work on WordPress 4.x through the current 6.9 line.
Set your site values
Set your WordPress root and table prefix once and every SQL query, WP-CLI command, and grep example below picks them up. The default wp_ prefix is the most common; if yours is different (custom installs often use a random 3-4 character prefix like x4z_), swap it in and every query in the article rewrites itself.
Set the WordPress install root and your database's table prefix. The default wp_ is the most common; custom installs sometimes use a random short prefix like x4z_. Every SQL query and shell command below picks up your values automatically.
What a fake admin looks like in your database
Fake admin accounts usually share a few telltale traits:
- A recent
user_registeredtimestamp you can't account for, no signup form was filled out, no admin invited anyone. - A throwaway-looking email at a freemail provider (gmail, yahoo, protonmail, or temp-mail-like services) or sometimes a non-existent domain.
- A username that's either randomized (
adm1n_x42,wpsupport,helpadmin) or mimics a legitimate username with a digit (admin2,john1, your-name plus_). - An administrator capability in
wp_usermeta, this is what makes them dangerous; a contributor or subscriber account doesn't have edit-files permission. - Sometimes a
user_status = 0like every other user, sometimes hidden via meta tricks that make them not appear in the admin Users list.
The detection has to look at multiple tables because WordPress splits user data across wp_users (the account) and wp_usermeta (capabilities, profile data, hidden flags).
The detection SQL
The most useful starting query is "every user with administrator capability, ordered by most recently registered":
SELECT
u.ID,
u.user_login,
u.user_email,
u.user_registered,
m.meta_value AS capabilities
FROM :{table_prefix}users u
JOIN :{table_prefix}usermeta m ON u.ID = m.user_id
WHERE m.meta_key = ':{table_prefix}capabilities'
AND m.meta_value LIKE '%administrator%'
ORDER BY u.user_registered DESC;If your prefix is not wp_, change it in the panel above and the query rewrites itself. The capabilities meta key always uses the same prefix as the tables.
Compare the output against the admins you know. Any name you don't recognize is a candidate. If you're on a multisite install:
-- Same idea but for network-level super-admins
SELECT option_value FROM :{table_prefix}sitemeta WHERE meta_key = 'site_admins';The site_admins option holds the network super-admin usernames; if names appear there that you don't recognize, that's a network-level backdoor.
The patterns that scream "fake admin"
Beyond the user list, several wp_usermeta patterns are strongly correlated with malicious accounts. Run these to find suspicious meta:
-- Users with no display name set (legitimate admins almost always have one)
SELECT u.ID, u.user_login, u.user_email, u.display_name, u.user_registered
FROM :{table_prefix}users u
WHERE u.display_name = u.user_login
AND EXISTS (
SELECT 1 FROM :{table_prefix}usermeta m
WHERE m.user_id = u.ID
AND m.meta_key = ':{table_prefix}capabilities'
AND m.meta_value LIKE '%administrator%'
);
-- Users whose email domain is a known freemail or disposable provider
SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM :{table_prefix}users u
WHERE u.user_email REGEXP '@(yopmail|mailinator|guerrillamail|tempmail|10minutemail|throwawaymail|maildrop|trashmail)\\.'
OR u.user_email REGEXP '^[a-z]{6,}[0-9]{3,}@(gmail|yahoo|outlook|protonmail)\\.';
-- Users whose user_login matches the suspicious-name patterns
SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM :{table_prefix}users u
WHERE u.user_login REGEXP '^(adm1n|wpadmin|wpsupport|helpadmin|admin[0-9]+|support[0-9]+|root[0-9]?|sys[0-9]+)$'
OR u.user_login REGEXP '^[a-z]+[0-9]{3,}$';
-- Users with administrator capability but zero authored posts (often pure-API backdoor accounts)
SELECT u.ID, u.user_login, u.user_email, u.user_registered,
(SELECT COUNT(*) FROM :{table_prefix}posts p WHERE p.post_author = u.ID) AS posts
FROM :{table_prefix}users u
WHERE EXISTS (
SELECT 1 FROM :{table_prefix}usermeta m
WHERE m.user_id = u.ID
AND m.meta_key = ':{table_prefix}capabilities'
AND m.meta_value LIKE '%administrator%'
)
HAVING posts = 0
ORDER BY u.user_registered DESC;None of these on their own are proof; together they make a strong signal. An administrator account with no display name, a throwaway email, a randomized username, and zero posts is almost always malicious.
The four mechanisms that create them
Once you've identified the fake accounts, the next step is figuring out how they got there. Four mechanisms cover almost every case:
1. A plugin CVE that lets unauthenticated users register as admin
Several plugin vulnerabilities over the years have allowed an unauthenticated attacker to create an administrator account via a single HTTP request. The major ones I've seen as entry points:
| CVE | Plugin | Affected versions | Fixed | Impact |
|---|---|---|---|---|
| CVE-2017-9603 | WP Statistics | < 12.0.8 | 12.0.8 (2017) | SQL injection leading to admin account creation |
| CVE-2024-2879 | LayerSlider | 7.9.11 - 7.10.0 | 7.10.1 (March 2024) | Unauthenticated SQL injection; database content extraction and admin creation |
| CVE-2024-28000 | LiteSpeed Cache | < 6.4.1 | 6.4.1 (Aug 2024) | Unauthenticated privilege escalation; the attacker becomes admin in one request |
| CVE-2023-6634 | LiteSpeed Cache | < 5.7.0.1 | 5.7.0.1 (Oct 2023) | Stored XSS via crawler; usable for admin session takeover |
| CVE-2021-24884 | Formidable Forms | < 4.09.05 | 4.09.05 (2021) | PHP deserialization that allowed admin escalation |
If any of these were installed at the vulnerable version, that's likely your entry. Track the Patchstack vulnerability database or Wordfence's threat intel feed for the current list; new ones land every few weeks.
2. A backdoor file in the site that creates the user on every request
The persistence variant: the attacker drops a PHP file (often in wp-content/uploads/, sometimes in mu-plugins/, sometimes in a normal plugin directory) that checks whether the malicious admin exists every time it's loaded. If the admin has been deleted, the file recreates them.
Detection: search for wp_create_user, wp_insert_user, or add_user_to_blog in suspicious file locations:
# Any PHP file calling wp_create_user or wp_insert_user
grep -rnE "wp_(create|insert)_user|add_user_to_blog|wp_update_user.*role.*admin" \
:wp_root/wp-content/ \
--include="*.php" 2>/dev/null
# Also check mu-plugins and drop-ins specifically
ls -la :wp_root/wp-content/mu-plugins/ 2>/dev/null
ls -la :wp_root/wp-content/advanced-cache.php :wp_root/wp-content/object-cache.php :wp_root/wp-content/db.php 2>/dev/nullThe file is often disguised as a cache or utility file. Read each match in full, legitimate plugins do call wp_create_user in their user-registration flows, so context matters.
3. A scheduled WP-Cron hook that creates the user on a timer
The variant that's harder to catch: the user-creation logic isn't on every request, it's scheduled. A WP-Cron event fires every hour or every day; on each fire, the registered hook checks whether the malicious admin exists, creates them if not, then exits silently.
Detection:
# List every scheduled event with its hook name
wp cron event list --path=:wp_root --allow-root --format=table
# For any hook name you don't recognize, find what function it calls
grep -rnE "add_action\s*\(\s*['\"]<hook-name>['\"]|wp_schedule_event.*['\"]<hook-name>" :wp_root/wp-content/Malicious hook names mimic legitimate ones: wp_admin_check, wp_user_sync, wp_db_maintenance. The legitimate WordPress hooks all have well-known names; anything that doesn't match a plugin or theme you have installed is suspicious.
The brute-force fix:
# Wipe all scheduled events; WordPress and its plugins will re-register the legitimate ones
wp option delete cron --path=:wp_root --allow-rootThat's safe, WordPress core, your plugins, and your themes all re-add their hooks on the next request. Anything malicious doesn't.
4. Stolen credentials with a direct admin Add
The simplest mechanism: the attacker has admin credentials (from a credential-stuffing attack, a phishing page, or a leaked password) and logs in normally. They then create a new admin user through the standard admin Users page. There's no backdoor file, no scheduled task, no plugin CVE. Just legitimate-looking admin activity, which is why this one is the hardest to detect after the fact.
Detection: cross-reference user creation against legitimate admin login times. If an admin user was created at 03:14 UTC but you and the only other admin were asleep, the creation happened from a compromised session.
The fix: every admin password gets rotated, every active session is invalidated by rotating wp-config.php salts, and 2FA is added to every admin account so stolen credentials alone aren't enough.
A complete fake-admin detection script
Save as wp-fake-admin-detect.sh, chmod +x, run from inside the WordPress directory:
#!/usr/bin/env bash
# wp-fake-admin-detect.sh, find suspicious admin accounts and the mechanisms that create them.
# Source: https://techearl.com/wordpress-fake-admin-users
# Site: https://techearl.com/
# Reports only; does NOT delete.
#
# Usage: ./wp-fake-admin-detect.sh /path/to/wordpress
set -e
WP_ROOT="${1:-$PWD}"
PREFIX=$(grep -oE "table_prefix\s*=\s*['\"][^'\"]+" "$WP_ROOT/wp-config.php" | sed -E "s/.*['\"]//")
PREFIX="${PREFIX:-wp_}"
echo "========================================="
echo " Fake Admin Detection Pass"
echo " WP root: $WP_ROOT"
echo " Table prefix: $PREFIX"
echo "========================================="
# 1. All current admin accounts, most recent first
echo
echo "--- 1. All administrator accounts (most recent first) ---"
wp db query "SELECT u.ID, u.user_login, u.user_email, u.user_registered \
FROM ${PREFIX}users u \
JOIN ${PREFIX}usermeta m ON u.ID = m.user_id \
WHERE m.meta_key = '${PREFIX}capabilities' \
AND m.meta_value LIKE '%administrator%' \
ORDER BY u.user_registered DESC" \
--path="$WP_ROOT" --allow-root
# 2. Admin accounts with no posts (possible backdoor)
echo
echo "--- 2. Admin accounts with zero published posts (possible backdoor) ---"
wp db query "SELECT u.ID, u.user_login, u.user_email, u.user_registered, \
(SELECT COUNT(*) FROM ${PREFIX}posts p WHERE p.post_author = u.ID AND p.post_status = 'publish') AS pub_posts \
FROM ${PREFIX}users u \
JOIN ${PREFIX}usermeta m ON u.ID = m.user_id \
WHERE m.meta_key = '${PREFIX}capabilities' \
AND m.meta_value LIKE '%administrator%' \
HAVING pub_posts = 0 \
ORDER BY u.user_registered DESC" \
--path="$WP_ROOT" --allow-root
# 3. Suspicious email patterns
echo
echo "--- 3. Admin accounts with throwaway-looking email patterns ---"
wp db query "SELECT u.ID, u.user_login, u.user_email, u.user_registered \
FROM ${PREFIX}users u \
JOIN ${PREFIX}usermeta m ON u.ID = m.user_id \
WHERE m.meta_key = '${PREFIX}capabilities' \
AND m.meta_value LIKE '%administrator%' \
AND (u.user_email REGEXP '@(yopmail|mailinator|guerrillamail|tempmail|10minutemail|throwawaymail|maildrop|trashmail)\\.' \
OR u.user_email REGEXP '^[a-z]{4,}[0-9]{3,}@(gmail|yahoo|outlook|protonmail)\\.')" \
--path="$WP_ROOT" --allow-root
# 4. Backdoor file scan: PHP files calling wp_create_user
echo
echo "--- 4. PHP files calling wp_create_user / wp_insert_user (review each) ---"
grep -rlnE "wp_(create|insert)_user\s*\(|add_user_to_blog\s*\(" \
"$WP_ROOT/wp-content/" --include="*.php" 2>/dev/null \
| grep -vE "wp-content/(plugins/woocommerce|plugins/buddypress|plugins/wpforms|plugins/gravityforms)" \
| head -20
# 5. Scheduled cron events
echo
echo "--- 5. WP-Cron scheduled events (look for unrecognized hooks) ---"
wp cron event list --format=table --fields=hook,next_run_relative \
--path="$WP_ROOT" --allow-root 2>/dev/null | head -30
# 6. Multisite super-admin list
echo
echo "--- 6. Network super-admins (if multisite) ---"
wp db query "SELECT meta_value FROM ${PREFIX}sitemeta WHERE meta_key = 'site_admins'" \
--path="$WP_ROOT" --allow-root 2>/dev/null || echo " (not a multisite install)"
echo
echo "========================================="
echo " Review each section. Cross-reference"
echo " the admin list against admins you"
echo " recognize. Anything else is a candidate."
echo "========================================="Cleaning fake admins safely
Once you've identified the malicious accounts, the cleanup has two parts that have to happen in the right order:
Part 1: Stop the creation mechanism FIRST
If you delete the accounts before killing the mechanism that creates them, the next request, next cron tick, or next attacker visit recreates them. Order:
- Audit the file system for the backdoor file pattern (section 2 above). If found, delete every backdoor.
- Clear WP-Cron events to drop scheduled recreators (section 3).
wp option delete cron --path=... --allow-rootis the surgical version that lets WordPress and your plugins re-register only the legitimate hooks. - Patch the plugin CVE that was the original entry vector (section 1). Update to the patched version, or remove the plugin entirely if you don't need it.
- Rotate all admin passwords and the
wp-config.phpsalts (section 4 and the credential-rotation step in WordPress malware removal).
Until all four parts of part 1 are complete, do not delete the accounts.
Part 2: Delete the accounts (after part 1 is complete)
The cleanest method is WP-CLI, which handles the user record, the usermeta, and reassigns any orphaned posts:
# Reassign their content (if any) to user ID 1 (the legitimate primary admin) and delete
wp user delete <user_id> --reassign=1 --path=:wp_root --allow-rootFor a multisite install, also remove them from each subsite they were added to:
wp user remove-role <user_id> administrator --url=<subsite-url> --path=:wp_root --allow-root
wp user delete <user_id> --network --reassign=1 --path=:wp_root --allow-rootIf WP-CLI isn't available, the SQL equivalent has to clean three tables:
-- Replace 99, 100, 101 with the IDs of the accounts you confirmed are malicious
-- 1. Reassign their posts to user ID 1
UPDATE :{table_prefix}posts SET post_author = 1 WHERE post_author IN (99, 100, 101);
-- 2. Remove their user meta
DELETE FROM :{table_prefix}usermeta WHERE user_id IN (99, 100, 101);
-- 3. Delete the user records
DELETE FROM :{table_prefix}users WHERE ID IN (99, 100, 101);For a multisite install, you also need to clean the wp_X_capabilities rows in wp_usermeta for every subsite (where X is each subsite's blog ID).
Common mistakes
The patterns that turn fake-admin cleanup into a recurring problem:
Deleting the account from the admin Users page without removing the creation mechanism. The standard "Delete User" workflow handles wp_users and wp_usermeta cleanly, but if a backdoor file or scheduled task is the creator, the deleted user is back within minutes.
Not checking for the user meta hide trick. Some malware adds a meta entry like _wp_user_hidden = 1 (custom, not a real WP feature) that some sloppy admin UI plugins respect. The standard Users list shows fewer rows than the database has. Always go to the database, not the admin UI, when looking for hidden admins.
Skipping the multisite super-admin list. On a multisite install, network super-admins are stored separately in wp_sitemeta.site_admins as a serialized PHP array. A user can be a super-admin without appearing in any subsite's user list. Check it explicitly.
Trusting the "Last login" column. Most security plugins (Wordfence, WP Activity Log) display a "Last Login" column for users, but the data comes from a hook the plugin registered. If the malware created the account and then immediately deactivated the security plugin (see Why Wordfence Got Silently Disabled), no login was logged. A "Last Login: Never" column is not proof of innocence.
Forgetting to rotate credentials after cleanup. If the original entry vector was stolen credentials and you deleted the fake admin but didn't reset the legitimate admin's password, the attacker is back in within hours. Rotation is mandatory.
Reactivating the same vulnerable plugin after cleanup. Many sites have CMS-managed plugin auto-updates disabled. If your entry vector was LiteSpeed Cache < 6.4.1 and after cleanup you don't update, the same CVE is exploitable again the same day.
Frequently asked questions
See also
- How to Remove WordPress Malware: The Practitioner's Playbook: the broader cleanup methodology. Fake admin removal is one step of seven in a full recovery.
- Why WordPress Malware Keeps Coming Back: Persistence Mechanisms: the deep dive on the file-system and scheduled-task mechanisms that recreate fake admins after deletion. Required reading if your cleanup keeps reverting.
- How to Find the Original Entry Point in a WordPress Compromise: access-log analysis that identifies which plugin CVE or credential vector was the initial entry. Without this, the same attacker creates the same accounts again the next day.
- The Fake Cloudflare Verification Attack on WordPress (ClickFix): the social-engineering payload that often accompanies fake-admin creation in 2024+ campaigns. If you have fake admins AND visitors are complaining about a Cloudflare verification page, you're dealing with the same intrusion.
- Why Wordfence Got Silently Disabled (and How to Stop It Happening Again): the security-plugin-bypass pattern that often precedes fake-admin creation. If admin alerts went quiet right before the fake user appeared, this is the explanation.
- Cross-Site Contamination on Shared WordPress Hosting: when fake admins appear simultaneously on multiple sites in a shared hosting account, the structural cause is at the hosting layer.
- How to Change a WordPress Password: the credential rotation that must follow any fake-admin cleanup.





