Skip to content
KitploitKITPLOIT
HerramientasBlog
Log in
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
Herramientas/GitHubGitHub/0xmrma/cve-2026-34212
Análisis de VulnerabilidadesExplotaciónSeguridad WebPruebas de PenetraciónPapers e InvestigaciónAprendizaje y Educación
GitHub0xmrma/cve-2026-34212

CVE-2026-34212

Docmost aceptó una URL javascript: dentro de un nodo de adjunto, la preservó a través del almacenamiento y la representación, y la convirtió de nuevo en un ancla cliqueable en el origen de Docmost.

Ver Repositorio
4hace 3 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-34212

Docmost aceptó una URL javascript: dentro de un nodo de adjunto, la preservó durante el almacenamiento y renderizado, y la devolvió a un ancla enlaceable en el origen de Docmost.

Introducción

Identifiqué, divulgué responsablemente y reproduje un problema de XSS almacenado de gravedad Alta en Docmost, la plataforma colaborativa de documentación de código abierto.

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

El error se encontraba en un lugar fácil de pasar por alto en los sistemas de texto enriquecido:

no en la extensión de enlace normal, sino en un tipo de nodo personalizado separado utilizado para archivos adjuntos.

Estaba revisando la tubería del editor con una pregunta muy específica en mente:

si los enlaces normales bloquean las URLs javascript:, ¿los nodos de adjuntos aplican la misma regla antes de llegar a un sumidero de ancla?

En versiones vulnerables, no lo hacían.

Docmost aceptaba un nodo de adjunto malicioso en el JSON de la página, almacenaba su atributo url sin cambios, y luego renderizaba ese valor de vuelta a un elemento <a href="javascript:..."> enlaceable.

Ese problema se convirtió en CVE-2026-34212.

Docmost: docmost/docmost
Aviso: GHSA-cf68-cff9-hq4w
CVE: CVE-2026-34212
Corregido en: v0.71.0

foto0

Cadena de Ataque

URL del nodo de adjunto controlada por atacante -> JSON de página aceptado y almacenado sin cambios -> Renderizado HTML/React convierte esa URL en href de ancla -> víctima hace clic en la acción del adjunto -> JavaScript controlado por atacante se ejecuta en el origen de Docmost


Qué Hace Esta Parte de Docmost

Docmost almacena el contenido de la página en un formato JSON compatible con ProseMirror/Tiptap.

Ese modelo de contenido incluye nodos de bloque personalizados para cosas como:

  • imágenes
  • diagramas
  • incrustaciones
  • adjuntos

El nodo de adjunto almacena campos como:

  • url
  • name
  • mime
  • size
  • attachmentId

El servidor acepta contenido de página en varios formatos:

  • json
  • markdown
  • html

y lo normaliza a JSON de ProseMirror antes de almacenarlo.

Eso significa que cualquier tipo de nodo que pueda transportar una URL es parte de un límite de confianza directo.

Si uno de esos tipos de nodo eventualmente se renderiza en un <a href>, el manejo del esquema de URL no es opcional. Es parte del modelo de seguridad.


Por Qué Esta Superficie Valía la Pena Investigarla

Las extensiones personalizadas del editor son una fuente frecuente de desviación en la seguridad.

El sistema base puede saber cómo manejar URLs peligrosas correctamente, pero cada nodo personalizado aún debe reaplicar las mismas reglas en sus propios sumideros.

Eso crea una estrategia de revisión predecible:

  • encontrar cada tipo de nodo que almacene un campo similar a una URL
  • rastrear dónde se acepta ese campo
  • rastrear dónde se renderiza ese campo
  • comparar su comportamiento de sanitización con el manejo normal de enlaces de la plataforma

Eso es exactamente lo que expuso este error.

La extensión de enlace normal de Docmost ya trataba javascript: como peligroso.

Su nodo de adjunto no lo hacía.

Una vez que ves esa asimetría, la pregunta de seguridad se vuelve obvia:

¿puedo persistir un nodo de adjunto cuya url es javascript: y conseguir que se renderice de vuelta en un ancla en vivo?

La respuesta fue sí.


