Mailbox Breaches

In our first article, we established the fundamentals of the Microsoft Graph Activity Log, highlighting its critical role in providing visibility into API-driven activities within Microsoft 365. Now, we move from theory to practice. This second article in the series demonstrates how to use this powerful data source to investigate one of the most common and damaging security incidents: a mailbox breach.

Consider this scenario: the phone rings in the accounts department. A client is on the line, asking for confirmation of an email they just received containing new bank details for an outstanding invoice. The accounts team is confused; they check their shared mailbox’s “Sent Items” and find no record of such an email. Soon, another client calls with the same query. This is the subtle but alarming starting point of a sophisticated mailbox breach, one designed to defraud both the organization and its clients. The attacker, having gained access, is sending emails and then deleting the evidence.

This scenario highlights the urgent need for a deep investigative capability that goes beyond what’s visible in a user’s mailbox. It’s where the Microsoft Graph Activity Log becomes indispensable, providing the granular, unalterable audit trail necessary to understand what happened, how it happened, and what the attacker did. This article will guide you through the process of using the Graph Activity Log and Kusto Query Language (KQL) to hunt for common indicators of mailbox compromise. We will cover how to identify suspicious access patterns, detect malicious inbox rules, and piece together the evidence to form a clear picture of an attack.

While this article focuses on investigation, it is crucial to note that the best defense is always prevention. A significant percentage of security breaches can be prevented by enabling Multi-Factor Authentication (MFA). It provides a critical layer of security that makes it exponentially harder for attackers to gain access, even if they have a user’s password. Before diving into reactive measures, organizations should prioritize proactive security. For a step-by-step guide, refer to the official Microsoft documentation on how to Set up multifactor authentication for users.

Example Scenario

To bring it to life, consider adding a scenario that paints a familiar picture. The content below is comprehensive and helpful; relating it to a real-life example (or similar to a real-life example you’ve experienced) is helpful, for example, this scenario could be one where an accounts payable mailbox is compromised, and the suspicious report could be that clients have been phoning the accounts team asking why they’ve been contacted with new account details; but the team cannot see any outbound emails of this nature from the team member’s mailbox; that could be the jumping off point to explain the impact of a breach like this and the effect it could have on the organization

[Consider a brief note and link to a relevant article on protecting against these breaches – i.e., prevention is better than the cure, potentially dropping in a statistic such as the percentage of accounts without MFA enabled.]

Anatomy of a Mailbox Breach: What to Look For

Before diving into the investigation queries, it is critical to first understand the typical behavior of attackers once they gain access to a mailbox. Compromise often begins with tactics such as phishing, credential stuffing, or password spray attacks, allowing unauthorized access to a user’s account. However, gaining access is only the initial step in a broader attack chain. Threat actors frequently follow a defined pattern, such as exploring the mailbox contents, harvesting data, setting up inbox rules to reroute or hide sensitive messages, and in many cases, deleting evidence to avoid detection. These actions are rarely random; they leave behind a sequence of identifiable traces that, when examined carefully, can tell the story of the breach. This is where the Microsoft Graph Activity Log becomes indispensable. By analyzing these logs, we can uncover the digital footprints of the attacker, piece together their activities, and begin forming a timeline of compromise. This understanding sets the stage for a focused and effective investigation using KQL.

Key indicators of compromise (IOCs) include:

  • Anomalous Access Patterns: Logins and API calls from unfamiliar IP addresses, countries, or User-Agents. An application suddenly accessing a mailbox it never has before is a major red flag.
  • High-Volume Activity: A sudden spike in the number of emails being read, downloaded, or synchronized. This often indicates an attempt to exfiltrate the entire contents of the mailbox.
  • Creation of Malicious Inbox Rules: Attackers frequently create rules to automatically forward sensitive emails to an external address or delete messages that might alert the user or security teams.
  • Suspicious Permission Grants: An application being granted high-privilege delegated or application permissions like Mail.ReadWrite.All or Mail.ReadBasic.All outside of a normal change window.
  • Unusual Deletion Activity: A large number of emails are being deleted, especially from the ‘Sent Items’ or ‘Deleted Items’ folders, to cover the attacker’s tracks.

Investigative Playbook: Core KQL Queries

With these IOCs in mind, Log Analytics can be used for analysis. The following KQL queries are designed to pinpoint suspicious activities related to a user’s mailbox.

