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
Herramientas/GitHubGitHub/squeeze440/supernote-obsidian-plugin-poc
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebVirtualización de SeguridadPruebas de PenetraciónPapers e Investigación
GitHubsqueeze440/supernote-obsidian-plugin-poc

supernote-obsidian-plugin-PoC

PoC — path traversal mediante sincronización maliciosa de dispositivos en el plugin Supernote Obsidian (GHSA-3gx3-r874-5pp4, CVE-2026-86999, CVSS 5.6).

Ver Repositorio
hace 7 díasAú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

Resumen

Estado del CVE: solicitado, pendiente de asignación. Este hallazgo se publica como GHSA-3gx3-r874-5pp4. Al asignarse el CVE, este repositorio se renombra CVE-YYYY-NNNNN-supernote-obsidian-plugin-PoC y este banner se reemplaza con el enlace del CVE.

InvestigadorDostxodjayev Abdullox (@squeeze440)
AvisoGHSA-3gx3-r874-5pp4
CVSS 3.15.6 (Medio)
DebilidadCWE-22, CWE-73

Resumen

El Path Traversal en la función de sincronización automática del dispositivo del plugin de Obsidian Supernote (Unofficial) (philips/supernote-obsidian-plugin) v2.9.1 permite que un dispositivo "Supernote" malicioso o no autorizado (o un atacante en la ruta/en la LAN que suplante la IP del dispositivo emparejado) haga que el cliente Obsidian de la víctima escriba un archivo nuevo arbitrario en cualquier lugar donde el proceso de escritorio pueda escribir —incluyendo fuera de la carpeta de sincronización configurada y fuera del propio vault— mediante un campo uri manipulado en la respuesta de listado de directorios del dispositivo.

Producto

