Urgent Security Advisory Stored XSS in WPBakery//Published on 2025-10-15//CVE-2025-11161

ZESPÓŁ DS. BEZPIECZEŃSTWA WP-FIREWALL

WPBakery Page Builder Vulnerability CVE-2025-11161

Nazwa wtyczki WPBakery Page Builder
Type of Vulnerability Zapisane XSS
CVE Number CVE-2025-11161
Pilność Niski
CVE Publish Date 2025-10-15
Source URL CVE-2025-11161

WPBakery Page Builder <= 8.6.1 — Stored XSS via vc_custom_heading shortcode (CVE-2025-11161): What WordPress site owners must do now

Opublikowany: 15 October 2025
Powaga: CVSS 6.5 (Medium / Low patch priority)
Affected: WPBakery Page Builder plugin versions <= 8.6.1
Naprawiono w: 8.7
CVE: CVE-2025-11161
Reported by: independent researcher

As a WordPress security practitioner working with site owners every day, I want to give you a clear, practical guide to this vulnerability, explain real-world risks and detection options, and walk through immediate mitigations you can apply — whether you run a single blog or manage hundreds of client sites.

This vulnerability is a stored cross-site scripting (XSS) issue originating in the vc_custom_heading shortcode in WPBakery Page Builder. A user with the ability to add or edit content (the public disclosure indicates the required minimum privilege is a Contributor) can insert payloads that are stored in the database and later rendered on public pages. When that malicious content is viewed, the attacker’s script executes in visitors’ and potentially administrators’ browsers.

Below I’ll cover:

  • What exactly is wrong and why it matters
  • Who is at risk and realistic exploitation scenarios
  • How to find whether your site is vulnerable or already injected
  • Immediate and layered mitigations: update, virtual patching/WAF rules, content sanitization and hardening
  • Incident response if you discover an infection
  • How WP-Firewall protects you and a simple, secure path to get started

This is written from the perspective of a professional WordPress security team — pragmatic, focused on defender actions, and with remediation options you can apply right away.


Executive summary (short)

  • The vulnerability allows stored XSS via the vc_custom_heading shortcode in WPBakery Page Builder (<= 8.6.1). The plugin renders user-supplied heading content without adequate sanitization or escaping.
  • Fixed in WPBakery Page Builder 8.7. Upgrading the plugin to 8.7+ is the primary long-term fix.
  • Immediate mitigations include applying a WAF/virtual patch, removing or sanitizing dangerous shortcode content, auditing contributor-created content, and hardening user privileges.
  • If you suspect compromise, follow incident response steps: isolate, preserve evidence, scan and clean, rotate credentials.

Technical background — root cause explained

Shortcodes in WordPress are a mechanism that allows plugins and themes to declare a textual token that expands to HTML during content rendering (for example, [vc_custom_heading ...]). The WPBakery Page Builder plugin exposes many shortcodes to store visual components.

This issue is a classic stored XSS:

  1. A user with the ability to create or edit content (Contributor or higher) inserts a crafted payload into a shortcode attribute or content field managed by the vc_custom_heading shortcode.
  2. The plugin stores that content in the database (as part of the post content or post meta).
  3. On page render, the plugin outputs the stored value into the HTML without adequate escaping or with a permissive filtering policy that allows script-capable attributes (for example, inline event handlers or unfiltered HTML).
  4. When another user or visitor loads the page, the malicious script executes in their browser context.

Because the payload is stored, this is a persistent vector — it continues to affect visitors until cleaned.

Important context:

  • Required privilege: Contributor (per the disclosure) — that means non-admin user accounts obtained by registration, compromise or unvetted contributors can be abused.
  • Attack surface: primarily front-end visitors; however, an XSS that targets admin users (e.g., site editors or administrators who view the injected content in the admin area) can be escalated to remote code execution or site takeover via CSRF/JS-powered API calls.

