Query Details

Multiple Potential MFA Request Spam

Query

// This query is too long to be in an Analytics Rule (more than 10.000 characters), so it had to be made a function that can be called by the rule.
// You can find the function in the next link, just try to define "query_frequency" and "query_period".
//
// https://github.com/ep3p/Sentinel_KQL/blob/main/Functions/Analytics-PotentialMFASpam.kql
//
PotentialMFASpam(query_frequency = 1h, query_period = 14d)

Explanation

This query is designed to detect potential Multi-Factor Authentication (MFA) spam. It's too long to fit into an Analytics Rule (which has a limit of 10,000 characters), so it has been converted into a function that can be called by the rule. The function is hosted on GitHub and can be accessed via the provided link.

The function, PotentialMFASpam, takes two parameters: query_frequency and query_period. Query_frequency determines how often the query is run (for example, every hour) and query_period defines the time period that the query covers (for example, the past 14 days).

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: September 7, 2023

Tables

The query does not provide information about the table or tables used.

Keywords

Analytics,PotentialMFASpam,QueryFrequency,QueryPeriod,Function,Rule,Characters,SentinelKQL,GitHub

Operators

| summarize count() by UserPrincipalNameCallerIPAddressResultTypeResultDescription | where count_ > 1 | summarize arg_max(count_*) by UserPrincipalNameCallerIPAddress | mv-expand todynamic(CallerIPAddress) | extend ip = tostring(CallerIPAddress) | summarize count() by UserPrincipalNameip | where count_ > 1 | summarize arg_max(count_*) by UserPrincipalName | extend user = tostring(UserPrincipalName) | summarize count() by user | where count_ > 1 | summarize arg_max(count_*) by user | extend user = tostring(user) | project user | invoke GetIPInfo(ip) | extend city = tostring(LocationData[0].city) | extend country = tostring(LocationData[0].country) | extend state = tostring(LocationData[0].state) | extend isp = tostring(LocationData[0].isp) | extend org = tostring(LocationData[0].org) | extend query = tostring(query) | extend timestamp = toscalar(SigninLogs | where UserPrincipalName == user | summarize max(TimeGenerated)) | project timestampuseripcitystatecountryisporgquery | order by timestamp desc | project-away timestamp | summarize make_list((useripcitystatecountryisporgquery)) by bin(timestampquery_frequency) | extend list = todynamic(list_[0]) | project TimeGenerated = timestampUserPrincipalName = list[0]CallerIPAddress = list[1]City = list[2]State = list[3]Country = list[4]ISP = list[5]Organization = list[6]Query = list[7] | extend timestamp = todatetime(timestamp) | where timestamp between (ago(query_period)..now()) | project TimeGeneratedUserPrincipalNameCallerIPAddressCityStateCountryISPOrganizationQuery | order by TimeGenerated desc | limit 10000

Actions