Man-in-the-Middle RDP en FreeRDP, corregido en 3.32.2 (CVE-2026-121392)
Antes de 3.32.2, dos comprobaciones de identidad artesanales de FreeRDP permiten a un man-in-the-middle con CA de confianza interceptar sesiones RDP. Actualice.
TL;DR
- La comprobación de identidad TLS del lado cliente de FreeRDP acepta un certificado cuyo Common Name coincide aunque sus Subject Alternative Names no, y compara destinos IP literales contra nombres DNS. Un man-in-the-middle con un certificado de una CA en la que el cliente confía puede interceptar sesiones RDP.
- Todas las versiones de FreeRDP anteriores a 3.32.2 están afectadas, según el registro CVE. El aviso de julio de 2026 que describía estos casos anunciaba una corrección en 3.29.0 que nunca cubrió dos de sus tres casos.
- El registro CVE de VulnCheck puntúa la falla 9.1 Critical en CVSS v4.0 y 7.4 High en CVSS v3.1. No se conoce explotación, y el CVE no figura en el catálogo Known Exploited Vulnerabilities (KEV) de CISA al 4 de noviembre de 2026.
- Actualice a FreeRDP 3.32.2 o posterior ahora. No se documenta ninguna solución alternativa de configuración; vea «¿Cómo corregirlo?» para los productos que integran FreeRDP.
¿Está afectado?
Está expuesto cuando se cumplen todas estas condiciones:
- Ejecuta un cliente basado en FreeRDP, o un producto que integra FreeRDP, en cualquier versión anterior a 3.32.2. El registro CVE marca como afectada toda versión desde la primera.
- Un atacante puede interceptar o redirigir la conexión TCP RDP y presentar un certificado que encadene a una CA en la que el cliente ya confía. El aviso nombra los escenarios: una CA pública que emite mal, una CA privada comprometida o fraudulenta, u otra anomalía de la cadena de confianza.
- El cliente verifica los certificados del servidor. La divulgación original reprodujo las fallas con
AutoAcceptCertificate = FALSE,AutoDenyCertificate = TRUEyAuthenticationLevel = 2.
Un atacante que no pueda obtener un certificado de confianza no se beneficia de esta falla. El defecto debilita la verificación de identidad del servidor en escenarios de CA de confianza; no crea la confianza.
| Versión | Afectada | Versión corregida |
|---|---|---|
| 3.32.1 y toda versión anterior | Sí, en la comprobación de identidad por omisión | 3.32.2, publicada el 2026-10-12 |
| 3.32.2 y posteriores, incluida 3.33.0 | No | Siga actualizando |
3.32.2 y 3.33.0 tienen un defecto conocido en la nueva comprobación de IP, y falla de forma cerrada: rechaza coincidencias válidas en lugar de aceptar incorrectas (vea «¿Por qué funciona la corrección?»).
Compruebe lo que ejecuta:
# The FreeRDP 3 client front-ends all print the version
xfreerdp3 --version
# Debian-family package installs
dpkg -l | grep freerdp
# Library copies, including those bundled inside other products
ldconfig -p | grep libfreerdp
Un producto que integra FreeRDP necesita su propia actualización; la versión que informa el producto es la que hay que comparar con el aviso.
¿Cómo corregirlo?
- Actualice a FreeRDP 3.32.2 o posterior. Los commits de la corrección llegaron en 3.32.2, publicada el 12 de octubre de 2026, y toda versión posterior los incluye.
- Actualice los productos que integran FreeRDP cuando su proveedor publique la biblioteca corregida. El rango afectado del registro CVE empieza en la primera versión, así que las copias integradas más antiguas están dentro del alcance.
- Trate una sesión como comprometida si pudo pasar por un interceptor que tenga un certificado de confianza para el cliente. Ese atacante ve el contenido de la pantalla, las pulsaciones y el portapapeles y puede inyectar entradas, así que cambie cada credencial usada en esa sesión y revise lo que se hizo en ella. La falla no le da al atacante esa posición de red; permite que un atacante que ya la tiene actúe dentro de la sesión.
No se documenta ninguna solución alternativa de configuración. El aviso de seguimiento que encontró la corrección incompleta no propone ninguna mitigación. Su corrección sugerida es lo que implementa 3.32.2: el Common Name solo cuenta cuando no existe ningún SAN DNS, y los destinos IP solo coinciden con entradas SAN iPAddress. FreeRDP escribió esa lógica por su cuenta en lugar de llamar a X509_check_host() y X509_check_ip_asc() de OpenSSL, que según el aviso aplican las mismas reglas.
¿Cuál es la causa raíz?
FreeRDP realiza sus propias comparaciones de cadenas de Common Name y SAN DNS en lugar de usar las API de validación de identidad de OpenSSL. El bloque vulnerable, libfreerdp/crypto/tls.c líneas 1859 a 1879 en el tag 3.32.1:
/* compare against common name */
if (common_name)
{
if (tls_match_hostname(common_name, common_name_length, hostname))
hostname_match = TRUE;
}
/* compare against alternative names */
if (dns_names)
{
for (size_t index = 0; index < dns_names_count; index++)
{
if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname))
{
hostname_match = TRUE;
break;
}
}
}
Dos propiedades de este bloque rompen la verificación de identidad del servidor:
- La comprobación del Common Name y la de los SAN DNS forman un OR incondicional. La sección 6.4.4 de la RFC 6125 exige lo contrario: cuando un certificado lleva cualquier entrada SAN dNSName, el Common Name es irrelevante. Un certificado con
CN=rdp01.corp.exampley una lista SAN sin coincidencias comoDNS:files.corp.examplepasa de todos modos en una conexión ardp01.corp.example. x509_utils_get_dns_names()solicita solo entradas GEN_DNS. El código nunca lee entradas SAN iPAddress en ninguna parte. Cuando el destino RDP es un IP literal, FreeRDP compara la IP como una cadena simple contra el CN y las entradas SAN DNS.
Un aviso de julio de 2026, GHSA-43hh-p3vw-hfx3, documentaba tres debilidades de comprobación de identidad y afirmaba que la versión 3.29.0 las corregía. Su conjunto de correcciones solo cerró el primer caso, un truncamiento por NUL incrustado. Los casos del Common Name y de la IP literal permanecieron idénticos byte a byte desde el tag 3.29.0, y GHSA-jq25-3374-4262 documentó ese hueco residual en octubre de 2026.
¿Cómo se explota?
- El atacante consigue una posición para interceptar o redirigir la conexión RDP de la víctima. También necesita un certificado encadenado a una CA en la que el cliente confía, cuyo Common Name sea el nombre de host de destino o cuyo SAN DNS contenga la IP de destino como texto.
- El cliente FreeRDP de la víctima se conecta y ejecuta
tls_verify_certificate()sobre el certificado del atacante. Las comprobaciones de cadena y validez pasan, porque el certificado está realmente emitido por una CA de confianza. - La rama del Common Name pone
hostname_match = TRUEaunque las entradas SAN no coincidan, o el destino IP literal coincide con una cadena SAN DNS. El cliente acepta el certificado como identificación del servidor. - El extremo del atacante transporta entonces la sesión RDP: contenido de pantalla, pulsaciones, portapapeles e inyección de entrada, en la descripción de impacto del aviso de seguimiento.
No existe prueba de concepto pública más allá de las notas de reproducción de los dos avisos. El aviso original reprodujo los tres casos en la ruta de handshake TLS de FreeRDP, con una CA de prueba local instalada en un almacén de certificados FreeRDP aislado. El seguimiento llamó directamente a tls_verify_certificate() sin modificar sobre certificados con cadena verificada, sin un handshake de red completo.
¿Cómo fluye el ataque?
Un man-in-the-middle con un certificado de confianza sirve la sesión RDP, y la comprobación de identidad artesanal acepta el certificado sin coincidencia.
certificado de CA de confianza"] --> B["La víctima se conecta al
servidor RDP"] B --> C["tls_verify_certificate se ejecuta sobre
el certificado del atacante"] C --> D["El CN coincide, las entradas SAN
no: OR incondicional"] D --> E["Ninguna entrada SAN iPAddress
se lee nunca para destinos IP"] E --> F["El cliente acepta la
identidad del atacante"] F --> G["Tráfico de sesión: pantalla,
pulsaciones, portapapeles"]
¿Qué puede hacer un atacante?
El atacante intercepta la sesión RDP completa: contenido de pantalla, pulsaciones, portapapeles e inyección de entrada, según la declaración de impacto del aviso de seguimiento. Ninguno de los dos avisos reivindica ejecución de código ni extracción directa de credenciales, y el aviso original tampoco reivindica corrupción de memoria.
El ataque necesita primero la posición de red y el certificado de confianza. Un certificado cuyo emisor no es de confianza para el cliente falla la comprobación de cadena antes de la comparación de identidad. Un cliente que fija o verifica manualmente certificados con independencia de esta ruta queda fuera del escenario descrito.
¿Por qué funciona la corrección?
Dos commits de 3.32.2 sustituyen la comparación artesanal en los dos casos. El commit 1a23b21c37, «do not check common name if there are DNS SAN», traslada la comparación del Common Name detrás de un else if:
common_name = freerdp_certificate_get_common_name(cert, &common_name_length);
dns_names = freerdp_certificate_get_dns_names(cert, &dns_names_count, &dns_names_lengths);
-
- /* compare against common name */
-
- if (common_name)
- {
- if (tls_match_hostname(common_name, common_name_length, hostname))
- hostname_match = TRUE;
- }
-
/* compare against alternative names */
-
if (dns_names)
{
for (size_t index = 0; index < dns_names_count; index++)
@@
}
}
+ /* compare against common name */
+ else if (common_name)
+ {
+ if (tls_match_hostname(common_name, common_name_length, hostname))
+ hostname_match = TRUE;
+ }
El Common Name ahora se consulta solo cuando el certificado no tiene ninguna entrada SAN DNS, que es la regla de la sección 6.4.4 de la RFC 6125.
El commit 350539ec9a, «add IP SAN checks», añade una ruta de extracción de iPAddress. Cuando el destino de la conexión se analiza como una IP, el bucle la compara con las entradas SAN iPAddress del certificado mediante una comparación binaria. Nunca cae a la comparación de cadenas DNS.
La corrección es completa para los dos casos reportados. El bloque que reescriben ambos commits contiene las únicas llamadas a tls_match_hostname() del proyecto. El tercer caso del aviso original, el truncamiento por NUL incrustado, ya estaba corregido en 3.29.0.
Queda un defecto en la nueva ruta IP, y falla de forma cerrada. En 3.32.2 y 3.33.0, la función utils_compare_ip_strings() rechaza toda dirección válida porque comprueba el valor de retorno equivocado de inet_pton(). Por eso un destino IP literal nunca coincide con una entrada SAN iPAddress, y el cliente recurre a su comprobación known-hosts o a la solicitud al usuario. El commit f5e8a54fb2 corrigió esa comprobación el 3 de noviembre de 2026, después de la publicación de 3.33.0.
¿Cómo sé si fui comprometido?
No hay indicadores publicados, y los avisos no reportan explotación. Una intercepción exitosa parece una sesión TLS ordinaria para el cliente.
El cliente no guarda ningún rastro del ataque. Cuando la cadena es de confianza y el nombre coincide, tls_verify_certificate() acepta el certificado sin leer ni actualizar el almacén known-hosts de FreeRDP, así que el certificado del atacante no deja ninguna entrada allí. Los registros de conexión RDP de sus servidores son una pista mejor: una sesión desde una dirección de origen inesperada merece revisarse, pero no prueba una intercepción.
Cronología
- 2026-07-15: Se publica GHSA-43hh-p3vw-hfx3, documentando tres debilidades de comprobación de identidad y afirmando que 3.29.0 las corregía.
- 2026-10-07: Armin Novak redacta los dos commits de corrección para los casos residuales.
- 2026-10-12: Se publica FreeRDP 3.32.2 con las correcciones, dentro de una versión de endurecimiento más amplia.
- 2026-10-19: Se publica GHSA-jq25-3374-4262, documentando que 2 de los 3 casos nunca se corrigieron en 3.29.0.
- 2026-11-03: VulnCheck publica CVE-2026-121392 con la puntuación CVSS v4.0 de 9.1 Critical.
El aviso decía que 3.29.0 cerraba estos casos. Los commits de corrección llegaron 84 días después de ese aviso, y la versión que los publicó, 89 días después.
FAQ
¿Por qué la falla tiene a la vez una puntuación Critical y una High?
Las dos puntuaciones vienen de dos versiones de CVSS aplicadas a los mismos hechos. VulnCheck, la autoridad de numeración CVE (CNA) que publicó el registro, la puntúa 9.1 Critical en CVSS v4.0, CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N. VulnCheck y el propio aviso de FreeRDP la puntúan ambos 7.4 High en CVSS v3.1, CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N.
¿Afecta al servidor o al proxy de FreeRDP?
La falla está en la verificación de identidad del servidor del lado cliente, en tls_verify_certificate(). Las notas de la versión 3.32.2 enumeran otros 44 avisos en el mismo lote, que endurecen el código frente a mensajes maliciosos de clientes y servidores.
¿Estaba equivocado el aviso original?
Su campo de versiones corregidas decía 3.29.0. El seguimiento mostró que el conjunto de correcciones de 3.29.0 solo cerraba el caso del NUL incrustado. Los otros dos casos eran idénticos byte a byte desde ese tag. El campo era inexacto para 2 de los 3 casos documentados.
¿Quién reportó estas fallas?
El aviso original de los tres casos fue reportado por Bin Luo, de la Universidad de Ciencia y Tecnología Electrónica de China (UESTC), usuario de GitHub 1121984919, según el aviso. El informe de seguimiento que documenta la corrección incompleta no nombra a ningún informante. Revela que Claude se usó como herramienta bajo la dirección del informante. Este verificó de nuevo cada commit y línea citados antes del envío.
¿Actualizar a 3.33.0 también lo corrige?
Sí. Las correcciones llegaron en 3.32.2, y 3.33.0, publicada el 2 de noviembre de 2026, las incluye. La versión 3.32.2 es el mínimo.
Encuentre las copias de FreeRDP de su patrimonio antes de que lo haga un interceptor
Toda versión de FreeRDP anterior a 3.32.2 está en el rango afectado, incluidas las copias integradas en productos. El análisis SCA y SBOM de Ostorlab inventaría las dependencias de sus repositorios e imágenes, para saber qué componentes y productos llevan un FreeRDP afectado.
Referencias
- CVE-2026-121392, registro CVE de VulnCheck con ambos vectores CVSS: https://www.cve.org/CVERecord?id=CVE-2026-121392 (consultado el 4 de noviembre de 2026)
- Aviso de VulnCheck, «FreeRDP before 3.32.2 Certificate Identity Validation Bypass via tls_verify_certificate»: https://www.vulncheck.com/advisories/freerdp-before-3.32.2-certificate-identity-validation-bypass-via-tls-verify-certificate (consultado el 4 de noviembre de 2026)
- GHSA-jq25-3374-4262, aviso de FreeRDP, «Incomplete fix for GHSA-43hh-p3vw-hfx3»: https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-jq25-3374-4262 (consultado el 4 de noviembre de 2026)
- GHSA-43hh-p3vw-hfx3, aviso de FreeRDP, «TLS certificate identity validation accepts malformed and non-authoritative identities»: https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-43hh-p3vw-hfx3 (consultado el 4 de noviembre de 2026)
- Commit de corrección 1a23b21c37, «[crypto,tls] do not check common name if there are DNS SAN»: https://github.com/FreeRDP/FreeRDP/commit/1a23b21c37 (consultado el 4 de noviembre de 2026)
- Commit de corrección 350539ec9a, «[crypto,tls] add IP SAN checks»: https://github.com/FreeRDP/FreeRDP/commit/350539ec9a (consultado el 4 de noviembre de 2026)
- Commit de seguimiento f5e8a54fb2, «[core,utils] fix return check for inet_pton»: https://github.com/FreeRDP/FreeRDP/commit/f5e8a54fb2 (consultado el 4 de noviembre de 2026)
- Notas de la versión FreeRDP 3.32.2: https://github.com/FreeRDP/FreeRDP/releases/tag/3.32.2 (consultadas el 4 de noviembre de 2026)
- Fuente vulnerable, libfreerdp/crypto/tls.c en el tag 3.32.1: https://raw.githubusercontent.com/FreeRDP/FreeRDP/3.32.1/libfreerdp/crypto/tls.c (consultada el 4 de noviembre de 2026)
- Catálogo Known Exploited Vulnerabilities de CISA, feed JSON, búsqueda de CVE-2026-121392: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (consultado el 4 de noviembre de 2026)
Etiquetas:
security, CVE, FreeRDP, RDP, TLS, certificate validation, man-in-the-middle, vulnerability