Auditing who checked your email metadata in M365

  1. Background
  2. Accessing email message trace
    1. Overview
    2. Access email message trace via Defender XDR (Explorer)
    3. Access email message trace via Defender XDR (Advanced hunting)
    4. Access email message trace via Exchange Admin Center
    5. Analyzing the logs
  3. Conclusion

Background

One of the things organizations typically gain when moving into cloud, is visibility. Especially, when you’re using a single vendor (such as Microsoft), you can get very wide visibility into the organizations cloud infrastructure, assuming you have the privileges required (and you know how to navigate 10 different portals). For security professionals this is typically positive, because you can see what’s happening and how things have been configured. But it can have a negative impact as well, especially when thinking of privacy.

In this blog, I’ll have a brief look at one specific use case, that is related to the issue of visibility and how it impacts privacy.

The use case is this:

Certain people (e.g., SOC analysts) need to be able to see certain information about all emails being sent and received. This information includes:

  • Sender email address
  • Recipient email address
  • Subject of the email
  • Time (when it was sent)
  • Verdict(s) from the security solutions being used (did the email contain malware, attachments, URLs, etc.)

The problem is, that this information can be very sensitive. Even when you’re not able to view the contents of an email, being able to see sender, recipient and email subject means that this can be subject to very strict data privacy regulations. The question is:

  • Do you know who has access to this information?
  • Are you able to track who has accessed this information?

In an on-premise environment, this would normally not be a problem. Typically, only very limited people have access to email infrastructure, such as Exchange servers or 3rd party mail gateways. In the cloud, however, this may not be the case. If you’re using Exchange Online (and Defender for Office 365), you have different ways of accessing this information. You also have a fairly complex permissions structure (Entra ID roles, Exchange Online role groups, Defender XDR unified RBAC, etc.) that determines who can access the information. Then you have various audit and activity logs, but do you know which actions are actually being logged?

Accessing email message trace

Overview

My goal here is to do a simple test, as follows:

  1. Create an account with Global Reader role in Entra ID. The reason for using this role is that in my experience it is quite widely being used in organizations, but it’s not necessarily being identified as being a very sensitive role, because it cannot make any changes to the environment.
    • Note: I verified the tests also with an account with Security Reader role, which is a role with much more limited permissions. While Security Reader cannot access some of the views in the Defender XDR portal, the overall result is the same, as noted in the conclusion section.
  2. Verify, if the account can access the information described in the use case (sender, recipient, subject, etc.) and try to access the information via different portals.
  3. Verify (with an admin account) what kind of audit trail is left behind in the following logs:
    • Activity log of Defender for Cloud Apps (in Defender XDR portal)
    • CloudAppEvents table in Defender XDR advanced hunting
    • Microsoft 365 audit log (in Microsoft Purview portal or Defender XDR portal)

Access email message trace via Defender XDR (Explorer)

Let’s start from the Defender XDR portal, under Email & collaboration. Global Reader can access more or less everything here (regardless of whether Defender XDR Unified RBAC has been implemented or not).

Now, let’s open Threat Explorer (Explorer in the navigation). Among other things, we can see the following information from all of the email processed by Exchange Online and Defender for Office 365:

  • Sender address (and domain)
  • Recipient address
  • Subject
  • Time
  • Delivery location
  • Threat type

By default, the view shows emails from the last two days (since the beginning of the day before). However, you can change the filter to search emails from further back.

Is there an audit trail for opening this view? Yes and no – the logs show a bunch of activities, most of which are related to RBAC. One of the log entries shows that the user opened the ThreatInstanceList, which relates to opening the Threat Explorer. However, it doesn’t give any information about what the user saw, i.e. what the filter was. So you cannot really determine, whether the user searched for emails for the last two days, or the last two weeks. Not very useful.

Now, let’s click one of the emails, which opens a more detailed view of an email.

Is there an audit trail for opening this view? Yes – in this case, the logs show that the user opened the specific email (the message ID is logged).

Access email message trace via Defender XDR (Advanced hunting)

Next, let’s look at Advanced Hunting. In the EmailEvents table, we can see all the information that we’re interested in right now (sender, recipient, subject, etc.).

Is there an audit trail for opening this view? No – I could not find any log entries of the query being made.

Access email message trace via Exchange Admin Center

