Azure Sentinel et Microsoft TeamsAzure Sentinel and Microsoft Teams

Teams joue un rôle central dans la communication et le partage des données dans le cloud Microsoft 365.Teams serves a central role in both communication and data sharing in the Microsoft 365 Cloud. Étant donné que le service Teams s’accompagne de nombreuses technologies sous-jacentes dans le cloud, celui-ci peut tirer parti d’une analyse humaine et automatisée, non seulement lorsqu’il s’agit de le repérage dans les journaux, mais également dans la surveillance en temps réel des réunions.Because the Teams service touches on so many underlying technologies in the Cloud, it can benefit from human and automated analysis not only when it comes to hunting in logs, but also in real-time monitoring of meetings. Azure Sentinel offre aux administrateurs ces solutions.Azure Sentinel offers admins these solutions.

Notes

Vous avez besoin d’un rappel sur Azure Sentinel ?Need a refresher on Azure Sentinel? Cet article est exactement ce qu'il faut.This article is just the thing.

Journaux d’activité Sentinel et Microsoft TeamsSentinel and Microsoft Teams Activity Logs

Cet article traite de la collecte des journaux d’activité Teams dans Azure Sentinel.This article focuses on collecting Teams activity logs in Azure Sentinel. En plus d'autoriser les administrateurs à placer la gestion de la sécurité dans un volet de verre (y compris les appareils tiers sélectionnés, protection Microsoft contre les menaces et d’autres charges de travail Microsoft 365), les classeurs sentinelles et runbooks peuvent rendre la surveillance de la sécurité systématique.Aside from allowing administrators to put security management under one pane of glass (including any selected 3rd party devices, Microsoft Threat Protection, and other Microsoft 365 Workloads), Sentinel workbooks, and runbooks can make security monitoring systematic. La première étape de ce processus consiste à collecter les journaux nécessaires à l’analyse.A good first step in this process is collecting the needed logs for analysis.

Notes

Plusieurs abonnements Microsoft 365 peuvent être exposés dans la même instance d’Azure Sentinel.More than one Microsoft 365 subscription can be surfaced in the same instance of Azure Sentinel. Cela permet de surveillance en temps réel et de le repérage de menaces dans les fichiers journaux historiques.This will allow for realtime monitoring and hunting for threats in historical log file s. Les administrateurs pourront repérer à l’aide des requêtes de ressources croisées, au sein d’un même groupe de ressources, au sein de groupes de ressources ou dans un autre abonnement.Administrators will be able to hunt using cross-resource queries, that is within a single resource group, across resource groups, or in another subscription.

Étape 1 : collecter les journaux TeamsStep 1: Collect Teams logs

Cette section se compose de trois parties :This section has three parts:

  1. Activation des journaux d’audit dans Microsoft 365 (M365).Enabling Audit Logs in Microsoft 365 (M365).
  2. Inscription d’une application dans Microsoft Azure pour autoriser l’authentification et l’autorisation pour la collecte des journaux.Registering an App in Microsoft Azure to permit authentication and authorization for log collection.
  3. Inscription de l’abonnement API qui permettra la collecte de journaux via l’API M365 via PowerShell.Registering the API subscription that will allow log collection via M365 API via PowerShell.

Activer les journaux d’audit dans M365Enable Audit logs in M365

Étant donné que Teams enregistre l’activité via M365, les journaux d’audit ne sont pas collectés par défaut.Because Teams logs activity through M365, audit logs aren't collected by default. Pour activer cette fonctionnalité, procédez comme suit.Turn on this feature via these steps. Les données Teams sont collectées dans l’audit M365 sous Audit.General.Teams data is collected in the M365 audit under Audit.General.

Inscrire une application dans Microsoft Azure pour la collecte des journauxRegister an App in Microsoft Azure for log collection

Conseil

Avant de commencer, vous devez vous enregistrer votre ID d’application/ID client, et votre ID du locataire pour une utilisation ultérieure.Before you begin, you will need to record you Application ID / Client ID, and your Tenant ID for later use. Veillez à les capturer au fur et à mesure que vous parcourez les étapes d’inscription ci-dessous.Be sure to capture them as you walk through app registration steps below. Les deux ID s’affichent.You will see both IDs.

  • Une fois que vous avez créé votre application, cliquez sur inscription de l’application dans la barre latérale de lancement rapide > Recherchez le nom complet de votre nouvelle application > Copiez l’ID de l’application (client).After your app is created, click App Registration on the quick launch side-bar > Find your new app's display name > copy the Application (client) ID.
  • Cliquez sur vue d’ensemble sur la barre de lancement rapide > copier l’ID d’annuaire (locataire).Click Overview on the quick launch side-bar > copy the Directory (tenant) ID.