Realistic exploitation scenarios

  • Malicious registered user (or attacker who registers) creates a page or post using WPBakery elements and places a payload in the heading field. The site publishes the content. Every visitor including admins who view that page can execute attacker-supplied JavaScript.
  • Compromised contributor account is used to inject payloads into frequently visited pages (home page, resource pages) to maximize spread and persistence.
  • An attacker crafts a payload that performs background requests to wp-admin/admin-ajax.php or REST API endpoints with the currently authenticated admin cookie — this can be used to create a new admin user, change settings, or upload a backdoor (depending on available endpoints and CSRF protections).
  • Advertising or redirect payloads that affect SEO and reputation, or credential phishing pages to harvest user credentials.
  • Drive-by malware: the injected JavaScript can include third-party payloads that attempt to deliver cryptomining scripts or redirect users to malicious sites.

Why this is more than an annoyance: stored XSS can lead to full admin takeover if the victim is an authenticated admin who visits a poisoned page. It’s also a privacy and trust problem for your users.


Who is at risk?

  • Any site running WPBakery Page Builder version <= 8.6.1.
  • Sites that allow users with Contributor or higher roles to publish or save content (e.g., membership/community sites, multi-author blogs, some e-commerce setups with vendor roles).
  • Sites that have not patched to 8.7+ and that do not have protective firewall rules or content sanitization.

If you host dozens or hundreds of WordPress sites or provide managed WordPress services for clients, treat this as a priority to assess and remediate even if your site has few contributors.


How to check your site — discovery & detection

The first step is to confirm whether WPBakery Page Builder is present and the plugin version.

  1. Check plugin version
      – WordPress admin: Plugins → Installed Plugins → find WPBakery Page Builder.
      – If you cannot access admin or for a remote check, look for code references or readme files. The safest path is via the WordPress admin or via file inspection on the server.
  2. Identify posts using the vulnerable shortcode
    You want to find posts that contain the vc_custom_heading shortcode or suspicious HTML attributes.

SQL (run carefully, preferably on a staging copy):
– Find posts that reference the shortcode:
SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%vc_custom_heading%';

– Find posts that include suspicious attributes or script-like content:
SELECT ID, post_title FROM wp_posts WHERE post_content REGEXP '<(script|img|iframe|svg|object|embed)[[:space:]]|onerror=|onload=|javascript:';

WP-CLI (faster for multiple sites):
wp post list --post_type=post,page --format=ids --field=ID --post_status=any --< use grep on content >
– Or export and grep: wp db export - && grep -R "vc_custom_heading" -n

  1. Searching for stored payloads in user-supplied meta or page builder meta
    – Page builders sometimes store configuration in post meta; search wp_postmeta.value for suspicious content:
    SELECT post_id, meta_key FROM wp_postmeta WHERE meta_value LIKE '%<script%' OR meta_value LIKE '%onerror=%' OR meta_value LIKE '%vc_custom_heading%';
  2. Log and traffic indicators
    – Unusual outbound requests from client browsers reported in analytics (referrer anomalies).
    – New administrator accounts created without expected workflow or emails.
    – An increase in errors or suspicious admin actions.
  3. Scan using a reputable malware scanner or an endpoint that can detect suspicious inline JavaScript in posts and post meta. If you use a web application firewall with virtual patching, it may have already flagged or blocked attempted exploit traffic.

Note: Always operate on a backup or staging copy when performing bulk finds and replaces. Don’t modify production content blindly.


Immediate steps you must take (triage)

