Attackers are actively exploiting a stored cross-site scripting vulnerability in the widely used Ninja Forms WordPress plugin to compromise websites, create administrator accounts and establish multiple independent persistence mechanisms. The flaw, tracked by Patchstack as CVE-2026-94504, affects Ninja Forms through version 3.15.3 and was fixed in version 3.15.4. Ninja Forms has more than 500,000 active installations, giving the campaign a potentially large target pool. Patchstack observed exploitation against Ninja Forms on October 5, one day after the same threat actor began exploiting a separate stored XSS vulnerability, CVE-2026-93836, in WPC Product Bundles for WooCommerce. Both campaigns retrieve the same JavaScript payload from imgcdn1[.]com, strongly indicating a common operation.
The Ninja Forms vulnerability is particularly effective because it does not require the attacker to compromise an administrator account first. An unauthenticated attacker submits malicious content through the normal Ninja Forms AJAX submission endpoint using action=nf_ajax_submit. The vulnerable plugin stores attacker-controlled values and later renders them unsafely in the legacy administrative submission editor. When a logged-in WordPress administrator reviews the poisoned form submission, the embedded JavaScript executes inside the trusted wp-admin origin.
The attack sequence can therefore be summarized as: attacker submits malicious Ninja Forms entry → WordPress stores the payload → administrator reviews the submission → stored JavaScript executes inside wp-admin → script inherits the administrator’s session → WordPress nonces are collected → malicious plugin installed → rogue administrator created → additional hidden persistence installed. The attacker does not need to trick the administrator into clicking a malicious external link. Reviewing form submissions is already part of the administrator’s normal job.
That distinction explains why stored XSS can be far more dangerous than its CVSS score may initially suggest. CVE-2026-94504 carries a CVSS 7.1 High rating, but the real campaign demonstrates how context can amplify the impact. Once attacker-controlled JavaScript executes in an authenticated administrator’s browser, it can perform anything that the browser session itself is authorized to perform. In this case, that means turning what begins as client-side script execution into persistent server-side compromise.
The JavaScript does not need to steal the administrator’s WordPress cookie. Because it executes in the same origin as the WordPress administration interface, normal browser requests automatically carry the administrator’s authenticated session. The malware loads privileged WordPress pages, extracts the CSRF nonces required for sensitive administrative actions and then replays those nonces to perform the operations itself. Even HttpOnly cookies do not stop this technique because the script never has to read the cookie directly. It simply rides the existing authenticated session.
This is an important security lesson. Session protection is not just about preventing cookie theft. If malicious code executes inside the trusted application origin, the browser can become an authenticated proxy for the attacker. The server sees legitimate requests carrying a legitimate administrator session and valid WordPress nonces. From the application’s perspective, the administrator appears to be performing the actions.
The malware first contacts attacker infrastructure at imgcdn1[.]com, where the C2 tracks which stages of compromise have already succeeded. It then uses the victim administrator’s session to access WordPress’s legitimate plugin-upload interface, retrieve the plugin-installation nonce and upload a malicious ZIP package through the normal WordPress installer.
The malicious plugin masquerades as “WP Smart Thumbnails” version 1.2.4 by “MediaPress Labs.” Patchstack found that the package contains a packed file manager and an additional script called emer-run.php responsible for installing durable persistence. The files are deliberately backdated to July 2026 so that they appear older than the campaign itself, reducing the effectiveness of investigations that simply search for recently modified files.
The file-manager component is also designed to avoid obvious detection. When WordPress loads the plugin normally, the malicious code returns immediately. The payload activates only when the plugin’s PHP file is requested directly. Its packed PHP code is decoded through several layers and then loaded through a custom stream wrapper rather than a straightforward eval(), potentially bypassing security rules focused on common PHP malware patterns.
Patchstack found that the resulting file manager does not expose a direct operating-system command shell, but it allows unauthenticated users to list, upload, delete, rename, create, read and modify files. That distinction offers limited comfort because arbitrary file write is generally enough to upload another PHP shell or replace existing application code. Anyone who discovers the path can access the file manager without a password or authorization check.
The campaign becomes more concerning when emer-run.php executes. A single successful XSS execution leaves the attacker with four separate ways back into the WordPress site. The first is a normal visible administrator account created through WordPress. The second is another administrator deliberately hidden from the WordPress user interface. The third is a secret login URL that silently authenticates the attacker as the oldest existing administrator on the site. The fourth is the unauthenticated file manager installed by the malicious plugin.
The hidden administrator mechanism is particularly effective against routine incident response. The malware creates a fully privileged WordPress administrator with an innocent-looking username such as support, updater, maintenance or backup, then installs a must-use plugin that modifies WordPress user queries so that the account does not appear under Users → All Users, does not appear when filtering for administrators and is not included in the administrator count displayed in the interface.
That means a site owner can log into the WordPress dashboard, review the administrator list and incorrectly conclude that no rogue administrator exists.
The malware is not merely hiding the account visually. It alters the database queries WordPress uses to produce the dashboard view.
This is why incident responders should not rely solely on the WordPress administrative interface when investigating suspected compromise. Administrators should enumerate privileged users directly from the WordPress database or through an independent method and compare the results with what the dashboard shows. Patchstack specifically recommends looking for discrepancies between database administrator records and the visible WordPress user list.
The attacker’s third persistence mechanism is even more subtle. A second must-use plugin creates a secret login URL using a _wplogin parameter. Anyone possessing the correct token can visit that URL and receive an authenticated session as the oldest existing administrator on the site, effectively impersonating the original site owner.
This creates an important forensic problem. Logs that record only the authenticated WordPress user ID could show the attacker operating as the legitimate administrator. The activity may therefore appear to originate from the real site owner even though the owner never authenticated.
That is exactly the sort of persistence mechanism that turns a cleanup exercise into an incident-response problem.
Deleting the visibly malicious administrator is not enough.
Removing the fake WP Smart Thumbnails plugin is not enough.
Updating Ninja Forms is not enough.
Patchstack explicitly notes that removing the vulnerable plugin, or even the malicious plugin, does not remove several of the installed persistence mechanisms. The hidden administrator and magic login mechanism are implemented through separate must-use plugins, which load automatically and do not appear in the normal WordPress Plugins screen.
This is probably the most important operational lesson from the campaign:
patching stops future exploitation; it does not clean a site that was already compromised.
Organizations running Ninja Forms 3.15.3 or earlier should therefore treat upgrading and compromise assessment as two separate tasks.
The first question is:
“Are we still vulnerable?”
The second is:
“Was the vulnerability already used?”
Answering the first does not answer the second.
Patchstack observed the first activity associated with this operation on October 4, 2026, targeting WPC Product Bundles for WooCommerce. The same payload appeared against Ninja Forms the following day. The common JavaScript payload, C2 infrastructure and post-exploitation behavior link the two attacks to the same campaign.
That cross-plugin reuse is strategically important. The attackers have built a plugin-agnostic WordPress post-exploitation framework. They do not need a unique malware chain for every vulnerable plugin. Any stored XSS flaw capable of placing JavaScript in front of a logged-in WordPress administrator can potentially become an entry point for the same second stage.
In other words, the vulnerability is interchangeable.
The payload is the product.
That model is efficient for attackers because WordPress provides a standardized administrative environment once the JavaScript gains execution. Regardless of whether the entry point is Ninja Forms, WooCommerce or another plugin, the post-exploitation actions remain largely the same: identify the site origin, inherit the administrator session, retrieve nonces, upload a plugin, create administrators and establish persistence.
This should concern defenders because Patchstack has currently confirmed only two vulnerable plugins used by the campaign, but explicitly warns that those two should not be assumed to be the final number. Any unauthenticated stored XSS that can reach an administrator could theoretically serve as another delivery mechanism.
The campaign therefore demonstrates why stored XSS in administrative workflows deserves more attention than the label sometimes receives. Vulnerability severity is often interpreted according to the immediate technical primitive: “JavaScript can execute in an administrator’s browser.” But the practical consequence depends on what that administrator can do.
In WordPress, an administrator can usually install plugins.
A plugin can execute PHP.
PHP can modify the site.
The real chain is therefore:
stored XSS → administrator browser → authenticated administrative actions → plugin installation → PHP execution → persistent website compromise.
No separate server-side vulnerability is required.
This is another example of why exploit chains should sometimes be evaluated as a whole rather than treating each primitive in isolation.
The campaign also demonstrates a highly effective attacker strategy: use legitimate administrative functions wherever possible. The JavaScript uses WordPress’s genuine plugin installer and legitimate nonce system. The site itself performs the installation.
That reduces the number of obviously malicious operations defenders can detect.
The attacker does not need to exploit the filesystem directly to install the first plugin.
They persuade WordPress to install it for them.
The campaign’s persistence design also shows deliberate awareness of how WordPress administrators typically investigate compromises. Rogue accounts are hidden from the normal dashboard. Must-use plugins do not appear in the conventional plugin list. Malicious files are deliberately backdated so they do not appear in searches for recent filesystem changes. The primary malicious plugin looks like an ordinary thumbnail utility.
These are not random persistence choices.
They are designed around expected administrator behavior.
For organizations investigating possible exposure, several indicators are particularly useful. Patchstack recommends searching historical web and application logs for references to imgcdn1.com, /fz/x.js and /fz/c.php, and checking for the directory /wp-content/plugins/wp-smart-thumbnails/. Administrators should also inspect /wp-content/mu-plugins/ for files named class-wp-token-validate.php and class-wp-query-*.php.
The WordPress options table should also be checked for fz_emer_done_v1 and fz_emer_login_tokens, while web logs should be reviewed for requests to wp-login.php containing the _wplogin query parameter.
File modification time is specifically not a reliable indicator in this campaign because the persistence installer intentionally finds the oldest timestamp in the WordPress installation and applies it to newly created malicious files. Incident responders should therefore search by content, filename, behavior and database state rather than sorting the filesystem by recent modification time.
Administrators should upgrade Ninja Forms to 3.15.4 or later and WPC Product Bundles for WooCommerce to 8.6.7 or later. But any site that ran vulnerable versions while exposed during the campaign should also be checked for compromise.
A practical response sequence is:
upgrade affected plugin → preserve logs → search for campaign infrastructure → inspect fake WP Smart Thumbnails plugin → inspect must-use plugins → enumerate administrators directly from the database → inspect WordPress options → search for _wplogin activity → validate filesystem integrity → rotate WordPress administrator credentials and relevant secrets if compromise is confirmed → rebuild from a trusted backup where site integrity cannot be established
The rebuild decision matters because four independent persistence routes mean partial cleaning can easily leave one behind.
If an attacker achieved administrative plugin installation, defenders should assume the integrity boundary has moved beyond Ninja Forms itself. PHP code may have been written elsewhere, other plugins may have been modified and credentials may have been collected. A clean plugin list does not prove a clean WordPress installation.
The campaign also offers a broader lesson for application security teams. Stored XSS severity depends heavily on where the payload executes. JavaScript running in an anonymous visitor’s context is one problem. JavaScript running inside the browser of a fully authenticated application administrator is another.
When prioritizing XSS vulnerabilities, organizations should therefore consider the likely victim role.
If the payload is stored specifically in content routinely reviewed by administrators, the privilege of that browsing context effectively becomes part of the exploit chain.
This is what makes CVE-2026-94504 valuable to the attacker.
The attacker does not compromise the administrator.
They wait for the administrator to bring the privilege to the payload.
The complete campaign can currently be summarized as:
unauthenticated attacker submits poisoned Ninja Forms data → payload stored in WordPress → administrator opens submission → JavaScript loads from attacker infrastructure → authenticated session and WordPress nonces abused → fake WP Smart Thumbnails plugin installed → visible administrator created → hidden administrator created → magic login URL installed → unauthenticated file manager deployed → persistence survives removal of the original vulnerable or malicious plugin.
That is why this should not be treated merely as “a WordPress XSS bug being exploited.”
It is a reusable administrator-session hijacking and persistence framework delivered through stored XSS.
The vulnerability gets the attacker into the browser.
The administrator’s own privileges do the rest.
Hackers are exploiting stored cross-site scripting (XSS) vulnerabilities in two unrelated WordPress plugins, Ninja Forms and WPC Product Bundles for WooCommerce, to install backdoors and create rogue admin accounts. [...]
Source: Ninja Forms plugin flaw exploited to hack WordPress sites via Bleeping Computer — published 06 Oct 2026.
Was this article helpful?
Your feedback helps us improve the knowledge base.