CVE-2026-95530 Subscriber to administrator in PixelYourSite

Intrudify's autonomous pentesting engine discovered a stored cross-site scripting vulnerability in PixelYourSite, affecting all versions up to and including 11.4.1. A write permission callback on the plugin's WordPress Abilities performs no capability check, so a default Subscriber can store a payload that reaches an unescaped sink on an administrator-only screen and executes with administrator authority.

Discovered by
Intrudify autonomous engine
Validated & reported by
Tudor Lasuschevici
Disclosure
23 September 2026
Severity
Medium · CVSS 6.5
Vulnerability class
Stored XSS, CWE-79
Affected
PixelYourSite ≤ 11.4.1
Fixed in
11.4.2
Active installs
500,000+
Privilege required
Subscriber (read only)
Precondition
WordPress 6.9+ and a co-installed MCP server

Summary

PixelYourSite is a tracking and pixel management plugin for WordPress. Intrudify's autonomous testing engine found that the plugin registers WordPress Abilities, the API added in WordPress 6.9, whose write permission callback contains no capability check at all. Those abilities are published globally, so any other plugin on the site that exposes an MCP server will offer them to any caller holding the read capability. A default Subscriber has read.

One of the values a Subscriber can write is then printed into an HTML attribute on an administrator-only screen with no escaping. The result is stored XSS in the admin context, and a chain that ends in a new administrator account.

Technical detail

A permission callback with no permission check

The write abilities share a base class at includes/mcp/Abilities/AbstractWriteAbility.php:41:

public static function permissionCallback( $input = null ) {
    if ( Capabilities::isReadOnly() ) {
        return ErrorEnvelope::readOnly();
    }
    return true;
}

There is no current_user_can, no ownership test and no role test. The single gate is a global read-only toggle, and that toggle defaults to off, so the write abilities are live on a default install. The subsystem loads unconditionally: McpServer::instance() runs at pixelyoursite.php:121, and the readiness check tests only for WordPress 6.9 and the plugin's bundled adapter. No administrator configuration is involved.

The plugin guards its own endpoint, but not its abilities

This is the part worth understanding, because the plugin's own transport is correct. McpServer::register_server() passes an auth callback that requires an authorization header, and requests to PixelYourSite's own endpoint are properly gated. The abilities, however, are also published to every other MCP server on the site, and those transports gate only on read.

So the exploitable path does not go through the vulnerable plugin's endpoint at all. It goes through a co-installed plugin's server, which faithfully offers abilities it did not write and cannot vet. In testing we used an SEO plugin as the transport. Any plugin publishing an MCP server would do. Deactivating it removes the route entirely, which is why it appears as a precondition rather than as part of the vulnerability.

The sink

At includes/views/html-main-events-edit.php:1478:

<input type="text"
  name="pys[event][facebook_params][<?= $field['label'] ?>]"
  value="<?= $param_value; ?>"

$param_value is printed with <?= ?> and no escaping function, inside a double-quoted HTML attribute. This is the plugin's own convention broken in exactly one place: esc_attr() appears twenty times in that same file, and the custom parameters block forty lines further down escapes both of its fields correctly.

The chain

A default Subscriber calls the co-installed server's ability-execution tool, naming pixelyoursite/set-custom-event, and writes a facebook_params value shaped to close the value=" attribute and append event-handler attributes:

// abbreviated. tags are stripped on save, attributes are not,
// so the payload is an attribute rather than a tag.
"content_name": "POC\" style=\"animation:rotation 1s\" onanimationstart=\"..."

Two details decide whether the chain works. The write must include a trigger, because the event factory skips any event with no triggers and hands the view an empty object. And the execution trigger is a CSS animation rather than autofocus, because @keyframes rotation is already defined in wp-admin/css/forms.css and is therefore available on that page.

The same request also enables the parameters panel, so the element is rendered rather than left in a collapsed container. When an administrator opens the event and expands the Meta card, the animation starts, the handler fires, and the payload runs with administrator authority.

Impact

The attacker account is a default Subscriber whose capabilities are exactly read and level_0. We reset the role to stock WordPress and re-ran the full chain to confirm nothing else was required.

We ran the chain to completion. The end state is a new WordPress account holding the administrator role, read back from wp_usermeta rather than from a JavaScript marker, with manage_options, install_plugins, edit_users and promote_users. Since install_plugins permits arbitrary PHP upload, the ceiling is full site compromise starting from an account that could do nothing but read.

The user interaction required is an administrator expanding a settings card on an event they have already opened to inspect, which is ordinary behaviour rather than something the attacker must engineer.

Remediation

Update PixelYourSite to 11.4.2 or later.

If you cannot update immediately, enable the plugin's read-only toggle, which disables the write abilities, and review which co-installed plugins publish an MCP server.

For maintainers, there are two independent defects and fixing either breaks the chain. Put a real capability check in the write permission callback rather than relying on a global toggle. And escape the sink with esc_attr(), matching what the rest of that file already does.

Why this class of bug is new

The Abilities API landed in WordPress 6.9, and it changes who can reach a plugin's code. A permission callback used to govern the plugin's own routes. An ability is published globally, so the same callback now governs an attack surface exposed by transports belonging to other plugins, written by other authors, with their own and possibly weaker authentication.

The generalisable lesson: if a plugin registers write abilities, the permission callback is the only control it has, because it cannot assume anything about the transport that will carry the call. A correctly secured own endpoint provides no protection. Anyone auditing plugins that adopted the Abilities API should check the write callbacks first.

Disclosure timeline

  • 2026-09-21 Vulnerability identified and full chain validated by Intrudify
  • 2026-09-21 Reported to the vendor via Patchstack
  • 2026-09-23 Public disclosure · CVE-2026-95530 assigned
  • 2026-09-29 Fixed release 11.4.2 published by the vendor

Questions

What is CVE-2026-95530?

CVE-2026-95530 is a stored cross-site scripting vulnerability in the PixelYourSite WordPress plugin, versions 11.4.1 and earlier. It was discovered autonomously by the Intrudify AI penetration testing engine. A write permission callback with no capability check lets a default Subscriber store a payload that executes in an administrator's browser.

Which versions of PixelYourSite are affected?

All PixelYourSite versions up to and including 11.4.1 are affected. The MCP subsystem that introduces the issue was added in 11.3.0. The vulnerability is fixed in 11.4.2.

How do I fix CVE-2026-95530?

Update PixelYourSite to version 11.4.2 or later. If you cannot update immediately, enable the plugin's read-only toggle to disable the write abilities, and audit any co-installed plugin that publishes an MCP server.

References

More advisories from Intrudify

Join the Future of
AI-Driven Pentesting