If you are running an affected version, prioritize actions in this order:

  1. Update WPBakery Page Builder to 8.7 or later immediately.
    – This is the single most important corrective step where possible.
    – If the site cannot be updated instantly due to compatibility concerns, proceed with the mitigations below until you can patch.
  2. Apply virtual patching (WAF rule) to block exploitation attempts.
    – Block requests that attempt to submit shortcodes or suspect attributes via POSTs to post creation endpoints, admin-ajax endpoints and REST endpoints.
    – Block requests containing patterns such as vc_custom_heading combined with event handlers (onerror, załadować), <script> tags, or javascript: URIs.
  3. Audit content produced by Contributor and Author accounts.
    – Search posts/pages and post meta for vc_custom_heading and suspicious attributes.
    – Remove or sanitize detected payloads or temporarily unpublish affected content.
  4. Harden user privileges:
    – Temporarily restrict or disable the ability for Contributors to publish or use page builder tools until you confirm site cleanliness.
    – Use a stricter publish workflow: require editors or admins to review content from non-trusted users.
  5. Rotate secrets and sessions where appropriate:
    – Reset passwords for administrative users if you suspect any administrative account was exposed or if an admin visited a poisoned page.
    – Invalidate active sessions where possible (force logout users — change salts or use tools to terminate sessions).
  6. Take a full backup (files + database), then scan with malware scanner and perform manual inspection.

If you find active malicious scripts or backdoors, follow an incident response strategy (below).


Example WAF rules and virtual patching guidance

If you operate a web application firewall (WAF) or have ModSecurity/NGINX/Cloud WAF controls, you can deploy targeted rules that block exploit attempts while you patch. Below are defensive examples meant for site administrators and security teams.

Note: Rules should be tested in blocking and monitoring modes on staging before full production enforcement to avoid false positives.

ModSecurity example (conceptual — adapt to your ruleset syntax):

# Block attempts to submit vc_custom_heading with inline script or event attributes
SecRule REQUEST_BODY|ARGS|ARGS_NAMES "vc_custom_heading" "phase:2,deny,log,status:403,id:100001,msg:'Block attempt to exploit vc_custom_heading stored XSS',chain"
  SecRule REQUEST_BODY|ARGS "(

NGINX+Lua or NGINX with regex-based blocking (simplified):

