Austin Lai | April 30th, 2022
A collection of Azure Monitor or Sentinel Kusto Queries for your reference.
- Collection of Azure Monitor or Sentinel Kusto Queries
- Table of Contents
- Queries
- Monitor or Changes in Policy Detected
- Detect Successful SSH Brute Force Attack using watchlist - that extract username list from potention SSH Brute Force Attack
- Check specific host IP with SSH authentication failure using uid=0
- Linux add user to group via groupadd
- Linux add user via useradd
- Linux delete user via userdel
- Linux monitor sudo command
- Linux Password Change for user via passwd
- Linux Login with invalid user with IP address extracted
- Linux Failed Login with wrong password
- Linux Root SSH Failed Attempt
- Linux SSH with publickey
- Linux switch user to root via su command
- Finding MaliciousIP connect to VM
- Monitor Get Secret in Kubernetes Cluster or KubeServices within last 30 minutes
- Monitor Edit Secret in Kubernetes Cluster or KubeServices within last 30 minutes
- Linux SSH Brute Force attempts
- Monitor OPENVPN with MFA used Google Authenticator
- Monitor Azure DB for MySQL within specific resource group
- Monitor Azure Blob Storage
- Monitor Kubernetes Services available in Azure
- Check Azure Log Analytics Agent for Windows and Linux Heatbeat within last 5 minutes
- Linux audit log with Username and IP address extracted
- Search Kubernetes Logs in Azure with Azure Category
- Search container log in Azure
AzureActivity
| where parse_json(Properties).entity contains "policyDefinitions/XXXXX" or parse_json(Properties).entity contains "policyassignments/XXXXX"
| where isnotempty(ActivityStatusValue) and isnotnull(Properties_d) == true and isnotnull(parse_json(Properties_d).requestbody)
| sort by TimeGenerated desc
Detect Successful SSH Brute Force Attack using watchlist - that extract username list from potention SSH Brute Force Attack
Syslog
| where ProcessName =~ "sshd"
| where SyslogMessage contains "Accepted publickey" or SyslogMessage contains "Accepted password"
| extend
user = extract(@"(?:^Accepted publickey for |^Accepted password for )(\S+)", 1, SyslogMessage),
ip = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| where user in (
( _GetWatchlist('SSH-Brute-Force-user-list')
| project SSHBruteForceUserList))
Syslog
| where HostIP contains "x.x.x.x"
| where SyslogMessage contains "authentication failure" and SyslogMessage contains " uid=0"
| parse SyslogMessage with * "rhost=" ExternalIP
Syslog
| where ProcessName == "groupadd" and ( SyslogMessage !contains "syslog" or SyslogMessage !contains "omsagent" )
Syslog
| where ProcessName == "useradd" and ( SyslogMessage !contains "syslog" or SyslogMessage !contains "omsagent" )
Syslog
| where ProcessName == "userdel"
Syslog
| where ProcessName == "sudo" and SyslogMessage !contains "omsagent" and SyslogMessage !contains "session opened" and SyslogMessage !contains "session closed" and SyslogMessage !contains "waagent"
//| summarize by SyslogMessage
| parse kind=relaxed SyslogMessage with * ""
Syslog
| where ProcessName == "passwd"
| parse kind=relaxed SyslogMessage with * "password changed for " USER
Syslog
| where SyslogMessage contains "Invalid user" and SyslogMessage !contains "omsagent"
| extend IP_ADDRESS = extract(@"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})", 1, SyslogMessage)
| summarize count(IP_ADDRESS) by IP_ADDRESS, SyslogMessage
OR
Syslog
| where SyslogMessage contains "Invalid user" and SyslogMessage !contains "omsagent"
| extend IP_ADDRESS = extract(@"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})", 1, SyslogMessage)
| summarize count(Computer) by IP_ADDRESS, SyslogMessage, Computer
Syslog
| where SyslogMessage startswith "Failed Password"
| extend User = extract("for(?s)(.*)from",1,SyslogMessage)
| extend IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
| project HostName, SyslogMessage, EventTime, IPaddr, User
| summarize Count=count() by IPaddr
OR
Syslog
| where SyslogMessage contains "Failed password"
| extend IP_ADDRESS = extract(@"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})", 1, SyslogMessage)
| extend USER_NAME = extract(@"invalid\suser\s(\S+)", 1, SyslogMessage)
| summarize count(IP_ADDRESS) by IP_ADDRESS
OR
Syslog
| where SyslogMessage contains "Failed password"
| extend IP_ADDRESS = extract(@"(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})", 1, SyslogMessage)
| extend USER_NAME = extract(@"invalid\suser\s(\S+)", 1, SyslogMessage)
| summarize count(Computer) by IP_ADDRESS, USER_NAME, Computer, SyslogMessage
Syslog
| where (SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where SyslogMessage !contains "Disconnected" and SyslogMessage !contains "Connection closed"
| where ProcessName =~ "sshd"
| extend
USER = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)", 1, SyslogMessage),
S_IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
S_Port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| where USER == "root"
| summarize Count = count() by S_IPaddr, USER, Computer,_ResourceId
OR
Syslog
| where (SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where SyslogMessage !contains "Disconnected" and SyslogMessage !contains "Connection closed"
| where ProcessName =~ "sshd"
| extend
USER = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)", 1, SyslogMessage),
S_IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
S_Port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| project TimeGenerated, EventTime, Computer, USER, S_IPaddr, S_Port, SyslogMessage
| where USER == "root"
| summarize PerHourCount = count() by S_IPaddr, USER, Computer
Syslog
| where SyslogMessage contains "Accepted publickey"
| where ProcessName =~ "sshd"
| parse kind=relaxed SyslogMessage with * "Accepted publickey for " USER " from " S_IPaddr " port" S_Port " ssh2" *
Syslog
| where ProcessName == "su" and SyslogMessage !contains "omsagent"
| parse kind=relaxed SyslogMessage with * "su for " USER " by " *
VMConnection
| where MaliciousIp != ""
AzureDiagnostics
| where TimeGenerated > ago(30m)
| where ResourceType == "MANAGEDCLUSTERS"
| where Category == "kube-audit"
| where log_s contains "secret"
| where log_s contains "get"
AzureDiagnostics
| where TimeGenerated > ago(30m)
| where ResourceType == "MANAGEDCLUSTERS"
| where Category == "kube-audit"
| where parse_json(log_s).requestURI == "/api/v1/namespaces/XXX/secrets/XXX?fieldManager=kubectl-edit"
Syslog
| where (SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where SyslogMessage !contains "Disconnected" and SyslogMessage !contains "Connection closed"
| where ProcessName =~ "sshd"
| extend
USER = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)", 1, SyslogMessage),
S_IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
S_Port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| project TimeGenerated, EventTime, Computer, USER, S_IPaddr, S_Port, SyslogMessage
| summarize EventTimes = make_list(EventTime), PerHourCount = count() by S_IPaddr, USER, Computer
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes)
| summarize
StartTimeUtc = min(EventTimes),
EndTimeUtc = max(EventTimes),
UserList = tostring(makeset(USER)),
Count = sum(PerHourCount)
by S_IPaddr, Computer
OR
Syslog
| where (SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where SyslogMessage !contains "Disconnected" and SyslogMessage !contains "Connection closed"
| where ProcessName =~ "sshd"
| extend USER = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)",1,SyslogMessage), S_IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage), S_Port = extract(@".*?port\s(\S+)",1,SyslogMessage)
| project EventTime, Computer, USER, S_IPaddr, S_Port, SyslogMessage
| summarize EventTimes = make_list(EventTime), PerHourCount = count() by S_IPaddr, USER, Computer
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes)
| summarize StartTimeUtc = min(EventTimes), EndTimeUtc = max(EventTimes), UserList = tostring(makeset(USER)), Count = sum(PerHourCount) by S_IPaddr, Computer
| sort by EndTimeUtc desc
OR
let threshold = 3;
Syslog
// | where TimeGenerated > ago(5m)
| where (SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where SyslogMessage !contains "Disconnected" and SyslogMessage !contains "Connection closed"
| where ProcessName =~ "sshd"
| extend USER = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)",1,SyslogMessage), S_IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage), S_Port = extract(@".*?port\s(\S+)",1,SyslogMessage)
| project EventTime, Computer, USER, S_IPaddr, S_Port, SyslogMessage
//| summarize EventTimes = make_list(EventTime), PerHourCount = count() by S_IPaddr, USER, Computer, bin(EventTime, 5m)
| summarize EventTimes = make_list(EventTime), PerHourCount = count() by S_IPaddr, USER, Computer, bin(EventTime, 4h)
| where PerHourCount > threshold
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes)
| summarize StartTimeUtc = min(EventTimes), EndTimeUtc = max(EventTimes), UserList = tostring(makeset(USER)), Count = sum(PerHourCount) by S_IPaddr, Computer
| sort by StartTimeUtc desc
OR
let threshold = 5;
Syslog
| where (SyslogMessage contains "Failed password for invalid user" or SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where ProcessName =~ "sshd"
//| parse kind=relaxed SyslogMessage with * "invalid user" user " from " ip " port" port " ssh2"
| extend
user = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)", 1, SyslogMessage),
ip = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| project user, ip, port, SyslogMessage, EventTime, Computer
| summarize EventTimes = make_list(EventTime), PerHourCount = count() by ip, bin(EventTime, 4h), user, Computer
| where PerHourCount > threshold
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes)
| summarize
StartTimeUtc = min(EventTimes),
EndTimeUtc = max(EventTimes),
UserList = makeset(user),
sum(PerHourCount)
by IPAddress = ip, Computer
| extend UserList = tostring(UserList)
| extend
timestamp = StartTimeUtc,
IPCustomEntity = IPAddress,
AccountCustomEntity = UserList
OR
let threshold = 5;
Syslog
| where (SyslogMessage contains "Failed password for invalid user" or SyslogMessage contains "invalid user" or SyslogMessage contains "Failed password")
| where ProcessName =~ "sshd"
//| parse kind=relaxed SyslogMessage with * "invalid user" user " from " ip " port" port " ssh2"
| extend
user = extract(@"(?:^Failed password for invalid user |^Failed password for |^Invalid user )(\S+)", 1, SyslogMessage),
ip = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))", 1, SyslogMessage),
port = extract(@".*?port\s(\S+)", 1, SyslogMessage)
| project user, ip, port, SyslogMessage, EventTime, Computer
| summarize EventTimes = make_list(EventTime), PerHourCount = count() by ip, bin(EventTime, 4h), user, Computer
| where PerHourCount > threshold
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes)
| summarize
StartTimeUtc = min(EventTimes),
EndTimeUtc = max(EventTimes),
UserList = makeset(user),
sum(PerHourCount)
by IPAddress = ip, Computer
| extend UserList = tostring(UserList)
| extend
timestamp = StartTimeUtc,
IPCustomEntity = IPAddress,
AccountCustomEntity = UserList
If you have OpenVPN VM and services setup in Azure, you will be able to monitor OpenVPN activities.
//Accepted google_authenticator for user
//Invalid verification code for user
//Did not receive verification code from user
//Failed to read "/etc/openvpn/google-authenticator/user" for "user"
Syslog
| where ProcessName == "openvpn(pam_google_authenticator"
AzureDiagnostics
| where ResourceGroup contains "XXX"
| where Category contains "MySqlAuditLogs"
You can refer to this link for more information -> https://docs.microsoft.com/en-us/azure/storage/blobs/blob-storage-monitoring-scenarios
StorageBlobLogs
| where OperationName == "PutBlob" or
OperationName == "PutBlock" or
OperationName == "PutBlockList" or
OperationName == "AppendBlock" or
OperationName == "SnapshotBlob" or
OperationName == "CopyBlob" or
OperationName == "SetBlobTier"
| extend ContainerName = split(parse_url(Uri).Path, "/")[1]
| summarize WriteSize = sum(RequestBodySize), WriteCount = count() by tostring(ContainerName)
KubeServices
| summarize by SelectorLabels
Heartbeat
| summarize LastCall = max(TimeGenerated) by Computer
| where LastCall < ago(5m) | count
Syslog
| where (Facility == 'authpriv' and SyslogMessage has 'sshd:auth' and SyslogMessage has 'authentication failure') or (Facility == 'auth' and ((SyslogMessage has 'Failed' and SyslogMessage has 'invalid user' and SyslogMessage has 'ssh2') or SyslogMessage has 'error: PAM: Authentication failure'))
| extend User = extract("for(?s)(.*)from",1,SyslogMessage)
| extend IPaddr = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
| where EventTime < ago(60m)
| summarize Count=count() by Computer
AzureDiagnostics
| where Category contains "kube"
| summarize by Category
ContainerLog
| summarize by LogEntry
You may look into Azure table listed below for further log search:
- ContainerImageInventory
- ContainerInventory
Or looking into container registry from Azure Table below:
- ContainerRegistryRepositoryEvents
- ContainerRegistryLoginEvents
Do let me know any command or step can be improve or you have any question you can contact me via THM message or write down comment below or via FB