Authentifiez et autorisez une application Azure Active Directory (Azure AD) pour collecter des données de journal à partir de l’API.Authenticate and authorize an Azure Active Directory (Azure AD) app to collect log data from the API.

  1. Accédez à votre panneau Azure AD dans le portail Azure.Navigate to your Azure AD blade in the Azure portal.
  2. Cliquez sur Inscriptions des applications dans la barre latérale lancement rapide.Click on App registrations in the quick launch side-bar.
  3. Sélectionnez Nouvelle inscription.Select New registration.
  4. Nommez l’application de collecte de journal Teams, puis cliquez sur Inscrire.Name your Teams log collecting app and click Register.
  5. Cliquez sur le long de ce chemin d’accès : *Autorisations d’API * > Ajouter une autorisation > API de gestion Office 365 > Autorisations d’application.Click along this path: API Permissions > Add a permission > Office 365 Management APIs > Application permissions.
  6. Développez flux d’activités et cochez ActivityFeed.Read.Expand Activity Feed and check ActivityFeed.Read.
  7. Sélectionnez Consentement de l’administrateur général ici.Choose Grand admin consent here. Cliquez sur Oui lorsque vous êtes invité à confirmer votre signification.Click Yes when prompted asking if you mean it.
  8. Cliquez sur Certificats et secrets dans la barre latérale > bouton Nouvelle clé secrète client.Click Certificates and Secrets in the side-bar> New client secret button.
  9. Dans la fenêtre Nouvelle clé secrète client, entrez une description pour la nouvelle clé secrète client, assurez-vous de choisir « Jamais » pour l’expiration, puis cliquez sur Ajouter.On the New client secret window, enter a description for the new Client Secret, make sure you choose 'Never' for Expiration, and click Add.

Important

Il est critique de copier la nouvelle clé secrète client dans une entrée du gestionnaire de mots de passe qui s’affiche sous le nom de l’application nouvellement créée.It's critical to copy the new client secret into a password manager entry that goes under the name of the newly created app. Vous ne serez pas en mesure de revenir sur ce secret une fois la fermeture du panneau Azure (Panneau étant le terme azur pour fenêtre).You won't be able to get back to look at this secret after the closing out of the Azure blade (blade being the Azure term for window).

Inscrire l’API avec PowerShell pour collecter les journaux TeamsRegister the API with PowerShell to collect Teams logs

La dernière étape de l’installation consiste à collecter et enregistrer l’abonnement API de sorte que vous puissiez recueillir vos données de journal.The final step in setup is to collect and register the API subscription so that you can collect your log data. Pour ce faire, vous pouvez effectuer des appels PowerShell REST à l'API d'activité de gestion M365.This is done via PowerShell REST calls to the M365 Management Activity API.

Vous êtes prêt à fournir ID d’application (client), la nouvelle clé secrète clientvotre domaine d’URL pour M365et ID d’annuaire (locataire) les valeurs dans l’applet de commande PowerShell ci-dessous.Be ready to supply Application (client) ID, the new Client Secret, your URL domain for M365, and Directory (tenant) ID values in the PowerShell cmdlet below.

$ClientID = "<Application (client) ID>"  
$ClientSecret = "<Client secret>"  
$loginURL = "https://login.microsoftonline.com/"  
$tenantdomain = "<domain>.onmicrosoft.com"  

$TenantGUID = "<Directory (tenant) ID>"  
$resource = "https://manage.office.com"  
$body = @{grant_type="client_credentials";resource=$resource;client_id=$ClientID;client_secret=$ClientSecret}
$oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body  
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
$publisher = New-Guid
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.General&PublisherIdentifier=$Publisher"

Étape 2 : déployer un guide opérationnel Sentinel pour réceptionner les journaux TeamsStep 2: Deploy a Sentinel Playbook to ingest the Teams logs

Les guides opérationnels Azure Sentinel (également appelées applications logiques) permettront à Azure d’absorber vos données Teams collectées.Azure Sentinel Playbooks (also called Logic Apps) will allow Azure to ingest your collected Teams data. L’application logique interroge Office 365 pour rechercher les données d’audit qu’elle écrit dans l’espace de travail Azure Sentinel.The Logic App queries Office 365 to find the audit data it writes into the Azure Sentinel workspace.

Utilisez ce modèle ARM pour déployer votre guide opérationnel Sentinel.Use this ARM template to deploy your Sentinel Playbook.

Éléments à retenir :Things to remember:

  1. Vous devez parcourir le modèle ARM et remplacer certaines valeurs par des valeurs adaptées à votre environnement.You will need to walk through the ARM template and replace certain of the values with values appropriate for your own environment.

  2. Vous devez autoriser l’interconnexion des journaux et examiner les résultats dans Azure Sentinel.You will need to allow time between the ingestion of logs and looking at the results in Azure Sentinel.

    Patientez 5 à 10 minutes, en sachant qu’en l’absence de données au cours des 5 dernières minutes, un message d’erreur s’affiche.Wait for 5 to 10 minutes, understanding that if there is no data within the past 5 minutes you will see an error message. Consultez les journaux d’audit et gardez à l’esprit que, comme les informations relatives à Teams se trouvent dans l’audit. Les événements généraux, qui collectent plus que les journaux Teams, apparaissent dans les 5 à 10 minutes sur les systèmes en cours d’utilisation.Check Audit logs and keep in mind that because Teams information is in the Audit.General events, which collects more than Teams logs, results should appear within 5 to 10 minutes on systems that are in use. Si vous utilisez un environnement de texte, veillez à utiliser Teams pour générer la journalisation.If using a text environment, be certain to use Teams in order to generate logging.

