Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-34213 — Un usuario de Docmost con pocos privilegios podría proporcionar un attachmentId de una víctima al endpoint de carga genérico y sobrescribir el adjunto almacenado de otra página dentro del mismo espacio de trabajo. | Kitploit
Herramientas/GitHubGitHub/0xmrma/cve-2026-34213
Análisis de VulnerabilidadesExplotación de Aplicaciones WebPruebas de PenetraciónPapers e InvestigaciónAprendizaje y Educación
GitHub0xmrma/cve-2026-34213

CVE-2026-34213

Un usuario de Docmost con pocos privilegios podría proporcionar un attachmentId de una víctima al endpoint de carga genérico y sobrescribir el adjunto almacenado de otra página dentro del mismo espacio de trabajo.

Ver Repositorio
44hace 2 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-34213

Un usuario de Docmost con pocos privilegios podría proporcionar un attachmentId de una víctima al endpoint de carga genérico y sobrescribir el archivo adjunto de otra página dentro del mismo espacio de trabajo.

Introducción

Identifiqué, divulgué responsablemente y reproduje una falla de autorización de severidad Alta en Docmost, la plataforma de documentación colaborativa de código abierto.

El sitio oficial de Docmost la presenta como una wiki local preparada para empresas con más de 3 millones de descargas, y afirma que es utilizada por equipos en organizaciones como Vilnius City, Bechtle, el Gobierno Australiano, la Cruz Roja y ETS Quebec.

El error residía en la ruta genérica de carga de archivos que Docmost también utiliza para los flujos de guardado/actualización de diagramas.

Estaba revisando ese código con una pregunta muy específica en mente:

¿Qué sucede si el endpoint de carga verifica el acceso de edición en una página, pero el objetivo de sobrescritura se selecciona con un ID de adjunto separado controlado por el usuario?

En este caso, esa pregunta condujo directamente a una falla real de vinculación de objetos.

Docmost permitía que un llamador enviara:

  • un pageId de una página que tenía permiso para editar, y
  • un attachmentId perteneciente a una página diferente en el mismo espacio de trabajo
---

El servidor realizaba una comprobación de consistencia de sobrescritura, pero la guardia utilizaba la lógica booleana incorrecta.

Eso significaba que la solicitud podía pasar la autorización y sobrescribir el archivo adjunto de la víctima de todas formas.

Este problema se convirtió en CVE-2026-34213.

Docmost: docmost/docmost
Aviso de seguridad: GHSA-89fp-2hch-j9gp
CVE: CVE-2026-34213
Corregido en: v0.71.0

photo0

Cadena de Ataque

pageId controlado por el atacante con acceso de edición -> attachmentId de la víctima controlado por el atacante -> guardia de sobrescritura defectuosa trata la sobrescritura entre páginas como válida -> ruta de almacenamiento reconstruida a partir del attachmentId de la víctima -> bytes del atacante reemplazan el archivo de la víctima -> la página de la víctima continúa sirviendo el archivo adjunto modificado


Qué Hace Esta Parte de Docmost

Docmost almacena los archivos adjuntos subidos a las páginas como registros en la base de datos más archivos de respaldo en almacenamiento.

Para cargas normales, el servidor crea un nuevo ID de archivo adjunto y escribe un nuevo archivo.

Para los flujos de guardado/actualización de diagramas, sin embargo, el cliente reutiliza intencionalmente un attachmentId existente para que el mismo archivo de diagrama pueda actualizarse en el mismo lugar en lugar de generar un nuevo registro de archivo adjunto cada vez.

Ese comportamiento es legítimo por sí solo.

El problema es que crea una ruta de alto riesgo:

  • una entrada identifica la página que se está autorizando
  • otra entrada identifica el archivo adjunto que se está sobrescribiendo

Siempre que un endpoint combina esas dos responsabilidades, la implementación debe vincularlas exactamente.

Docmost no lo hizo.


Por Qué Vale la Pena Examinar Esta Superficie

Los endpoints mixtos de creación/actualización son lugares comunes para errores de autorización.

La razón es simple:

  • los flujos de creación generalmente se autorizan contra el objeto contenedor
  • los flujos de actualización generalmente se autorizan contra el registro existente
  • si un endpoint intenta hacer ambas cosas, es fácil validar lo incorrecto primero y tratar el segundo identificador como "solo metadatos"