philips/supernote-obsidian-plugin ("Supernote (Unofficial)"), un plugin de la comunidad de Obsidian.md que sincroniza las notas de un dispositivo físico de tinta electrónica Supernote dentro de un vault a través del servidor HTTP local "Browse and Access" del dispositivo (http://<device-ip>:8089, sin autenticación, por diseño de la función del dispositivo).

Versión probada

  • Plugin: v2.9.1, commit 48db5bf4dcfb100632c84699c830c1309da1abb9
  • Submódulo supernote-typescript: commit 195415b3a1f74147... (no implicado en sí mismo — el fallo está enteramente en el propio código de planificación de sincronización del plugin)
  • Aplicación anfitriona: Obsidian desktop 1.13.4 (binario real, probado dinámicamente)

CVSS v3.1 estimado

5.6 Medio — CVSS:3.1/AV:A/AC:H/PR:N/UI:R/S:C/C:N/I:H/A:N

  • AV:A — el servidor HTTP del dispositivo es texto plano, sin autenticación, y se configura mediante una dirección IPv4 simple (IP_VALIDATION_PATTERN, src/settings.ts:6); alcanzarlo como "el dispositivo" requiere una posición de red adyacente a la LAN (AP rogue, ARP spoofing, o reclamar la IP), no acceso remoto arbitrario.
  • AC:H — la explotación depende de que el atacante ya ocupe esa posición de red cuando el plugin de la víctima se comunica con directConnectIP:8089; no es un disparador remoto de un solo intento.
  • UI:R — requiere que la víctima ejecute "Sync supernote notes now" (o que tenga ya habilitado el interruptor de sincronización automática) mientras apunta al endpoint controlado por el atacante.
  • S:C — el componente vulnerable es un plugin de Obsidian con alcance de vault; la escritura aterriza completamente fuera del vault, en el sistema de archivos del host subyacente, que es un ámbito de seguridad diferente.
  • C:N — esto es una primitiva de solo escritura; nada se lee de vuelta hacia el atacante.
  • I:H — bytes controlados por el atacante aterrizan en una ruta elegida por el atacante con un nombre de archivo elegido por el atacante. Advertencia acotada: writeBinaryAt() (src/syncEngine.ts:40-47) solo toma la rama de creación para rutas que aún no están registradas en el propio manifiesto de sincronización del plugin, y el propio Vault.createBinary() de Obsidian se niega a sobrescribir silenciosamente un archivo que ya existe físicamente en la ruta resuelta (confirmado empíricamente — véase PoC) — por lo que esto es "plantar un archivo nuevo en cualquier lugar", no "sobrescribir cualquier archivo existente".
  • A:N — no se demostró impacto en la disponibilidad.

Detalles

runDeviceSync() (src/syncEngine.ts:100-196) lista los archivos del dispositivo emparejado mediante scanDeviceSupernoteTree() (src/FileListModal.ts:53-68), que recorre recursivamente el propio listado de directorios HTTP del dispositivo y conserva cualquier entrada cuyo name coincida con /\.(note|spd)$/i (src/FileListModal.ts:46,63). El campo uri de cada entrada —una cadena separada e independientemente controlada en el mismo objeto JSON devuelto por el dispositivo— nunca se valida contra name ni contra nada más.

Ese uri sin procesar se introduce directamente en deviceUriToVaultPath() (src/deviceSync.ts:153-161):

root@kitploit:~
const INVALID_FILENAME_CHARS = /[\\:*?"<>|]/g;   // deviceSync.ts:144

export function deviceUriToVaultPath(syncFolder: string, deviceUri: string): string {
    const segments = deviceUri
        .split('/')
        .filter((s) => s.length > 0)
        .map((s) => s.replace(INVALID_FILENAME_CHARS, '_'));

    const cleanRoot = syncFolder.replace(/^\/+|\/+$/g, '');
    return cleanRoot ? `${cleanRoot}/${segments.join('/')}` : segments.join('/');
}

INVALID_FILENAME_CHARS elimina \ : * ? " < > | pero nunca elimina ni rechaza segmentos de ruta ... Un uri de dispositivo de /../../PWNED.txt sobrevive intacto y se une a la carpeta de sincronización configurada (por defecto "Supernote sync") para producir vaultPath = "Supernote sync/../../PWNED.txt".

syncEngine.ts:128 calcula este vaultPath a partir del uri sin procesar del listado, y luego ensureFolder()/writeBinaryAt() (syncEngine.ts:146-148, 21-47) lo pasan directamente a app.vault.getAbstractFileByPath() / createBinary() / modifyBinary() sin ninguna comprobación de traversal. La propia resolución de rutas de Obsidian normaliza entonces los segmentos .. contra el directorio real del vault en disco, haciendo que la escritura aterrice por encima de la carpeta de sincronización —y, con suficientes segmentos ../, por encima de la raíz del vault por completo, en el sistema de archivos del host, dentro del propio ámbito de permisos del usuario del SO.

Comprobación de hermanos: cualquier otro sumidero de escritura en el vault en este código base (src/main.ts — captura de espejo de pantalla, importación de PDF/markdown, "attach to note", DownloadListModal en src/FileListModal.ts:245-246) construye su ruta de destino mediante el propio app.fileManager.getAvailablePathForAttachment(file.name) de Obsidian, que solo consume el campo name del dispositivo y no es explotable de esta manera. deviceUriToVaultPath() en la función de sincronización automática más reciente es el único sumidero que, en su lugar, construye a mano una ruta a partir del campo uri del dispositivo, y es el que omitió el saneamiento de .. — un caso claro de "comprobado en todos los hermanos menos en este".

La propia interfaz de ajustes del plugin afirma: "El comando de sincronización nunca escribe en ningún lugar fuera de esta carpeta" (src/settings.ts, descripción de Sync folder) — este PoC falsifica directamente esa garantía.

Prueba de concepto

Confirmado dinámicamente de extremo a extremo contra un binario de escritorio real y sin modificar de Obsidian 1.13.4 (Xvfb + fluxbox + xdotool + scrot), ejecutando el main.js compilado real del plugin — sin simulación del código del plugin.

  1. Se compiló el plugin desde el código fuente (./scripts/build) y se cargó en un vault de prueba nuevo (Supernote (Unofficial) v2.9.1, habilitado mediante "Trust author and enable plugins").
  2. Se estableció el ajuste del plugin Supernote IP address en 127.0.0.1, se dejó Sync folder en su valor por defecto Supernote sync.
  3. Se levantó un mock de Node de un solo archivo del servidor "Browse and Access" del dispositivo en 127.0.0.1:8089, simulando un dispositivo malicioso/no autorizado. Su listado de directorios devuelve:
    root@kitploit:~
    {"name":"Quick notes.note","size":61,"date":"2026-07-31 00:00:00",
     "uri":"/../../PWNED_BY_DEVICE_SYNC.txt","extension":"note","isDirectory":false}
    
    (name pasa el filtro de extensión .note; uri lleva el traversal.) Cualquier GET se responde con 200 y los bytes del archivo — los clientes HTTP reales normalizan .. fuera de la ruta de la solicitud saliente antes de que llegue al cable (RFC 3986), lo cual es irrelevante aquí ya que el código vulnerable calcula la ruta de escritura a partir de la cadena JSON original, no de la URL de solicitud normalizada.
  4. Se ejecutó la acción de la paleta de comandos "Supernote (Unofficial): Sync supernote notes now".
  5. Resultado: un archivo nuevo, PWNED_BY_DEVICE_SYNC.txt, que contenía los bytes del atacante, se creó un directorio por encima de la raíz del vault (hermano de testvault/, completamente fuera del vault). El propio data.json del plugin registró la ruta calculada textualmente: "vaultPath": "Supernote sync/../../PWNED_BY_DEVICE_SYNC.txt".

Evidencia (capturas genuinas, ~/engagements/supernote-obsidian-plugin/evidence/):

  • 01-vault-escape-file-write.png — terminal real: ls -la testvault/ PWNED_BY_DEVICE_SYNC.txt mostrando el archivo como hermano del directorio del vault, además de su contenido controlado por el atacante mediante cat.
  • 02-plugin-data-json-vaultpath.png — el propio archivo de estado de sincronización persistido del plugin registrando "vaultPath": "Supernote sync/../../PWNED_BY_DEVICE_SYNC.txt".
  • 03-plugin-settings-security-claim.png — la interfaz de ajustes del plugin afirmando que el comando de sincronización "nunca escribe en ningún lugar fuera de esta carpeta".

Impacto

Un atacante que pueda responder como el dispositivo Supernote configurado de la víctima (adyacente a la LAN: AP rogue, ARP spoofing, o reclamar la IP del dispositivo) puede hacer que el cliente Obsidian de la víctima cree un archivo nuevo controlado por el atacante en cualquier ruta del sistema de archivos donde el proceso de escritorio pueda escribir — dentro del vault (p. ej. un archivo completamente nuevo bajo .obsidian/plugins/<new-id>/, sentando las bases para un mayor abuso de confianza de plugins) o completamente fuera de él (p. ej. ~/.config/autostart/*.desktop, un nuevo drop-in de crontab, o cualquier otra ubicación donde un archivo nuevo —no una sobrescritura— sea suficiente para obtener ejecución o persistencia). No puede sobrescribir silenciosamente un archivo que ya existe en la ruta resuelta (el createBinary de Obsidian lanza "File already exists" en ese caso, confirmado empíricamente), lo que acota la primitiva a la plantación de archivos nuevos en lugar de una sobrescritura universal.

Debilidades

  • CWE-22: Limitación inadecuada de un nombre de ruta a un directorio restringido ('Path Traversal')
  • CWE-73: Control externo del nombre de archivo o ruta (el uri proporcionado por el dispositivo determina directamente el destino en disco)

Remediación

En deviceUriToVaultPath() (src/deviceSync.ts:153-161), rechazar o eliminar los segmentos de ruta .. (y los vacíos/solo .) después de dividir deviceUri, p. ej.:

root@kitploit:~
const segments = deviceUri
    .split('/')
    .filter((s) => s.length > 0 && s !== '.' && s !== '..')
    .map((s) => s.replace(INVALID_FILENAME_CHARS, '_'));

Además, scanDeviceSupernoteTree() (src/FileListModal.ts:63) debería validar que el uri de una entrada del listado sea coherente con su name (p. ej. que uri termine con el mismo nombre de archivo), en lugar de confiar en los dos campos de forma independiente — la misma clase de defensa en la que ya se confía implícitamente en todo el resto del código base, que construye rutas solo a partir de name/basename mediante getAvailablePathForAttachment().

Crédito

Dostxodjayev Abdullox

Canal de reporte

No hay ningún SECURITY.md presente en este repositorio. El reporte privado de vulnerabilidades de GitHub está confirmado como habilitado para philips/supernote-obsidian-plugin (gh api repos/philips/supernote-obsidian-plugin/private-vulnerability-reporting --jq .enabled → true; 0 avisos de seguridad publicados previamente). Se aplica el flujo estándar de GHSA: https://github.com/philips/supernote-obsidian-plugin/security/advisories/new.

Descargar herramienta