RCE no autenticada en Plone, corregida en 6.2.3 (Hotfix 20261110)
Una cadena de cinco fallos permite a un visitante anónimo ejecutar código en Plone 6.0 a 6.2 y versiones anteriores; unas actualizaciones la cierran.
Resumen
- Una cadena de cinco fallos permite a un visitante anónimo ejecutar código en un sitio Plone. Los modificadores de versionado publican su método edit sin control de permisos, y cuatro paquetes abren módulos Python enteros al código restringido.
- Todas las versiones compatibles están afectadas: Plone 6.2, 6.1 y 6.0, y las versiones anteriores hasta 4.3 mediante el paquete de correcciones.
- Fallo principal: CVSS v3.1 9.8 Crítica, CWE-862. Sin CVE, sin explotación conocida, y ninguno de los fallos está en el catálogo Known Exploited Vulnerabilities de CISA a 11 de noviembre de 2026.
- Actualice los cinco paquetes a las versiones corregidas, o instale Products.PloneHotfix20261110 y reinicie cada instancia de Zope. Véase «¿Cómo lo corrijo?».
¿Estoy afectado?
Todas estas condiciones deben cumplirse:
- Ejecuta Plone 6.2, 6.1 o 6.0, o una versión anterior hasta 4.3. El aviso enumera todas las versiones compatibles y afirma que las versiones anteriores también están afectadas.
- Su sitio tiene
Products.CMFEditions, el paquete de versionado que proporciona la herramientaportal_modifier. Plone lo incluye por defecto. - Un atacante puede alcanzar el sitio por HTTP. El punto de entrada es el objeto
portal_modifier, que la adquisición de Zope hace accesible bajo cualquier carpeta.
La cadena funciona sin cuenta. Uno de los fallos también se ejecuta cuando un editor guarda contenido versionado, así que un sitio que no está expuesto directamente conserva una ruta interna. El proveedor afirma que el conjunto completo de actualizaciones de paquetes es la corrección recomendada; el paquete de correcciones cubre las versiones anteriores que ya no reciben actualizaciones.
La imagen de contenedor oficial plone/plone-backend tiene 486,034 descargas en Docker Hub, una señal del número de despliegues que ejecutan esta pila.
| Rama | Versiones afectadas | Conjunto corregido |
|---|---|---|
| Plone 6.2 | Todas las versiones anteriores a 6.2.3 | Products.CMFPlone 6.2.3, Products.CMFEditions 5.0.2, Products.MimetypesRegistry 4.0.2, plone.batching 3.0.3, plone.restapi 10.2.1 |
| Plone 6.1 | Todas las versiones anteriores a 6.1.6 | Products.CMFPlone 6.1.6, Products.CMFEditions 4.0.7, Products.MimetypesRegistry 3.0.2, plone.batching 2.0.8, plone.restapi 9.15.8 |
| Plone 6.0 | Todas las versiones anteriores a 6.0.16 | Products.CMFPlone 6.0.16, Products.CMFEditions 4.0.7, Products.MimetypesRegistry 3.0.2, plone.batching 2.0.8, plone.restapi 9.15.8 |
| Plone 4.3 a 5.2 | Todas las versiones | Sin actualizaciones de paquetes; instale Products.PloneHotfix20261110 |
Compruebe los paquetes instalados desde el entorno virtual o el buildout que ejecuta Zope:
pip show Products.CMFPlone Products.CMFEditions Products.MimetypesRegistry plone.batching plone.restapi
# Buildout: print the pinned versions
bin/buildout annotate | grep -E 'Products.CMFPlone|Products.CMFEditions|Products.MimetypesRegistry|plone.batching|plone.restapi'
# Constraints or requirements files used to build the instance
grep -R -E 'Products.CMFPlone|Products.CMFEditions|Products.MimetypesRegistry|plone.batching|plone.restapi' constraints.txt requirements.txt buildout.cfg
La nueva página @@check-modifier-conditions en la raíz del sitio Plone es la forma más rápida de ver si se modificó una condición de modificador. Inicie sesión como Manager y abra https://your-site/@@check-modifier-conditions. Compara las condiciones actuales con las predeterminadas y ofrece restaurarlas.
¿Cómo lo corrijo?
Actualice cada paquete de la fila correspondiente a su rama de Plone y reinicie todas las instancias de Zope. Si ejecuta una versión anterior, instale el paquete de correcciones en su lugar.
- En Plone 6.2, instale el conjunto corregido:
pip install -U "Products.CMFPlone==6.2.3" "Products.CMFEditions==5.0.2" "Products.MimetypesRegistry==4.0.2" "plone.batching==3.0.3" "plone.restapi==10.2.1" - En Plone 6.1, instale el conjunto corregido:
pip install -U "Products.CMFPlone==6.1.6" "Products.CMFEditions==4.0.7" "Products.MimetypesRegistry==3.0.2" "plone.batching==2.0.8" "plone.restapi==9.15.8" - En Plone 6.0, instale el conjunto corregido:
pip install -U "Products.CMFPlone==6.0.16" "Products.CMFEditions==4.0.7" "Products.MimetypesRegistry==3.0.2" "plone.batching==2.0.8" "plone.restapi==9.15.8" - En Buildout, fije las mismas versiones en la sección
[versions]y vuelva a ejecutarbin/buildout. En un archivo de restricciones, use la forma de doble igual, por ejemploProducts.CMFPlone==6.2.3. - En cualquier versión de 4.3 a 6.2 que no pueda recibir las actualizaciones de paquetes, instale el paquete de correcciones y reinicie cada instancia de Zope:
pip install Products.PloneHotfix20261110El paquete funciona con Python 2.7 y Python 3. Si el índice de paquetes no lo encuentra con esa grafía, use el nombre normalizadoproducts-plonehotfix20261110. - Reinicie todas las instancias de Zope, no solo la primera. La corrección se carga al iniciar el proceso.
Si no puede actualizar aún, bloquee toda URL que contenga portal_modifier en el proxy inverso. La adquisición de Zope hace accesible el objeto bajo cualquier carpeta, así que bloquéelo en cualquier parte de la ruta, no solo en la raíz del sitio.
- En nginx, añada una ubicación regex antes de la ubicación general:
location ~ portal_modifier { return 403; } - En Apache con
mod_rewrite, añada la regla antes de la regla de proxy:RewriteEngine On RewriteRule portal_modifier - [F,L] - Mantenga la regla después de la actualización. El preaviso la recomienda y no tiene coste.
La regla del proxy es una medida provisional. Apache, nginx y Traefik decodifican el porcentaje antes de comparar, así que /%70ortal_modifier se detecta. Varnish no decodifica la ruta, así que su regla debe cubrir también las formas codificadas. La regla no corrige las evasiones del entorno restringido si otra ruta alcanza código restringido.
¿Cuál es la causa raíz?
La cadena empieza porque los modificadores de versionado de Plone nunca declaran quién puede llamar a sus métodos. Un modificador es un SimpleItem que envuelve una operación de versionado. Zope publica un método que tiene docstring si ninguna declaración de seguridad lo protege. ConditionalModifier.edit y ConditionalTalesModifier.edit no tenían declaración, así que un visitante anónimo podía llamarlos.
La clase vulnerable en Products/CMFEditions/Modifiers.py en 5.0.1 es:
class ConditionalModifier(SimpleItem):
"""This is a wrapper for a modifier."""
modifierEditForm = PageTemplateFile(
"www/modifierEditForm.pt", globals(), __name__="modifierEditForm"
)
def edit(self, enabled=None, title="", REQUEST=None):
"""See IConditionalModifier."""
self.title = title
if enabled is not None and (
enabled == "True" or (isinstance(enabled, bool) and enabled)
):
self._enabled = enabled
else:
self._enabled = False
ConditionalTalesModifier hereda de ella y añade la condición:
class ConditionalTalesModifier(ConditionalModifier):
"""This is a wrapper with a tales condition for a modifier."""
def edit(self, enabled=None, condition=None, title="", REQUEST=None):
"""See IConditionalTalesModifier."""
ConditionalModifier.edit(self, enabled, title)
if condition is not None and condition != self.getTalesCondition():
self._condition = Expression(condition)
Expression(condition) compila la cadena del atacante como una expresión TALES. La expresión se evalúa después en isApplicable, que la maquinaria de versionado llama para decidir si se aplica un modificador:
def isApplicable(self, obj, portal=None):
"""See IConditionalTalesModifier."""
if not self._enabled or not self.getTalesCondition() or self.isBroken():
return False
context = createExpressionContext(obj, portal)
return self._condition(context)
Por sí sola, la falta de comprobación de permisos permite a un visitante anónimo cambiar una condición y activarla. La segunda mitad de la cadena es la evasión del entorno restringido. Se supone que Python restringido solo expone los nombres que un paquete declara públicos. Cuatro paquetes abrían en cambio módulos enteros con allow_module, lo que publica todos los nombres del módulo, incluidos los módulos y funciones que importa.
Products.CMFPlone en 6.2.2 abría tres módulos:
allow_module("Products.CMFPlone.utils")
# ...
allow_module("Products.CMFPlone.browser.navtree")
# ...
allow_module("OFS.ObjectManager")
Products.MimetypesRegistry abría su módulo common:
allow_module("Products.MimetypesRegistry.common")
plone.batching abría su propio paquete:
allow_module("plone.batching")
plone.restapi abría el módulo json de la biblioteca estándar:
allow_module("json")
El módulo json es inofensivo para la serialización, pero algunas de sus funciones pueden llamar a métodos protegidos de objetos, así que también ampliaba lo que podía hacer el código restringido. El grafo de importaciones es la clave: un módulo abierto de esta forma publica los ayudantes que importó, y esos ayudantes pueden alcanzar os. El aviso del proveedor afirma que los nombres expuestos permitían al código restringido alcanzar módulos como os y escapar del entorno restringido.
¿Cómo se explota?
- El atacante envía una petición a un objeto
portal_modifiery llama a su métodoedit. El método es publicable porque la clase no tiene declaración de seguridad. - La petición define
enabledy un valorcondition.ConditionalTalesModifier.editguarda el valor como unaExpressioncompilada. - El atacante activa el modificador, o espera a que un editor guarde contenido versionado para que la maquinaria llame a
isApplicable. isApplicableevalúa la condición guardada con los privilegios del proceso o del editor que guardó el contenido.- Python restringido detendría normalmente la expresión en el límite del entorno. Las declaraciones
allow_modulegenerales exponen los módulos e importaciones que llevan aos, así que la expresión escapa y ejecuta un comando. - El comando se ejecuta con el usuario del proceso Zope. En un despliegue predeterminado, ese usuario posee la instancia de Plone, su conexión a la base de datos y sus archivos subidos.
No hay código de explotación público. El aviso afirma que existe una prueba de concepto funcional y que se compartió con el proveedor, pero no publica la secuencia de peticiones. Los pasos anteriores siguen la descripción del aviso y la ruta de código de los paquetes corregidos.
¿Cómo se desarrolla el ataque?
El ataque convierte una petición no autenticada en una expresión TALES almacenada y luego abusa de las declaraciones de módulos demasiado amplias para evaluar esa expresión fuera del entorno restringido.
sin declaración de permisos"] B --> C["Condición TALES guardada
como Expression"] C --> D["isApplicable evalúa
la condición"] D --> E["allow_module expone
los ayudantes importados"] E --> F["Python restringido alcanza os"] F --> G["El código se ejecuta con el usuario del proceso Zope"]
¿Qué puede hacer un atacante?
Un atacante que alcanza la cadena puede ejecutar código con los privilegios del proceso Zope que sirve el sitio Plone. El aviso afirma que la ruta anónima puede ejecutar código en el servidor, y la misma condición puede ejecutarse con los privilegios de un editor cuando este guarda contenido versionado.
El impacto incluye:
- Leer y modificar el contenido del sitio y los archivos a los que llega el proceso Zope.
- Leer la configuración y los secretos disponibles para el proceso, incluidos los parámetros de conexión a la base de datos.
- Ejecutar comandos en el host con la cuenta de servicio de Plone.
- Usar el sitio como punto de apoyo hacia la red que alcanza su base de datos y sus servicios internos.
Las condiciones son las de «¿Estoy afectado?»: una versión afectada, la herramienta portal_modifier accesible por HTTP y ninguna actualización ni bloqueo en el proxy. El aviso de CMFEditions califica el fallo principal como CVSS v3.1 9.8 Crítica, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, y CWE-862. El aviso no afirma que el fallo dé root en el host. Afirma que se ejecuta código en el servidor, y los privilegios del proceso limitan lo que ese código puede hacer.
¿Por qué funciona la corrección?
La corrección cierra el punto de entrada anónimo y reduce el entorno restringido en la misma versión. El primer cambio añade una declaración de seguridad a las clases de modificadores en Products.CMFEditions 5.0.2:
class ConditionalModifier(SimpleItem):
"""This is a wrapper for a modifier."""
+ security = ClassSecurityInfo()
+
modifierEditForm = PageTemplateFile(
"www/modifierEditForm.pt", globals(), __name__="modifierEditForm"
)
+ @security.protected(ManagePortal)
def edit(self, enabled=None, title="", REQUEST=None):
"""See IConditionalModifier."""
@security.protected(ManagePortal) significa que Zope comprueba el permiso Manage portal antes de llamar a edit. Un visitante anónimo ya no pasa. Los accesores reciben ViewManagementScreens, y los hooks de comprobación de tamaño pasan a ser privados.
El segundo cambio sustituye cada allow_module general por una lista explícita. En plone.batching 3.0.3:
-from AccessControl import allow_module
+from AccessControl import ModuleSecurityInfo
from plone.batching.batch import Batch
-allow_module("plone.batching")
+# Only declare Batch public. Do not use allow_module here: that would make
+# every name in this module available to restricted code, including
+# imported names like allow_class.
+ModuleSecurityInfo("plone.batching").declarePublic("Batch")
allow_class(Batch)
Products.CMFPlone 6.2.3 sustituye allow_module("Products.CMFPlone.utils") por un objeto ModuleSecurityInfo y una lista de nombres públicos. Products.MimetypesRegistry 4.0.2 solo declara público MimeTypeException. plone.restapi 10.2.1 elimina por completo allow_module("json"). El entorno sigue exponiendo los nombres que necesitan las plantillas y los scripts, pero no los módulos que esos nombres importan.
La corrección es completa para los cinco paquetes que nombra el proveedor. Un complemento de terceros que llame a allow_module sobre su propio módulo todavía puede ampliar el entorno; el aviso no cubre esos complementos. Revise sus complementos para el mismo patrón si ejecuta código no confiable.
¿Cómo compruebo si estuve comprometido?
Empiece por el nuevo comprobador de condiciones. Como Manager, abra https://your-site/@@check-modifier-conditions en la raíz del sitio. La página enumera cada modificador cuya condición difiere de la predeterminada, muestra el valor actual y ofrece restaurarlo. Una diferencia es una pista, no una prueba: los valores predeterminados han cambiado con el tiempo, y un administrador o un complemento puede haber establecido una condición a propósito.
Busque en los registros del servidor web las peticiones a la herramienta de modificadores. Decodifique el porcentaje y no distinga mayúsculas, porque la adquisición de Zope hace accesible el objeto a cualquier profundidad y un atacante puede codificar la ruta:
grep -iE 'portal[_%]?modifier' /var/log/nginx/access.log
grep -iE 'portal(%5F|_)modifier' /var/log/apache2/access.log
Una petición que contiene el marcador merece abrirse: muestra la dirección de origen, el método y el código de respuesta. Un 200 en una petición edit a un objeto modificador es una pista más fuerte que un 404 de un escáner. El aviso no publica indicadores de compromiso, y un intento fallido puede no dejar rastro más allá de la línea de petición.
Si el comprobador muestra una condición modificada que usted no estableció, restáurela, rote los secretos que el proceso Zope podía leer y revise el sitio en busca de contenido modificado. Si confirma la ejecución de código, trate el servidor como comprometido y reconstrúyalo desde una copia de seguridad sana, porque la ejecución de código puede dejar puertas traseras fuera de los archivos que inspecciona el comprobador.
Cronología
- 2026-11-02: Plone publica un preaviso y el rodeo por proxy para
portal_modifier. - 2026-11-10 13:26 UTC: los paquetes corregidos y Products.PloneHotfix20261110 se suben a PyPI.
- 2026-11-10 13:30 UTC: el aviso público describe los cinco fallos y las versiones corregidas.
Preguntas frecuentes
¿Hay un CVE?
No hay ningún CVE asignado a 11 de noviembre de 2026. Los cinco avisos se publican con identificadores GHSA, y el anuncio afirma que se solicitaron números CVE. Siga las entradas GHSA en lugar de esperar un CVE antes de actualizar.
¿Afecta a Plone 5 o Plone 4?
Sí. El aviso afirma que las versiones anteriores están afectadas hasta al menos Plone 4.3. Plone 5.2 y anteriores ya no reciben soporte de seguridad, así que la vía compatible para esas versiones es el paquete de correcciones, no una actualización de paquetes.
¿Necesito las cinco actualizaciones de paquetes?
Actualice los cuatro paquetes críticos y plone.restapi si está instalado. El anuncio afirma que puede omitir plone.restapi si no lo usa; es el complemento de gravedad media que amplía la evasión de permisos, no el punto de entrada. Los otros cuatro son necesarios para la cadena que describe el aviso.
¿Se explota en la naturaleza?
No se conoce explotación a 11 de noviembre de 2026. El proveedor publicó los detalles técnicos el día de la versión, y el preaviso advertía de que las herramientas de IA actuales podrían convertir la descripción en un exploit funcional rápidamente. Trate la ventana entre el preaviso y su actualización como exposición.
¿Puedo mantener el bloqueo en el proxy en lugar de actualizar?
El bloqueo en el proxy detiene las peticiones cuya ruta contiene portal_modifier, y el proveedor recomienda mantenerlo después de la actualización. No corrige las evasiones del entorno si otra ruta alcanza código restringido, y no cubre una ruta interna de editor que activa una condición guardada. Úselo como medida provisional, no como corrección.
¿El paquete de correcciones funciona con Python 2.7?
Sí. El paquete de correcciones es compatible con Python 2.7 y Python 3, y funciona en Plone 4.3, 5.0, 5.1, 5.2, 6.0, 6.1 y 6.2.
Encuentre sitios Plone expuestos con Ostorlab
El escaneo web agéntico profundo de Ostorlab cartografía un sitio Plone expuesto a Internet, sus puntos de entrada accesibles y sus componentes expuestos, para ver qué instancias siguen respondiendo antes de corregirlas. Ejecútelo contra los sitios de su inventario y use los resultados para priorizar los que son accesibles desde Internet.
Referencias
- Anuncio del paquete de seguridad Plone 20261110: https://community.plone.org/t/plone-security-hotfix-20261110/23313 (consultado el 11 de noviembre de 2026)
- Preaviso y rodeo por proxy: https://community.plone.org/t/pre-announcement-plone-security-fix-20261110/23303 (consultado el 11 de noviembre de 2026)
- Aviso de Products.CMFEditions GHSA-67qm-q795-jh7h: https://github.com/plone/Products.CMFEditions/security/advisories/GHSA-67qm-q795-jh7h (consultado el 11 de noviembre de 2026)
- Aviso de Products.CMFPlone GHSA-92qw-564p-pmg6: https://github.com/plone/Products.CMFPlone/security/advisories/GHSA-92qw-564p-pmg6 (consultado el 11 de noviembre de 2026)
- Aviso de Products.MimetypesRegistry GHSA-xcj2-cmp9-fj62: https://github.com/plone/Products.MimetypesRegistry/security/advisories/GHSA-xcj2-cmp9-fj62 (consultado el 11 de noviembre de 2026)
- Aviso de plone.batching GHSA-6hcx-c235-cxh3: https://github.com/plone/plone.batching/security/advisories/GHSA-6hcx-c235-cxh3 (consultado el 11 de noviembre de 2026)
- Aviso de plone.restapi GHSA-vgxm-pwch-89q6: https://github.com/plone/plone.restapi/security/advisories/GHSA-vgxm-pwch-89q6 (consultado el 11 de noviembre de 2026)
- Products.PloneHotfix20261110 en PyPI: https://pypi.org/project/Products.PloneHotfix20261110/ (consultado el 11 de noviembre de 2026)
- Código de Products.CMFEditions 5.0.2: https://github.com/plone/Products.CMFEditions/blob/5.0.2/src/Products/CMFEditions/Modifiers.py (consultado el 11 de noviembre de 2026)
- Código de plone.batching 3.0.3: https://github.com/plone/plone.batching/blob/3.0.3/src/plone/batching/init.py (consultado el 11 de noviembre de 2026)
- Código de Products.CMFPlone 6.2.3: https://github.com/plone/Products.CMFPlone/blob/6.2.3/src/Products/CMFPlone/init.py (consultado el 11 de noviembre de 2026)
- Código de plone.restapi 10.2.1: https://github.com/plone/plone.restapi/blob/10.2.1/src/plone/restapi/init.py (consultado el 11 de noviembre de 2026)
- Descargas del contenedor Plone backend: https://hub.docker.com/r/plone/plone-backend (consultado el 11 de noviembre de 2026)