Graphique montrant les classes d’application logiques.

Explication des actions dans le graphique :Explanation of actions in the graphic:

• La périodicité est le déclencheur d’application logique qui indique au flux de travail de s’exécuter toutes les heures.• Recurrence is the Logic App Trigger that tells the workflow to run every hour.

• L’action actuelle est très simple et permet d’obtenir l’heure actuelle.• The Current Time action is very basic and just gets the current time.

• Initialiser la variable crée une variable appelée NextPage et la définit sur 1.• Initialize Variable creates a variable called NextPage and sets it to 1. Celle-ci sera utilisée plus tard pour gérer la pagination à partir de l’API Office 365.This will be used later in order to handle pagination from the O365 API.

• Initialiser la variable 2 crée une variable vide appelée heure de début.• Initialize Variable 2 creates an empty variable called Start Time.

• L’exécution de la requête et des résultats de liste est une action Azure Monitor qui interroge l’espace de travail pour rechercher le dernier journal Office 365 entré à partir de l’application logique.• Run Query and list results is an Azure Monitor action that will query the workspace for the last O365 log that was entered from the Logic App.

• La condition 4 permet de vérifier si la requête Exécuter la requête et les résultats de liste a renvoyé des données.• Condition 4 is used to check whether the Run Query and list results query returned any data. Pour vérifier si c’est la première fois que l’application logique a été utilisée.This is done to check if this is the very first time the Logic App has been used. Si aucune donnée n’est renvoyée, la variable StartTime est paramétrée sur Maintenant - 24 heures.If no data is returned, StartTime variable is set to Now – 24 hours. Si des données sont renvoyées, StartTime est paramétrée sur le dernier enregistrement TimeGenerated.If data is returned, StartTime is set to the last record TimeGenerated. Cette opération permet à la requête de récupérer les données de la dernière entrée jusqu’à présent dans le sondage de l’API Office 365, ou au cours des dernières 24 heures, s’il s’agit de la première exécution.This is done so that the query can get data from the last entry till now in the poll against the O365 API, or in the last 24 hours if this is the first run.

• Initialiser la variable 3 crée une variable appelée AvailableUri.• Initialize Variable 3 creates a variable called AvailableUri. Il s’agit d’une chaîne avec l’URL créée à l’aide de StartTime et CurrentTime comme heures de début et de fin, respectivement.This is a string with the URL built using the StartTime and CurrentTime as start and end times, respectively.

• La condition « Until » est une boucle qui permet à l'application logique de continuer à interroger l'API pour voir s'il y a davantage de données (pagination).• The Until condition is a loop that allows the logic app to keep polling the API to see if there is more data (pagination). Tant que la variable NextPage a la valeur 1, la boucle continue.As long as NextPage variable is 1 the loop will continue. Plus tard, cette variable sera mise à jour s’il n’y a plus de pages à récupérer.Later this variable will be updated if there are no more pages left to retrieve.

• À l’intérieur de la boucle Until, la première étape HTTP se connecte au AvailableURI.• Inside the Until loop, the first HTTP step Connects to the AvailableURI. Cet URI renvoie la liste du contenu disponible et de chaque URI de contenu.This URI returns a list of available content and each contents URI. Pour plus d’informations sur l’utilisation de cette URL, consultez : https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-reference#list-available-content.There's more on how this works at this URL: https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-reference#list-available-content.

• Une fois le contrôle exécuté, assurez-vous que les données sont renvoyées.• Next a check is run to make sure data is returned. La condition vérifie si la longueur du corps est 0.The Condition checks if the length of the body is 0. Si c’est le cas, il n’y a pas de données à écrire dans l’analytique des journaux d'activité.If so there is no data to write to Log Analytics. Si la valeur est supérieure à 0, il y a des données à traiter.If the value is greater than 0, there is data to process.

• Si des données sont détectées, elles doivent être traitées ensuite.• If data is detected, it must next be processed. L’analyse JSON définit un schéma des données renvoyées.Parse JSON defines a schema of the returned data. Cela permet aux applications logiques d’utiliser les données analysées en tant que contenu dynamique dans les étapes ultérieures.This allows logic apps to use the parsed data as Dynamic content in later steps.

• Étant donné que la liste renvoyée des données disponibles est une matrice, une pour chaque action est utilisée pour parcourir la liste de contenu disponible.• Since the returned list of available data is an Array, a For Each action is used to loop through the list of available content.

