Many companies are using cloud services such as Microsoft 365 for email, file sharing and communicating. If an attacker gains access to valid credentials that allows them to authenticate to the account, all information within the account is usually instantly accessible. Therefor, implementing multi factor authentication is one of the most important steps while securing your cloud environment.

As a pentester I like the tool MFASweep, its helpful to check the multifactor authentication (MFA) configuration of a Microsoft tenant. If there is some misconfiguration within the tenants MFA configuration, the tool could help to identify this. MFASweep logs in through different authentication protocols on a Microsoft environment. If a login without multifactor authentication is possible, it will return this to the user:

MFASWeep in action.

In our Attic app we are using Azure Sentinel to monitor for potentially malicious behavior in the Microsoft tenants of our customers. At Zolder we believe its important to translate our pentesters experience into security mitigations or detection rules. Because of our pentester experience with MFASweep, we came up with an idea: are we able to detect the usage of MFASweep against our customers, that would be pretty awesome, right? 🙂

We ended up with the following KQL-query for Azure Sentinel. The Azure Active Directory data connector is required including the sign-in logs for our query to work. It works as follows: we look for a pattern of login attempts from the same user across multiple protocols.

let Apps = dynamic([
    "1b730954-1685-4b74-9bfd-dac224a7b894", // Azure Active Directory PowerShell
    "1950a258-227b-4e31-a9cf-717495945fc2", // Microsoft Azure PowerShell
    "00000002-0000-0ff1-ce00-000000000000"  // Office 365 Exchange Online
  ]);
  let Auths = dynamic([
    "Exchange ActiveSync",
    "Browser",
    "Exchange Web Services",
    "Mobile Apps and Desktop clients"
  ]);
  SigninLogs 
  | where AppId in (Apps)
  | extend ClientAppUsed
  | where ClientAppUsed in (Auths)
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by ClientAppUsed, Identity, AlternateSignInName, ResultType, ResultDescription
  | where (ClientAppUsed == "Browser" and count_ >= 2)
    or (ClientAppUsed == "Exchange ActiveSync" and count_ >= 1)
    or (ClientAppUsed == "Mobile Apps and Desktop clients" and count_ >= 2)
    or (ClientAppUsed == "Exchange Web Services" and count_ >= 1)
  | summarize StartTime = min(StartTime), EndTime = max(EndTime), count() by Identity, AlternateSignInName, ResultType, ResultDescription
  | where count_ == 4

It’s far from perfect, but it works in detecting the default scan behavior of MFASweep against your Microsoft tenant. The ResultType field can be used to validate whether the attackers succeeded his attack (used valid credentials). ResultType 0 means a successful login, while 50126 is a failed login attempt. The query yml file with all meta-information can be found on our Github page.

We also implemented the Sentinel rule into our app. Whenever MFASweep is detected at a customer, he receives a push notification about the MFASweep detection. We are using jinja to show which account was targeted and whether the sweep was successful. Here is a video of the app in action (it’s in Dutch):