A Blue Team guide to Azure & Office 365 monitoring

A few weeks ago I thought that 0x00sec didn’t have enough Blue Team focused posts. Yet, detection of potential threats is incredibly important in any organization. Because Azure and Office 365 are widely used, I decided to start with this. I hope you will find it useful because unfortunately, there is a lack of good resources other than Microsoft when it comes to monitoring Azure with a SIEM and I had to spend many hours to study the logs and figure out what was relevant.

This is not a complete guide for all the products and services within Azure. I just don’t have enough spare time to do it all. But I think it’s a great starting point to monitor the commonly used services.

Most of the queries here are built for Splunk because of its increasing popularity as a SIEM. If you or your organization doesn’t use Splunk, you can convert them manually or try using Sigma. The queries are meant to be base searches that you can built on top of and customize to your needs.

Requirements for log ingestion

This depends on your SIEM. Most SIEM or log management platforms provide apps or connectors to easily ingest logs from Azure. For Splunk specifically, you will likely need the following apps:

Splunk Add-on for Microsoft Office 365
Splunk Add-on for Microsoft Cloud Services
Microsoft Office 365 Reporting Add-on for Splunk

If you’re using ArcSight, you’ll find the connectors on the ArcSight Marketplace.

If such app is not available for the solution you are using, you can script the pulling with the Azure API. The logs provided are in JSON format so the parsing should be easy.

The details on the integration for Azure activity logs can be found here in the integration column for each type of log. For Office 365 specifically, details can be found here.

Building a list of known attackers

Chances are that you are the target of password spray and bruteforce attacks. This is something that your organization can use to its advantage by building a list of “known attackers”. You can then use this list for:

  • Identifying other services targeted by the attackers
  • Succesful authentication from these sources to identify compromised account
  • Conduct intelligence gathering to determine if your organization is specifically targeted

A reliable way I found to build such list using Azure is to look for an unknown IP generating an account lockout on N distinct accounts. In the search below, I first start by looking for sources generating locked accounts and exclude the IP addresses that already exist in the list to avoid duplicates. Then, I run the iplocation command in Splunk to get the country and city of each one of them. Finally, I do a distinct count on the UserId field by ClientIP and the source IPs that locked 5 or more accounts are appended to the list.

sourcetype=o365:management:activity
Operation=UserLoginFailed
LogonError=IdsLocked
`comment("Don`t include duplicates")`
NOT [|inputlookup azure_ipv4_blacklist | fields ClientIP]
| iplocation ClientIP
| stats dc(UserId) as DistinctUsersLocked by ClientIP, Country, City
| where DistinctUsersLocked >= 5
| fields ClientIP, Country, City
| outputlookup append=t azure_ipv4_blacklist

Exchange Online

Users sending large amount of emails to external recipients

Attempts to detect potentially compromised hosts spamming external recipients. In large organization, this might generate multiple false-positive due to internal communications, mass mailing, etc so I recommend filtering by excluding some legitimate senders. The threshold can be modified in the where RecipientCount >= 100 part.

sourcetype=ms:o365:reporting:messagetrace
RecipientAddress!="*@<yourdomain.tld>" 
| stats dc(RecipientAddress) by SenderAddress, Subject 
| rename dc(RecipientAddress) as RecipientCount 
| where RecipientCount >= 100 
| sort - RecipientCount

Mail forwarding to external recipients

Email forwarding can be set up by both administrators and users. It’s very important to monitor these kind of events to detect insider threats. The difference is that administrators can set up new mail transport rules that affect one or multiple users within Exchange Online while users can only do it on their own mailbox from the Outlook client.

Forwarding to an external recipient by an administrator

Administrator using New-TransportRule cmdlet

sourcetype=o365:management:activity
Workload=Exchange
Operation="New-TransportRule" 
| eval kv=mvzip('Parameters{}.Name', 'Parameters{}.Value', "=") 
| mvexpand kv 
| rex field=kv "^(?<kv_key>[^=]+)=(?<kv_value>[^=]+)$" 
| eval {kv_key}=kv_value 
| search kv_key=RedirectMessageTo 
| rename kv_value as dest 
| search dest!="*@<yourdomain.tld>" 
| table _time, user, src, dest 

Forwarding to an external recipient by a user

User using the Outlook client

sourcetype=o365:management:activity
Workload=Exchange
Operation="UpdateInboxRules"
"Forward"
| eval kv=mvzip('OperationProperties{}.Name', 'OperationProperties{}.Value', "=") 
| mvexpand kv 
| rex field=kv "^(?<kv_key>[^=]+)=(?<kv_value>[^=]+)$" 
| eval {kv_key}=kv_value 
| search kv_key=RuleActions
| rename kv_value as RuleActions
| rex field=RuleActions "(?<dest>(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,}))"
| search dest!="*@<yourdomain.ltd>"
| table _time, user, dest, Operation, RuleActions

Mailbox item deleted by a user other than a mailbox owner

Useful to monitor users with permissions on another user’s mailbox deleting items that could be sensitive or that shouldn’t be deleted in the first place.

index=azure sourcetype=o365:management:activity
Workload=Exchange 
AND (Operation=HardDelete OR Operation=SoftDelete) 
| where MailboxOwnerUPN!=user 
| table _time, MailboxOwnerUPN, Operation, AffectedItems{}.ParentFolder.Path, AffectedItems{}.Subject, user

OneDrive

Users sharing OneDrive items with individuals outside of the organizations

