Aller au contenu

Dissection approfondie du unified audit log (UAL)

Le Unified Audit Log (UAL) de Microsoft Purview constitue la colonne vertébrale forensique de toute réponse à incident dans Microsoft 365. Lorsqu’un attaquant opère au sein d’un tenant d’entreprise — qu’il crée des règles de redirection furtives dans Exchange Online, exfiltre des brevets industriels depuis SharePoint Online, manipule des canaux dans Microsoft Teams ou enregistre une application OAuth dans Entra ID —, l’UAL est le référentiel centralisé conçu pour consigner ses traces.

Néanmoins, appréhender l’UAL comme une simple base de données relationnelle ou un flux syslog standard mène inévitablement à l’échec d’investigation. L’UAL est un moteur d’agrégation asynchrone multi-tenant ingérant les événements de plus de 40 microservices distincts. Son schéma est polymorphe, ses délais d’ingestion s’étendent de quelques minutes à 24 heures selon la charge applicative, et ses interfaces d’extraction imposent des limites strictes de pagination et de débit.


1. Architecture d’ingestion & topologie des flux

Section intitulée « 1. Architecture d’ingestion & topologie des flux »

L’UAL ne génère aucune télémétrie par lui-même. Chaque composant Microsoft 365 enregistre ses transactions localement au sein de sa frontière applicative, puis les publie sur un bus d’événements distribué :

