Neutron, nuestro motor de IA, obtuvo un 97.1% en el benchmark CyberGym de UC Berkeley. Más información

Análisis a fondo

Análisis a fondo

RCE pre-auth en el Viewer de TightVNC, corregida en 2.8.90

Un servidor VNC malicioso ejecuta código en el Viewer de TightVNC antes de cualquier petición de contraseña; actualice a 2.8.90 y cierre el modo de escucha.

TL;DR

  • El Viewer de TightVNC para Windows trunca a 32 bits el tamaño de su búfer de píxeles. Un servidor VNC malicioso que anuncie una pantalla enorme puede ejecutar código en el viewer antes de cualquier petición de contraseña. SSD Secure Disclosure publicó la falla con un exploit funcional el 29 de octubre de 2026.
  • Todo Viewer anterior a 2.8.89 en la línea 2.8 está afectado; el investigador demostró el exploit en 2.8.88 x64. Los puestos de ayuda que usan el modo de escucha exponen el viewer en el TCP 5500 sin contraseña ni restricción de IP, lo que convierte la falla en ejecución remota de código previa a la autenticación.
  • El fabricante corrigió el viewer en 2.8.89 y 2.8.90, publicadas el 2 y el 5 de octubre de 2026, y recomienda 2.8.90. No hay CVE asignado, no existe aviso del fabricante y no se conoce explotación; la falla no está en el catálogo KEV de CISA al 2 de noviembre de 2026.
  • Actualice todo Viewer de TightVNC a 2.8.90. Donde la actualización deba esperar, desactive el modo de escucha o restrinja el TCP 5500 a equipos de confianza, y nunca dirija el viewer a un servidor desconocido; ninguna restricción de red cubre esa segunda vía.

¿Está afectado?

Está expuesto cuando se cumplen todas estas condiciones:

  • El equipo ejecuta el Viewer de TightVNC para Windows, versión 2.8.88 o cualquier versión anterior de la línea 2.8. El investigador demostró el exploit en 2.8.88 x64, y las comprobaciones que lo detienen aparecen por primera vez en 2.8.89.
  • Una de las dos vías de ataque es alcanzable. En modo de escucha, iniciado con tvnviewer.exe -listen o desde el menú de la bandeja, el viewer escucha en 0.0.0.0:5500 y acepta cualquier servidor que se conecte, sin contraseña ni restricción de IP. Aparte, un usuario que conecte el viewer a un servidor controlado por el atacante o comprometido queda comprometido al conectarse, antes de la petición de contraseña.
  • El atacante puede alcanzar el puerto 5500, para la vía de escucha, o puede operar o tomar el control de un servidor VNC al que el viewer se conecte.
Rama Versiones afectadas Versión corregida
TightVNC 2.8.x hasta 2.8.88 2.8.90 (2.8.89 cerró parte)
TightVNC 2.8.x en equipos de la era Windows XP hasta 2.8.88 2.8.90, la última línea que aún admite XP
TightVNC 4.x sin aviso; publicada tras las correcciones no aplica; versión actual 4.0.2 (28 de octubre de 2026)

El Viewer 2.8.88 se publicó el 19 de junio de 2026, así que el 2 de noviembre de 2026 el código afectado llevaba 136 días en las instalaciones recientes; las más antiguas lo llevan más tiempo. La divulgación describe el modo de escucha como «el despliegue habitual de los puestos de ayuda», y eso hace de la vía de escucha la exposición práctica.

Compruebe la versión instalada del viewer y si está escuchando:

(Get-Item "C:\Program Files\TightVNC\tvnviewer.exe").VersionInfo.FileVersion
netstat -ano | findstr :5500

El primer comando muestra la versión de archivo del viewer en una instalación por defecto; ajuste la ruta si TightVNC está en otro lugar. El segundo muestra cualquier socket local a la escucha en el TCP 5500, es decir, el modo de escucha expuesto; un resultado vacío significa que esa vía está cerrada.