General Activity Audit for a Specific User

    Establishing a timeline of a user’s recent activity is the starting point of any investigation. By examining Microsoft Graph API requests associated with a specific user, it’s possible to identify anomalies such as access from unknown applications, unusual IP addresses, or devices that the user doesn’t typically use. This helps build a contextual understanding of whether the activity aligns with expected behavior or suggests unauthorized access.

    The below KQL query retrieves all Graph API requests made on behalf of a specific user within the past 24 hours. Replace <UserPrincipalName> with the actual user’s email or ID.

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(1d)
    | where UserId == "<UserPrincipalName>" 
    | project TimeGenerated, AppId, IPAddress, UserAgent, RequestMethod, RequestUri
    | sort by TimeGenerated desc

    Reviewing this output makes it easier to spot suspicious usage patterns and gain visibility into what applications or IPs interacted with the user’s mailbox.

    Detecting Malicious Inbox Rule Creation

    Attackers frequently create forwarding or deletion rules to silently redirect or suppress emails without the user’s awareness. These inbox rules can be set via the Graph API and are commonly used for data exfiltration or to hide warning messages from security tools and end users. Detecting such rule creation events, especially from unfamiliar IP addresses or applications which can reveal covert malicious actions.

    The below KQL query identifies attempts to create inbox rules via POST requests over the past 30 days:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(30d)
    | where RequestUri has "mailFolders/inbox/rules" and RequestMethod == "POST"
    | project TimeGenerated, AppId, UserId, IPAddress

    This query enables the detection of unauthorized forwarding mechanisms, allowing analysts to confirm if suspicious rules were applied to the mailbox.

    Identifying High-Volume Mail Synchronization

    When attackers gain access to a mailbox, one of their primary objectives is to exfiltrate as much data as possible. This often results in a spike in message reads or synchronization requests. Monitoring these activities helps in identifying cases where an application is used to harvest mailbox data at scale, particularly when volumes are far beyond normal user behavior.

    The below KQL query summarizes high-frequency access operations related to message reads or synchronization activity:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where RequestUri has "/messages" or RequestUri has "Sync"
    | project TimeGenerated, AppId, UserId, IPAddress
    | summarize RequestCount = count() by AppId, UserId, IPAddress, bin(TimeGenerated, 1h)
    | where RequestCount > 500
    | order by RequestCount desc

    This aggregated view highlights abnormal spikes, helping investigators focus on possible mass data downloads and identify potentially malicious automation tools.

    Investigating Suspicious Deletions

    A common post-compromise tactic for threat actors is to delete email evidence, particularly from folders like “Sent Items” or “Inbox,” to cover their tracks. By narrowing the search to DELETE requests made by a specific user from a known suspicious IP address, security teams can trace whether sensitive emails were intentionally removed. Replace the placeholders “user@example.com” and “123.45.67.89” in the query below with the actual user ID and IP address to investigate.

    The below KQL query identifies deletion operations associated with message objects, filtered by user and IP, within the past 7 days:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where UserId == "user@example.com" and IPAddress == "123.45.67.89"
    | where RequestMethod == "DELETE" and RequestUri has "/messages/"
    | project TimeGenerated, AppId, RequestUri

    This approach enables precise validation of deletion events and helps correlate them with the timeframe and source reported by the user.

    Executing the Investigation with PowerShell

    While KQL is powerful and flexible for querying data within the Azure portal, investigations often need to be conducted repeatedly, shared with other teams, or even automated as part of a larger incident response workflow. Manually running queries via the portal can become time-consuming, especially when investigating multiple users, IP addresses, or timeframes.

    This is where PowerShell becomes an essential companion to the Graph Activity Log and KQL. With Azure PowerShell installed and configured, security teams can embed KQL queries into scripts to:

    • Standardize investigations: Provide a repeatable and consistent method for querying critical breach indicators.
    • Automated processes: Save time by streamlining the connection to Log Analytics, executing queries, and exporting results.
    • Enable broader access: Allow non-KQL experts to run investigations by simply entering parameters such as usernames or IP addresses.
    • Integrate with workflows: Support end-to-end incident handling by connecting with alert responses, report generation, or ticketing systems.

    The following PowerShell script offers a user-friendly, interactive way to execute the four main investigative queries discussed earlier. It prompts the analyst to choose which type of activity to investigate, dynamically builds the corresponding KQL query, executes it against the designated Log Analytics workspace, and optionally exports the results to a CSV file for deeper offline analysis or reporting.

    This bridges the gap between analysis and action, ensuring that defenders can respond to incidents quickly, consistently, and with all necessary evidence at hand.

    Here’s the full script:

    Connect-AzAccount
    
    # Define Workspace ID and output file path
    $workspaceId = "<replace_this_with_your_work_place_id>"
    $csvFilePath = ".\graph_activity_result.csv"
    
    # Prompt for user input
    Write-Host "`nSelect the query to run:"
    Write-Host "1. User-specific activity in last 1 day"
    Write-Host "2. Inbox rules creation (last 30 days)"
    Write-Host "3. High-volume message/sync activity (all time)"
    Write-Host "4. Message deletion from specific IP/user (last 7 days)"
    $selection = Read-Host "Enter the query number (1-4)"
    
    # Prompt for dynamic inputs if needed
    switch ($selection) {
        '1' {
            $userId = Read-Host "Enter UserPrincipalName (e.g., user@example.com)"
            $query = @"
    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(1d)
    | where UserId == '$userId'
    | project TimeGenerated, AppId, IPAddress, UserAgent, RequestMethod, RequestUri
    | sort by TimeGenerated desc
    "@
        }
        '2' {
            $query = @"
    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(30d)
    | where RequestUri has "mailFolders/inbox/rules" and RequestMethod == "POST"
    | project TimeGenerated, AppId, UserId, IPAddress
    "@
        }
        '3' {
            $query = @"
    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where RequestUri has "/messages" or RequestUri has "Sync"
    | project TimeGenerated, AppId, UserId, IPAddress
    | summarize RequestCount = count() by AppId, UserId, IPAddress, bin(TimeGenerated, 1h)
    | where RequestCount > 500
    | order by RequestCount desc
    "@
        }
        '4' {
            $userId = Read-Host "Enter UserPrincipalName (e.g., user@example.com)"
            $ip = Read-Host "Enter IP Address (e.g., 123.45.67.89)"
            $query = @"
    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where UserId == '$userId' and IPAddress == '$ip'
    | where RequestMethod == "DELETE" and RequestUri has "/messages/"
    | project TimeGenerated, AppId, RequestUri
    "@
        }
        default {
            Write-Error "Invalid selection. Please run the script again and choose between 1–4."
            exit
        }
    }
    
    Write-Host "`nRunning selected Graph API activity query..."
    try {
        $result = Invoke-AzOperationalInsightsQuery -WorkspaceId $workspaceId -Query $query -ErrorAction Stop
    
        if ($result.Results) {
            Write-Host "`nQuery successful. Displaying results:"
            $result.Results | Format-Table -AutoSize
    
            Write-Host "`nSaving results to $csvFilePath"
            $result.Results | Export-Csv -Path $csvFilePath -NoTypeInformation -Encoding UTF8
        } else {
            Write-Host "Query ran successfully but returned no results."
        }
    } catch {
        Write-Error "Query failed: $($_.Exception.Message)"
    }
    Write-Host "`nInvestigation complete."

    Here, this script replaces the placeholder $workspaceId with the actual Log Analytics Workspace ID to establish the connection required for querying Microsoft Graph Activity Logs. The Microsoft Graph Activity Log provides an essential layer of visibility, transforming raw API requests into a clear audit trail. By using targeted KQL queries, security teams can move beyond reactive alerts and proactively investigate the subtle indicators of a mailbox compromise. This capability is fundamental to understanding the scope of an incident and enabling a swift, evidence-based response. The techniques shown are a practical application of the principles discussed in Practical Protection: Getting Started with Graph Threat Hunting.

    With the investigative techniques for mailbox breaches now in place, the focus shifts to another major exfiltration vector, which is document access. The next article in this series, Investigating Document Exfiltration with the Graph Activity Log, will explain how the same principles can be applied to detect suspicious file access, download behavior, and sharing activity across SharePoint Online and OneDrive.

    About the Author

    Mezba Uddin

    Mezba Uddin is a Microsoft MVP, Technical Architect, and Microsoft Certified Trainer (MCT) with deep expertise in Microsoft 365, Exchange Online, Azure, Windows 365 and automation. With a strong background in IT infrastructure and cloud management, Mezba specializes in automating Microsoft 365 administration using PowerShell and Microsoft Graph API, helping organizations streamline operations and enhance security. As a recognized thought leader in the Microsoft ecosystem, Mezba actively contributes to the MVP community, mentors IT professionals, and shares insights through technical writing, public speaking, and Meetup groups. He runs MrMicrosoft.com, a platform dedicated to Microsoft 365 automation, PowerShell, and enterprise IT solutions, where he publishes in-depth guides and tools for IT professionals. Follow his latest insights on MrMicrosoft.com, connect on LinkedIn, or join his Meetup groups for live discussions on Microsoft technologies.

    Leave a Reply