Ese es exactamente el patrón aquí.

POST /api/files/upload validaba que el llamador pudiera editar la página nombrada por pageId.

Pero si también se proporcionaba attachmentId, el servidor cambiaba a una ruta de sobrescritura y seleccionaba un registro de archivo adjunto existente por separado.

Eso generó la pregunta crítica de seguridad:

¿la ruta de sobrescritura prueba que el archivo adjunto seleccionado realmente pertenece a la página autorizada?

La respuesta en las versiones vulnerables fue no.


Causa Raíz

La causa raíz fue una omisión de autorización a través de una clave controlada por el usuario, combinada con un error de lógica booleana en la guardia de sobrescritura.

El flujo vulnerable se veía así:

  1. AttachmentController.uploadFile() leía pageId de los datos del formulario multipart.
  2. Cargaba esa página y llamaba a validateCanEdit(page, user).
  3. Por separado, aceptaba un attachmentId opcional de la misma solicitud.
  4. AttachmentService.uploadFile() cargaba el archivo adjunto existente por ese ID proporcionado por el atacante.
  5. La guardia de sobrescritura intentaba verificar que el archivo adjunto existente coincidiera con la página autorizada.
  6. La guardia usaba && en lugar de rechazar ante cualquier discrepancia.

La guardia vulnerable era:

root@kitploit:~
if (
  existingAttachment.pageId !== pageId &&
  existingAttachment.fileExt !== preparedFile.fileExtension &&
  existingAttachment.workspaceId !== workspaceId
) {
  throw new BadRequestException("File attachment does not match");
}

Esa condición solo rechazaba la solicitud si:

  • el ID de la página no coincidía, y
  • la extensión del archivo no coincidía, y
  • el ID del espacio de trabajo no coincidía

todo al mismo tiempo.

Eso es lo opuesto a lo que debería hacer una guardia de sobrescritura.

Para el caso de ataque real, el atacante permanecía intencionalmente dentro del mismo espacio de trabajo.

Entonces:

  • existingAttachment.workspaceId !== workspaceId era false

Una vez que ese operando se volvía falso, toda la condición && se evaluaba como falsa, incluso si el archivo adjunto pertenecía a una página diferente.

Por lo tanto, el servidor trataba una sobrescritura entre páginas como válida.

Esa fue la primera mitad del error.

La segunda mitad es lo que hizo real el impacto.

Después de la comprobación, el servicio reconstruía la ruta de almacenamiento de destino usando el attachmentId y el nombre de archivo proporcionados por el atacante:

root@kitploit:~
const filePath =
  `${getAttachmentFolderPath(AttachmentType.File, workspaceId)}/` +
  `${attachmentId}/${preparedFile.fileName}`;

Luego, en la ruta de actualización, Docmost solo actualizaba metadatos mutables como:

  • fileSize
  • updatedAt

No volvía a vincular la propiedad a la página del atacante.

Así que la página de la víctima seguía apuntando al mismo registro de archivo adjunto y al mismo ID de archivo adjunto. Solo cambiaban los bytes del archivo subyacente.

Por eso esto no era una discrepancia inofensiva.

Era una primitiva de sobrescritura no autorizada persistente.


Por Qué Esto Es un Problema de Seguridad, No Solo un Error Lógico

Esto no era un error cosmético ni un problema de colisión de nombres de archivo.

El atacante no necesitaba una condición de carrera. El atacante no necesitaba adivinar una ruta aleatoria. El atacante no necesitaba acceso de escritura a la página de la víctima.

Solo necesitaba:

  • acceso de lectura para conocer una referencia de archivo adjunto de la víctima, y
  • acceso de escritura a cualquier otra página en el mismo espacio de trabajo

A partir de ahí, podía reemplazar los bytes del archivo almacenado para el archivo adjunto de otra página mientras la página de la víctima continuaba referenciando y sirviendo ese archivo adjunto como si nada hubiera cambiado.

Eso es una falla directa de integridad.

En términos prácticos, el atacante podía:

  • manipular diagramas
  • reemplazar archivos adjuntos con contenido engañoso
  • corromper archivos referenciados
  • crear pistas de auditoría confusas porque el archivo adjunto seguía pareciendo pertenecer a la página de la víctima

