# Laboratorio Docker para reproducir CVE-2026-27541, una escalada de privilegios autenticada en WooCommerce Wholesale Prices. Compara las versiones vulnerable y parcheada, incluye script PoC y análisis de la causa raíz.

Este proyecto es un laboratorio Docker local para analizar y reproducir el comportamiento de CVE-2026-27541 en el plugin WooCommerce Wholesale Prices / Wholesale Suite, comparando las versiones vulnerable y parcheada lado a lado en la misma máquina.
El problema principal es un Control de Acceso Roto en el siguiente endpoint de la API REST:
/wp-json/wwp/v1/admin/save
Según el código fuente utilizado en este laboratorio, esa ruta ya tiene un permission_callback tanto en la versión vulnerable como en la parcheada. Sin embargo, la versión afectada utiliza una comprobación de capacidades demasiado amplia para una acción de escritura de ajustes de administrador:
2.2.6) → current_user_can( 'manage_woocommerce' )2.2.7) → current_user_can( 'manage_options' )En este laboratorio, el usuario shopmgr, que tiene el rol shop_manager, tiene manage_woocommerce=true pero no manage_options. Como resultado, este usuario de bajo privilegio puede utilizar una sesión válida iniciada más un X-WP-Nonce válido para invocar el endpoint de guardado de ajustes de administrador en la versión vulnerable, mientras que la versión parcheada devuelve 403 rest_forbidden para la misma solicitud.
2.2.6): el usuario shopmgr puede llamar con éxito a POST /wp-json/wwp/v1/admin/save y modificar los ajustes del plugin.2.2.7): la misma solicitud de shopmgr es rechazada con 403.403, lo que es coherente con que se trate de un problema de escalada de privilegios post-autenticación.wwp_see_wholesale_prices_replacement_text=PWNED_BY_POC, mientras que la versión parcheada sigue devolviendo See wholesale prices.vuln → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.6patched → WordPress + WooCommerce + WooCommerce Wholesale Prices 2.2.7db-vuln / db-patched → bases de datos MariaDB separadasseed-vuln / seed-patched → trabajos de inicialización wp-cli que instalan WordPress, instalan el plugin, crean usuarios y crean un producto para pruebashttp://localhost:8081 → vulnerablehttp://localhost:8082 → parcheadowordpress:6.8.1-php8.2-apachemariadb:11.4.5wordpress:cli-php8.2.
├── docker-compose.yml
├── README.md
├── patched/
│ └── Dockerfile
├── vuln/
│ └── Dockerfile
├── scripts/
│ └── seed-wp.sh
└── poc.py
docker-compose.yml — define las pilas vulnerable y parcheada con bases de datos separadasscripts/seed-wp.sh — instala WordPress, WooCommerce, el plugin objetivo y crea usuarios de prueba y datos de productopoc.py — PoC de mínimo daño para inicio de sesión, extracción de nonce e invocación del endpoint RESTUna vez que la pila está lista, el script de inicialización crea lo siguiente:
admin / AdminPass!234shopmgr / ShopMgrPass!234lab-product10050WooCommerce: 10.6.0
WooCommerce Wholesale Prices:
2.2.62.2.7El script también escribe lab-secrets.json en cada contenedor de WordPress para que los datos inicializados y la información de versión puedan verificarse.
Este PoC no intenta tomar el control del sitio, cambiar roles, instalar plugins ni ejecutar un shell.
Solo hace lo siguiente:
shopmgrwpApiSettings.noncewwp_see_wholesale_prices_replacement_text = PWNED_BY_POC
Este ajuste se utiliza como marcador observable para demostrar que un usuario de bajo privilegio puede modificar configuración exclusiva de administradores.
POST /wp-json/wwp/v1/admin/saveAunque el endpoint requiere una sesión válida y un nonce, la versión vulnerable aún permite que un usuario de bajo privilegio como shopmgr con el rol shop_manager invoque un endpoint que debería ser exclusivo de administradores.
Esta no es una vulnerabilidad no autenticada.
Si el endpoint se llama directamente sin una sesión iniciada, tanto la versión vulnerable como la parcheada rechazan la solicitud. El error existe en la autorización después de la autenticación, no en la autenticación en sí.
La versión vulnerable no carece de un permission_callback. El fallo es que utiliza una comprobación de capacidades demasiado amplia (manage_woocommerce) para una acción REST que escribe ajustes del lado del administrador.
Del código fuente en includes/class-wwp-admin-settings.php:
POST /wp-json/wwp/v1/admin/savesave_registered_settings()permission_admin_check() como permission_callback2.2.6)if ( ! current_user_can( 'manage_woocommerce' ) ) {
return new WP_Error( 'rest_forbidden', ... );
}
2.2.7)if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error( 'rest_forbidden', ... );
}
En este laboratorio, el usuario shopmgr, que tiene el rol shop_manager, tiene manage_woocommerce=true pero no manage_options, por lo que supera la comprobación vulnerable pero falla en la parcheada.
La versión parcheada hace más que cambiar la respuesta de 200 a 403. Cambia la lógica de control de acceso al endurecer el requisito de capacidad de manage_woocommerce a manage_options.
Además, la ruta de guardado en la versión parcheada está aún más reforzada al alejarse del filtrado basado en prefijos hacia listas blancas explícitas y una sanitización más fuerte.
El PoC sigue el mismo flujo que un contexto de navegador real:
shopmgrwpApiSettings.noncePOST /wp-json/wwp/v1/admin/saveDebido a que la versión vulnerable permite que los usuarios con manage_woocommerce invoquen esta acción de escritura de ajustes, la solicitud tiene éxito y resulta en un cambio persistente de opción.
Un nonce ayuda a proteger contra CSRF, pero no es un control de autorización.
Tener una sesión válida y un nonce válido no significa que un usuario deba tener permitido realizar una acción de administrador. Usar una capacidad demasiado amplia en un endpoint privilegiado es suficiente para crear una omisión de autorización de bajo privilegio.