
Rapport et code pour CVE-2025-11492, CVE-2025-11493 - RCE dans ConnctWise Automate RMM via Adversary-in-the-Middle
Dans le cadre d'un test de pénétration, j'ai découvert plusieurs vulnérabilités dans l'agent ConnectWise Automate Remote Monitoring and Management (RMM). ConnectWise est utilisé par de nombreux fournisseurs de services gérés (MSP) pour gérer et surveiller les appareils clients. Ces vulnérabilités permettaient une exécution de code à distance si un attaquant parvenait à établir un Adversaire-au-Milieu réseau, ou pouvaient être utilisées pour une élévation de privilèges locale et une persistance furtive si un attaquant obtenait une exécution de code ou un accès physique à un appareil exécutant l'agent ConnectWise Automate.
Les vulnérabilités ont été signalées à ConnectWise le 20 août 2025. ConnectWise a attribué des identifiants CVE et a publié un correctif dans la version 2025.9 le 16 octobre 2025.
Bulletin ConnectWise :
Identifiants CVE :
2025.9 et publie le bulletin de sécurité et les CVE.J'ai apprécié la réactivité de ConnectWise et leur approche collaborative en matière de correction, ainsi que leur volonté de discuter de la meilleure façon de classer et de corriger les vulnérabilités.
Classer ces vulnérabilités a été un défi intéressant. Passer au HTTPS résout pratiquement tous les scénarios de ce rapport, mais il était évident qu'il s'agissait à l'origine d'un choix de conception (pour supporter HTTP) visant à améliorer la fiabilité de la communication agent-serveur. Le schéma de chiffrement semblait partiellement reconnaître/tenter d'atténuer le risque d'AiTM, mais n'était pas appliqué de manière cohérente. Approfondir la question a nécessité de déterminer si la faiblesse provenait de http lui-même ou de l'absence de chiffrement au-dessus de HTTP, ainsi que de la prévention du rejeu, de la validation des plugins, etc., qui constituaient toutes leurs propres vulnérabilités. À un moment donné, ConnectWise envisageait 5 CVE ou plus pour différents aspects des vulnérabilités.
De plus, la portée et le vecteur d'attaque changeaient selon que la vulnérabilité était considérée du point de vue d'un AiTM (exemple : Wi-Fi de café) ou d'une élévation de privilèges locale/accès physique. Une autre approche aurait été de considérer chaque scénario comme une vulnérabilité distincte, par exemple AiTM RCE, LPE, prise de contrôle de persistance, etc.
Un dernier enseignement : même en 2025, nous avons toujours du mal à partager des fichiers efficacement :D (la sécurité des e-mails n'aimait pas que j'envoie des fichiers .dll ou des .zip les contenant).
Ce rapport est publié suite à la publication par ConnectWise d'un correctif et à la divulgation des CVE, et avec leur accord qu'une telle divulgation ne nuit pas à leurs utilisateurs. De plus, je pense que la divulgation publique de ces vulnérabilités et de leurs atténuations aidera d'autres fournisseurs et professionnels de la sécurité à mieux comprendre et atténuer les risques à la fois dans ConnectWise Automate et dans d'autres systèmes RMM.
Le contenu est destiné uniquement à des fins légitimes de recherche en sécurité autorisée et d'éducation. Toute utilisation non autorisée de ces informations pour compromettre des systèmes, réseaux ou données est illégale et contraire à l'éthique. Le contenu est fourni "en l'état" et sans garantie d'aucune sorte. Le(s) auteur(s) décline(nt) toute responsabilité pour tout dommage résultant de l'utilisation ou de la mauvaise utilisation de ces informations.
Si vous utilisez ce code ou ces informations pour des recherches ultérieures, pratiquez une divulgation responsable en signalant toute vulnérabilité découverte au(x) fournisseur(s) concerné(s).
En plus du rapport ci-dessous, ce dépôt contient un code PoC pour démontrer les vulnérabilités. Voir automate_server/README.md pour les détails sur l'implémentation du faux serveur et les instructions d'utilisation.
Ce code pourrait également être utilisé pour effectuer des recherches de sécurité (éthiques) supplémentaires sur ConnectWise Automate.
Le rapport suivant (ou une version proche), ainsi que le code Python PoC de ce dépôt, a été fourni à ConnectWise, avec les atténuations recommandées.
La section des atténuations supprimées détaille davantage les modifications qui pourraient être apportées à l'agent Automate pour le renforcer de plusieurs manières contre ces vulnérabilités.
Comme certaines de ces modifications sont encore à l'étude par ConnectWise, cette section a été supprimée de cette divulgation publique.
L'agent ConnectWise Automate Remote Monitoring and Management (RMM) (testé sur la dernière version d'août 2025, chaîne de version 250.252) est vulnérable à une exécution de code à distance basée sur le réseau dans certaines configurations. Si l'agent est configuré pour utiliser un transport HTTP non chiffré (soit principalement, soit comme repli) pour son Server Address et qu'un attaquant peut effectuer une attaque d'adversaire-au-milieu (AiTM), alors il peut exécuter du code à distance en tant que SYSTEM. Cette configuration a été observée dans la nature chez plusieurs fournisseurs de services gérés (MSP).
L'exploitation est également possible si l'attaquant obtient un accès physique à l'appareil en tant que non-administrateur ou peut autrement connecter l'appareil à un réseau contrôlé par l'attaquant (c'est-à-dire que la vulnérabilité peut être utilisée comme une élévation de privilèges locale). Bien qu'Automate utilise un système de chiffrement pour chiffrer et valider la plupart des commandes RMM, son système de plugins manque de protection adéquate et reste susceptible à l'exécution de code à distance.
En implémentant un serveur personnalisé imitant le serveur de contrôle d'Automate, l'agent Automate peut être amené à télécharger et exécuter un plugin malveillant.
L'agent compromis peut également servir de forme de persistance intéressante. En utilisant la RCE pour extraire les clés de chiffrement symétriques de l'agent, le serveur personnalisé peut envoyer des commandes arbitraires à l'agent en utilisant le canal RMM standard. Dans le scénario AiTM, cela permet à l'attaquant d'exécuter des commandes RMM arbitraires, y compris l'extraction de fichiers, le dump de mots de passe, l'exécution de commandes et la modification de la configuration. L'attaquant pourrait également modifier le Server Address du RMM pour pointer vers son propre serveur, obtenant ainsi une persistance furtive même après l'AiTM. Alternativement, la RCE peut être exploitée pour exécuter directement des commandes au niveau du système.
Server Address incluant un point de terminaison http://. Des configurations vulnérables ont été observées chez deux MSP distincts (les domaines et IP réels des MSP ont été remplacés) :
https://automate.msp-one.com|http://automate.msp-one.comhttps://msp.msp-two.com|http://msp.msp-two.com|http://12.346.6.78http:// est utilisé comme repli si la connexion https:// échoue, mais un attaquant peut simuler cela en bloquant https.L'agent Automate peut être configuré pour utiliser une URL http:// comme adresse serveur. Cette configuration est probablement définie par le script/package d'installation, mais peut être vérifiée en consultant la clé de registre HKLM\SOFTWARE\LabTech\Service\Server Address.