¿Cómo corregirlo?

Actualice todo Viewer de TightVNC a 2.8.90:

  1. Descargue el instalador 2.8.90 de su plataforma desde la página de descargas del fabricante y ejecútelo; reemplaza el viewer en su sitio.
  2. Confirme la corrección con la verificación de versión anterior; la versión de archivo debe decir 2.8.90.
  3. Reinicie cualquier viewer que corra en modo de escucha, para que el binario nuevo sea el que escucha en el 5500.

El propio aviso de versión del fabricante para 2.8.90 indica que las versiones 2.8.89 y 2.8.90 «abordan varios problemas de seguridad en el Viewer» y recomienda encarecidamente 2.8.90. Mientras llega la actualización, dos pasos estrechan la exposición, y ambos tienen límites:

  1. Desactive el modo de escucha en los puestos de ayuda, o restrinja el TCP 5500 en el cortafuegos a las direcciones de los servidores VNC de confianza. El límite es claro: la falla sigue en el viewer, y cualquier camino no confiable hacia el 5500 la deja abierta.
  2. Indique a los operadores que nunca conecten el viewer a un servidor que no controlen. Ninguna restricción de red cubre esta vía, porque la conexión es saliente.

¿Cuál es la causa raíz?

El viewer dimensiona su búfer de píxeles con un resultado de 32 bits truncado. Sus escrituras usan luego desplazamientos derivados de las dimensiones completas que el servidor anunció. El código está en FrameBuffer, en la capa rfb del viewer, citado de la distribución fuente 2.8.88.

El cálculo del tamaño multiplica en 64 bits y después descarta todo lo que exceda 32:

int FrameBuffer::getBufferSize() const
{
  return (int)((UINT64)m_dimension.area() * m_pixelFormat.bitsPerPixel / 8);
}

bool FrameBuffer::resizeBuffer()
{
  if (m_buffer != 0) {
    delete []m_buffer;
  }
  if ((m_buffer = new UINT8[getBufferSize()]) == 0) {
    return false;
  }
  return true;
}

Un servidor que anuncia una pantalla de 32768 por 32769 a 32 bits por píxel hace desbordar el producto. El producto de 64 bits supera los 4 GB, el cast (int) conserva 128 KB, y el viewer asigna 128 KB. Las dimensiones almacenadas conservan los 32768 por 32769 anunciados, así que el lienzo lógico y la asignación difieren en exactamente 2^32 bytes (4,294,967,296).

El desplazamiento de escritura se calcula luego en un int de 32 bits con signo, sobre el ancho anunciado, sin comparación alguna con lo asignado:

void *FrameBuffer::getBufferPtr(int x, int y) const
{
  char *ptr = (char *)m_buffer;
  ptr += (y * m_dimension.width + x) * getBytesPerPixel();

  return (void *)ptr;
}

Un y grande desborda el producto y lo extiende con su signo, así que un rectángulo de actualización de pantalla puede colocar sus píxeles en cualquier desplazamiento elegido respecto al búfer, positivo o negativo. Las actualizaciones llegan del servidor como rectángulos Raw o Tight, a través de RemoteViewerCore::receiveFbUpdateRectangle hacia los decodificadores, y el protocolo RFB deja al servidor elegir tanto la geometría como cada rectángulo.

¿Cómo se explota?

El exploit publicado necesita una sola conexión y ninguna credencial:

  1. El servidor RFB del atacante saluda al viewer y ofrece un único tipo de seguridad, None. La autenticación RFB es unidireccional, el servidor nunca se prueba ante el viewer, y el viewer de TightVNC acepta None.
  2. El ServerInit anuncia la geometría envenenada, 32768 por 32769 a 32 bits por píxel. El saludo ocupa 45 bytes y asigna el búfer de 128 KB que el exploit llama FB1.
  3. Un rectángulo Raw en las coordenadas (7492, 32767) cae en FB1 - 0x18AF0, el campo m_dataLength de un objeto BufferedOutputStream situado bajo el framebuffer. El exploit escribe allí un valor grande.