This is self-explanatory and can be useful to detect individuals voluntarily sharing confidential information with external party or typo mistakes. Here too, a list of trusted third-party (partners, subsidiaries, etc) emails could be useful to filter out the noise.

index=azure sourcetype=o365:management:activity
Workload=OneDrive
Operation=AddedToSecureLink
TargetUserOrGroupName!="*@<organization.tld>" 
| stats count by _time, UserId, ObjectId, Operation, TargetUserOrGroupName

Azure Active Directory

A note on MFA and legacy authentication

Azure allows legacy authentication using ActiveSync. That means that even if you have MFA enforced in Azure across your organization, as long as legacy authentication is enabled, MFA is useless against successful bruteforcing, password spraying or authentication to a mailbox with a compromised account.

Locked accounts

Identifies locked accounts from existing users. I say existing, because oddly, Azure detects and locks non-existing accounts (I’m not kidding) which explain the exclusion of unknown actor IDs in the "Actor{}.ID"!="Unknown" part. Removing this would return many false-positive as it would include any locks for accounts that don’t exist in your organization.

sourcetype=o365:management:activity
Workload=AzureActiveDirectory
Operation=UserLoginFailed
LogonError=IdsLocked "Actor{}.ID"!="Unknown"
| iplocation ClientIP
| stats count by UserId, ClientIP, Country, City, ResourceName
| sort - count

Succesful authentication without the use of MFA from a blacklisted IP

This is where the blacklist from earlier can be useful. You can detect compromised accounts using a blacklist of known attackers of your own or using threat intelligence feeds.

sourcetype=o365:management:activity
NOT "OAuth2"
AND Operation=UserLoggedIn 
LogonError!=UserAccountNotFound 
    [| inputlookup <blacklist_lookup> 
    | fields <ip_field> 
    | rename <ip_field> as ClientIP] 
| table _time, UserId, ClientIP

If using Splunk Enterprise Security, you can replace the |inputlookup command part and use the ip_intel macro instead to match on the threat intel aggregated in Splunk.

sourcetype=o365:management:activity
Workload=AzureActiveDirectory
Operation=UserLoggedIn
LogonError!=UserAccountNotFound 
[| `ip_intel` 
| search threat_collection=ip_intel ip!="" 
| fields ip 
| rename ip as ClientIP] NOT "OAuth2"

Users with a high count of failed MFA challenges

That one can be noisy because the integration with some products can be very buggy. Otherwise, it’s very useful to detect accounts for which the password was compromised but the authentication failed because MFA was enabled. It’s really up to you and your knowledge of your environment to define the threshold and what is a high count. If you happen to have multiple offices or remote users, I strongly suggest excluding a list of known legitimate public IPs to reduce the noise.

sourcetype=o365:management:activity
LogonError=UserStrongAuthClientAuthNRequiredInterrupt 
| stats count by user, ClientIP 
| where count >= 10

eDiscovery

eDiscovery is a very sensitive feature, part of the Security & Compliance Center. It can be used to search anything in an indiviual account or all user accounts in your organization. While that seems creepy (and it is, which is why its use should be monitored), it is extremely useful for acquiring evidence in legal cases.

Compliance search started or exported

Anytime actions such as an eDiscovery compliance search is ran, it should be reported and validated by whoever is responsible for overseeing legal cases or HR in your company, due to the sensitivity of the information.

sourcetype=o365:management:activity
Workload=SecurityComplianceCenter 
Operation=AlertTriggered
Category=ThreatManagement
Name="eDiscovery search started or exported"
`comment("Use regex to re-write the user field with the user name that triggered the alert.
          Otherwise, the user appearing by default is 'SecurityComplianceAlerts'")`
| rex "(?<user>[\w\-\.]+@[\w\-]+\.+[\w\-]{2,4})"
| `map_notable_fields`

Compliance search requesting the deletion of items

Legitimate most of the time (i.e. removing phishing emails from users mailboxes), it can also be used to remove incriminating evidence. Pay a particular attention to the -PurgeType argument shown in the logs as a HardDelete indicates permanent removal.

sourcetype=o365:management:activity
Workload=SecurityComplianceCenter
"New-ComplianceSearchAction"
"-Purge"

As important as eDiscovery searches is monitoring users added as administrator in Security & Compliance Center.

sourcetype=o365:management:activity
Workload=SecurityComplianceCenter
"Add-eDiscoveryCaseAdmin"

Conclusion

I hope this will help you getting start. Feel free to message me privately here or on Twitter if you need any help or have any questions. You can also email me at [email protected]

Edit: Added a section on log ingestion and change the format of the queries so they are easier to read.

14 Likes

This is great content, thanks! I’ll take all the blue team info you have to post, always lots to learn. I never thought to pay attention to -PurgeType, for instance, but that’s a great tip to add to the ol’ workflow.

1 Like

I’ve registered here just to say thank you!
Am I only one who feels a lack of good articles with real-life experience from defenders? Not here but in general.

4 Likes

I completely agree! Blue team feels like a hidden secret art that every single company has their own, private, secret sauce.

I really would love a quick overview article of defense and how organizations are defending, what solutions and how they’re getting there.

3 Likes

Hello tr4cefl0w, could you provide us information on how did you configure Azure to send logs to your siem? I think I’m not doing it well because I don’t see the same kind of logs you are talking about.

Hey, I updated the article following your comment. You’ll find a section on that subject at the beginning. The search are based on fields extracted from the raw JSON logs.

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.