Plugin Name | WPBakery Page Builder |
---|---|
Type of Vulnerability | Stored Cross-Site Scripting |
CVE Number | CVE-2025-11160 |
Urgency | Low |
CVE Publish Date | 2025-10-15 |
Source URL | CVE-2025-11160 |
WPBakery Page Builder <= 8.6.1 — Stored XSS in Custom JS Module (CVE-2025-11160)
A recent disclosure identified a stored Cross-Site Scripting (XSS) vulnerability in WPBakery Page Builder (versions up to and including 8.6.1) — tracked as CVE-2025-11160. The vulnerability is triggered via the plugin’s Custom JS module by a user with Contributor-level privileges. It has been fixed in version 8.7.
As a WordPress security team that operates and maintains an active Web Application Firewall (WAF) for thousands of sites, we want to give you:
- a clear, practical explanation of the vulnerability,
- measurable risk guidance for different site types,
- hands-on detection and cleanup steps you can run today,
- robust short-term mitigations (virtual patching / WAF rules),
- and long-term hardening & monitoring recommendations.
This is written for WordPress owners, site admins, developers, and security-conscious editors. No fluff — just actionable, tested guidance.
Executive summary (TL;DR)
- Vulnerability: Stored XSS in WPBakery Page Builder’s Custom JS module.
- Affected versions: WPBakery <= 8.6.1
- Fixed in: WPBakery 8.7
- CVE: CVE-2025-11160
- Required privilege: Contributor (authenticated, lower-privileged user)
- CVSS (as reported): 6.5 — moderate/low depending on context
- Primary impact: Injected JavaScript stored in site content and executed in the context of site visitors and potentially admins — can be used for persistent defacement, redirect, tracking, cookie theft, and pivoting at scale.
- Immediate mitigation: Update WPBakery to 8.7+, review stored content for malicious scripts, apply WAF rules/virtual patching and restrict which roles can add Custom JS modules.
Read on for the detailed breakdown and step-by-step remediation actions.
What is Stored XSS and why this matters for WordPress sites
Stored XSS occurs when an attacker manages to store malicious JavaScript on the target server (for example inside a post, page, widget, or plugin-managed field), which is later served to other users without proper output encoding or sanitization. Because the payload is persistent, it runs every time any user (including admins) views the infected page.
On WordPress sites, stored XSS is particularly dangerous because:
- It can execute in an admin’s browser, potentially stealing authentication cookies or session tokens.
- It can be used to inject further malicious content, create backdoors, or create persistent redirects and SEO spam.
- Attackers can weaponize it for large-scale attacks by adding code to widely-visible pages (homepages, footers, editor screens).
In this case, the vector is a plugin module that is designed to allow custom JavaScript. If that input is stored and later rendered unsafely, an authenticated user (Contributor privilege) can inject script that will run for site visitors.
Technical overview: How this WPBakery vulnerability works
- The plugin provides a “Custom JS” module/component whose content is stored in the site database as part of the page/post structure (shortcode, postmeta, or custom storage specific to the plugin).
- The module is intended to allow benign JavaScript for front-end enhancements; however, input is not properly constrained or sanitized before being saved/served in certain contexts.
- A user with Contributor privileges can add a payload which gets stored in the page. Because the site later renders the stored content directly into the HTML, the script runs in any visitor’s browser (including admins who browse the site or preview pages).
Danger scenarios:
- An attacker with a contributor account injects a script that steals authentication cookies from any admin who views the page, then sends those tokens to an external server.
- The script performs a redirect to an attacker-controlled domain or loads malicious resources (malware, ads, cryptomining).
- The script modifies the page DOM to add spam links, steal form data, or create further admin-level changes via AJAX calls.
Note: The vulnerability requires at least a Contributor account, so attackers typically need either a compromised account or registration enabled (open registration) where a malicious actor signs up and uses the lower-privileged account to inject scripts.
Who is at risk?
- Sites running WPBakery Page Builder version 8.6.1 or older.
- Sites that allow users to register or accept guest contributions from untrusted users (for example, community blogs, membership sites, sites that permit user-submitted content).
- Multi-author blogs where contributor roles exist.
- Sites where admins preview pages while logged in (standard behavior) — admin viewing a page containing malicious script could result in privilege escalation.
If you run WPBakery and meet any of the above, consider this a high-priority check even though the CVSS is listed as moderate; web context matters — an attacker only needs one site with exposed contributor accounts to make this work.
Immediate actions (first 1–2 hours)
- Confirm plugin version
– Dashboard: Plugins > Installed Plugins and verify WPBakery version.
– WP-CLI:wp plugin list --format=csv | grep js_composer || wp plugin get js_composer --field=version
- If you can update safely, update WPBakery to 8.7 or later immediately
– If the update is allowed by your license and theme compatibility matrix, update via Dashboard or WP-CLI:wp plugin update js_composer --clear-plugins-cache
– If the update must be scheduled, jump to virtual patching (WAF rules) below and apply while scheduling a planned update during maintenance.
- Lock down contributor access (if you cannot update immediately)
– Temporarily remove the Contributor role or restrict who can add WPBakery modules.
– Use a role manager plugin to adjust permissions or remove the capability that allows adding custom JS modules. - Scan for injected <script> tags and suspicious JS in content
– WordPress DB search (carefully):SELECT ID, post_title, post_type FROM wp_posts WHERE post_content LIKE '%<script%';
– Search postmeta:
SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_value LIKE '%<script%';
– WP-CLI example:
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%' LIMIT 50;"
– Also search for ‘onerror=’, ‘onclick=’, ‘innerHTML=’ or suspicious base64 strings.
- Put your site into a limited-access maintenance mode if you suspect active exploitation while you investigate.
- Take a fresh backup (files + DB) before you remove anything — keep a forensic copy.
Detecting injected payloads (practical queries & patterns)
Look for patterns attackers commonly use:
- Script tags: <script> … </script>
- Inline event handlers: onerror=, onclick=, onload=
- Document.cookie, XMLHttpRequest, fetch, new Image(), postMessage to external domains
- Obfuscated payloads: base64, eval(atob(…)), setTimeout with long encoded strings
Suggested searches:
- Database (generic SQL):
- Posts:
SELECT ID, post_title FROM wp_posts WHERE post_content REGEXP '<script|onerror=|document\\.cookie|eval\\(|atob\\(';
- Postmeta:
SELECT post_id, meta_key FROM wp_postmeta WHERE meta_value REGEXP '<script|onerror=|document\\.cookie|eval\\(|atob\\(';
- WP-CLI grep approach (files):
wp db export - | grep -iE '<script|onerror=|document\.cookie|eval\(|atob('
- File scanning (uploads/themes/plugins):
grep -R --line-number -E "<script|document\.cookie|eval|atob|new Image\(|fetch(" wp-content
If you find matches, record each affected post ID, the stored field (post_content vs postmeta), and export snapshots for forensic purposes before modifying.
Containment & cleanup (detailed steps)
- Take a backup (again) and snapshot the DB and files for later analysis.
- Remove or neutralize malicious entries:
– For simple script tags in post content, remove offending script blocks and save the clean content.
– For plugin-managed storage (postmeta), update meta_value to clean content or delete the meta row if it’s purely malicious.
Example WP-CLI removal (use with caution):# Replace unwanted-string with an actual safe clean-up script or blank. wp post update 123 --post_content="$(wp post get 123 --field=post_content | sed 's/<script>malicious.*</script>//g')"
- Rotate credentials for any users that might have been compromised (especially Contributor accounts that could have been used to inject content).
- Force password resets for administrative and editorial accounts with suspicious activity.
- Scan installed plugins and themes with a reliable malware scanner (server-side scanning preferred). Prioritize manual review of any plugin or theme that allows storing JavaScript.
- Harden registrations and roles:
– Disable open registration if you don’t need it.
– Convert contributor accounts that haven’t been verified.
– Consider using invite-only or approval-based user creation. - Check server logs for HTTP POST requests to endpoints like admin-ajax.php or REST API endpoints from unknown IPs near the time the malicious content was created.
- If you cannot confidently clean every instance, consider restoring to a pre-infection backup and then applying the update and hardening steps before reintroducing content.
Short-term mitigation with WAF (virtual patching)
If you cannot update right away and need immediate protection, apply WAF rules that block exploit attempts and known payload patterns. Virtual patching is a fast, low-risk mitigation strategy that reduces exposure while you perform a full remediation.
Recommended WAF rule set examples (conceptual — implement via your WAF/UAM):
- Block POST requests to endpoints that manage WPBakery content where payload contains suspicious inline scripts:
– Match POST bodies for “<script”, “document.cookie”, “eval(“, “atob(“, “new Image(“, “fetch(“.
– Return 403 for matches from non-admin users. - Prevent contributors from submitting content containing script tags:
– If request is from a user role lower than Editor/Author and POST body contains<script
oronerror=
, block the request. - Filter out inline event handlers in content submissions:
– Block or sanitize requests that includeonerror=
,onclick=
,onload=
,innerHTML=
. - Rate-limit or block suspicious POST submissions to admin endpoints from unknown IPs.
- Implement a positive filter for allowed JS (if your site uses only specific known snippets), and block everything else.
- Deploy a Content Security Policy (CSP) that disallows inline script execution and only allows scripts from trusted origins:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.example; object-src 'none'; base-uri 'self';
Note: CSP helps reduce impact of XSS but may break legitimate inline scripts. Test carefully.
Why virtual patching works: attackers rely on input being stored and later executed. A WAF rule that blocks or sanitizes the dangerous input pattern prevents the stored payload from being created, or blocks its execution in transit.
Hardening and long-term prevention
- Update everything regularly (plugin, theme, WordPress core).
- Apply the principle of least privilege:
– Limit who can post or edit content.
– Reconsider the need for Contributor-level accounts — prefer Author-level with review workflows if feasible. - Use an approval workflow for user-submitted content (moderation).
- Disable or limit modules that allow direct JavaScript editing by lower roles.
- Sanitize output at the theme level — escape content where possible (wp_kses, esc_js, esc_html).
- Add CSP with nonce-based policy for the admin area where possible.
- Audit plugin capabilities: identify which plugins allow storing and executing raw JS or HTML and restrict them.
- Implement multi-factor authentication (MFA) for all admin/editor accounts.
- Monitor logs and set alerting for suspicious changes — new postmeta entries with script tags, new users registering and immediately creating content with scripts.
- Keep a well-documented incident response plan: backup, isolate, restore, notify if necessary.
Incident response checklist (for suspected compromise)
- Isolate the site (maintenance mode / IP restriction).
- Take full backups and forensic copies (DB + file system).
- Identify and remove injected malicious content.
- Rotate credentials and force password resets.
- Review recently added users and remove untrusted accounts.
- Check for additional backdoors in themes/plugins/uploads.
- Compare site files to known-good copies (if available).
- Update all software to fixed versions.
- Restore from clean backup if contamination breadth is uncertain.
- Notify stakeholders and, if required, follow legal notification obligations.
Why this vulnerability should not be downplayed
Although the CVSS score published with the disclosure is moderate (6.5), real-world impact depends on context:
- A brochure site with no contributor accounts and no admin logins on public pages is lower risk.
- But a community blog, membership site, multisite, or any installation that accepts contributions is significantly more at risk.
- Stored XSS can be used to compromise admin sessions, and once an admin is compromised, the entire site can be taken over.
Treat stored XSS as a high-priority hygiene issue — even if an attacker needs a contributor account, many sites allow user signup or use third-party integrations that can be abused to create that account.
Monitoring & detection: what to log and watch
- Log and alert on any POST to admin-ajax.php, REST endpoints or other admin endpoints containing “<script”.
- Monitor changes to postmeta and post_content for script tags.
- Alert when new users register and, within a short time, create or edit posts with embedded scripts.
- Keep an eye on outgoing requests from your site to external domains from PHP (suspicious cron activity).
- Keep WAF logs for blocked attempts and examine them for patterns or repeated attackers.
How WP-Firewall protects your WordPress site (our perspective)
At WP-Firewall we operate a managed WAF and malware mitigation service designed specifically for WordPress. When a new vulnerability like the stored XSS in WPBakery is disclosed, we take a layered approach to protect customers:
- Rapid virtual patching: We create precise WAF rules that block exploit attempts targeting the vulnerable module and payload signatures without disrupting legitimate traffic.
- Behavioral detection: We monitor for suspect content-submission patterns from low-privilege accounts and apply temporary hard blocks.
- Content scanning: Our malware scanner hunts for stored script tags and common XSS patterns across posts, postmeta, and uploads so you can identify infected pages faster.
- Automatic mitigation workflows: For users on managed plans, we can quarantine or sanitize suspicious content and inform admins with remediation guidance and links.
- Notifications & guidance: We provide prioritized remediation steps, including update guidance and a checklist for cleanup and recovery.
We strive to balance security and availability — our rules are tuned to avoid false positives and minimize impact on legitimate site functionality.
Practical examples: sample WAF rule (conceptual)
Below is a conceptual example of what a WAF rule might look like. Implementation details vary by WAF product.
- Rule: Block POST requests if:
- Request path contains /wp-admin/ or /wp-json/wp/v2/ or admin-ajax.php
- AND request body contains
<script
ORonerror=
ORdocument.cookie
OReval(
ORatob(
- AND requester is not a known, trusted admin IP (or user is contributor role)
- Action: Return HTTP 403 and log the request.
Note: Do NOT implement a blanket block for all script tags if your site legitimately needs them. Tuning is critical — test rules in monitoring mode first.
Step-by-step update & remediation plan for site owners
- Immediate check (0–1 hour)
– Confirm WPBakery version.
– If on <= 8.6.1, put site in maintenance mode (if high-risk) and continue. - Deploy virtual patch (0–4 hours)
– Enable WAF rule that detects script patterns from non-admin roles.
– Implement CSP for inline script suppression if feasible. - Update (0–24 hours)
– Update WPBakery to 8.7+.
– Update other plugins & core while monitoring for theme/plugin compatibility issues. - Clean (0–48 hours)
– Run DB & file scans for injected JavaScript.
– Remove or sanitize malicious content after backing up.
– Rotate passwords and review recent user activity. - Harden (48–72 hours)
– Enforce MFA for all privileged accounts.
– Disable unnecessary capabilities for contributor role.
– Set up continuous monitoring & alerting. - Post-incident review
– Document the incident, timelines and corrective actions.
– Adjust policies (user registration, moderation, plugin vetting).
Frequently asked questions
Q: If my site doesn’t have contributor accounts, am I safe?
A: Your risk is much lower, but not zero. Attackers can use other exploitation chains, and some plugins/themes can expose similar functionality to other roles. Confirm plugin version and still update.
Q: Can a WAF break WPBakery functionality?
A: If rules are too broad, yes. That’s why we prefer targeted rules that block known malicious patterns or prevent script injection from low-privileged users. Testing rules in monitoring mode first is best practice.
Q: My site was injected — how long should I expect remediation to take?
A: It depends on infection scope. A single post cleanup can be minutes; deep infections (backdoors, multiple files) may require 24–72 hours with a full forensic clean.
New: Protect your site with WP-Firewall Free Plan (try now)
Start protecting your WordPress site with WP-Firewall Basic — free and fast
If you’re looking for a low-friction way to add multiple defense layers while you update and clean your site, our Basic (Free) plan includes essential protections tailored for WordPress:
- Managed firewall and WAF tuned for WordPress threats
- Unlimited bandwidth behind the firewall
- Malware scanner to find stored script injections and known payloads
- Mitigation rules for OWASP Top 10 risks, including XSS patterns
Sign up now and get immediate virtual patching and scanning to reduce exposure while you perform updates and cleanups: https://my.wp-firewall.com/buy/wp-firewall-free-plan/
(Upgrading to Standard or Pro adds automated removal, IP control, monthly security reports and auto virtual patching for continuous protection.)
Final words — prioritize the update, but plan for defense-in-depth
This WPBakery stored XSS is a timely reminder that even popular content-building features that accept JavaScript can become high-risk vectors if used without strict role controls and sanitization. The fix (WPBakery 8.7) removes the immediate bug — but the broader lesson stands:
- Apply updates promptly.
- Limit and monitor the ability to add script-like content.
- Use a managed WAF to stop exploit attempts immediately (virtual patching).
- Scan and clean stored content regularly.
- Enforce least privilege for account roles and use MFA.
If you need help with rules, scanning, or incident cleanup, the WP-Firewall team can provide guided remediation and managed WAF tuning — or you can start with our free Basic plan (link in the section above) to get instant coverage while you work through the update and cleanup steps.
Stay safe and keep a close eye on any features that accept or render raw JS — they’re useful, but they must be protected.
— WP-Firewall Security Team