Prevent phishing based on domain registrations

Business email compromise and phishing are just two of the threats sent to hundreds and thousands of email inboxes around the world every day. As defenders, we use various tools and methods to limit the delivery of these emails to the intended target.

In most cases, the attackers start by registering a domain that closely resembles the target company’s own domain name, or simply include the company name in the domain (e.g. microsoftsonliine[.]com).

Microsoft Defender for Office 365 has a built-in feature to combat these attempts: Impersonation Detection. This anti-phishing protection detects similar names in either the user or domain part of the sender’s email address and quarantines those emails.

This already keeps out quite the amount of bad data. But what if the attacker does not rely on email to send the initial lure to the target? What if they choose to use an unmonitored channel such as LinkedIn to send a phishing lure?

For those scenarios you can use Microsoft Defender SmartScreen and Enhanced Phishing Protection.

But what if you want to add an extra layer of protection for all devices protected by Microsoft Defender for Endpoint?

As long as you know the domain name you want to block access to, Microsoft Defender for Endpoint combined with custom block indicators is the way to go.

Microsoft Defender for Endpoint indicators

Network protection must be enabled on the endpoint to allow the block to work outside of Microsoft Edge.

But that means you need to know about potential malicious domain registrations and make that data available for your SOC analyst to act on.

Domain registration information

Let’s start with the basics. You must keep track of all newly registered domains that resemble your domain name. Of course, you could try to gather this data yourself. But there are many different services, that keep track of domain registrations for you.

When you chose a service make sure that it offers you an REST API to access the data and allows you to limit the search results based on a keyword and a timespan.


Example flow using different Azure services

As you can see in the illustration above my basic workflow is straight forward and build solely, except for of the domain tracking service, using Azure services.

  1. A Logic App is triggered once a day
  2. It retrieves the newly registered domain names
  3. This data is sent to the Sentinel Log Analytics workspace and
  4. analyzed by a Analytics Rule which creates an incident for each new domain.
  5. The SOC than can go ahead, check the domain and if needed block the domain name completely.

Logic App

A basic Logic App flow used to query the domain information API and send the data to the log analytics workspace

The Logic App itself is also not very complicated, but quite effective.

  1. Once a day it is triggered by the scheduler
  2. It then retrieves the API secret from a Key Vault using the Managed Identity of the Logic App
  3. Then it initializes and sanitizes the needed information, like how far to look back in the domain registration database and what keyword to search for.
  4. Next it queries the services Rest API using the initialized data and parses the result.
  5. Based on the outcome of the result either the logic app does nothing (no new data available) or it sends all the results into a custom table in the Microsoft Sentinel Log Analytics workspace named RegisteredDomains.

Microsoft Sentinel

Custom table

The custom table itself can be quired, like any other table. But because it is not a Microsoft provided table, it will have the suffix _CL.

So, when querying the data you must use RegisteredDomains_CL instead of RegisteredDomains.

Results in RegisteredDomains_CL

In this example I tracked all the newly registered domain, containing the keyword “microsoft”, in the last 10 days. This resulted in a list of 138 unique new domains, some already flagged on (e.g., support-microsoft[.]tech).

Analytics rule

Now, with the data available in Sentinel, we can use the following KQL query to get a list of all newly added domains.

| where QueryType_s == "domainsearch"
| summarize arg_max(TimeGenerated,*) by DomainName_s
| project TimeGenerated, DomainName=DomainName_s, SearchTerm=SearchTerm_s

Based on this let’s built an Analytics rule to create an incident for reach out the results.

One great feature in Microsoft Sentinel is the ability to detonate URLs and provide the Analyst with a screenshot of the website and additional information like the final URL reached when visiting the website.

To achieve this you must expose the URL as an URL entity within the incident as described in this, slightly outdated, blog post. So in this case we add two custom URLs to support both HTTP and HTTPS.

| where QueryType_s == "domainsearch"
| summarize arg_max(TimeGenerated,*) by DomainName_s
| project TimeGenerated, DomainName=DomainName_s, SearchTerm=SearchTerm_s
| extend DetonateHTTPUrl = strcat("http://",DomainName)
| extend DetonateHTTPSUrl = strcat("https://",DomainName)

Then you configure in the entity mapping configuration of the Analytics rule all three values. I also included the fields DomainIsActive and SearchTerm as custom details, to expose this information directly to the analyst without the need to rerun the query.

URLs mapped to entities in Sentinel

The result can be viewed in the investigation graph as well as the new incident overview.

The URL detonation in the Sentinel investigation view

The new incident overview showing the entities

Set the rule to run once a day, otherwise you might create duplicate incidents, and since the new information if only updated daily, there is no need to do it more often.

It’s also very important to disable event grouping, to trigger a new alert for each event.

Event grouping disabled to create a new alert per entry in the results


This is the Analytics Rule as YAML file which you also find in my GitHub repository.

enabled: true
  createIncident: true
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionEnabled: false
  aggregationKind: AlertPerResult
kind: Scheduled
id: 91302a05-c2ca-414d-8341-c82658ea3c1f
- ResourceDevelopment
triggerOperator: gt
suppressionDuration: 5h
query: |+
  | where QueryType_s == "domainsearch"
  | summarize arg_max(TimeGenerated,*) by DomainName_s
  | project TimeGenerated, DomainName=DomainName_s, IsActive=Active_s, SearchTerm=SearchTerm_s
  | extend DetonateHTTPUrl = strcat("http://",DomainName)
  | extend DetonateHTTPSUrl = strcat("https://",DomainName)  
queryFrequency: 1d
  DomainIsActive: IsActive
  SearchTerm: SearchTerm
queryPeriod: 1d
name: Potential malicous domain registration
triggerThreshold: 0
severity: Low
  alertnameFormat: 'New domain registered: {{DomainName}}'
  alertDescriptionFormat: "A new domain was registered that contains the keyword \"{{SearchTerm}}\"\n\nThis could be a first step in setting up malicious infrastructure for e.g. phishing or email fraud attempts.\n\nCheck if this domain was registered by your company, otherwise block this URL for incoming emails and add it as custom block indicator for Microsoft Defender for Endpoint and Microsoft Sentinel "
  alertDynamicProperties: []
- T1583
description: This rule checks the custom log RegisteredDomains and alerts when new entries are added. The custom log is created using a logic app, checking for newly created domains that contain a certain keyword
- entityType: URL
  - identifier: Url
    columnName: DetonateHTTPSUrl
- entityType: URL
  - identifier: Url
    columnName: DetonateHTTPUrl
- entityType: DNS
  - identifier: DomainName
    columnName: DomainName

Next steps

Just creating the incident is not enough. Your analysts must know what to do with this information.

Depending on your operational process you could create an automation rule in Sentinel to add the different tasks, that the analyst should perform.

Of course you also could create another logic app that performs those tasks automatically, but this part is out of scope for this blog post.

Add custom tasks to the incident based on the Analytics rule