
Détecter en toute sécurité la contournement d'authentification de Veeam Service Provider Console CVE-2026-58073
Un détecteur sûr et non authentifié pour les vulnérabilités KB4893 de la Veeam Service Provider Console (publié le 04/08/2026). Il répond à une question par cible, avant toute authentification ou TLS : les correctifs KB4893 sont-ils présents sur cette console ?
La paire principale est une chaîne. CVE-2026-58073 (CVSS 9.5) permet à un pair réseau non authentifié d'usurper un agent de gestion connecté et de recevoir le véritable certificat de cet agent, car la poignée de main de l'agent décide de l'autorisation à partir du GUID que le pair a écrit dans son propre certificat. CVE-2026-58072 (CVSS 9.0) est une écriture de fichier arbitraire accessible une fois que vous détenez une identité d'agent. Enchaînées, elles constituent une exécution de code à distance non authentifiée sur la console qui gère les sauvegardes de chaque locataire. Le même avis corrige également CVE-2026-58071 (CVSS 8.2, API d'appliance proxyfiée en tant qu'administrateur du portail) et CVE-2026-58067 (CVSS 8.7, déni de service non authentifié par épuisement de la mémoire). CVE-2026-58073 et CVE-2026-58072 ont été signalées à Veeam via HackerOne ; l'avis ne nomme pas le rapporteur.
Ce script ne tente pas d'usurpation, ne demande pas de certificat et n'écrit aucun fichier. Il lit la génération de protocole annoncée par le routeur et rien d'autre.
Oui. Le détecteur est conçu pour une utilisation en production et en évaluation :
ConnectorSaveFilesChannelHostProxy.m_multiplexers. Aucun récepteur n'est enregistré, aucun canal ou
multiplexeur n'est construit, aucun enregistrement d'agent n'est touché. Une poignée de main de type
Receiver enregistrerait un nom ; cet outil n'en envoie jamais.ConnectionHub.log, chacune portant le nom de récepteur bf-probe-<uuid4> afin qu'un défenseur
puisse distinguer un scan d'une attaque. Les lignes exactes figurent ci-dessous.VULNERABLE qu'après avoir prouvé
qu'elle est un ConnectionHub VSPC (voir ci-dessous), de sorte qu'un service TCP silencieux ne peut pas
être confondu avec une console non corrigée.Par cible, l'outil ouvre deux connexions TCP et envoie une poignée de main ConnectionHub sur chacune,
nommant un récepteur bf-probe-<uuid4> qui n'existera pas.
Avec le --transport auto par défaut, une cible dont le transport n'est pas celui impliqué par son port
coûte une connexion supplémentaire : la sonde de mauvais transport est rejetée pendant la poignée de
main, avant qu'aucun nom de récepteur ne soit lu, et le bon transport est ensuite utilisé pour les deux
véritables sondes. Épinglez --transport direct ou --transport gateway pour le maintenir à exactement
deux connexions — utile si vous avez cité un nombre de connexions dans une demande de modification.
État modifié sur le serveur : aucun. Le chemin de code Connector effectue une recherche de
dictionnaire dans ChannelHostProxy.m_multiplexers, échoue et renvoie une erreur. Aucun récepteur n'est
enregistré, aucun multiplexeur ou canal n'est créé, aucune session TLS n'est négociée, aucun enregistrement
d'agent n'est touché. Cet outil n'envoie jamais de poignée de main de type Receiver, qui est celle qui
enregistrerait un nom.
Les entrées de journal sont écrites dans
%ProgramData%\Veeam\Veeam Availability Console\Log\Server\ConnectionHub.log. Textuellement depuis un
ConnectionHub 9.2.1.33875 en direct, avec horodatages et JSON de portée tronqués :
sonde 1 (version 6), builds corrigés et non corrigés :
[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",...}
sonde 2 (version 7), builds corrigés uniquement :
les mêmes trois lignes
sonde 2 (version 7), builds non corrigés :
[INFO] ChannelHostProxy: Accept connection begin
[WARN] ChannelHostProxy: Handshake failed. Reason:Unsupported client version "7"
[INFO] ChannelHostProxy: Accept connection end
Deux connexions, six lignes, aucune autre entrée et aucun changement d'état — confirmé sur un hôte en direct.
La chaîne littérale bf-probe- dans ConnectionHub.log identifie le trafic de cet outil, de sorte
qu'un défenseur peut l'attribuer et qu'une équipe de scan peut prouver ce qu'elle a envoyé. Modifiez
RECEIVER_PREFIX dans le source si vous avez besoin d'un marqueur différent.
Le routeur d'agents de gestion ConnectionHub lit une poignée de main client avant toute authentification
ou TLS, et Request.Read valide la version de protocole annoncée par le client par rapport à une plage
codée en dur. Le correctif a élargi cette plage dans le même build qui a corrigé les CVE :
| Build | Vérification | Accepte |
|---|---|---|
<= 9.2.1.33875 (vulnérable) | (uint)(versionByte - 3) <= 3 | 3, 4, 5, 6 |
>= 9.3.0.35057 (corrigé) | (uint)(versionByte - 3) <= 4 | 3, 4, 5, 6, 7 |
Ainsi, une poignée de main annonçant la version 7 est un discriminateur binaire propre. Le détecteur envoie deux sondes par cible, dans cet ordre pour une raison précise (la détection du transport peut en ajouter une troisième — voir ci-dessous) :
| Sonde | Annonce | Objectif |
|---|---|---|
| 1 | version 6 | Doit renvoyer Requested receiver not found, prouvant que la cible est réellement un ConnectionHub VSPC |
| 2 | version 7 | Une réponse signifie PATCHED ; le silence signifie VULNERABLE |
Sans la barrière de l'étape 1, le silence de la sonde 2 correspondrait également à tout service TCP silencieux sur Internet, et les pare-feu seraient signalés comme des consoles Veeam vulnérables.
Il fonctionne sur les deux chemins qu'utilise un agent de gestion :
| Transport | Port | Exposition |
|---|---|---|
| Direct vers le ConnectionHub | 9999 | généralement interne |
| Via une passerelle Veeam Cloud Connect | 6180 | exposé à Internet par conception |
Le chemin de la passerelle nécessite un prologue de relais que le chemin direct ne doit pas avoir, donc
la sonde 1 sert également de détection de transport. Avec le --transport auto par défaut, il essaie un
transport, et si la barrière d'empreinte ne passe pas, il essaie l'autre. Celui qui passe est
verrouillé, et la sonde 2 le réutilise — un fichier de cibles mixte ne nécessite aucune annotation
par hôte.
Le verrouillage est essentiel. Si la sonde 2 pouvait réessayer sur l'autre transport, le silence ne
serait plus attribuable à la vérification de version, mais seulement à « l'un des deux chemins d'octets
n'a pas répondu », ce qui est ainsi qu'un faux VULNERABLE est fabriqué.
Le transport essayé en premier est décidé par le port, et ce n'est pas cosmétique — les deux inadéquations échouent à des vitesses très différentes :
| Inadéquation | Comment l'extrémité distante la lit | Coût |
|---|---|---|
| Prologue de relais → hub direct | méta int16 de hostType 44 / versionByte 0, échoue à la vérification de plage de Request.Read | éliminé en un aller-retour |
| Poignée de main directe → passerelle | longueur de trame int32 de 1 012 729 346 | la passerelle attend des octets qui n'arrivent jamais ; brûle le délai d'attente complet |
Donc auto mène avec le service qui possède le port : passerelle d'abord sur 6180, direct partout
ailleurs. Cela maintient le cas courant à une seule tentative et l'inadéquation coûteuse hors du chemin
rapide. Une sonde qui ne parvient pas à ouvrir TCP du tout court-circuite sans essayer le second
transport, donc les hôtes morts dans un balayage large coûtent un délai d'attente, pas deux.
VULNERABLE signifie « les correctifs KB4893 ne sont pas présents », pas « c'est la 9.2.1.33875 ». Les
builds plus anciens que 9.2.1 partagent la même vérification de version, donc ils devraient signaler le
protocole 6 et être signalés vulnérables également (déduit du code, pas mesuré — voir Limites), mais
l'outil ne peut pas distinguer 9.2.1 de 9.1 ou 8.1. Confirmez le build exact dans l'interface de la
console si vous en avez besoin.
# hôte unique (TCP/9999 par défaut)
./cve_2026_58073_check.py vspc.example.com
# port explicite, plusieurs hôtes
./cve_2026_58073_check.py vspc.example.com:9999 10.0.0.5
# scanner une liste, une cible par ligne (commentaires '#' autorisés), sortie compacte
./cve_2026_58073_check.py -f targets.txt --brief
# sortie lisible par machine pour les pipelines
./cve_2026_58073_check.py -f targets.txt --json > results.json
# une passerelle Veeam Cloud Connect — le transport de relais est détecté automatiquement
./cve_2026_58073_check.py cc-gw.example.com:6180
# épingler le transport pour ignorer la détection (le port passe alors par défaut à 6180)
./cve_2026_58073_check.py --transport gateway cc-gw.example.com
# valider les codecs filaires sans accès réseau
./cve_2026_58073_check.py --self-test
| Option | Description |
|---|---|
targets | Un ou plusieurs HOST[:PORT] (le port par défaut est 9999, ou 6180 avec --transport gateway) |
-f, --targets-file FILE | Lire les cibles depuis un fichier (une par ligne ; commentaires #) |
--transport {auto,direct,gateway} | Comment atteindre le ConnectionHub. auto (par défaut) le détecte par cible ; gateway ajoute le prologue de relais Cloud Connect et définit le port par défaut sur 6180 |
-p, --port PORT | Remplacer le port par défaut |
--timeout SECS | Délai d'attente par sonde (par défaut : 8) |
--workers N | Cibles simultanées (par défaut : 16) ; la sortie reste dans l'ordre d'entrée |
-b, --brief | Une seule ligne alignée par cible — idéal pour scanner de nombreux hôtes |
--json | Émettre du JSON structuré, y compris chaque sonde envoyée par cible |
--no-color | Désactiver la sortie colorée (respecte également NO_COLOR et les non-TTY) |
--self-test | Valider les codecs filaires .NET et quitter ; aucun accès réseau |
Une console non corrigée (la sortie par défaut sur deux lignes). Le marqueur [!] et VULNERABLE
s'affichent en rouge sur un 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)
Une console corrigée :
$ ./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)
Une cible passerelle, avec le transport de relais auto-détecté. Le suffixe (gateway) nomme le
transport sur lequel le verdict a été atteint :
$ ./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)
Balayage d'un parc, une ligne alignée par hôte (--brief). Le statut de sortie est 1 si un hôte
est VULNERABLE, sinon 0 — pratique dans les 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
Sortie lisible par machine pour les pipelines (--json). Chaque sonde est incluse par cible, de
sorte qu'un résultat peut être redérivé à partir des preuves plutôt que d'être cru. transport est celui
sur lequel le verdict a été atteint, et chaque sonde porte le transport qu'elle a utilisé — donc une
cible auto-détectée montre également la tentative rejetée :
$ ./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": ""
}
]
}
]
| Verdict | Étiquette de raison | Signification |
|---|---|---|
VULNERABLE | protocol-7-rejected | ConnectionHub VSPC confirmé qui accepte le protocole 6 et rejette le 7. Les correctifs KB4893 sont absents (<= 9.2.1.33875). |
PATCHED | protocol-7-accepted | ConnectionHub VSPC confirmé qui accepte le protocole 7. Les correctifs KB4893 sont présents (>= 9.3.0.35057). |
UNAFFECTED | not-vspc | TCP accepté mais n'a pas répondu à une poignée de main ConnectionHub valide sur aucun transport essayé, donc ce n'est pas un ConnectionHub VSPC. |
INCONCLUSIVE | unexpected-reply | A répondu à la sonde d'empreinte avec autre chose que Requested receiver not found. |
INCONCLUSIVE | inconclusive-discriminator | A passé la barrière d'empreinte, puis a répondu à la sonde de version 7 d'une manière qui n'est ni un succès ni un échec — ou cette seconde connexion a échoué complètement. Réessayez. |
ERROR | unreachable | Impossible de se connecter, ou la passerelle Cloud Connect a refusé le prologue de relais sur le premier transport essayé (ce qui sous auto signifie toute cible sur le port 6180). |
| Code | Signification |
|---|---|
0 | Aucune cible n'était VULNERABLE |
1 | Au moins une cible est VULNERABLE |
2 | Erreur d'utilisation (arguments invalides / fichier de cibles illisible) |
--transport auto ; épinglez --transport direct pour garder le chemin non testé hors d'un balayage.VULNERABLE concerne l'état des correctifs, pas de savoir si
quelqu'un a exploité la console. L'exploitation laisse ses propres traces dans les journaux de la
console sur les builds corrigés et non corrigés ; recherchez-les séparément.Mettez à niveau vers Veeam Service Provider Console 9.3.0.35057 ou ultérieur (KB4893). Les quatre problèmes sont corrigés dans ce seul build, et il n'y a pas de rétroportage 9.2.x, donc la remédiation est une mise à niveau de version plutôt qu'un correctif.
Deux choses que la mise à niveau ne fait pas. Elle ne restreint pas qui peut atteindre TCP/9999, qui ne devrait répondre qu'aux sous-réseaux où vivent vos agents de gestion. Et elle ne révoque pas un certificat d'agent que la console a déjà émis, y compris un émis à un attaquant pendant qu'elle n'était pas corrigée. Si vous trouvez des preuves d'exploitation, ouvrez un dossier de support Veeam pour obtenir des conseils sur les certificats d'agent compromis : leur rotation n'est pas une procédure documentée, et les certificats que vous pouvez gérer dans le portail ne sont pas l'AC qui signe les certificats d'agent.
Ce code est distribué sous une licence MIT.
L'utilisation de cet outil pour attaquer des cibles sans consentement mutuel préalable est illégale. Il est de la responsabilité de l'utilisateur final de respecter toutes les lois locales, étatiques et fédérales applicables. Les développeurs n'assument aucune responsabilité et ne sont pas responsables de toute utilisation abusive ou de tout dommage causé par ce programme.