El punto importante es este:

el servidor aceptó un objetivo de sobrescritura elegido por el atacante, sin vincularlo a la página cuyo permiso de edición se había comprobado realmente.

Eso es una falla de control de acceso, no solo una mala higiene booleana.


Por Qué la Explotación Era Práctica

El exploit era especialmente práctico para archivos adjuntos de diagramas.

El cliente de Docmost reutiliza intencionalmente attachmentId para guardar diagramas y usa nombres de archivo deterministas:

  • diagram.excalidraw.svg
  • diagram.drawio.svg

Eso importa porque reduce los requisitos del atacante.

Para archivos adjuntos genéricos, el atacante necesita ambos:

  • el ID del archivo adjunto de la víctima
  • el nombre del archivo de la víctima

Para diagramas, el nombre de archivo ya es predecible.

Por lo tanto, si el atacante puede leer el contenido de la página de la víctima, a menudo puede recuperar la única pieza faltante que necesita:

  • el attachmentId de la víctima

En mi configuración de validación, usé exactamente esa ruta:

  • el atacante tenía solo acceso de lectura al espacio de la víctima
  • el atacante tenía acceso de escritura a un espacio controlado por el atacante diferente
  • ambos espacios pertenecían al mismo espacio de trabajo

Eso fue suficiente.

El exploit cruzó los límites de página y los límites de espacio dentro del mismo espacio de trabajo, mientras seguía satisfaciendo la comprobación defectuosa del espacio de trabajo.


Prueba de Concepto

Validé el problema en vivo contra Docmost v0.70.3 usando un laboratorio desechable construido a partir de docmost/docmost:0.70.3, Postgres y Redis.

El flujo de la PoC fue:

  1. Crear una cuenta de propietario.
  2. Crear un espacio de la víctima y un espacio controlado por el atacante en el mismo espacio de trabajo.
  3. Invitar a un segundo usuario como atacante.
  4. Conceder al atacante:
    • acceso de lectura al espacio de la víctima
    • acceso de escritura al espacio controlado por el atacante
  5. En el espacio de la víctima, subir un archivo adjunto de diagrama a una página de la víctima.
  6. Como atacante, recuperar la información de la página de la víctima y anotar el attachmentId de la víctima.
  7. Enviar POST /api/files/upload con:
    • pageId = ID de la página del atacante
    • attachmentId = ID del archivo adjunto de la víctima
    • file = archivo de reemplazo controlado por el atacante usando el nombre de archivo de la víctima
  8. Descargar el archivo adjunto de la víctima antes y después de la sobrescritura y comparar hashes.

La forma mínima de la solicitud era:

root@kitploit:~
POST /api/files/upload
Content-Type: multipart/form-data

pageId=<attackerPageId>
attachmentId=<victimAttachmentId>
[email protected];filename=diagram.excalidraw.svg

El resultado observado en vivo fue:

  • ID de archivo adjunto de la víctima aprendido por el atacante: 019d18ae-b176-751c-8525-b5f3cede131d
  • ID de página del atacante utilizado para la solicitud de sobrescritura: 019d18ae-b15b-70e9-ac67-64948e87cc5e
  • El ID de la página propietaria de la víctima permaneció: 019d18ae-b12f-75ec-8c1c-5aff3ba6be9c
  • Respuesta del servidor a la solicitud de sobrescritura: 200 OK
  • SHA-256 del archivo de la víctima antes de la sobrescritura:
root@kitploit:~
686a0a0ede90ece1cbb975bb29304a6c3a90373a9c3ab2496345cf7ca59cc8fa
  • SHA-256 del archivo de la víctima después de la sobrescritura:
root@kitploit:~
e0168298846cdaf75c4d880f4b721d7c0ef0ef310f75617bf2b833af34cdbeba
  • SHA-256 del payload del atacante:
root@kitploit:~
e0168298846cdaf75c4d880f4b721d7c0ef0ef310f75617bf2b833af34cdbeba
  • El almacenamiento montado confirmó que la ruta de la víctima ahora contenía:
root@kitploit:~
Attacker replacement from another page

