Detect and alert on unusual high phish or malware email volume
Microsoft does a great job in detecting and preventing malware and phishing emails with Microsoft Defender for Office 365. Since this feature works mostly in the background, even larger than usual malware campaigns are prevented without the Administrator noticing.
Using the campaign view you can check for those events and get a good overview which kind of malware is targeting your users.
You can also drill down even further to identify which users were targeted as part of this campaign.
This data allows you to concentrate your attack simulation training or other in house security training on those users to make them more vary about potential threats.
But this method has one major drawback, your team has to have the time and resources to do this kind of retroactive work, while on the other hand not being alerted that there is higher than usual activity against your users.
Custom detection based on a fixed baseline
So let’s use advanced hunting and custom detections to implement a solution to get an alert when a manual defined threshold is reached.
The following KQL query visualizes the incoming mails that are send directly to quarantine and are categorized as either malware or phishing attempts.
let Threshold = 50; EmailEvents | where EmailAction == "Send to quarantine" | where ThreatTypes has "Malware" or ThreatTypes has "Phish" | summarize Count=count() by bin(Timestamp, 1h) | extend Threshold = Threshold | render timechart
Based on this chart you can go ahead and tweak the threshold for your environment. I would recommend to use the data from the last 30 days for this.
But since a custom detection needs additional information to get triggered, namely an timestamp and a report id, we have to add this information.
// Author: Fabian Bader // Blog: https://cloudbrothers.info/en/detect-alert-unusual-high-phish-malware-email-volume // // Change the threshold based on your environment let Threshold = 50; // Query all mail that was send to quarantine and is either malware or phish let PhishAndMalwareEmail = EmailEvents | where EmailAction == "Send to quarantine" | where ThreatTypes has "Malware" or ThreatTypes has "Phish"; // Check in which timespan the threshold was breached let TimeAboveThreshold = PhishAndMalwareEmail | summarize Count=count() by bin(Timestamp, 1h) | extend Threshold = Threshold | where Count>=Threshold; // Only return mail events that are within in a timespan that breached the threshold PhishAndMalwareEmail | extend BinTime = bin(Timestamp, 1h) | where BinTime in (TimeAboveThreshold) // Only return a single result from the dataset, remove this line if you want alerts for every mail // This can result in many alerts and not all of them might be summarized in a single incident | limit 1
This query first queries all the malware and phish mail and then checks if the defined threshold was breached based on the total malicious mail within a one hour timespan. Only mail that was part of this timespan will be used to create an alert.
Because you will receive an alert for every mail, the default is to limit the result set to 1 item. Based on this alert you team can check the campaign view for additional information. If you remove the limit you will have the information about all targeted users in the created incident, but you might end up with multiple incidents. This is because not all mails will be related to the same campaign and the summarize logic will break them up.
To create your custom alert run the query and then click “Create detection rule” and fill out the necessary information. I would recommend to schedule this alert every hour.
As MITRE technique select “T1566 Phishing” or something appropriate to your environment.
For impacted entities select “RecipientEmailAddress” and “ReceipientObjectId”
Custom detection based on anomaly detection
The initial query uses a hardcoded threshold, but KQL supports a native way of detecting anomalies in your datasets.
This function calculates, based on a dynamic array, an anomaly score for each item and you can use this score to define your threshold and filter out normal volumes of quarantined mail items.
You still have to set a threshold, but it’s not based on the total number of emails, it’s based on how severe the anomaly needs to be.
I hope this custom detection rule and alerting technique is useful for you and your team or you just use it as a base to create your own KQL query. If this is the case, share your query with the community over on GitHub or Twitter.