if ($request_method = POST) {
    set $block 0;
    if ($request_body ~* "vc_custom_heading") {
        if ($request_body ~* "(

WordPress-level filter (temporary virtual patch via mu-plugin)
- Add an mu-plugin (must-use plugin) that sanitizes post content on save or strips unsafe attributes from vc_custom_heading before store or render. Example approach: intercept saving posts and strip event handlers and script tags for any content that contains vc_custom_heading.

Example mu-plugin skeleton (conceptual):

<?php
/*
Plugin Name: Temporary vc_custom_heading sanitizer (mu)
Description: Virtual patch - remove potentially dangerous attributes from vc_custom_heading
*/

add_filter('content_save_pre', 'vc_heading_virtual_patch', 10, 1);
function vc_heading_virtual_patch($content) {
    if (stripos($content,'vc_custom_heading') === false) {
        return $content;
    }
    // Remove script tags and event handlers
    $content = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content);
    $content = preg_replace('/\s(on\w+)\s*=\s*"[^"]*"/i', '', $content); // strips onerror="..." inline handlers
    $content = preg_replace("/javascript:/i", "", $content);
    return $content;
}

Important: This is a stop-gap measure. It aims to neutralize dangerous attributes, not replace a plugin update. The mu-plugin should be tested before enabling on production.


Sanitization and developer guidance (how the plugin should change)

From a secure development perspective the correct fixes are:

  • Escape all user-controlled values on output:
      - Use esc_html(), esc_attr(), esc_url() for the appropriate context.
  • Whitelist allowed HTML:
      - If the shortcode legitimately needs some HTML, use wp_kses() with a strict allowed elements and attributes list.
  • Avoid echoing raw user input inside attributes that can contain event handlers (on* attributes) or protocols (javascript:).
  • Sanitize data on save as an additional safeguard (defense-in-depth), but never rely only on save-time sanitization — always escape on output.

Example safe rendering strategy for a heading shortcode:

$allowed_tags = array(
  'strong' => array(),
  'em'     => array(),
  'br'     => array(),
  'span'   => array('class' => true),
  'a'      => array('href' => true, 'rel' => true, 'target' => true)
);
$safe_text = wp_kses( $raw_text, $allowed_tags );
echo '<h2 class="'.esc_attr($class).'">'.wp_kses_post($safe_text).'</h2>';

Hunting for injected content (practical queries & regex)

  • Find any instances of script tags inside posts:
    SELECT ID, post_title FROM wp_posts WHERE post_content REGEXP '<script[[:space:]]';
  • Locate event-handler attributes:
    SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%onerror=%' OR post_content LIKE '%onload=%' OR post_content LIKE '%onclick=%';
  • Post meta search (page builders often store serialized arrays):
    SELECT post_id, meta_key FROM wp_postmeta WHERE meta_value REGEXP '<script|onerror=|onload=';
  • Regex to search exported content (grep):
    grep -R --line-number -E "(vc_custom_heading|onerror=|<script|javascript:)" wp-content

When you find suspicious content, export that post to a safe environment and inspect it carefully. If you are unsure, restore from the pre-injection backup and contact a security professional.


If you find a compromise — incident response checklist

  1. Isolate and preserve
    - Put the site into maintenance mode or block inbound traffic to limit further damage.
    - Make a full forensic backup: files + database, preserve timestamps and logs.
    - Take screenshots and save logs for later analysis.
  2. Identify scope
    - Determine which pages, users and uploads are modified.
    - Check for new admin users and unexpected scheduled tasks (wp_options -> cron entries).
    - Inspect uploads for webshells or newly modified PHP files.
  3. Clean & restore
    - Remove injected content from posts or restore clean versions from a verified pre-infection backup.
    - Replace core, plugin and theme files with fresh copies from trusted sources.
    - Remove unknown users and rotate passwords (admin accounts, FTP, database, hosting control panel).
  4. Strengthen
    - Update all plugins, themes and WordPress core.
    - Harden admin access (2FA for admins, limit login attempts, IP restrictions for wp-admin if feasible).
    - Apply virtual patch (WAF) and confirm attacks are blocked.
  5. Monitor and verify
    - Keep enhanced logging for 30 days and monitor for re-infection.
    - Scan files and database for anomalies weekly for a period.
    - Consider a professional incident response if the compromise is extensive.
  6. Post-incident review
    - Conduct root cause analysis: how did the contributor account get created/hijacked?
    - Update policies: contributor workflows, registration checks, content review.

If you are uncertain or if the site is business-critical, engage a professional incident response team.


Long-term hardening and best practices

  • Update promptly: keep WPBakery and all plugins/themes up to date.
  • Principle of least privilege:
      - Only grant Contributor or higher to people who genuinely need to create content.
      - Consider using a dedicated editorial workflow plugin where contributors submit for review.
  • Limit or sanitize page builder usage by untrusted roles:
      - Prevent page builder shortcodes from being used by untrusted users, or strip them on save.
  • Content filtering:
      - Use wp_kses() or tight sanitizers in theme/plugin code where user content is allowed.
  • Backups and restore practice:
      - Maintain automated daily backups and test restores periodically.
  • Use a reputation-aware WAF (managed rules) and continuous malware scanning.
  • File integrity monitoring: detect unexpected file changes early.

How WP-Firewall protects your site (managed defender perspective)

If you are evaluating or using WP-Firewall, here is how our platform helps you defend against issues like this:

  • Managed Web Application Firewall (WAF): Our WAF includes targeted rules for page-builder shortcodes and stored XSS patterns. These rules can be deployed immediately across your site to block exploit attempts for known vulnerabilities, including the vc_custom_heading vector, even before a plugin update is applied.
  • Virtual patching: We can apply virtual patches that sanitize or block exploit payloads at the HTTP layer, buying you time to test and apply the vendor-supplied plugin update.
  • Malware scanner & cleaning: Continuous content scanning that detects injected scripts in post content, post meta and files. For paid plans, automatic malware removal reduces manual remediation time.
  • Role-aware protections: Restrict or monitor actions from contributor and author roles to prevent untrusted users from placing dangerous shortcodes or HTML.
  • Unlimited bandwidth and reliable protection: Our system scales with your site without throttling legitimate traffic while keeping requests scanned in real time.
  • Alerts & reports: Notifications of detections and remediation guidance that help administrators act fast and confidently.

We always recommend a layered approach: plugin updates + WAF + content hygiene + incident response readiness.


Practical remediation playbook (step-by-step)

  1. Backup now: create a full backup of files and DB (download offsite).
  2. Update WPBakery Page Builder to 8.7+ on a staging copy. Verify functionality.
  3. Test the plugin update on staging. If all is well, deploy to production.
  4. If you cannot immediately update:
      - Deploy a WAF rule as shown above (or enable our managed protection).
      - Add a mu-plugin that strips event handlers and script tags on save.
      - Temporarily restrict contributor publishing or disable frontend access to page builder for untrusted roles.
  5. Search & clean:
      - Run the detection SQL/grep queries and remove suspicious content.
      - If lots of content is affected, restore clean backups for those posts/pages when possible.
  6. Rotate credentials and terminate sessions for admins.
  7. Keep monitoring for at least 30 days.

Sample detection regexes and admin workflows

Regex to find common inline event handlers and javascript: URIs:
/(on\w+\s*=|<script\b|javascript:)/i

Recommended admin workflow:

  • Set up a “content review” user role and require two-person review for pages containing shortcodes.
  • Flag any content containing vc_custom_heading for manual review with a one-click quarantine option.

Protect your WordPress site in minutes — try the WP-Firewall Basic (Free) plan

If you want a fast, low-friction way to reduce risk immediately, the WP-Firewall Basic (Free) plan gives you essential protection instantly: a managed firewall, WAF rules, unlimited bandwidth, malware scanner, and mitigation against the OWASP Top 10. It’s an easy first line of defense while you schedule updates and harden contributors’ workflows. Start a free account today and get proactive protection in place: https://my.wp-firewall.com/buy/wp-firewall-free-plan/

(If you need automatic malware removal and IP blacklisting controls, consider upgrading to Standard. For organizations that want monthly reports and auto virtual-patching, the Pro plan adds advanced visibility and managed support.)

Plan snapshot:

  • Basic (Free): managed firewall, WAF, malware scanner, unlimited bandwidth, OWASP Top 10 mitigation
  • Standard ($50/year): Basic + automatic malware removal + IP allow/deny up to 20 entries
  • Pro ($299/year): Standard + monthly security reports + auto vulnerability virtual patching + premium add-ons and managed services

Closing notes — practical takeaways

  • Upgrade WPBakery Page Builder to 8.7+ as soon as possible. This is the definitive fix for CVE-2025-11161.
  • In parallel, deploy a WAF rule or a temporary server-side filter to block exploit payloads and sanitize content created by untrusted users.
  • Hunt for injected content using the SQL, WP-CLI and grep patterns above. Clean or restore affected content and rotate credentials if you find malicious content.
  • Reconsider contributor workflows and reduce the blast radius of non-admin roles. Enforce content review and sanitize content at both save and output time.
  • If you would like managed prevention and virtual patching so you can focus on your business, consider the WP-Firewall Basic (Free) plan to start protecting your site immediately.

If you’d like help triaging a site or need a managed remediation plan, our team can assist — from targeted virtual patches for this specific vulnerability to a full incident response and site hardening engagement.


wordpress security update banner

Otrzymaj WP Security Weekly za darmo 👋
Zarejestruj się teraz
!!

Zarejestruj się, aby co tydzień otrzymywać na skrzynkę pocztową aktualizacje zabezpieczeń WordPressa.

Nie spamujemy! Przeczytaj nasze Polityka prywatności Więcej informacji znajdziesz tutaj.