Eso es una prueba completa de sobrescritura de extremo a extremo, no solo una revisión teórica del código fuente.


Por Qué Se Eligió Así la PoC

Durante el triaje, usé dos estilos de prueba:

  • un harness independiente y reducido que reflejaba la lógica de sobrescritura vulnerable, y
  • un exploit HTTP completo en vivo contra una instancia desechable de Docmost

El harness independiente fue útil para aislar el fallo de lógica booleana.

La PoC HTTP en vivo fue el artefacto más sólido porque demostró la historia de seguridad completa:

  • la autorización de la página se realizó correctamente en la página del atacante
  • el attachmentId de la víctima fue aceptado
  • la solicitud de sobrescritura devolvió éxito
  • la página de la víctima siguió siendo el propietario lógico
  • los bytes almacenados en disco cambiaron a contenido controlado por el atacante

Esa distinción importa en errores de control de acceso.

"la condición está mal" no es suficiente por sí solo.

"la condición está mal y la aplicación puede ser llevada de extremo a extremo a una sobrescritura no autorizada persistente" es el caso completo.


Análisis de la Corrección

La corrección se envió en v0.71.0 y cambió la guardia de sobrescritura de && a ||:

root@kitploit:~
if (
  existingAttachment.pageId !== pageId ||
  existingAttachment.fileExt !== preparedFile.fileExtension ||
  existingAttachment.workspaceId !== workspaceId
) {
  throw new BadRequestException("File attachment does not match");
}

Ese parche es mínimo, directo y correcto para el error que se informó.

Restablece la regla correcta:

la sobrescritura solo se permite cuando el archivo adjunto existente coincide exactamente con las suposiciones de página/espacio de trabajo/tipo autorizadas.

Una vez que la guardia rechaza cualquier discrepancia:

  • las sobrescrituras entre páginas fallan
  • las sobrescrituras entre espacios de trabajo fallan
  • las discrepancias de tipo/extensión fallan

Este fue el tipo correcto de corrección:

  • sin rediseño
  • sin lógica de compatibilidad vaga
  • sin intento de "mejor esfuerzo" de recuperación

Solo un vínculo estricto entre la página autorizada y el objetivo de sobrescritura.

Todavía queda una lección de ingeniería más amplia:

los endpoints de carga genéricos que también sirven flujos de actualización in situ deben tratarse como superficies API de alto riesgo.

Incluso cuando el error inmediato se corrige, los diseños más sólidos a largo plazo son:

  • endpoints de actualización dedicados para flujos de guardado de diagramas
  • comprobaciones de vinculación inmutables en el nombre de archivo además del ID del archivo adjunto
  • cobertura de regresión que modele explícitamente intentos de sobrescritura entre páginas

Pero para la vulnerabilidad en sí, el parche publicado cerró el problema principal limpiamente.


Casos de Regresión Que Importan

Independientemente de si el proyecto añadió sus propias pruebas privadas en torno a la corrección, estos son los casos que importan para la cobertura a largo plazo:

  • la sobrescritura con el mismo ID de página y el mismo ID de archivo adjunto debería tener éxito
  • la sobrescritura con un ID de página diferente y el mismo espacio de trabajo debería fallar
  • la sobrescritura con un espacio de trabajo diferente debería fallar
  • la sobrescritura con una extensión de archivo no coincidente debería fallar
  • la sobrescritura usando un nombre de archivo de diagrama conocido pero un ID de archivo adjunto foráneo debería fallar
  • la sobrescritura nunca debe preservar silenciosamente la propiedad de la víctima después de que se escriban bytes controlados por el atacante

El objetivo de estas pruebas no es solo la corrección.

Es fijar la vinculación de autorización para que futuras refactorizaciones "útiles" de carga no reabran la misma clase de error.


Severidad y Clasificación

El aviso de seguridad publicado clasificó este problema como:

  • CWE-639: Omisión de autorización mediante clave controlada por el usuario
  • CVSS v3.1:
root@kitploit:~
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

Eso da un puntaje de 7.1 / Alto, que es la conclusión correcta.

La métrica importante aquí es Integridad.

Esto no era un error de metadatos de bajo nivel. El atacante controlaba completamente los bytes de reemplazo escritos en la ruta del archivo adjunto de otra página, y la página de la víctima continuaba sirviendo el objeto modificado después.