Causa Raíz

La causa raíz fue la sanitización inconsistente de URLs entre tipos de nodos de contenido.

La ruta de contenido del lado del servidor aceptaba URLs de adjunto arbitrarias siempre que el contenido general coincidiera con el esquema de ProseMirror.

En la versión vulnerable:

  • CreatePageDto aceptaba content?: string | object
  • PageService.parseProsemirrorContent() normalizaba markdown, html o json
  • el servidor luego llamaba a jsonToNode(prosemirrorJson)
  • si la validación del esquema pasaba, el contenido se almacenaba

Ese paso de validación verificaba la validez estructural, no la seguridad de la URL.

La parte crítica de la lógica vulnerable del servidor era efectivamente:

prosemirrorJson = content;
jsonToNode(prosemirrorJson);
return prosemirrorJson;

No ocurría ninguna normalización del esquema de URL de adjunto allí.

Más tarde, la extensión de adjunto renderizaba el valor controlado por el atacante directamente.

El nodo de adjunto vulnerable hacía esto:

url: {
  default: "",
  parseHTML: (element) => element.getAttribute("data-attachment-url"),
  renderHTML: (attributes) => ({
    "data-attachment-url": attributes.url,
  }),
},

y luego:

[
  "a",
  {
    href: HTMLAttributes["data-attachment-url"],
    class: "attachment",
    target: "blank",
  },
  `${HTMLAttributes["data-attachment-name"]}`,
]

En el lado del cliente, la vista de nodo React lo envolvía de nuevo en:

<a href={getFileUrl(url)} target="_blank">

Pero getFileUrl() solo tenía casos especiales para:

  • URLs absolutas http
  • /api/...
  • /files/...

Cualquier otra cosa se devolvía sin cambios.

Así que una carga útil como:

javascript:alert(document.domain)

sobrevivía a:

  • almacenamiento JSON
  • validación del esquema del lado del servidor
  • renderizado HTML
  • manejo de URL del lado del cliente

Eso solo ya sería suficiente para XSS almacenado.

Lo que hace especialmente clara la causa raíz es el punto de comparación.

La extensión de enlace normal de Docmost bloqueaba explícitamente javascript::

  • rechazaba javascript: en parseHTML()
  • vaciaba un href javascript: en renderHTML()

Así que el producto ya sabía que este esquema era peligroso.

El nodo de adjunto simplemente no aplicaba la misma política.

Por eso esto no era "XSS genérico en el editor".

Era una brecha en el límite de confianza específica del nodo.


Por Qué Esto Es un Problema de Seguridad, No Solo una Sanitización Faltante

Este error no se trataba meramente de una estética HTML insegura.

Permitía que un atacante que pudiera editar una página persistiera una carga útil maliciosa que luego se ejecutaría en el origen de Docmost cuando otro usuario interactuara con el adjunto renderizado.

Eso importa porque un script en el origen puede:

  • leer datos a los que la víctima tiene acceso
  • emitir solicitudes autenticadas como la víctima
  • modificar contenido que la víctima tiene permitido modificar
  • abusar de cualquier superficie DOM o API expuesta a la sesión

El requisito de un clic no reduce esto a un problema trivial.

El clic es parte del comportamiento normal del producto: la UI presenta intencionalmente el adjunto como un enlace/icono accionable.

Así que la pregunta de seguridad no es "¿puede el atacante forzar JS arbitrario sin ninguna interacción?"

La verdadera pregunta es:

¿almacena la aplicación contenido controlado por atacante que contiene scripts y luego lo presenta de vuelta a otros usuarios como una ruta de interacción confiable?

En versiones vulnerables, lo hacía.

Eso es XSS almacenado.


Por Qué la Explotación Era Práctica

La ruta de explotación era directa:

  • cualquier usuario con derechos de edición de página podía plantar la carga útil
  • la URL maliciosa sobrevivía al almacenamiento sin cambios
  • la página se renderizaba normalmente
  • los espectadores solo necesitaban acceso estándar a la página
  • un clic en la acción del adjunto era suficiente para desencadenar la ejecución
Descargar herramienta