Azure-Sentinel/Tools/RuleMigration/AnalyticsUseCases.md

7.1 KiB

Azure Sentinel Analytics Usecases

No Use-case Artefacts
1 Receive an alert when users are accessing resources outside a specified time range. Data Sources – Azure AD Sign-in logs, Defined time range Azure AD Group
that will be monitored for login activity, a logic app that pulls members of AD Group
into a LA table, Analytics rule that will trigger an incident when a member of the AD Group
signs in outside of the defined time range. KQL Query:
SigninLogs |extend TimeInUK = CreatedDateTime|extend day = (dayofweek(TimeInUK))
| extend daystarting = tostring(day) //daystrating definitions, 1=Monday, 2=Tuesday,
3=Wednesday, 4=Thursday, 5=Friday, 6=Saturday, 7=Sunday| where daystarting
== "6.00:00:00" or daystarting == "7.00:00:00" or hourofday(TimeInUK)
!between (7...18)| project TimeGenerated , TimeInUK , UserPrincipalName
, day , AppDisplayName , username = UserPrincipalName |
join (UserWatchlist_CL | project-rename username = Username_s )
on username | project TimeInUK , day , username , AppDisplayName)*
2 Use a watchlist to dismiss expected alerts Data Sources – Azure Defender for IoT, list of user and device pairs uploaded into a Watchlist,
Analytics rule that will look up the watchlist and a Playbook that will close incidents from expected alerts.KQL Query:
let alert = (SecurityAlert | where TimeGenerated > ago(14d) |where DisplayName
 == "Brute force attempt"|extend DeviceID = tostring(parse_json(ExtendedProperties)
"DeviceId"])| extend UserID = tostring(parse_json(ExtendedProperties)["UserId"])
|extend UserName = tostring(parse_json(ExtendedProperties)["UserName"])
| project DeviceID, UserName,SystemAlertId);let watchlst =
 (_GetWatchlist("iwatch"));alert| join kind=inner watchlst on
 $left.DeviceID == $right.device and $left.UserName == $right.username
3 Detect priviledge escalation-user created then deleted within 10 minutes Data sources: Azure AD and Windows Security Events. KQL Query:
let timeframe = 10m;let lookback = 1d;let account_created =SecurityEvent
| where TimeGenerated > ago(lookback+timeframe)| where EventID == "4720"
// A user account was created| where AccountType =~ "User"
| project creationTime = TimeGenerated, CreateEventID =
EventID,Activity, Computer, TargetUserName, UserPrincipalName,
AccountUsedToCreate = SubjectUserName, TargetSid,
SubjectUserSid;account_created | join kind= inner (account_deleted)
on Computer, TargetUserName| where deletionTime - creationTime
< lookback| where tolong(deletionTime - creationTime)
>= 0|extend timestamp = creationTime, AccountCustomEntity
= AccountUsedToCreate, HostCustomEntity = Computer*
4 Detect Solorigate Network Beacon Data sources: DNS, CISCO ASA, Palo Alto Networks, Microsoft 365 Defender. KQL Query:
let domains = dynamic(["incomeupdate.com","zupertech.com","databasegalore.com","panhardware.com","avsvmcloud.com","digitalcollege.org","freescanonline.com","deftsecurity.com","thedoccloud.com","virtualdataserver.com","lcomputers.com","webcodez.com","globalnetworkissues.com","kubecloud.com","seobundlekit.com","solartrackingsystem.net","virtualwebdata.com"]);let timeframe = 6h;(union isfuzzy=true(CommonSecurityLog | where TimeGenerated >= ago(timeframe)| parse Message with * '(' DNSName ')' *  | where DNSName in~ (domains) or DestinationHostName has_any (domains) or RequestURL has_any(domains)  | extend AccountCustomEntity = SourceUserID, HostCustomEntity = DeviceName, IPCustomEntity = SourceIP  ),(DnsEvents  | where TimeGenerated >= ago(timeframe) | extend DNSName = Name| where isnotempty(DNSName)| where DNSName in~ (domains) | extend IPCustomEntity = ClientIP),VMConnection| where TimeGenerated >= ago(timeframe)| parse RemoteDnsCanonicalNames with * '["' DNSName '"]' *| where isnotempty(DNSName)| where DNSName in~ (domains)| extend IPCustomEntity = RemoteIp ),(DeviceNetworkEvents | where TimeGenerated >= ago(timeframe)| where isnotempty(RemoteUrl)| where RemoteUrl  has_any (domains)| extend DNSName = RemoteUrl| extend IPCustomEntity = RemoteIP| extend HostCustomEntity = DeviceName)) *
5 An IP address that had (failed) attempts to sign in to one or more disabled accounts signed in successfully to another account. Data Sources: Azure AD.Analytics that looks for specific Azure AD Sign-In log entries
50057 = User account is disabled.The account has been disabled by an administrator.KQL Query:
let lookBack = 1d;SigninLogs | where TimeGenerated >= ago(lookBack)
| where ResultType == "50057"| where ResultDescription == "User account
is disabled.The account has been disabled by an administrator."|
summarize StartTimeUtc = min(TimeGenerated),EndTimeUtc
= max(TimeGenerated),
disabledAccountLoginAttempts = count(),disabledAccountsTargeted
= dcount(UserPrincipalName), applicationsTargeted
= dcount(AppDisplayName), disabledAccountSet
= makeset(UserPrincipalName),applicationSet
= makeset(AppDisplayName) by IPAddress| order by
disabledAccountLoginAttempts desc| join
kind= leftouter (// Consider these IPs suspicious - and alert
any related successful sign-insSigninLogs| where TimeGenerated
>= ago(lookBack)| where ResultType == 0| summarize
successfulAccountSigninCount = dcount(UserPrincipalName), successfulAccountSigninSet
= makeset(UserPrincipalName, 15) by IPAddress// Assume IPs associated with sign-ins
from 100+ distinct user accounts are safe| where successfulAccountSigninCount
< 100) on IPAddress// IPs from which attempts to authenticate
as disabled user accounts originated, and had a non-zero
success rate for some other account| where successfulAccountSigninCount
!= 0| project StartTimeUtc, EndTimeUtc, IPAddress, disabledAccountLoginAttempts,
disabledAccountsTargeted, disabledAccountSet, applicationSet,
successfulAccountSigninCount,successfulAccountSigninSet|
order by disabledAccountLoginAttempts| extend timestamp =
StartTimeUtc,IPCustomEntity = IPAddress
6 Detect Brute Force attack based on statistical detections Data sources: Azure AD. KQL Query:
let signin_threshold = toscalar(SigninLogs | where TimeGenerated >= startofday(ago(7d))
and TimeGenerated < startofday(now()) | where ResultType !in ("0", "50125", "50140")
| where IPAddress != "127.0.0.1" | summarize cnt=count() by IPAddress, bin(TimeGenerated, 1d)
| summarize percentile(cnt, 95)); | SigninLogs| where signin_threshold > 10 and Location == "KE"