Eso es exactamente el tipo de manipulación entre registros almacenados que merece una Integridad Alta.

Que la Disponibilidad se mantenga en Baja también tiene sentido porque corromper un diagrama o documento adjunto puede hacer que el contenido de la víctima sea inutilizable, pero el impacto principal sigue siendo la modificación no autorizada en lugar de una interrupción completa del servicio.


Divulgación

Informé el problema de forma privada a través de los Avisos de Seguridad de GitHub con:

  • análisis de causa raíz
  • una PoC HTTP en vivo
  • evidencia de solicitud/respuesta
  • hashes de archivos antes/después
  • una configuración de laboratorio desechable fijada

El problema fue aceptado por el mantenedor, asignado CVE-2026-34213, y publicado el 14 de abril de 2026.

El aviso público enumera:

  • versiones afectadas: >= v0.3.0
  • versión corregida: v0.71.0

Ese historial también coincidió con mi revisión local del código fuente: la lógica de sobrescritura vulnerable estaba presente en la versión etiquetada más antigua que revisé en la línea vulnerable.


Lo Que Realmente Enseña Este Error

La lección interesante aquí no es meramente "usa || en lugar de &&."

Eso es el síntoma.

La lección más profunda es:

si un campo controlado por el usuario prueba la autorización y otro campo controlado por el usuario selecciona el objeto que se está actualizando, esos dos campos deben vincularse explícita y exactamente.

Esa regla aparece en todas partes:

  • archivos adjuntos de documentos
  • medios de perfil
  • referencias a objetos en la nube
  • ediciones de problemas/comentarios
  • reprocesamiento de trabajos en segundo plano

En el momento en que un sistema dice:

  • "puedes editar la página X"
  • "por favor, dime también qué registro existente actualizar"

ha creado un límite de seguridad que debe hacerse cumplir con invariantes de coincidencia exacta.

Cualquier cosa más suave que eso se convierte tarde o temprano en un error de clave controlada por el usuario.

Este problema también refuerza un segundo punto que es fácil de subestimar:

los pequeños errores booleanos en el código de protección pueden tener consecuencias de seguridad de primer orden.

Una condición de tres cláusulas que "parece razonable" a simple vista fue suficiente para invertir el modelo de protección de la ruta de sobrescritura.

Por eso estas superficies merecen una revisión deliberada en lugar de una confianza casual.


Puntos Clave

  • Docmost usó un mismo endpoint tanto para nuevas cargas como para actualizaciones de archivos adjuntos en el mismo lugar.
  • La autorización se verificó contra el pageId proporcionado por el llamador, pero la selección del objetivo de sobrescritura usó un attachmentId separado proporcionado por el llamador.
  • La guardia de sobrescritura rechazaba solo cuando todas las condiciones de discrepancia eran verdaderas al mismo tiempo.
  • En el caso de ataque dentro del mismo espacio de trabajo, esa comprobación fallaba abiertamente.
  • El servicio reconstruía la ruta de almacenamiento a partir del ID del archivo adjunto de la víctima y escribía bytes controlados por el atacante en ella.
  • El registro del archivo adjunto seguía vinculado a la página de la víctima después de la sobrescritura.
  • Los nombres de archivo de diagrama deterministas hicieron que la explotación fuera especialmente práctica.
  • La corrección en v0.71.0 cambió correctamente la guardia para rechazar ante cualquier discrepancia.

Palabras Finales

Esta vulnerabilidad no trataba sobre un comportamiento de almacenamiento exótico.

Trataba sobre una ruta de actualización que confiaba en un identificador de objeto seleccionado por el atacante más de lo que debería.

Docmost probó el acceso de edición en una página, aceptó un ID de archivo adjunto existente de otra página, y luego dejó que una comprobación de sobrescritura defectuosa convirtiera esa discrepancia en un reemplazo exitoso de archivo entre páginas.

Por eso se convirtió en CVE-2026-34213.

El parche en v0.71.0 corrigió el problema inmediato limpiamente, pero la lección más amplia sigue siendo valiosa:

cuando la autorización y la selección de objetos se dividen en campos separados controlados por el usuario, la vinculación exacta es la propiedad de seguridad.

Descargar herramienta