L'utilisation conjointe des points de terminaison HTTPS et HTTP, séparés par un tube |, garantit qu'en théorie, si la connexion HTTPS rencontre un problème, l'agent Automate reviendra à la connexion HTTP.
Si un attaquant obtient un accès Adversaire-au-Milieu (AiTM) au trafic réseau entre l'agent Automate et le serveur, il peut perturber intentionnellement la connexion HTTPS, la faisant revenir à HTTP. Cela permet à l'attaquant d'intercepter, surveiller et modifier le trafic échangé entre l'agent et le serveur. Un attaquant peut ensuite faire du proxy inverse du trafic vers https://automate.msp-one.com, résultant en un "fonctionnement normal" de l'agent, mais avec l'attaquant capable d'écouter les données. De plus, il peut injecter ou modifier des réponses, voire configurer un serveur Automate complètement frauduleux qui répond aux requêtes de l'agent Automate.
Ceci a été réalisé en configurant un point d'accès Wi-Fi "malveillant" (hostapd, dnsmasq, IP forwarding + NAT) et en utilisant des règles iptables pour la redirection de trafic et un script mitmproxy pour le proxy inverse.

D'autres données sensibles, y compris les programmes en cours d'exécution, la configuration réseau complète et les chemins de documents, ont parfois été observées dans les réponses de l'agent Automate.
iptables -t nat -A PREROUTING -i wlx90916440139a -p tcp --dport 80 -j REDIRECT --to-port 8080 iptables -A FORWARD -i wlx90916440139a -p tcp --dport 443 -j DROP
#### mitmproxy script:```python
# run with AiTMweb --mode transparent@8080 -s automate_AiTM.py
def request(flow: http.HTTPFlow):
if flow.request.pretty_host == 'automate.msp-one.com':
flow.request.url = 'https://automate.msp-one.com' + flow.request.path
Les solutions ZTNA peuvent légèrement compliquer cette interception, mais elles ont été contournées de manière fiable par d'autres scripts mitmproxy pour détecter et bloquer conditionnellement ZTNA, ce qui a entraîné un repli sur HTTP non tunnelé.
L'agent Automate utilise un protocole personnalisé au-dessus de HTTP pour communiquer avec le serveur.
L'agent RMM sur l'appareil envoie périodiquement des requêtes HTTP(S) au point de terminaison /LabTech/agent.aspx du serveur. Les types de requêtes pertinents sont :
Notez que la capitalisation incohérente des chemins n'est pas une faute de frappe ; c'est ainsi que l'agent Automate envoie ces requêtes. Tous les chemins de points de terminaison sont entourés de backticks pour plus de clarté.
/LabTech/Agent.aspx?DEPS et reçoit une réponse XML contenant une liste de fichiers de dépendance, leurs numéros de version et leurs sommes de contrôle./LabTech/Agent.Aspx?DepCheck=1&DependancyId=<id> et reçoit un fichier binaire obscurci contenant la dépendance déguisée en fichier image (vraisemblablement, l'obscurcissement vise à empêcher les filtres de contenu de bloquer le téléchargement)./LabTech/agent.aspx?<id>?c<CMD id>&<arg count> et reçoit une réponse personnalisée contenant des données spécifiques à la commande.InitialCommandRetrieve), renvoyer les résultats des commandes et effectuer le chat.Les « commandes distantes » sensibles sont souvent chiffrées à l'aide d'un schéma de chiffrement basé sur DES3 avec une dérivation de clé personnalisée à partir d'un mot de passe système et d'un mot de passe ordinateur prédéfinis. Sans les mots de passe corrects, un attaquant ne peut pas visualiser/injecter/modifier les commandes légitimes envoyées à l'agent ; cependant, les réponses aux commandes ne sont pas chiffrées et il a été observé qu'elles incluent fréquemment des données sensibles en texte clair, y compris les fichiers ouverts, les chemins, les noms d'utilisateurs, les programmes installés, etc.
La vérification des dépendances (/LabTech/Agent.aspx?DEPS) et les téléchargements (/LabTech/Agent.Aspx?DepCheck=1&DependancyId=<id>) ne sont ni chiffrés ni signés, ce qui signifie qu'un attaquant peut modifier la réponse pour inclure des dépendances malveillantes qui seront téléchargées et chargées par l'agent RMM. Si la vérification des dépendances est falsifiée pour inclure une somme de contrôle différente, l'agent RMM effectuera un nouveau téléchargement du fichier de dépendance non concordant et le chargera (à condition que le fichier nouvellement téléchargé corresponde à la somme de contrôle falsifiée). Les plugins sont des assemblages .NET qui implémentent une interface spécifique, et l'agent Automate chargera tout assemblage .NET correspondant à l'interface de plugin attendue, puis appellera l'interface du plugin, ce qui entraînera une exécution de code.
Il existe un deuxième niveau de validation pour les dépendances de plugins téléchargées, où l'agent envoie une commande cmdGetPlugins et valide également que la somme de contrôle correspond ici, mais cette commande n'utilise pas le schéma de chiffrement.
En utilisant dnSpy, il a été possible d'injecter du code malveillant dans le fichier ScreenConnectRemotePlugin.dll légitime utilisé par l'agent. Un serveur Automate personnalisé avec les fonctionnalités des commandes DEPS, DepCheck et cmdGetPlugins a été créé. Le faux serveur a reproduit les plugins et dépendances attendus, mais a modifié le hachage de ScreenConnectRemotePlugin.dll pour un hachage calculé du plugin modifié de manière malveillante. Le faux serveur a également fourni le ScreenConnectRemotePlugin.dll malveillamment modifié en réponse à la demande DepCheck (dans le format d'image factice obscurci) et a implémenté cmdGetPlugins également avec le hachage manipulé. Enfin, le script mitmproxy a été mis à jour pour diriger l'agent Automate vers le faux serveur au lieu du serveur légitime (HTTPS).
Lors de la connexion, le plugin malveillant est téléchargé et chargé par l'agent. Le code injecté exfiltre les mots de passe « système » et « ordinateur » vers le faux serveur. Les mots de passe system et computer sont stockés dans le registre mais sont décodés par une bibliothèque C# et native fortement obscurcie. Plutôt que d'extraire les valeurs du registre, le plugin patché utilise la réflexion pour obtenir les valeurs décodées, économisant ainsi l'effort de rétro-ingénierie de l'obscurcissement. Alternativement, tout le contenu de la clé de registre HKLM\SOFTWARE\LabTech\Service pourrait être exfiltré et utilisé avec une copie de l'agent RMM dans un environnement contrôlé pour extraire les mots de passe au moment de l'exécution à l'aide d'un débogueur .NET.


Alternativement, le plugin peut être utilisé pour exécuter directement des commandes au niveau système ; cependant, l'extraction des secrets a permis d'utiliser l'agent RMM lui-même comme canal de commande et de contrôle et a fait en sorte que le canal persistant ne soit pas détecté par l'EDR. La persistance peut être obtenue en mettant à jour l'Adresse du serveur vers un serveur contrôlé par l'attaquant, qui peut éventuellement transférer les commandes vers le serveur légitime pour éviter que l'appareil n'apparaisse comme manquant. Voir 3. Prise de contrôle du RMM (commandement et contrôle) pour plus de détails.
Il est attendu que même si la configuration par défaut ne comportait aucun plugin installé, un plugin vierge (correspondant aux interfaces C# requises pour les plugins) aurait pu être compilé et inséré dans les réponses DEPS et cmdGetPlugins pour le même effet.
Il a en outre été observé qu'une « commande distante » appelée UpdatePlugins peut être renvoyée par le serveur, déclenchant ainsi une mise à jour du plugin par l'agent. Comme les commandes n'incluent pas de protection contre la relecture, si un attaquant peut observer la commande envoyée par un serveur légitime, il peut la rejouer à d'autres agents pour déclencher la mise à jour. Cela permet d'exploiter la vulnérabilité RCE dans des scénarios supplémentaires, augmentant ainsi l'impact de cette vulnérabilité.
Il a également été observé que la mise à jour automatique n'était ni chiffrée ni signée. Une démonstration d'exécution de code à distance via la mise à jour automatique n'a pas été réalisée, mais on pense qu'elle est également vulnérable. Une modification malveillante de la mise à jour automatique serait plus difficile à effectuer discrètement et présente un risque plus élevé de casser l'agent Automate ; cependant, il est probable qu'une approche similaire d'injection de code .NET dans les exécutables/DLL fonctionnerait. Le processus de mise à jour automatique n'a pas été exploré plus avant car l'RCE par plugin était suffisante et jugée plus fiable.
Il est également possible que l'exploitation du processus de mise à jour automatique soit une solution viable à la limitation d'exploitabilité uniquement au redémarrage (par exemple, si une réponse pouvait être injectée ou rejouée pour déclencher une mise à jour automatique).
En utilisant l'approche de la section 2.1 pour extraire le mot de passe système et le mot de passe ordinateur, il a été possible de créer des charges utiles et des réponses de « commandes distantes » arbitraires. Plutôt que de faire de la rétro-ingénierie complète et de réimplémenter le schéma de dérivation de clé, il a été possible d'importer simplement LabTechCommonBase.dll et d'utiliser Utilities.LabTechHash.ComputeHash pour générer la clé DES3 à partir du « mot de passe ordinateur ». En utilisant la clé DES calculée et le IV codé en dur extrait de la DLL, il était alors possible de répondre avec des commandes arbitraires à l'agent RMM.```python
# IV extracted from decompiled LabTechSecurity.cs:
# this._initializationVector = new byte[] { 240, 3, 45, 29, 0, 76, 173, 59 };
iv = [240, 3, 45, 29, 0, 76, 173, 59]
# Helper functions for pythonnet, clr_loader, clr to load LabTechCommonBase.dll into python
labtech_net.setup_paths_and_references(str((Path(__file__).parent.parent / 'LTSvc').resolve()))
from LabTechCommonBase import Utilities
labtech_hash = Utilities.LabTechHash()
labtech_hash.ComputeHash(computer_password.encode('ascii')) # Use the exfiltrated computer password
byts = bytes(labtech_hash.GetDigestBytes())
cipher = DES3.new(byts, DES3.MODE_CBC, bytes(iv))
padded_data = pad(data.encode('utf-8'), DES3.block_size)
encrypted_data = cipher.encrypt(padded_data)
return base64.b64encode(encrypted_data).decode('utf-8')
Des gestionnaires pour divers types de « Commandes d'agent » ont été implémentés dans le faux serveur Automate, lui permettant d'envoyer des « Commandes à distance » à l'agent. Ces commandes peuvent être utilisées pour exécuter du code arbitraire sur l'appareil, comme télécharger et exécuter une charge utile, ou établir des tunnels réseau vers l'appareil (par exemple, configurer un proxy SOCKS qui pourrait être utilisé pour pivoter via l'accès ZTNA de l'appareil).
Pour un nettoyage immédiat à partir de la version 2.1, le serveur peut envoyer la commande chiffrée `UpdatePlugins` et fournir le plugin original légitime pour remplacer le plugin modifié malveillant. L'attaquant peut toujours interagir avec l'agent (en supposant que les mots de passe ont été extraits) et peut maintenir la persistance en mettant à jour l'`Server Address` vers un serveur contrôlé par l'attaquant.

L'exécution arbitraire de commandes a également été démontrée en utilisant la commande `Execute` pour lancer un ping, qui a été exécuté avec succès sur l'appareil dans un contexte administrateur. Cela peut être servi comme la commande `InitialCommandRetrieve` ou renvoyé en réponse à d'autres « commandes d'agent » / vérifications.```python
# Extract the RemoteCommandIDs enum from LabTechCommonBase.Constants using pythonnet
REMOTE_CMD_IDS = get_RemoteCommandIDs()
REMOTE_CMD_IDS_r = {str(v): k for k, v in REMOTE_CMD_IDS.items()}
# `register_cmd_handler` registers a command handler for the `/LabTech/agent.aspx?c<id>` endpoint.
# The string 'InitialCommandRetrieve' is mapped back to ID `36`.
# The full set of "Agent Command" and their IDs can be extracted from `LabTechCommonBase.Constants.modEnums.AgentCommandIDs`
@register_cmd_handler('InitialCommandRetrieve')
async def initial_command_retrieve(_req: dict) -> str:
cmd = '*!*'.join([
'202706506',
str(REMOTE_CMD_IDS_r['Execute']),
'!!!'.join([
'CMD.exe',
'/c ping -t -l 1337 192.168.20.2'
])
])
encrypted_cmd = encrypt(cmd)
return '|||'.join([
len_b64_gzip( # Simple helper to return '{len(data)}-{base64(gzip(data))}'
encrypted_cmd
),
# Generate common [latest-version, FILETIME(), queued command(s), MD5 hashes] encoding string expected in "agent command" responses
make_p2(),
])

L'utilisation de -l 1337 indique à ping d'utiliser une taille de charge utile de 1337 octets, ce qui est ensuite observé dans la deuxième capture d'écran (1337 octets de charge utile + 42 octets d'en-tête = 1379 octets au total) comme un « canary ».
Section retirée de la divulgation publique.