
Detectar de forma segura la omisión de autenticación de Veeam Service Provider Console CVE-2026-58073
Un detector seguro y no autenticado para las vulnerabilidades KB4893 en Veeam Service Provider Console (publicado el 2026-08-04). Responde una pregunta por objetivo, antes de cualquier autenticación o TLS: ¿están presentes las correcciones de KB4893 en esta consola?
El par principal es una cadena. CVE-2026-58073 (CVSS 9.5) permite que un par de red no autenticado suplante a un agente de gestión conectado y reciba el certificado real de ese agente, porque el handshake del agente decide la autorización a partir del GUID que el par escribió en su propio certificado. CVE-2026-58072 (CVSS 9.0) es una escritura arbitraria de archivos alcanzable una vez que tienes una identidad de agente. Encadenadas, son ejecución remota de código no autenticada en la consola que gestiona las copias de seguridad de cada inquilino. El mismo aviso también corrige CVE-2026-58071 (CVSS 8.2, API de appliance proxy como Portal Administrator) y CVE-2026-58067 (CVSS 8.7, DoS no autenticado por agotamiento de memoria). CVE-2026-58073 y CVE-2026-58072 fueron reportados a Veeam a través de HackerOne; el aviso no menciona al reportero.
Este script no intenta suplantación, no solicita un certificado ni escribe archivos. Lee la generación de protocolo anunciada por el router y nada más.
Sí. El detector está diseñado para uso en producción y evaluación:
Connector que nombra un receptor que no existirá. No se negocia ninguna sesión TLS, no se
presenta ningún certificado y nunca se llama.SaveFilesChannelHostProxy.m_multiplexers. No se registra ningún receptor, no se construye
ningún canal o multiplexor, no se toca ningún registro de agente. Un handshake de tipo Receiver
sí registraría un nombre; esta herramienta nunca envía uno.ConnectionHub.log, cada una con el nombre de receptor bf-probe-<uuid4> para que un defensor pueda
distinguir un escaneo de un ataque. Las líneas exactas están abajo.VULNERABLE después de
demostrar que es un VSPC ConnectionHub (ver abajo), por lo que un servicio TCP silencioso no puede
confundirse con una consola sin parchear.Por objetivo, la herramienta abre dos conexiones TCP y envía un handshake de ConnectionHub en cada una,
nombrando un receptor bf-probe-<uuid4> que no existirá.
Con el --transport auto predeterminado, un objetivo cuyo transporte no es el implicado por su puerto
cuesta una conexión extra: la sonda de transporte incorrecto se rechaza durante el handshake, antes de
que se lea cualquier nombre de receptor, y luego se usa el transporte correcto para ambas sondas reales.
Fija --transport direct o --transport gateway para mantenerlo en exactamente dos conexiones — vale la
pena si has cotizado un número de conexiones en una solicitud de cambio.
Estado cambiado en el servidor: ninguno. La ruta de código Connector realiza una búsqueda en el
diccionario de ChannelHostProxy.m_multiplexers, falla y devuelve un error. No se registra ningún
receptor, no se crea ningún multiplexor o canal, no se negocia ninguna sesión TLS, no se toca ningún
registro de agente. Esta herramienta nunca envía un handshake de tipo Receiver, que es el que
registraría un nombre.
Las entradas de registro se escriben en
%ProgramData%\Veeam\Veeam Availability Console\Log\Server\ConnectionHub.log. Textualmente de un
ConnectionHub 9.2.1.33875 en vivo, con marcas de tiempo y JSON de alcance recortados:
sonda 1 (versión 6), tanto builds parcheados como sin parchear:
[INFO] ChannelHostProxy: Accept connection begin {"RemoteEndPoint":"<ip>:<port>","Line":"1"}
[INFO] ChannelHostProxy: Accept connection end {"RemoteEndPoint":"<ip>:<port>","Line":"2",...}
[WARN] ChannelHostProxy: Cannot connect transmitter. Requested receiver not found
(receiver name:bf-probe-<uuid>) {"RemoteEndPoint":"<ip>:<port>","Line":"3",...}
sonda 2 (versión 7), solo builds parcheados:
las mismas tres líneas
sonda 2 (versión 7), builds sin parchear:
[INFO] ChannelHostProxy: Accept connection begin
[WARN] ChannelHostProxy: Handshake failed. Reason:Unsupported client version "7"
[INFO] ChannelHostProxy: Accept connection end
Dos conexiones, seis líneas, sin otras entradas y sin cambio de estado — confirmado en un host en vivo.
La cadena literal bf-probe- en ConnectionHub.log identifica el tráfico de esta herramienta, por lo
que un defensor puede atribuirlo y un equipo de escaneo puede demostrar lo que envió. Cambia
RECEIVER_PREFIX en el código fuente si necesitas un marcador diferente.
El router de agentes de gestión de ConnectionHub lee un handshake de cliente antes de cualquier
autenticación o TLS, y Request.Read valida la versión de protocolo anunciada por el cliente contra un
rango codificado. La corrección amplió ese rango en el mismo build que corrigió los CVE:
| Build | Comprobación | Acepta |
|---|---|---|
<= 9.2.1.33875 (vulnerable) | (uint)(versionByte - 3) <= 3 | 3, 4, 5, 6 |
>= 9.3.0.35057 (parcheado) | (uint)(versionByte - 3) <= 4 | 3, 4, 5, 6, 7 |
Así que un handshake que anuncia la versión 7 es un discriminador binario limpio. El detector envía dos sondas por objetivo, en este orden por una razón (la detección de transporte puede añadir una tercera — ver abajo):
| Sonda | Anuncia | Propósito |
|---|---|---|
| 1 | versión 6 | Debe devolver Requested receiver not found, demostrando que el objetivo realmente es un VSPC ConnectionHub |
| 2 | versión 7 | Una respuesta significa PATCHED; el silencio significa VULNERABLE |
Sin la compuerta de la etapa uno, el silencio en la sonda 2 también coincide con cualquier servicio TCP silencioso en internet, y los firewalls se reportarían como consolas Veeam vulnerables.
Funciona sobre ambas rutas que usa un agente de gestión:
| Transporte | Puerto | Exposición |
|---|---|---|
| Directo al ConnectionHub | 9999 | normalmente interno |
| A través de un gateway de Veeam Cloud Connect | 6180 | expuesto a internet por diseño |
La ruta del gateway necesita un prólogo de relay que la ruta directa no debe tener, por lo que la sonda 1
sirve también como detección de transporte. Con el --transport auto predeterminado, prueba un
transporte y, si la compuerta de huella no pasa, prueba el otro. El que pase queda fijado, y la sonda
2 lo reutiliza — un archivo de objetivos mixto no necesita anotación por host.
El fijado es esencial. Si la sonda 2 pudiera reintentar en el otro transporte, el silencio ya no sería
atribuible a la comprobación de versión, solo a "una de dos rutas de bytes no respondió", que es cómo se
fabrica un VULNERABLE falso.
Qué transporte se prueba primero lo decide el puerto, y eso no es cosmético — los dos desajustes fallan a velocidades muy diferentes:
| Desajuste | Cómo lo lee el extremo remoto | Coste |
|---|---|---|
| Prólogo de relay → hub directo | meta int16 de hostType 44 / versionByte 0, falla la comprobación de rango de Request.Read | descartado en un round trip |
| Handshake directo → gateway | longitud de frame int32 de 1,012,729,346 | el gateway espera bytes que nunca llegan; agota el timeout completo |
Así que auto lidera con el servicio que posee el puerto: gateway primero en 6180, directo en el resto.
Eso mantiene el caso común en un solo intento y el desajuste costoso fuera de la ruta rápida. Una sonda
que no logra abrir TCP en absoluto se cortocircuita sin probar el segundo transporte, por lo que los
hosts muertos en un barrido amplio cuestan un timeout, no dos.
VULNERABLE significa "las correcciones de KB4893 no están presentes", no "este es 9.2.1.33875". Los
builds más antiguos que 9.2.1 comparten la misma comprobación de versión, por lo que deberían reportar
protocolo 6 y también ser reportados como vulnerables (inferido del código, no medido — ver
Limitaciones), pero la herramienta no puede separar 9.2.1 de 9.1 u 8.1. Confirma el build exacto en la
UI de la consola si lo necesitas.
# host único (TCP/9999 predeterminado)
./cve_2026_58073_check.py vspc.example.com
# puerto explícito, varios hosts
./cve_2026_58073_check.py vspc.example.com:9999 10.0.0.5
# escanear una lista, un objetivo por línea (se permiten comentarios '#'), salida compacta
./cve_2026_58073_check.py -f targets.txt --brief
# salida legible por máquina para pipelines
./cve_2026_58073_check.py -f targets.txt --json > results.json
# un gateway de Veeam Cloud Connect — el transporte de relay se detecta automáticamente
./cve_2026_58073_check.py cc-gw.example.com:6180
# fijar el transporte para omitir la detección (el puerto entonces pasa a 6180)
./cve_2026_58073_check.py --transport gateway cc-gw.example.com
# validar los códecs de red sin acceso a red
./cve_2026_58073_check.py --self-test
| Flag | Descripción |
|---|---|
targets | Uno o más HOST[:PORT] (el puerto por defecto es 9999, o 6180 con --transport gateway) |
-f, --targets-file FILE | Leer objetivos de un archivo (uno por línea; comentarios #) |
--transport {auto,direct,gateway} | Cómo llegar al ConnectionHub. auto (predeterminado) lo detecta por objetivo; gateway antepone el prólogo de relay de Cloud Connect y establece el puerto por defecto en 6180 |
-p, --port PORT | Sobrescribir el puerto predeterminado |
--timeout SECS | Timeout por sonda (predeterminado: 8) |
--workers N | Objetivos concurrentes (predeterminado: 16); la salida mantiene el orden de entrada |
-b, --brief | Una línea alineada por objetivo — ideal para escanear muchos hosts |
--json | Emitir JSON estructurado, incluyendo cada sonda enviada por objetivo |
--no-color | Deshabilitar salida de color (también respeta NO_COLOR y no-TTY) |
--self-test | Validar los códecs de red .NET y salir; sin acceso a red |
Una consola sin parchear (la salida predeterminada de dos líneas). El marcador [!] y VULNERABLE
se muestran en rojo en una TTY:
$ ./cve_2026_58073_check.py vspc.example.com
[!] vspc.example.com:9999: VULNERABLE [protocol-7-rejected]
ConnectionHub rejects protocol 7 but accepts 6, so the KB4893 fixes are absent (<= 9.2.1.33875)
Una consola parcheada:
$ ./cve_2026_58073_check.py patched.example.com
[+] patched.example.com:9999: PATCHED [protocol-7-accepted]
ConnectionHub accepts protocol 7, so the KB4893 fixes are present (>= 9.3.0.35057)
Un objetivo de gateway, con el transporte de relay auto-detectado. El sufijo (gateway) nombra el
transporte sobre el que se alcanzó el veredicto:
$ ./cve_2026_58073_check.py cc-gw.example.com:6180
[!] cc-gw.example.com:6180 (gateway): VULNERABLE [protocol-7-rejected]
ConnectionHub rejects protocol 7 but accepts 6, so the KB4893 fixes are absent (<= 9.2.1.33875)
Barriendo un entorno, una línea alineada por host (--brief). El estado de salida es 1 si algún
host es VULNERABLE, si no 0 — útil en scripts:
$ ./cve_2026_58073_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE vspc.example.com:9999 protocol-7-rejected
PATCHED patched.example.com:9999 protocol-7-accepted
UNAFFECTED fileserver.example.com:9999 not-vspc
ERROR unused.example.com:9999 unreachable
exit: 1
Salida legible por máquina para pipelines (--json). Cada sonda se incluye por objetivo, por lo que
un hallazgo puede re-derivarse de la evidencia en lugar de confiarse. transport es aquel sobre el que
se alcanzó el veredicto, y cada sonda lleva el transporte que usó — así que un objetivo auto-detectado
también muestra el intento rechazado:
$ ./cve_2026_58073_check.py vspc.example.com --json
[
{
"target": "vspc.example.com:9999",
"host": "vspc.example.com",
"port": 9999,
"transport": "direct",
"verdict": "VULNERABLE",
"reason": "protocol-7-rejected",
"detail": "ConnectionHub rejects protocol 7 but accepts 6, so the KB4893 fixes are absent (<= 9.2.1.33875)",
"protocol_version": 6,
"affected_cves": [
"CVE-2026-58073",
"CVE-2026-58072",
"CVE-2026-58071",
"CVE-2026-58067"
],
"probes": [
{
"version_byte": 6,
"transport": "direct",
"connected": true,
"responded": true,
"status": "Error",
"message": "Cannot connect transmitter. Requested receiver not found (receiver name:bf-probe-b7c40be0-e2e8-41dc-9fc3-cbbd7345954a)",
"error": ""
},
{
"version_byte": 7,
"transport": "direct",
"connected": true,
"responded": false,
"status": "",
"message": "",
"error": ""
}
]
}
]
| Veredicto | Etiqueta de razón | Significado |
|---|---|---|
VULNERABLE | protocol-7-rejected | VSPC ConnectionHub confirmado que acepta protocolo 6 y rechaza 7. Las correcciones de KB4893 están ausentes (<= 9.2.1.33875). |
PATCHED | protocol-7-accepted | VSPC ConnectionHub confirmado que acepta protocolo 7. Las correcciones de KB4893 están presentes (>= 9.3.0.35057). |
UNAFFECTED | not-vspc | Aceptó TCP pero no respondió a un handshake válido de ConnectionHub en ningún transporte probado, por lo que no es un VSPC ConnectionHub. |
INCONCLUSIVE | unexpected-reply | Respondió a la sonda de huella con algo distinto de Requested receiver not found. |
INCONCLUSIVE | inconclusive-discriminator | Pasó la compuerta de huella, pero luego respondió a la sonda de versión 7 de una manera que no es ni un pase ni un fallo — o esa segunda conexión falló por completo. Reintenta. |
ERROR | unreachable | No se pudo conectar, o el gateway de Cloud Connect rechazó el prólogo de relay en el primer transporte probado (que bajo auto significa cualquier objetivo en el puerto 6180). |
| Código | Significado |
|---|---|
0 | Ningún objetivo fue VULNERABLE |
1 | Al menos un objetivo es VULNERABLE |
2 | Error de uso (argumentos incorrectos / archivo de objetivos ilegible) |
--transport auto; fija
--transport direct para mantener la ruta no probada fuera de un barrido por completo.VULNERABLE habla del estado del parche, no de si alguien explotó
la consola. La explotación deja sus propias trazas en los registros de la consola tanto en builds
parcheados como sin parchear; busca esas por separado.Actualiza a Veeam Service Provider Console 9.3.0.35057 o posterior (KB4893). Los cuatro problemas se corrigen en ese único build, y no hay backport de 9.2.x, por lo que la remediación es una actualización de versión en lugar de un hotfix.
Dos cosas que la actualización no hace. No restringe quién puede alcanzar TCP/9999, que solo debería responder a las subredes donde viven tus agentes de gestión. Y no revoca un certificado de agente que la consola ya emitió, incluido uno emitido a un atacante mientras estaba sin parchear. Si encuentras evidencia de explotación, abre un caso de soporte de Veeam para obtener orientación sobre certificados de agente comprometidos: rotarlos no es un procedimiento documentado, y los certificados que puedes gestionar en el portal no son la CA que firma los certificados de agente.
Este código se distribuye bajo una licencia MIT.
El uso de esta herramienta para atacar objetivos sin consentimiento mutuo previo es ilegal. Es responsabilidad del usuario final cumplir todas las leyes locales, estatales y federales aplicables. Los desarrolladores no asumen responsabilidad alguna y no son responsables de ningún uso indebido o daño causado por este programa.