
El subsistema de activos V2 de Plane confiaba en slugs de espacio de trabajo y UUIDs de activos sin aplicar las verificaciones de membresía correctas, lo que permitía a un usuario autenticado leer, copiar, eliminar y sobrescribir activos en otros espacios de trabajo.
El subsistema de activos V2 de Plane confiaba en slugs de espacios de trabajo y UUIDs de activos sin aplicar las comprobaciones de membresía correctas, lo que permitía a un usuario autenticado leer, copiar, eliminar y sobrescribir activos en otros espacios de trabajo.
Encontré este problema mientras revisaba Plane, la plataforma de gestión de proyectos de código abierto, con una pregunta muy específica en mente:
¿Los endpoints de activos V2 realmente imponen los límites del espacio de trabajo, o confían demasiado en los slugs de espacios de trabajo y los IDs de activos proporcionados por el atacante?
En este caso, la respuesta fue no.
El subsistema de activos V2 de Plane expuso dos fallos de autorización relacionados que rompían el aislamiento del espacio de trabajo para cualquier usuario autenticado:
Eso hizo posible el abuso de activos entre espacios de trabajo.
En mi PoC validado, un usuario normal en el espacio de trabajo Bravo pudo:
Ese problema luego fue asignado como CVE-2026-46558.
Plane: Plane en GitHub
CVE: CVE-2026-46558
Esto afectó a Plane, que en su sitio oficial se presenta como utilizado por más de 50.000 equipos en todo el mundo. Plane también destaca una fuerte adopción de código abierto, incluyendo más de 46.000 estrellas en GitHub y más de 1.000.000 de descargas en Docker, y muestra organizaciones como Tencent, Accenture, Microsoft y Amazon.
atacante autenticado en espacio de trabajo B → ruta de activos V2 a nivel de espacio de trabajo confía en el slug del espacio de trabajo destino y el UUID del activo sin comprobaciones de membresía adecuadas → lectura presignada / parche / eliminación contra activos del espacio de trabajo A + búsqueda de fuente de duplicación de activos confía en el UUID de fuente subido → divulgación, copia, eliminación y sobrescritura de marca entre espacios de trabajo
Plane es una plataforma de gestión de proyectos de código abierto utilizada para gestionar:
Eso significa que su subsistema de activos se encuentra en un límite de confianza real.
La pregunta importante aquí no era si Plane admite subidas.
La verdadera pregunta era:
¿Plane impone el aislamiento del espacio de trabajo cuando un usuario autenticado hace referencia a activos propiedad de otro espacio de trabajo?
En este caso, no lo hizo.
Muchas revisiones de aplicaciones multiinquilino se centran primero en endpoints de administrador obvios o actualizaciones directas de configuración.
Eso pasa por alto una clase de errores muy común y muy real:
acceso a objetos secundarios a través de subsistemas de archivos o activos compartidos
Los sistemas de activos son fáciles de equivocar porque a menudo combinan:
Eso es exactamente el tipo de lugar donde los límites del inquilino se debilitan silenciosamente.
Este problema no era sobre corrupción de almacenamiento. No era sobre S3 en sí mismo. No era sobre el manejo de MIME en las subidas.
Era una falla en el límite de autorización:
Eso es suficiente para crear una vulnerabilidad real.
No abordé Plane probando ciegamente endpoints aleatorios o adivinando UUIDs sin un modelo.
El enfoque más sólido fue identificar primero el límite de aislamiento más prometedor.
Para Plane, ese fue el subsistema de activos V2.
¿Por qué?
Porque un sistema de activos compartido se vuelve peligroso cuando:
Ese era el límite correcto para inspeccionar.
Y fue exactamente donde vivía el error.
En realidad, fueron dos fallos de autorización relacionados en el mismo subsistema.
Las rutas de activos a nivel de espacio de trabajo estaban expuestas a través de:
apps/api/plane/app/urls/asset.py:50-56Los controladores vulnerables estaban en:
apps/api/plane/app/views/asset/v2.py:314apps/api/plane/app/views/asset/v2.py:379apps/api/plane/app/views/asset/v2.py:400apps/api/plane/app/views/asset/v2.py:409El problema era simple.
WorkspaceFileAssetEndpoint aceptaba un slug de espacio de trabajo y un UUID de activo, luego resolvía objetos directamente como:
workspace = Workspace.objects.get(slug=slug)
y:
asset = FileAsset.objects.get(id=asset_id, workspace__slug=slug)
sin antes verificar que quien llamaba era realmente un miembro autorizado de ese espacio de trabajo destino.
Eso significaba que el endpoint aún podía:
para objetos de otro espacio de trabajo.
La ruta de duplicación de activos estaba mapeada a través de:
apps/api/plane/app/urls/asset.py:100-101La lógica vulnerable estaba en:
apps/api/plane/app/views/asset/v2.py:736-780El espacio de trabajo destino tenía un decorador de autorización. Pero la búsqueda del activo fuente no.
El objeto fuente se cargaba con:
original_asset = FileAsset.objects.filter(id=asset_id, is_uploaded=True).first()
Eso significaba que quien llamaba solo necesitaba:
No había ninguna comprobación de que quien llamaba perteneciera al espacio de trabajo fuente que realmente poseía ese activo.
Ese es todo el segundo error.
La distinción importante es el impacto entre espacios de trabajo.
Muchos errores de autorización se minimizan como:
“aún requiere inicio de sesión”
Eso no capta el punto.
La verdadera pregunta no es:
“¿Quien llama está autenticado?”
La verdadera pregunta es:
“¿Quien llama está autorizado para el espacio de trabajo específico y el activo específico sobre el que se actúa?”
En Plane, la respuesta fue no.
Eso convierte lo que podría parecer un manejo ordinario de objetos en un problema real de seguridad multiinquilino.
Hay una clara diferencia entre: