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" |