s.sendall(b"RFB 003.008\n")                 # greet as the server
s.sendall(bytes([1, 1]))                    # offer one security type: None
s.sendall(struct.pack(">I", 0))             # SecurityResult = OK
s.sendall(struct.pack(">HH", 32768, 32769)  # ServerInit: the poisoned geometry
          + PIXEL_FORMAT + struct.pack(">I", 3) + b"PWN")
  1. El siguiente flush() devuelve por el socket unos 100 KB de montón adyacente. Con esos bytes el exploit recupera la base de imagen de tvnviewer.exe y la dirección del framebuffer, lo que vence la aleatorización del espacio de direcciones.
  2. El exploit escribe un objeto falso y una cadena de programación orientada a retornos dentro del framebuffer de 128 KB, y después sobrescribe el puntero de vtable del BufferedOutputStream. La siguiente llamada virtual sigue el objeto falso, pivota la pila hacia el framebuffer, y la cadena termina en CreateProcessW.

El exploit funciona contra un viewer reciente y con su configuración por defecto con ASLR y DEP activados; el control-flow guard está ausente del binario. La divulgación publica el script completo para ambas vías, el viewer a la escucha y la conexión saliente, incluido un modo de relanzamiento que restaura el viewer tras cada ejecución.

¿Cómo fluye el ataque?

Al atacante le basta con que el viewer acepte una sesión, y la asignación truncada convierte esa sesión en control del proceso del viewer.