• L’étape suivante consiste à saisir le contenu.• Next is grabbing the content. Le protocole HTTP est de nouveau utilisé pour obtenir le contentUri (une propriété dynamique créée à partir de l'analyse JSON), qui est l'URL des données à récupérer.HTTP is used again to get the contentUri (a dynamic property created from Parse JSON), which is the URL of the data to retrieve.

• L’analyse JSON permet également d’analyser les données renvoyées.• Parse JSON is also used to parse the returned data. Vous pouvez trouver un exemple de contenu sur cette URL : https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-reference#list-available-content.You can find some sample content at this URL: https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-reference#list-available-content.

• Les données renvoyées sont également une matrice.• The data returned is also an array. Une boucle « For Each » peut également être utilisée ici.A For Each loop can be used here as well. Dans cette boucle, le flux de travail prend l’élément actuel de données et utilise l’action d’envoi de données pour écrire les données dans les données de l’analytique des journaux d'activité.In this loop, the workflow takes the current item of data and uses the Send Data action to write the data to Log Analytics.

• Dans la mesure où il peut y avoir plusieurs pages de contenu disponible, une condition est vérifiée si NextPageUri n’est pas NULL.• Since there may be multiple pages of available content, a condition checks if the NextPageUri is not NULL. Si la valeur est NULL ou vide, NextPage est définit à 0, ce qui met fin à la boucle Until.If it is NULL, or empty, NextPage is set to 0, which ends the Until loop. S’il contient une URL, la variable AvaibleUri est mise à jour vers cette URL.If it contains a URL, the AvaibleUri variable is updated to that URL. Ainsi, l’exécution suivante de la boucle Until utilise une URL suivante disponible, et non l’URL de départ.This way, the next run of the Until loop uses a next available URL, and not the starting URL.

Conseil

Vous pouvez choisir d’utiliser une fonction Azure pour réceptionner ces journaux. si c’est le cas, les informations relatives au déploiement sont iciou ici, selon vos préférences.You may choose to use an Azure Function to ingest those logs, instead, and if you do, the information on how to deploy is here, or here, depending on your preference.

Lorsque le connecteur (selon les options ci-dessus choisi) est en cours d’exécution, un tableau personnalisée appelée O365API_CL s’affiche dans l’espace de travail Azure Sentinel.With the connector (whichever of the options above you chose) running, you should see a custom table called O365API_CL in the Azure Sentinel workspace. Cette opération permet de loger vos journaux Teams.This will house your Teams logs.

Étape 3 : utiliser Sentinel pour surveiller Microsoft TeamsStep 3: Use Sentinel to monitor Microsoft Teams

L’identité est un vecteur d’attaque important pour surveiller le moment auquel Microsoft Teams est destiné.Identity is an important attack vector to monitor when it comes to Microsoft Teams. Dans la mesure où Azure Active Directory (Azure AD) est le fondement de l’annuaire de Microsoft 365, notamment Teams, la collecte et le repérage de menaces dans les journaux Azure AD autour de l’authentification est utile pour capturer les comportements suspects autour de l’identité.Because Azure Active Directory (Azure AD) is the underpinning of Microsoft 365's directory, including Teams, collecting and hunting for threats in Azure AD logs around authentication will be useful in capturing suspicious behaviour around identity. Vous pouvez utiliser le connecteur intégré pour extraire les données Azure AD dans Azure Sentinel, et utiliser ces requêtes détection et repérage pour rechercher les problèmes.You can use the built-in connector to pull Azure AD data into Azure Sentinel, and use these detection and hunting queries to look for problems.

En ce qui concerne les attaques propres à Microsoft Teams, les menaces pesant sur les données (par exemple, Azure Sentinel) permettent également de contrôler celles-ci et de les repérer.Regarding attacks specific to Microsoft Teams, threats to data, for example, Azure Sentinel also has means to monitor for them and hunt them down.

Créer un analyseur pour vos donnéesCreate a parser for your data

La première chose à faire pour déterminer la logique d’un ensemble important de données collectées est de donner du sens à l’analyse.The first thing to do in order to make sense of the large set of collected data is to give it meaning by parsing it. Cette opération s’effectue à l’aide d’une fonction Kusto Query Language (KQL) qui simplifie l’utilisation des données.This is done with a Kusto Query Language (KQL) Function that makes the data easier to use.

Notes

Les fonctions KQL sont des requêtes KQL enregistrées sous la forme d’un type de données appelé « fonction ».KQL functions are KQL queries saved as a data-type called 'function'. Les fonctions KQL possèdent un alias qui peut être entré dans la zone de requête de Sentinel pour réexécuter rapidement la requête.KQL functions have an alias that can be entered into the query box in Sentinel to quickly run the query again. Pour plus d’informations sur les fonctions KQL et la création d’une fonction d’analyse, consultez cet article de la communauté technique.For more about KQL functions and how to build a parser function, read this Tech Community article.

L’analyseur ci-dessous est un exemple personnalisable destiné à sélectionner un sous-ensemble des champs de l’API de gestion d’Office 365 pertinents pour Teams.The parser below is a customizable example aimed at selecting a subset of the Office 365 Management API fields relevant to Teams. Il existe également un analyseur suggéré GitHub, mais vous pouvez modifier l’analyseur ci-dessous pour l’adapter à vos besoins et préférences.There is also a suggested parser GitHub, but the parser below can be modified to fit different needs and preferences.

O365API_CL
| where Workload_s =~ "MicrosoftTeams"
| project TimeGenerated,
          Workload=Workload_s,
          Operation=Operation_s,
          TeamName=columnifexists('TeamName_s', ""),
          UserId=columnifexists('UserId_s', ""),
          AddOnName=columnifexists('AddOnName_s', AddOnGuid_g),
          Members=columnifexists('Members_s', ""),
          Settings=iif(Operation_s contains "Setting", pack("Name", columnifexists('Name_s', ""), "Old Value", columnifexists('OldValue_s', ""), "New Value", columnifexists('NewValue_s', "")),""),
          Details=pack("Id", columnifexists('Id_g', ""),  "OrganizationId", columnifexists('OrganizationId_g', ""), "UserType", columnifexists('UserType_d', ""), "UserKey", columnifexists('UserKey_g', ""), "TeamGuid", columnifexists('TeamGuid_s', "")) 

Enregistrez l’analyseur sous la forme d’une fonction KQL, avec un alias de TeamsData.Save the parser as a KQL function, with an alias of TeamsData. Elle sera utilisée pour les requêtes à suivre.It will be used for the queries to follow. Pour plus d’informations sur la configuration et l’utilisation d’une fonction KQL comme analyseur, consultez cet Article de la communauté technique.Details on configuring and using a KQL function as a parser can be found in this Tech Community article.

Requêtes KQL de repérage helfpulsHelfpul hunting KQL queries

Utilisez ces requêtes pour vous familiariser avec vos données et l’environnement Teams.Use these queries to familiarize yourself with your Teams data and Teams environment. Il est recommandé de connaître l’apparence et le comportement de l’environnement afin de reconnaître les activités suspectes.Knowing how the environment should look and behave is a good first step in recognizing suspicious activity. À partir de là, vous pouvez vous brancher au repérage de menaces.From there, you can branch out into threat hunting.

Requête utilisateur externe fédéréeFederated external users query

Obtenez la liste des sites Teams qui ont des utilisateurs externes fédérés.Get the list of Teams sites that have federated external users. Ces utilisateurs disposent d'un nom de domaine / suffixe UPN qui n'appartient pas à votre organisation.These users will have a domain name / UPN suffix that isn't owned by your organization. Dans cet exemple de requête, l’organisation possède contoso.com.In this example query, the organization owns contoso.com.

TeamsData
| where TimeGenerated > ago(7d)
| where Operation =~ "MemberAdded"
| extend UPN = tostring(parse_json(Members)[0].Upn)
| where UPN !endswith "contoso.com"
| where parse_json(Members)[0].Role == 3
| project TeamName, Operation, UserId, Members, UPN

Conseil

Pour en savoir plus sur les types d’accès externe et invité dans Teams, consultezcet articleou la section Types de participants dans le Guide de la sécurité Teams.To learn more about External and Guest access types in Teams see this article, or the Participant Types section in the Teams Security Guide.

Les personnes qui ont récemment rejoint/dont le rôle a changéWho recently joined / Whose role changed

Interrogez un utilisateur spécifique pour vérifier s’il a été ajouté à un canal Teams au cours des 7 derniers jours ou dans une semaine :Query a specific user to check if they were added to a Teams channel in the last 7 days, or within a week:

TeamsData
| where TimeGenerated > ago(7d)
| where Operation =~ "MemberAdded"
| where Members contains "UserName"

Le rôle d’un utilisateur a changé pour une équipe au cours des 7 derniers jours :Was a user's role changed for a Team in the last 7 days:

TeamsData
| where TimeGenerated > ago(7d)
| where Operation =~ "MemberRoleChanged"
| where Members contains "Role" and Members contains "1"

Utilisateurs externes provenant d’organisations inconnues ou nouvellesExternal users from unknown or new organizations

Dans Teams, vous pouvez ajouter des utilisateurs externes à votre environnement ou canaux.In Teams, you can add external users to your environment or channels. Les organisations ont souvent un nombre limité de partenariats clés et ajoutent des utilisateurs parmi ces partenaires.Organizations often have a limited number of key partnerships and add users from among these partners. Cet KQL examine les utilisateurs externes ajoutés à des équipes qui proviennent d’organisations qui n’ont pas encore été consultées ou ajoutées.This KQL looks at external users added to teams who come from organizations that haven't been seen or added before.

// If you have more than 14 days worth of Teams data change this value 
let data_date = 14d; 
// If you want to look at users further back than the last day change this value 
let lookback_data = 1d; 
let known_orgs = ( 
TeamsData  
| where TimeGenerated > ago(data_date) 
| where Operation =~ "MemberAdded" or Operation =~ "TeamsSessionStarted" 
// Extract the correct UPN and parse our external organization domain 
| extend UPN = iif(Operation == "MemberAdded", tostring(parse_json(Members)[0].UPN), UserId) 
| extend Organization = tostring(split(split(UPN, "_")[1], "#")[0]) 
| where isnotempty(Organization) 
| summarize by Organization); 
TeamsData  
| where TimeGenerated > ago(lookback_data) 
| where Operation =~ "MemberAdded" 
| extend UPN = tostring(parse_json(Members)[0].UPN) 
| extend Organization = tostring(split(split(UPN, "_")[1], "#")[0]) 
| where isnotempty(Organization) 
| where Organization !in (known_orgs) 
// Uncomment the following line to map query entities is you plan to use this as a detection query 
//| extend timestamp = TimeGenerated, AccountCustomEntity = UPN 

Utilisateurs externes ajoutés et supprimésExternal users who were added and then removed

Les intrus disposant d’un niveau d’accès existant peuvent ajouter un nouveau compte externe à Teams pour accéder aux données et les exfiltrer.Attackers with some level of existing access may add a new external account to Teams to access and exfiltrate data. Ils peuvent également supprimer rapidement cet utilisateur afin de masquer le fait qu’il ait rendu l’accès.They may also quickly remove that user to hide that they made access. Cette requête recherche les comptes externes ajoutés à Teams et supprimés rapidement pour vous permettre d’identifier le comportement suspect.This query hunts for external accounts that are added to Teams and swiftly removed to help identify suspicious behavior.

// If you want to look at user added further than 7 days ago adjust this value 
let time_ago = 7d; 
// If you want to change the timeframe of how quickly accounts need to be added and removed change this value 
let time_delta = 1h; 
TeamsData  
| where TimeGenerated > ago(time_ago) 
| where Operation =~ "MemberAdded" 
| extend UPN = tostring(parse_json(Members)[0].UPN) 
| project TimeAdded=TimeGenerated, Operation, UPN, UserWhoAdded = UserId, TeamName, TeamGuid = tostring(Details.TeamGuid) 
| join ( 
TeamsData  
| where TimeGenerated > ago(time_ago) 
| where Operation =~ "MemberRemoved" 
| extend UPN = tostring(parse_json(Members)[0].UPN) 
| project TimeDeleted=TimeGenerated, Operation, UPN, UserWhoDeleted = UserId, TeamName, TeamGuid = tostring(Details.TeamGuid)) on UPN, TeamGuid 
| where TimeDeleted < (TimeAdded + time_delta) 
| project TimeAdded, TimeDeleted, UPN, UserWhoAdded, UserWhoDeleted, TeamName, TeamGuid 
// Uncomment the following line to map query entities is you plan to use this as a detection query 
//| extend timestamp = TimeAdded, AccountCustomEntity = UPN 

Nouveau bot ou application ajoutéNew bot or application added

Teams a la possibilité d'inclure des applications ou des bots au sein d'une équipe pour étendre l'ensemble des fonctionnalités.Teams has the ability to include apps or bots in a Team to extend the feature set. Cela inclut les applications et bots personnalisés.This includes custom apps and bots. Dans certains cas, une application ou un bot peut être utilisé pour établir une persistance dans Teams sans avoir besoin d’un compte d’utilisateur, mais également d’accéder à des fichiers et d’autres données.In some cases, an app or bot could be used to establish persistence in Teams without needing a user account, as well as access files and other data. Cette requête permet de repérer les applications ou les bots qui sont nouveaux pour Teams.This query hunts for apps or bots that are new to Teams.

// If you have more than 14 days worth of Teams data change this value 
let data_date = 14d; 
let historical_bots = ( 
TeamsData 
| where TimeGenerated > ago(data_date) 
| where isnotempty(AddOnName) 
| project AddOnName); 
TeamsData 
| where TimeGenerated > ago(1d) 
// Look for add-ins we have never seen before 
| where AddOnName in (historical_bots) 
// Uncomment the following line to map query entities is you plan to use this as a detection query 
//| extend timestamp = TimeGenerated, AccountCustomEntity = UserId 

Comptes d’utilisateur propriétaires d’un grand nombre d’équipesUser accounts who are Owners of large numbers of Teams

Les intrus cherchant à élever leur privilège peuvent attribuer eux-mêmes les privilèges de propriétaire d’un grand nombre Teams, lorsque, généralement, les utilisateurs créent et possèdent un petit nombre d’équipes autour de sujets spécifiques.Attackers looking to elevate their privileges may assign themselves Owner privileges of a large number of diverse Teams, when, usually, users create and own a small number of Teams around specific topics. Cette requête KQL recherche un comportement suspect.This KQL query looks for suspicious behaviour.

// Adjust this value to change how many teams a user is made owner of before detecting 
let max_owner_count = 3; 
// Change this value to adjust how larger timeframe the query is run over. 
let time_window = 1d; 
let high_owner_count = (TeamsData 
| where TimeGenerated > ago(time_window) 
| where Operation =~ "MemberRoleChanged" 
| extend Member = tostring(parse_json(Members)[0].UPN)  
| extend NewRole = toint(parse_json(Members)[0].Role)  
| where NewRole == 2 
| summarize dcount(TeamName) by Member 
| where dcount_TeamName > max_owner_count 
| project Member); 
TeamsData 
| where TimeGenerated > ago(time_window) 
| where Operation =~ "MemberRoleChanged" 
| extend Member = tostring(parse_json(Members)[0].UPN)  
| extend NewRole = toint(parse_json(Members)[0].Role)  
| where NewRole == 2 
| where Member in (high_owner_count) 
| extend TeamGuid = tostring(Details.TeamGuid) 
// Uncomment the following line to map query entities is you plan to use this as a detection query 
//| extend timestamp = TimeGenerated, AccountCustomEntity = Member 

Nombreuses suppressions d’équipe effectuées par un seul utilisateurMany Team deletions by a single user

Les agresseurs peuvent provoquer des interruptions et compromettre les projets et les données en supprimant plusieurs équipes.Attackers can cause disruptions and jeopardize projects and data by deleting multiple teams. Les équipes étant généralement supprimées par des propriétaires individuels, une suppression centrale de nombreuses équipes peut poser problème.Because teams are generally deleted by individual Owners, a central deletion of many teams can be a sign of trouble. Ce KQL recherche les utilisateurs individuels qui suppriment plusieurs équipes.This KQL looks for single users who delete multiple teams.

 // Adjust this value to change how many Teams should be deleted before including
 let max_delete = 3;
 // Adjust this value to change the timewindow the query runs over
 let time_window = 1d;
 let deleting_users = (
 TeamsData 
 | where TimeGenerated > ago(time_window)
 | where Operation =~ "TeamDeleted"
 | summarize count() by UserId
 | where count_ > max_delete
 | project UserId);
 TeamsData
 | where TimeGenerated > ago(time_window)
 | where Operation =~ "TeamDeleted"
 | where UserId in (deleting_users)
 | extend TeamGuid = tostring(Details.TeamGuid)
 | project-away AddOnName, Members, Settings
 // Uncomment the following line to map query entities is you plan to use this as a detection query
 //| extend timestamp = TimeGenerated, AccountCustomEntity = UserId

Développez vos opportunités de repérage de menacesExpanding your thread hunting opportunities

Vous pouvez développer votre repérage en combinant des requêtes provenant de ressources telles qu’Azure Active Directory (Azure AD), ou d’autres charges de travail Office 365 avec vos requêtes Teams.You can expand your hunting by combining queries from resources like Azure Active Directory (Azure AD), or other Office 365 workloads with your Teams queries. Par exemple, vous combinez la détection de modèles suspects dans Azure AD SigninLogs et vous utilisez ces informations pendant la recherche de propriétaires Teams.One example is combining detection of suspicious patterns in Azure AD SigninLogs, and using that information while hunting for Team Owners.

let timeRange = 1d;
let lookBack = 7d;
let threshold_Failed = 5;
let threshold_FailedwithSingleIP = 20;
let threshold_IPAddressCount = 2;
let isGUID = "[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}";
let azPortalSignins = SigninLogs
| where TimeGenerated >= ago(timeRange)
// Azure Portal only and exclude non-failure Result Types
| where AppDisplayName has "Azure Portal" and ResultType !in ("0", "50125", "50140")
// Tagging identities not resolved to friendly names
| extend Unresolved = iff(Identity matches regex isGUID, true, false);
// Lookup up resolved identities from last 7 days
let identityLookup = SigninLogs
| where TimeGenerated >= ago(lookBack)
| where not(Identity matches regex isGUID)
| summarize by UserId, lu_UserDisplayName = UserDisplayName, lu_UserPrincipalName = UserPrincipalName;
// Join resolved names to unresolved list from portal signins
let unresolvedNames = azPortalSignins | where Unresolved == true | join kind= inner (
   identityLookup ) on UserId
| extend UserDisplayName = lu_UserDisplayName, UserPrincipalName = lu_UserPrincipalName
| project-away lu_UserDisplayName, lu_UserPrincipalName;
// Join Signins that had resolved names with list of unresolved that now have a resolved name
let u_azPortalSignins = azPortalSignins | where Unresolved == false | union unresolvedNames;
let failed_signins = (u_azPortalSignins
| extend Status = strcat(ResultType, ": ", ResultDescription), OS = tostring(DeviceDetail.operatingSystem), Browser = tostring(DeviceDetail.browser)
| extend FullLocation = strcat(Location,'|', LocationDetails.state, '|', LocationDetails.city)
| summarize TimeGenerated = makelist(TimeGenerated), Status = makelist(Status), IPAddresses = makelist(IPAddress), IPAddressCount = dcount(IPAddress), FailedLogonCount = count()
by UserPrincipalName, UserId, UserDisplayName, AppDisplayName, Browser, OS, FullLocation
| mvexpand TimeGenerated, IPAddresses, Status
| extend TimeGenerated = todatetime(tostring(TimeGenerated)), IPAddress = tostring(IPAddresses), Status = tostring(Status)
| project-away IPAddresses
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by UserPrincipalName, UserId, UserDisplayName, Status, FailedLogonCount, IPAddress, IPAddressCount, AppDisplayName, Browser, OS, FullLocation
| where (IPAddressCount >= threshold_IPAddressCount and FailedLogonCount >= threshold_Failed) or FailedLogonCount >= threshold_FailedwithSingleIP
| project UserPrincipalName);
TeamsData
| where TimeGenerated > ago(time_window)
| where Operation =~ "MemberRoleChanged"
| extend Member = tostring(parse_json(Members)[0].UPN) 
| extend NewRole = toint(parse_json(Members)[0].Role) 
| where NewRole == 2
| where Member in (failed_signins)
| extend TeamGuid = tostring(Details.TeamGuid)

De plus, vous pouvez rendre les détections SigninLogs spécifiques à Teams en ajoutant un filtre pour les connexions basées sur Teams uniquement à l’aide des éléments suivants :Also, you can make the SigninLogs detections specific to Teams by adding a filter for only Teams-based sign-ins by using:

| where AppDisplayName startswith "Microsoft Teams"

Pour vous aider à expliquer where AppDisplayName starts with "Microsoft Teams" plus loin, la KQL ci-dessous montre une connexion réussie à partir d’une adresse IP avec un échec provenant d’une adresse IP différente, mais réservé uniquement aux connexions Teams :To help explain using where AppDisplayName starts with "Microsoft Teams" further, the KQL below demonstrates a successful logon from one IP address with failure from a different IP address, but scoped only to Teams sign-ins:

let timeFrame = 1d;
let logonDiff = 10m;
SigninLogs 
  | where TimeGenerated >= ago(timeFrame) 
  | where ResultType == "0" 
  | where AppDisplayName startswith "Microsoft Teams"
  | project SuccessLogonTime = TimeGenerated, UserPrincipalName, SuccessIPAddress = IPAddress, AppDisplayName, SuccessIPBlock = strcat(split(IPAddress, ".")[0], ".", split(IPAddress, ".")[1])
  | join kind= inner (
      SigninLogs 
      | where TimeGenerated >= ago(timeFrame) 
      | where ResultType !in ("0", "50140") 
      | where ResultDescription !~ "Other"  
      | where AppDisplayName startswith "Microsoft Teams"
      | project FailedLogonTime = TimeGenerated, UserPrincipalName, FailedIPAddress = IPAddress, AppDisplayName, ResultType, ResultDescription
  ) on UserPrincipalName, AppDisplayName 
  | where SuccessLogonTime < FailedLogonTime and FailedLogonTime - SuccessLogonTime <= logonDiff and FailedIPAddress !startswith SuccessIPBlock
  | summarize FailedLogonTime = max(FailedLogonTime), SuccessLogonTime = max(SuccessLogonTime) by UserPrincipalName, SuccessIPAddress, AppDisplayName, FailedIPAddress, ResultType, ResultDescription 
  | extend timestamp = SuccessLogonTime, AccountCustomEntity = UserPrincipalName, IPCustomEntity = SuccessIPAddress

Informations et mises à jour importantesImportant information and updates

Nous vous remercions pour la collaboration au contenu, Pete Bryan, Nicholas DiCola et Matthew Martin.Thank you for content collaboration, Pete Bryan, Nicholas DiCola, and Matthew Lowe. Pete Bryan et les personnes avec lesquelles il collabore continueront à développer des requêtes de détection et de repérage pour Teams. Veillez donc à rester en contact avec ce référentiel GitHub pour les mises à jour.Pete Bryan and the people he collaborates with will continue to develop detection and hunting queries for Teams, so keep in touch with this GitHub repository for updates. Surveiller les mises à jour de l’analyseur et application logique utilisés dans cet article.Monitor for updates to the parser and logic app used in this article. Vous pouvez également participer et contribuer à la communauté Azure Sentinel.You can also join and contribute to the Azure Sentinel community. Merci !Thank you! Bon repérage.Happy hunting.

Inscrire votre application dans Azure ADRegistering your application in Azure AD

Activer ou désactiver la recherche dans le journal d’auditTurn audit log search on or off

Qu’est-ce que Azure SentinelWhat is Azure Sentinel