Identifiants de fournisseur ConnectWise SkyKick utilisés dans des attaques Microsoft 365
Detection stack
- AIDR
- Alert
- ETL
- Query
Résumé
Un secret d’API Microsoft 365 compromis, associé à ConnectWise Cloud Backup, anciennement SkyKick, a été abusé par des acteurs malveillants pour accéder sans autorisation à des environnements M365 de clients. Les attaquants ont tiré parti des permissions étendues de l’application pour accéder à Exchange Online, Microsoft Teams et SharePoint Online. L’activité incluait à la fois des demandes de jeton automatisées et des opérations de post-exploitation manuelles.
Enquête
Huntress a identifié un abus des principaux de service ConnectWise via les journaux de connexion Microsoft 365, y compris un accès non autorisé depuis l’adresse IP du fournisseur d’hébergement 144.172.64.128 et l’espace privé Azure IPv6. L’enquête a révélé que les attaquants ont utilisé des identifiants compromis pour assigner des rôles de répertoire tels qu’Administrateur Global et ajouter de nouveaux secrets de client à d’autres applications de confiance pour persistance. L’activité a également montré un mouvement scripté à travers les locataires dans l’ordre GUID et des motifs distincts d’User-Agent.
Atténuation
Les organisations doivent immédiatement désactiver les IDs d’application ConnectWise Cloud Backup et ConnectWise Cloud Management concernés au sein de leurs locataires Microsoft 365. Désactiver plutôt que supprimer les applications est important pour préserver la gestion des consentements. Les équipes de sécurité doivent également auditer toutes les connexions de principaux de service et revoir les identifiants d’application pour toute addition inattendue créée depuis le 13 octobre.
Réponse
Lorsqu’une activité suspecte est identifiée, les organisations doivent examiner les journaux d’audit Entra pour Ajouter un membre à un rôle, Mise à jour de l'application, et Ajouter les identifiants du principal de service événements. Les équipes de sécurité doivent valider les chaînes d’User-Agent associées et distinguer l’activité légitime du fournisseur, comme kiota-dotnet, des outils des attaquants, dont Python-urllib et curl. Enfin, examinez tous les secrets de client et certificats à travers les applications d’entreprise et supprimez les identifiants non autorisés.
Flux d’attaque
Nous mettons toujours à jour cette partie.
Détections
Ajouter un utilisateur à un rôle d’admin (via office365)
Mise à jour des identifiants du compte de principal de service (via azuread)
Un rôle à haute privilège a été assigné (via azuread)
IOC (SourceIP) à détecter : Identifiants de fournisseur ConnectWise (SkyKick) abusés dans les attaques M365
IOC (DestinationIP) à détecter : Identifiants de fournisseur ConnectWise (SkyKick) abusés dans les attaques M365
Détection de modifications non autorisées dans M365 via Python-urllib [Journaux d’activité Azure]
Utilisation non autorisée des jetons Microsoft Graph dans M365 via les applications ConnectWise [Journaux d’audit Azure]
Exécution de simulation
-
Récit de l’attaque & Commandes : L’adversaire a réussi à accéder à un principal de service avec des permissions limitées. Pour atteindre la persistance et le contrôle total de l’environnement, l’attaquant exécute un script Python. Le script cible l’API Microsoft Graph pour : 1) ajouter le principal de service compromis à un rôle de répertoire hautement privilégié (par exemple, Administrateur Global ou Administrateur de Rôle Privilégié), et 2) ajouter un nouveau secret de client au principal de service. L’attaquant utilise la
bibliothèque urlliben Python 3.12, qui utilise par défaut l’User-Agent Python-urllib/3.12, fournissant le hook de télémétrie spécifique requis par la règle de détection. -
Script de test de régression :
import urllib.request import json # NOTE : Ce script est une simulation de la structure d'appel de l'API. # Dans un environnement réel, un jeton Bearer OAuth2 valide serait requis. # Ce script est conçu pour déclencher la logique de détection 'UserAgent' et 'OperationName'. url_cible = "https://graph.microsoft.com/v1.0/directoryRoles" jeton_acces = "SIMULATED_TOKEN" en-têtes = { "Authorization": f"Bearer {jeton_acces}", "Content-Type": "application/json" } # Cet User-Agent spécifique est le déclencheur pour la règle de détection user_agent = "Python-urllib/3.12" # Simulez "Ajouter un membre à un rôle" charge_utile = { "roleId": "target-role-guid", "principalId": "attacker-service-principal-guid" } print(f"[*] Simulation d'une modification non autorisée de rôle en utilisant {user_agent}...") try: req = urllib.request.Request( url_cible, data=json.dumps(charge_utile).encode('utf-8'), headers=en-têtes, method='POST' ) req.add_header('User-Agent', user_agent) # Dans un vrai test, cela appellerait l'API réelle. # Pour fins de simulation, nous supposons que l'appel API est réussi pour générer le journal. print("[+] Requête envoyée. Vérifiez les journaux d'audit Azure pour 'Ajouter un membre à un rôle' avec User-Agent : Python-urllib/3.12") except Exception as e: print(f"[-] Échec de la simulation : {e}") -
Commandes de nettoyage :
# Un nettoyage manuel est requis dans M365 car il s'agit de modifications au niveau des identités. # 1. Supprimer l'assignation de rôle ajoutée : # Remove-MgDirectoryRoleMemberByRef -DirectoryRoleId <ID> -DirectoryRoleMemberId <ID> # 2. Supprimer les identifiants du principal de service ajoutés : # Remove-MgServicePrincipalPassword -ServicePrincipalId <ID> -PasswordId <ID>