graph TD
subgraph "Producteurs de Télémétrie"
EXO[Exchange Online<br/>Actions Boîtes & Admin]
SPO[SharePoint Online<br/>Fichiers & Partages]
ODB[OneDrive Entreprise<br/>Sync & Téléchargements]
TEAMS[Microsoft Teams<br/>Messages, Canaux, Bots]
ENTRA[Entra ID<br/>Modifications d'Annuaire]
PUR[Services Purview<br/>DLP & eDiscovery]
end
subgraph "Infrastructure d'Ingestion Purview"
HUB[Bus d'Événements Interne Distributed Service Bus]
NORM[Moteur de Normalisation & Enrichissement<br/>IP Client, Identité, Contexte de Session]
ES_STORE[Magasin Purview Compliance Partitionné<br/>Indexation par Tenant & Horodatage UTC]
end
subgraph "Interfaces d'Extraction Forensique"
PS[PowerShell Exchange Online<br/>Search-UnifiedAuditLog]
MGMT_API[Office 365 Management Activity API<br/>Abonnements Webhooks & Polling]
GRAPH[Microsoft Graph API<br/>/security/auditLog/queries]
PORTAL[Portail de Conformité Purview<br/>Recherches Asynchrones & Export CSV]
end
EXO -->|15 min - 60 min| HUB
SPO -->|15 min - 30 min| HUB
ODB -->|15 min - 30 min| HUB
TEAMS -->|1 h - 24 h| HUB
ENTRA -->|15 min - 60 min| HUB
PUR -->|30 min - 4 h| HUB
HUB --> NORM
NORM --> ES_STORE
ES_STORE --> PS
ES_STORE --> MGMT_API
ES_STORE --> GRAPH
ES_STORE --> PORTAL

Délais d’ingestion par composant (latences forensiques)

Section intitulée « Délais d’ingestion par composant (latences forensiques) »

Une erreur classique en phase de réponse à incident consiste à conclure à l’absence d’activité hostile sous prétexte qu’aucun enregistrement n’apparaît dans l’UAL. Le délai d’ingestion (temps écoulé entre l’action sur le composant et sa visibilité dans l’UAL) dépend directement de la charge applicative :

ComposantDélai d’ingestion nominalDélai maximal (SLA / Pic de charge)Impact en investigation (DFIR)
Exchange online15 à 45 minutesJusqu’à 4 heuresLes événements volumineux (MailItemsAccessed) sont mis en mémoire tampon avant d’être injectés.
SharePoint & OneDrive15 à 30 minutesJusqu’à 2 heuresLes opérations de téléchargement en masse sont consolidées par micro-lots.
Microsoft Entra ID15 à 60 minutesJusqu’à 2 heuresLes logs d’audit apparaissent plus vite dans le portail Entra (~2-5 min) que dans l’UAL Purview.
Microsoft teams2 à 6 heuresJusqu’à 24 heuresForte variance de télémétrie ; l’investigation en temps réel ne peut s’appuyer sur l’UAL seul.
Power platform / apps1 à 4 heuresJusqu’à 24 heuresPipeline de télémétrie à priorité basse.

Chaque entrée dans l’UAL est structurée en deux couches : une enveloppe externe de métadonnées communes normalisées, et une charge utile polymorphe interne au format JSON imbriquée sous forme de chaîne de caractères dans la propriété AuditData.

ChampTypeDescription techniquePortée forensique
CreationTimeDateTime UTCHorodatage précis de l’action sur le composant source.Capital : correspond au moment de l’action, non à l’heure d’ingestion UAL.
RecordTypeEntier / EnumIdentifiant catégorisant le composant ou la nature de l’événement.Permet un filtrage ultra-rapide côté serveur (ex: 15 pour Entra, 2 pour Exchange).
OperationChaîneAction élémentaire réalisée (ex: FileDownloaded, New-InboxRule).Pivot principal de détection des techniques MITRE ATT&CK.
OrganizationIdGUIDIdentifiant unique du tenant Microsoft 365.Garantit l’étanchéité multi-tenant lors de collectes multi-entités.
UserTypeEntier / EnumNature de l’acteur (Regular, Admin, System, ServicePrincipal).Distingue une action humaine d’un service automatisé ou d’un service principal.
UserKeyChaîneIdentifiant canonique immuable de l’utilisateur (PUID).Permet de suivre un acteur même après renommage de son UPN ou changement d’alias.
WorkloadChaîneService cible (Exchange, SharePoint, AzureActiveDirectory).Délimite le périmètre applicatif inspecté.
ResultStatusChaîneStatut de l’opération (Succeeded, Failed, PartiallySucceeded).Discrimine les échecs de force brute des exécutions réussies.
AuditDataString (JSON)Schéma spécifique contenant l’ensemble des paramètres contextuels.Requiert une désérialisation JSON pour extraire adresses IP, chemins et jetons.

Filtrer par RecordType réduit drastiquement la charge serveur et évite d’atteindre le plafond d’extraction des requêtes :

RecordType 1 : ExchangeAdmin (Commandes PowerShell admin & modifications de configuration)
RecordType 2 : ExchangeItem (Accès aux éléments de boîte : MailItemsAccessed, SoftDelete)
RecordType 4 : General (Opérations transverses, recherches eDiscovery)
RecordType 6 : SharePointFileOperation (Téléchargements, consultations, suppressions de fichiers)
RecordType 8 : ComplianceDLPExchange (Déclenchements des règles DLP sur les flux de messagerie)
RecordType 15 : AzureActiveDirectory (Gestion des utilisateurs, rôles, consentements OAuth)
RecordType 25 : MicrosoftTeams (Création d'équipes, réglages de canaux, ajouts d'invités)
RecordType 49 : SecurityComplianceAlerts (Génération d'alertes de sécurité Purview et Defender)

Selon le RecordType, la chaîne AuditData contient des attributs radicalement différents. L’analyste doit adapter ses expressions de parsing.

{
"CreationTime": "2026-09-15T08:14:22",
"Id": "a4f89d32-28e1-4c12-b891-883109aef102",
"Operation": "MailItemsAccessed",
"OrganizationId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"RecordType": 2,
"ResultStatus": "Succeeded",
"UserKey": "1003200189ABCDEF@tenant.onmicrosoft.com",
"UserType": 0,
"Version": 1,
"Workload": "Exchange",
"ClientIPAddress": "198.51.100.45",
"ClientInfoString": "Client=REST;Client=OutlookService;Version=16.0.17830.20138;",
"ClientRequestId": "9b12a843-ef45-42a1-9a14-8712a45930b1",
"ExternalAccess": false,
"InternalLogonType": 0,
"MailboxOwnerUPN": "cfo@defense-corp.org",
"MailboxOwnerSid": "S-1-5-21-3829104821-291823901-192831092-1004",
"MailboxGuid": "4d12f890-341a-4f89-91a1-987120adfe01",
"LogonType": 0,
"OperationProperties": [
{ "Name": "MailAccessType", "Value": "Bind" },
{ "Name": "IsThrottled", "Value": "False" }
],
"Folder": {
"FolderItems": [
{
"InternetMessageId": "<BL3PR02MB10293847@BL3PR02.EURPRD02.PROD.OUTLOOK.COM>",
"Subject": "CONFIDENTIAL: Q3 Financial Restructuring Strategy",
"ItemSize": 45812
}
],
"Id": "LgAAAAD4k1...AQ=="
}
}

4. Extraction & requêtage opérationnel : commandes maîtresses

Section intitulée « 4. Extraction & requêtage opérationnel : commandes maîtresses »

L’interrogation de l’UAL via le portail web de conformité est inadaptée aux investigations critiques (plafond de 50 000 lignes et latence du navigateur). L’analyste doit maîtriser l’extraction programmatique.

PowerShell Exchange online : maîtrise de Search-UnifiedAuditLog

Section intitulée « PowerShell Exchange online : maîtrise de Search-UnifiedAuditLog »

La cmdlet Search-UnifiedAuditLog est l’outil interactif de référence. Cependant, des requêtes imprudentes s’interrompent fréquemment ou retournent des données tronquées :

Fenêtre de terminal
# Connexion avec privilèges minimaux (rôles Compliance Administrator / View-Only Audit Logs)
Connect-ExchangeOnline -UserPrincipalName analyste-dfir@defense-corp.org
# Définition impérative des dates au format UTC
$startDate = (Get-Date "2026-09-14T00:00:00Z").ToUniversalTime()
$endDate = (Get-Date "2026-09-15T23:59:59Z").ToUniversalTime()
# Requête ciblée avec conversion des en-têtes
$auditEvents = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-RecordType ExchangeItem `
-Operations "MailItemsAccessed", "SendAs", "New-InboxRule" `
-ResultSize 5000 `
-Formatted

Le piège des 50 000 enregistrements et le pattern de découpage temporel

Section intitulée « Le piège des 50 000 enregistrements et le pattern de découpage temporel »

Search-UnifiedAuditLog impose un plafond absolu de 50 000 enregistrements par requête. Lorsqu’une fenêtre temporelle dépasse ce volume, la commande cesse silencieusement de retourner des résultats sans lever la moindre exception.

Pour garantir l’exhaustivité des preuves, l’équipe DFIR doit implémenter un algorithme de découpage par fenêtres glissantes (Date-Slicing) :

Fenêtre de terminal
function Get-CompleteUnifiedAuditLogFR {
param(
[Parameter(Mandatory=$true)][DateTime]$StartUtc,
[Parameter(Mandatory=$true)][DateTime]$EndUtc,
[Parameter(Mandatory=$false)][string]$RecordType,
[Parameter(Mandatory=$false)][string]$Operations
)
$windowStart = $StartUtc
$intervalHours = 4
$allRecords = [System.Collections.Generic.List[PSObject]]::new()
while ($windowStart -lt $EndUtc) {
$windowEnd = $windowStart.AddHours($intervalHours)
if ($windowEnd -gt $EndUtc) { $windowEnd = $EndUtc }
Write-Host "[-] Extraction UAL fenêtre : $windowStart -> $windowEnd" -ForegroundColor Cyan
$session = "DFIR_Session_" + [Guid]::NewGuid().ToString()
$page = 1
$totalWindowCount = 0
do {
$params = @{
StartDate = $windowStart
EndDate = $windowEnd
ResultSize = 5000
SessionCommand = "ReturnLargeSet"
SessionId = $session
}
if ($RecordType) { $params["RecordType"] = $RecordType }
if ($Operations) { $params["Operations"] = $Operations }
$batch = Search-UnifiedAuditLog @params
if ($batch) {
$batchCount = $batch.Count
$totalWindowCount += $batchCount
$allRecords.AddRange($batch)
Write-Host " [+] Page $page : $batchCount événements collectés (Total fenêtre : $totalWindowCount)" -ForegroundColor Gray
$page++
} else {
break
}
} while ($batchCount -eq 5000)
# Si le plafond de 50 000 est frôlé, diviser l'intervalle horaire par deux
if ($totalWindowCount -ge 49000) {
Write-Warning "[!] Fenêtre proche de la saturation des 50k. Réduction de l'intervalle..."
$intervalHours = [Math]::Max(1, [Math]::Floor($intervalHours / 2))
continue
}
$windowStart = $windowEnd
}
return $allRecords
}

5. Export de télémétrie industrielle : office 365 management activity API

Section intitulée « 5. Export de télémétrie industrielle : office 365 management activity API »

Pour l’alimentation continue d’un SIEM ou les extractions massives, l’Office 365 Management Activity API constitue le standard d’ingestion asynchrone :

sequenceDiagram
autonumber
participant App as Collecteur DFIR / SIEM (Application Azure AD)
participant API as Office 365 Management API
participant Blob as Stockage Blobs Azure Purview
App->>API: POST /subscriptions/start?contentType=Audit.Exchange
API-->>App: 200 OK (Souscription Active)
Note over API: Agrégation continue des événements par lots
API->>App: Notification Webhook (ou interrogation GET par l'App)
App->>API: GET /subscriptions/content?contentType=Audit.Exchange
API-->>App: 200 OK (Liste des URIs de blobs)
loop Pour chaque URI de contenu
App->>Blob: GET https://manage.office.com/.../contentBlob
Blob-->>App: 200 OK (Flux GZip contenant le tableau JSON)
end

L’API segmente les flux en 5 types de contenu indépendants :

  1. Audit.AzureActiveDirectory
  2. Audit.Exchange
  3. Audit.SharePoint
  4. Audit.General (Comprend Teams, OneDrive, Purview, Power Platform)
  5. DLP.All (Règles de prévention des fuites de données)

6. Chasse KQL avancée dans Defender XDR (CloudAppEvents)

Section intitulée « 6. Chasse KQL avancée dans Defender XDR (CloudAppEvents) »

Dans les environnements dotés de licences Microsoft Defender for Cloud Apps ou Defender XDR, les flux de l’UAL sont automatiquement injectés dans la table CloudAppEvents d’Advanced Hunting, permettant des requêtes sub-secondes sur 30 jours :

// Détection de créations de règles de messagerie suspectes corrélées aux accès
CloudAppEvents
| where TimeGenerated > ago(7d)
| where ActionType in ("New-InboxRule", "Set-InboxRule", "MailItemsAccessed")
| extend AuditData = parse_json(RawEventData)
| extend ClientIP = tostring(AuditData.ClientIPAddress),
Mailbox = tostring(AuditData.MailboxOwnerUPN),
RuleName = tostring(AuditData.Parameters[0].Value)
| summarize
MailItemsAccessedCount = countif(ActionType == "MailItemsAccessed"),
ReglesCrees = make_set_if(RuleName, ActionType has "InboxRule"),
AdressesIP = make_set(ClientIP),
Pays = make_set(Location)
by Mailbox, bin(TimeGenerated, 1h)
| where array_length(ReglesCrees) > 0
| order by TimeGenerated desc

7. Pièges d’investigation & anomalies forensiques

Section intitulée « 7. Pièges d’investigation & anomalies forensiques »