flowchart LR A["El atacante opera un
servidor RFB malicioso"] --> B["El viewer acepta,
tipo de seguridad None"] B --> C["ServerInit anuncia
32768 x 32769 a 32 bpp"] C --> D["Búfer dimensionado en 32 bits,
lienzo de 4 GB sobre 128 KB,
sin comprobación de límites"] D --> E["Los rectángulos escriben
fuera de la asignación"] E --> F["Fuga del montón, secuestro de vtable,
ROP hacia CreateProcessW:
código en el viewer"]

¿Qué puede hacer un atacante?

El impacto demostrado es ejecución de código en el proceso del viewer, con la cuenta que lo ejecuta, en el momento en que se abre la sesión:

  • En modo de escucha, cualquier equipo que alcance el TCP 5500 lo consigue sin contraseña, sin lista de IPs permitidas y sin intervención del operador. La divulgación describe este modo como el despliegue habitual de los puestos de ayuda, así que la población expuesta es la herramienta de soporte, no los servidores.
  • En una conexión saliente, la compromisión ocurre al conectar, antes de cualquier petición de contraseña, de modo que las contraseñas VNC del lado del servidor no ayudan contra un servidor tomado.
  • El exploit puede ocultar la ventana de su carga con CREATE_NO_WINDOW, relanzar el viewer tras la ejecución y bloquear el viewer a voluntad como denegación de servicio.

Las condiciones son estrechas: el atacante debe ser el servidor con el que habla el viewer, o alcanzar un viewer a la escucha. Un servidor TightVNC, tvnserver, no es el objetivo aquí; la dirección del ataque es el punto, la máquina atendida atacando a la máquina que ayuda.

¿Por qué funciona la corrección?

El código fuente 2.8.89 añade un archivo, viewer-core/ViewerBufferSize.h, con predicados de tamaño de 64 bits, y valida los anuncios del servidor antes de cualquier asignación:

inline bool fitsFrameBuffer(int width, int height, int bytesPerPixel)
{
  if (width < 0 || height < 0 || bytesPerPixel <= 0) {
    return false;
  }

  const UINT64 alignedWidth = (static_cast<UINT64>(width) + 3) & ~static_cast<UINT64>(3);
  const UINT64 alignedHeight = (static_cast<UINT64>(height) + 3) & ~static_cast<UINT64>(3);
  return alignedWidth * alignedHeight <= INT_MAX / bytesPerPixel;
}

RemoteViewerCore::setFbProperties, que consume la geometría del ServerInit, ahora la rechaza de entrada:

  if (!ViewerBufferSize::fitsFrameBuffer(width, height, bitsPerPixel / 8)) {
    throw Exception(_T("Framebuffer is too large"));
  }

El mismo patrón protege el camino del cursor enriquecido, y en 2.8.89 el decodificador Tight también rechaza rectángulos JPEG sobredimensionados. En 2.8.90 el decodificador Tight añade dos comprobaciones más: rechaza datos descomprimidos cuyo tamaño no coincide con el rectángulo declarado, y bloques comprimidos vacíos. Estos son los «varios problemas de seguridad en el Viewer» que nombra el aviso de versión del fabricante.

La corrección es completa para las vías demostradas. Ningún anuncio del servidor puede llegar ya a la asignación truncada, porque la geometría se valida en la frontera del protocolo antes de dimensionar el búfer. La aritmética truncante sigue en FrameBuffer::getBufferSize, sin cambios en 2.8.90, así que la seguridad depende ahora de que cada llamador valide primero. Es un estrechamiento a una vía de entrada validada, no la eliminación de la clase de falla.

¿Cómo sé si fui comprometido?

La divulgación no publica indicadores de compromiso. Los rastros del atacante están en el equipo del viewer, y cada comprobación siguiente se deriva de la mecánica publicada:

Get-WinEvent -LogName Application | Where-Object { $_.Message -match "tvnviewer" } | Select -First 20

Las caídas inexplicadas del viewer en el registro de aplicación marcan la variante de denegación de servicio. La carga del exploit corre como hijo del viewer, así que revise la telemetría de creación de procesos buscando cualquier proceso cuyo padre sea tvnviewer.exe. En equipos con Sysmon, el evento ID 1 con ese padre es la consulta.

En viewers a la escucha, revise los registros de red buscando conexiones entrantes al TCP 5500 seguidas de un flujo saliente del viewer de unos 100 KB. Ese flujo es la fuga del montón que el exploit lee de vuelta.

Si un viewer estuvo expuesto y muestra alguna de estas señales, trate la cuenta del operador como comprometida y rote todo lo que esa cuenta podía alcanzar.

Cronología

  • 2026-06-19: se publica TightVNC 2.8.88, la versión afectada demostrada.
  • 2026-10-02: TightVNC 2.8.89 añade comprobaciones para framebuffers sobredimensionados, actualizaciones RichCursor y rectángulos JPEG Tight; el fabricante recomienda la actualización.
  • 2026-10-05: TightVNC 2.8.90 añade las correcciones restantes del viewer, validando tamaños descomprimidos y rechazando bloques comprimidos vacíos; el código fuente lleva esta fecha.
  • 2026-10-29: SSD Secure Disclosure publica el análisis y el exploit, con crédito a SeongJoon Cho de SSD Labs Korea, e informa de que el fabricante nunca respondió al informe.
  • 2026-11-02: no hay CVE asignado, no hay aviso del fabricante y no se conoce explotación; la falla no está en el catálogo KEV de CISA.

Las correcciones salieron 105 días después de la versión afectada 2.8.88 y 24 días antes de la publicación del exploit
Figura 1: la versión afectada del 19 de junio, las dos correcciones del 2 y el 5 de octubre, y el exploit público del 29 de octubre. Fuente: notas de versión de tightvnc.com y SSD Secure Disclosure, comprobadas el 2 de noviembre de 2026

FAQ

¿El servidor de TightVNC también está afectado?

No. El objetivo es el Viewer, tvnviewer.exe, y el ataque viaja del servidor al viewer. Un equipo que solo ejecuta el servidor de TightVNC está expuesto solo en la medida en que sus operadores usen también el viewer para conectar hacia fuera.

¿Una contraseña VNC detiene esto?

No. El viewer acepta el tipo de seguridad None, y la autenticación RFB nunca prueba la identidad del servidor ante el viewer. La vía saliente se completa antes de cualquier petición de contraseña, así que una contraseña fuerte en el servidor real no sirve una vez que ese servidor es el del atacante.

¿Ha afectado antes esta clase a TightVNC?

Sí, dos veces, ambas desde un servidor malicioso. TightVNC 1.3.10 corrigió desbordamientos de entero en el viewer reportados por Core Security Technologies, que permitían a un servidor VNC modificado corromper el montón del viewer. La versión 2.8.63 corrigió un desbordamiento de búfer que permitía a un servidor «malvado» ejecutar código en la máquina del viewer. Las notas de versión del propio fabricante registran ambos casos.

¿TightVNC 4 está afectado?

Ningún aviso cubre la línea 4.x. Se publicó el 25 de octubre de 2026, después de las correcciones 2.8.89 y 2.8.90. El anuncio 4.0 describe un nuevo modelo de seguridad, no esta falla del viewer. Confirme con el fabricante si ejecuta 4.x; en 2.8.x, instale 2.8.90 en cualquier caso.

¿Por qué no hay CVE?

El fabricante no publica avisos, y el informe del investigador quedó sin respuesta, así que no se asignó ningún CVE al 2 de noviembre de 2026. Los analizadores de dependencias y vulnerabilidades no pueden marcar el rango afectado; la verificación de versión de «¿Está afectado?» es la única detección fiable.

¿Dónde consigo el viewer corregido?

En la página de descargas del fabricante, que ofrece los instaladores 2.8.90 junto a la línea 4.0.2. El código fuente 2.8.90 se publica bajo GPL en el mismo lugar, y de ahí viene el análisis de la corrección de este artículo.

Encuentre equipos que exponen un viewer de TightVNC a la escucha con Ostorlab

El descubrimiento de superficie de ataque de Ostorlab inventaría sus equipos expuestos en internet, para identificar qué máquinas de soporte responden en el TCP 5500 con un viewer a la escucha y actualizarlas primero.

Referencias

  • SSD Secure Disclosure, «TightVNC Viewer Pre-auth RCE», el análisis y el exploit, crédito a SeongJoon Cho de SSD Labs Korea: https://ssd-disclosure.com/tightvnc-viewer-pre-auth-rce/ (publicado el 29 de octubre de 2026; comprobado el 2 de noviembre de 2026)
  • Notas de versión de TightVNC, correcciones del Viewer en 2.8.89 y 2.8.90: https://www.tightvnc.com/whatsnew.php (comprobadas el 2 de noviembre de 2026)
  • Noticias de TightVNC, anuncios del 2 y el 5 de octubre y la versión 2.8.88 del 19 de junio: https://www.tightvnc.com/news.php (comprobadas el 2 de noviembre de 2026)
  • Página de descargas de TightVNC, instaladores 2.8.90 y 4.x y archivos fuente GPL: https://www.tightvnc.com/download/ (comprobada el 2 de noviembre de 2026)
  • Distribución fuente de TightVNC 2.8.88, el FrameBuffer vulnerable: https://www.tightvnc.com/download/2.8.88/tightvnc-2.8.88-src-gpl.zip (comprobada el 2 de noviembre de 2026)
  • Distribución fuente de TightVNC 2.8.90, ViewerBufferSize.h y los puntos de llamada de validación: https://www.tightvnc.com/download/2.8.90/tightvnc-2.8.90-src-gpl.zip (comprobada el 2 de noviembre de 2026)
  • CyberSecurityNews, «TightVNC Pre-Auth RCE Vulnerability Lets Hackers Execute Code on Windows PCs, PoC Released»: https://cybersecuritynews.com/tightvnc-pre-auth-rce-vulnerability/ (publicado el 2 de noviembre de 2026; comprobado el 2 de noviembre de 2026)
  • Catálogo KEV de CISA, comprobado para entradas de TightVNC: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (comprobado el 2 de noviembre de 2026)