Finally, let’s have a look at Exchange admin center (https://admin.exchange.microsoft.com) and go to Mail flow > Message trace. This allows us to query message trace logs, which contain the information that we are now interested in.

Let’s start a new message trace, and the results show again the same list of emails that we’ve seen before. Again, this is already sensitive information, because we can see the sender, recipient and subject of the emails.

Is there an audit trail for opening this view? No – using the message trace is not being logged. This is actually stated quite clearly also in the documentation (the message trace can be accessed also via Get-MessageTrace commandlet):

If we open one of the emails in the message trace, it will show us more information about the email delivery of that specific email.

Is there an audit trail for opening this view? No – I cannot see any log entries from opening the email, which is expected as the creating the message trace itself is not being logged.

Analyzing the logs

For each of the tests above, I’ve noted whether I was able to find anything from the audit logs or not. A few words about the logs themselves:

Defender for Cloud Apps (MDA) has a very useful Activity log feature (accessible via the Defender XDR portal), which consolidates logs from all connected sources. While the UI can be a bit slow sometimes, it is intuitive to use, and works well when there is not too much data.

In this case, the activity log contains a lot of entries for the test period. However, there is not a lot of information here, and you need to individually dig into the raw data to actually see some details.

Therefore, a better way is to use advanced hunting and the CloudAppEvents table, which I used for most of the analysis:

You can also use the Microsoft 365 audit log, which can be accessed either through the Defender XDR portal or through Microsoft Purview portal. The audit log search is a bit cumbersome, and when doing cross-checking I did not find any additional information in the audit log that wouldn’t be available in the CloudAppEvents table. This makes sense of course, because Defender for Cloud Apps pulls audit logs from Microsoft 365.

Finally, you can send the Microsoft 365 audit logs and/or the CloudAppEvents into Microsoft Sentinel and query the same information there. Because Sentinel runs on top of Log Analytics, you can actually audit all the queries being made there. However, that’s not what I wanted to test here (Sentinel also uses Azure RBAC permission model, and not Entra ID roles).

Conclusion

Overall, the audit logs are very limited when it comes to querying email message traces:

  • Queries made to the Exchange Online message trace through Exchange Admin Center are not being logged, even though the unified audit log ingestion has been enabled in Exchange Online.
  • Advanced hunting queries are not being logged, and you can see the message trace in the EmailEvents table (of course, you can a lot of other potentially sensitive information as well, such as device and network events from Defender for Endpoint).
  • Accessing the overview page the the Defender XDR threat explorer shows the email trace. While opening the page is being logged, it’s not clear what the user saw there. However, opening an individual email is being logged with the message ID.

Some final recommendations:

  • Global Reader has very wide read access (as its name suggests), but even Security Reader can use advanced hunting queries and access the Exchange message trace by default. Be mindful of this when assigning these roles.
  • Implement Defender XDR Unified RBAC, which allows more granular control of the permissions in Defender XDR. However, note that even after activation, users with the Entra ID roles will still have access to the data (you have to replace the Entra ID roles with the Defender XDR Unified RBAC roles).

Detecting and remediating emails with Defender XDR correlation

One of my customers have seen an interesting campaign, and they wanted help detecting and remediating it. Here’s a short summary of what they had observed:

  1. An email is sent to a shared mailbox from a consumer email address, such as Gmail. The purpose of these shared mailboxes is to allow consumers to contact via email, and therefore the users reading the mailboxes are used to getting lots of legitimate email from consumer email addresses. In this case, the same email is actually being sent to multiple shared mailboxes, but of course the users reading the email do not know that. The first email itself does not have any links or attachments, but simply asks for something specific. From the user’s perspective, there’s nothing suspicious about this email at this point.
  2. Once the user replies, they get another email continuing the story, this time there is a link asking them to fill in a form (or something like that). The link is to some legitimate cloud service (DropBox, Google Drive, what have you).
  3. When the user opens the link, it downloads a malicious piece of software. However, the software is benign enough not to be detected by the anti-malware engine (the EDR may detect it afterwards).

As these emails are coming from consumer email addresses, they will pass all the basic email authentication requirements (SPF, DKIM, DMARC). The customer is using Safe Links from Defender of Office (MDO), but that hasn’t been helping either (probably because the links are pointing to legitimate 3rd party cloud services). We cannot block these cloud services completely, because they have legitimate use in the organization.

A few options come to mind, which are not mutually exclusive:

  1. Try to protect the endpoint, and prevent the user from downloading the malicious file (or at least detect/prevent the file during execution).
  2. Try to identify the link in the second mail being malicious. Move the mail to Junk folder.
  3. Detect, when email is being sent from the same consumer email address to multiple shared mailboxes of this type. Move this email to the junk folder. If we’re fast enough, the user never sees the first email, and won’t reply to it. Or, if they reply to it, maybe we are able to move the second email (with the link) to Junk folder.

We could try to use Defender for Endpoint (MDE) to protect the endpoint (option 1), and in any case having an EDR is important for many reasons. However, unfortunately not all of the users reading these email have MDE installed (these are typically shared workstations). And for this particular case, remediating this threat via MDE is challenging. The users may also be getting legitimate links to the same cloud service, so we cannot really block that (e.g., using MDE Network Protection). And if the downloaded file is not detected by the anti-malware engine, how to separate valid links from malicious ones. Again, I still highly recommend having MDE in place, but it’s probably not our best solution for this particular threat.

We might be able to resolve option 2 with Exchange Online Protection (EOP) and Defender for Office 365 (MDO), by using transport rules and/or anti-spam policies. However, the challenge with this one is that these rules are analyzed on a per-mail basis. Again, how do we differentiate between malicious and legitimate emails, if both might be sent from the same consumer email provider, and have a link to the same cloud service?

Hence, we decided to try the option 3 instead.

Option 3: Detecting and remediating malicious emails via Defender XDR correlation

Our use case is pretty simple: if multiple shared mailboxes receive email from the same sender (using a consumer email address) during a short time period, move the email to Junk folder.

We could use either Microsoft Defender XDR for the detection, or we could use Microsoft Sentinel (if we’re sending the MDO events into Sentinel). If we use Sentinel, we need to automate the remediation with playbooks (at least until Sentinel becomes integrated with the Defender XDR portal). There doesn’t seem to be an easy way to do this with the Exchange Online or Defender XDR APIs, so I decided to create the detection logic directly in Defender XDR instead.

First, let’s send the same email from an outlook.com address to three different recipients in our test tenant. In this case, I’m adding all recipients into the same email, but in the real scenario they would be separate emails (this is tested later in the blog):

Once the email is sent, I’ll use the following KQL query to correlate the emails:

// Threshold: How many emails are tolerated from the sender
let Threshold = 2;
// Timespam: How far back are we looking into
let TimeSpan = 1h;
// List of sender domains that we are interested in (mainly consumer email)
let SenderDomains = dynamic([
"outlook.com",
"gmail.com",
]);
// Recipients that will be protected
let RecipientList = dynamic([
"recipient1@yourdomain.com",
"recipient2@yourdomain.com",
"recipient3@yourdomain.com"
]);
EmailEvents
| where Timestamp > ago(TimeSpan)
// Take only emails that are from specific domains (use envelope sender address)
| where SenderMailFromDomain in~ (SenderDomains)
// Take only emails that are sent to the list of recipients we are interested in
| where RecipientEmailAddress in~ (RecipientList)
// Filter based on number of emails sent from a single address
| summarize TotalEmailCount = count(), MessageIdList = make_set(NetworkMessageId), SubjectList = make_set(Subject) by SenderMailFromAddress
| where TotalEmailCount > Threshold
// Join back with EmailEvents table to get more information about each email, filter out emails that have already been remediated
| mv-expand MessageIdList
| extend NetworkMessageId = tostring(MessageIdList)
| join (
EmailEvents
| where LatestDeliveryLocation == "Inbox/folder"
) on NetworkMessageId
// Project relevant columns (note, that we need ReportId for the custom detection rule)
| project Timestamp, SenderMailFromAddress, RecipientEmailAddress, Subject, TotalEmailCount, LatestDeliveryLocation, LatestDeliveryAction, NetworkMessageId, ReportId
| sort by Timestamp desc

By running the query, you can see the results. Each line represents a recipient, and the TotalEmailCount shows the total number of recipients that got the email from this sender. The query will filter out emails that have already been remediated by using LatestDeliveryLocation column (we’ll see how this works later in the blog).

Now, let’s create a detection rule based on the query:

Enter basic information for the alert, such as:

  • Frequency: Every hour (minimum)
  • Severity: Info (we will automatically remediate the issue)

Select RecipientEmailAddress under Mailbox as the entity. This way we can target remediation actions for that mailbox.

In the Actions section, you can determine what to do with the emails. In my case, I want to move them to Junk folder.

Finalize the wizard:

One you’ve submitted the rule, it will run immediately. When looking at the detection rule, you can see that it has submitted actions on the three emails we sent (or in this case one email to three recipients):

It takes a few minutes for the action to be finalized. Once it’s done, we can rerun the advanced hunting query used for the custom detection rule, and see that there are no results:

If we comment out the LatestDeliveryLocation filter, we can see that all three emails have been moved to the Junk folder, just as we wanted.

Now, let’s try again. This time I’ll send three separate emails to individual recipients:

As we can see, the email gets delivered to inbox, because the detection rule is only executed once an hour.

We can also see the entries in the EmailEvents table, and that the latest delivery location is inbox. Note also, that the TotalEmailCount is now 6, because it counts in also the ones that were already delivered to junk (they were received within the 1-hour time window).

After an hour, the detection rule is triggered again. In the Incidents page, there are now four incidents. The first email that we sent to multiple recipients triggered one incident an hour earlier (with 3 mailboxes), while the individual emails will each trigger their own incident.

From one of the corresponding alerts, we can see that the Move to mailbox folder action has been triggered (there are two actions, because it shows also the action done for the first email sent to all three recipients):

When querying the EmailEvents table again, we can see that also these emails were delivered to the Junk folder.

And lo and behold, both emails are indeed in the recipient’s Junk Email folder:

Overall, the custom detection rules are very handy, when you want to perform automatic remediations for certain scenarios, that might be complex with Microsoft Sentinel automation/playbooks. We could also leverage other information available in Defender XDR, e.g., use the EmailAttachmentInfo and/or EmailUrlInfo tables to further correlate information. But in our case, we wanted to catch the first mail, which doesn’t have any links or attachments. It’s still not perfect, as we have the 1 hour window, when the user may respond to the email, receive the reply, and then click the link. But it’s good first step, and we can easily use the query for threat hunting first, and tweak the query accordingly.

As always, feedback is more than welcome! And if you’ve used some other way of remediating these kinds of campaigns, feel free to share :).