Merge pull request #1069 from Azure/insights_folderRename
Insights folder rename
This commit is contained in:
Коммит
db24d633b0
|
@ -1,4 +1,4 @@
|
|||
Id: guid
|
||||
Id: guid
|
||||
DisplayName: string
|
||||
Description: string
|
||||
InputEntityType: string
|
||||
|
|
|
@ -11,7 +11,7 @@ QueryPeriodBefore: 1d
|
|||
QueryPeriodAfter: 1d
|
||||
DataSources:
|
||||
- SecurityEvent
|
||||
Tactics:
|
||||
Tactics:
|
||||
- Persistence
|
||||
- Discovery
|
||||
- LateralMovement
|
||||
|
@ -37,14 +37,14 @@ query: |
|
|||
| extend p_Account_NTDomain = case(
|
||||
v_Account_NTDomain has '\\', tostring(split(v_Account_UPNSuffix, '\\')[0]),
|
||||
v_Account_NTDomain
|
||||
)
|
||||
)
|
||||
// parse Account sections
|
||||
| extend Account_UPNSuffix = iff(Account has '@', tostring(split(Account,'@')[1]),'')
|
||||
| extend Account_NTDomain = iff(Account has '\\', tostring(split(Account,'\\')[0]),'')
|
||||
| extend Account_Name = extract(@'^([^\\]*\\)?([^@]+)@?',2,Account)
|
||||
// filter by account: Name has to match, NTDomain and UPNSuffix should not be different
|
||||
| where ( (isnotempty(Account_Name) and Account_Name==p_Account_Name)
|
||||
and
|
||||
| where ( (isnotempty(Account_Name) and Account_Name==p_Account_Name)
|
||||
and
|
||||
iff(isnotempty(p_Account_NTDomain) and isnotempty(Account_NTDomain) ,p_Account_NTDomain==Account_NTDomain,true )
|
||||
and
|
||||
iff(isnotempty(p_Account_UPNSuffix) and isnotempty(Account_UPNSuffix) ,p_Account_UPNSuffix==Account_UPNSuffix,true )
|
||||
|
@ -53,8 +53,8 @@ query: |
|
|||
by Computer, Account
|
||||
| top 10 by Host_Aux_FailedLoginsCount
|
||||
| parse Computer with Host_NTDomain '\\' *
|
||||
| extend Host_HostName = tostring(split(Computer,'.')[0]),
|
||||
| extend Host_HostName = tostring(split(Computer,'.')[0]),
|
||||
Host_DnsDomain = strcat_array(array_slice(split(Computer,'.'),1,256),'.')
|
||||
| project-away Computer, Account
|
||||
| project-away Computer, Account
|
||||
};
|
||||
MostFailedLogins('<Name>','<NTDomain>','<UPNSuffix>')
|
||||
|
|
|
@ -28,7 +28,7 @@ query: |
|
|||
| where SyslogMessage has v_Account_Name
|
||||
| extend info = pack('HostName', HostName, 'HostIP', HostIP)
|
||||
| summarize Process_Aux_StartTime=min(EventTime), Process_Aux_EndTime=max(EventTime), count(), Process_Aux_info = makeset(info) by Computer, ProcessName, ProcessID
|
||||
| top 10 by count_ asc nulls last
|
||||
| top 10 by count_ asc nulls last
|
||||
| project Process_Aux_StartTime, Process_Aux_EndTime, Process_Host_UnstructuredName=Computer, Process_ImageFile_FullPath=ProcessName, Process_ProcessId=ProcessID, Process_Aux_info
|
||||
};
|
||||
// change <Name> value below
|
||||
|
|
|
@ -44,7 +44,7 @@ query: |
|
|||
| extend ServiceAccount = tostring(EventDataParse.DataItem.EventData.Data[4]['#text'])
|
||||
| where ImagePath !has '\\ProgramData\\Microsoft\\Windows Defender\\Definition Updates\\'
|
||||
| extend Process_Aux_Account_info = pack('ServiceName', ServiceName, 'ServiceType', ServiceType, 'StartType', StartType, 'ServiceAccount', ServiceAccount)
|
||||
| summarize Process_Host_Aux_StartTimeUtc = min(TimeGenerated), Process_Host_Aux_EndTimeUtc = max(TimeGenerated) by Process_Host_UnstructuredName = Computer, Process_Account_Name,
|
||||
| summarize Process_Host_Aux_StartTimeUtc = min(TimeGenerated), Process_Host_Aux_EndTimeUtc = max(TimeGenerated) by Process_Host_UnstructuredName = Computer, Process_Account_Name,
|
||||
Process_Account_NTDomain, Process_Account_UnstructuredName = UserName, Process_ImageFile_FullPath = ImagePath, tostring(Process_Aux_Account_info)
|
||||
| top 10 by Process_Host_Aux_StartTimeUtc desc nulls last
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ query: |
|
|||
| summarize min(TimeGenerated), max(TimeGenerated), IP_Aux_info = makeset(info) by ClientIP
|
||||
| project IP_Aux_StartTime = min_TimeGenerated, IP_Aux_EndTime = max_TimeGenerated, ClientIP, IP_Aux_info
|
||||
| project-rename IP_Address=ClientIP
|
||||
| top 10 by IP_Aux_StartTime desc nulls last
|
||||
| top 10 by IP_Aux_StartTime desc nulls last
|
||||
};
|
||||
// change <Name> value below
|
||||
GetAllIPbyAccount ('<Name>')
|
||||
|
|
|
@ -14,7 +14,7 @@ Tactics:
|
|||
- Persistence
|
||||
- Discovery
|
||||
- LateralMovement
|
||||
- Collection
|
||||
- Collection
|
||||
query: |
|
||||
|
||||
let GetAllHostsbyAccount = (v_Account_Name:string){
|
||||
|
@ -32,7 +32,7 @@ query: |
|
|||
| extend info = pack('UserDisplayName', UserDisplayName, 'UserPrincipalName', UserPrincipalName, 'AppDisplayName', AppDisplayName, 'ClientAppUsed', ClientAppUsed, 'Browser', tostring(Browser), 'IPAddress', IPAddress, 'ResultType', ResultType, 'ResultDescription', ResultDescription, 'Location', Location, 'State', State, 'City', City, 'StatusCode', StatusCode, 'StatusDetails', StatusDetails)
|
||||
| summarize min(TimeGenerated), max(TimeGenerated), Host_Aux_info = makeset(info) by RemoteHost , tostring(OS)
|
||||
| project min_TimeGenerated, max_TimeGenerated, RemoteHost, OS, Host_Aux_info
|
||||
| top 10 by min_TimeGenerated desc nulls last
|
||||
| top 10 by min_TimeGenerated desc nulls last
|
||||
| project-rename Host_UnstructuredName=RemoteHost, Host_OSVersion=OS, Host_Aux_StartTime=min_TimeGenerated, Host_Aux_EndTime=max_TimeGenerated
|
||||
};
|
||||
// change <Name> value below
|
||||
|
|
|
@ -13,7 +13,7 @@ DataSources:
|
|||
Tactics:
|
||||
- LateralMovement
|
||||
- CredentialAccess
|
||||
query: |
|
||||
query: |
|
||||
|
||||
let BRUTEFORCE_THRESHOLD = 10;
|
||||
let SuccessfulLoginEventId = 4624;
|
||||
|
@ -30,13 +30,13 @@ query: |
|
|||
| where p_Host_HostName=~Host_HostName and (isempty(p_Host_DnsDomain) or isempty(Host_DnsDomain) or p_Host_DnsDomain=~Host_DnsDomain)
|
||||
| extend Fails = (EventID == FailedLoginEventId), Success = (EventID == SuccessfulLoginEventId)
|
||||
| extend Account = tolower(Account)
|
||||
| summarize Account_Aux_SuccessPerMin = countif(Success), Account_Aux_FailPerMin = countif(Fails) by Account, bin(TimeGenerated, 1m)
|
||||
| summarize Account_Aux_SuccessPerMin = countif(Success), Account_Aux_FailPerMin = countif(Fails) by Account, bin(TimeGenerated, 1m)
|
||||
| where Account_Aux_FailPerMin > BRUTEFORCE_THRESHOLD and Account_Aux_SuccessPerMin > 0
|
||||
| extend EventData = pack('FailPerMin',Account_Aux_FailPerMin, 'SuccessPerMin', Account_Aux_SuccessPerMin, 'Time', TimeGenerated )
|
||||
| summarize Max = max(Account_Aux_FailPerMin), Account_Aux_EventsData=makeset(EventData) by Account
|
||||
| top 10 by Max
|
||||
| parse Account with Account_NTDomain '\\' *
|
||||
| extend Account_Name = extract(@'^([^\\]*\\)?([^@]+)(@.*)?$',2,Account),
|
||||
| extend Account_Name = extract(@'^([^\\]*\\)?([^@]+)(@.*)?$',2,Account),
|
||||
Account_UPNSuffix = extract(@'^([^\\]*\\)?([^@]+)(@(.*))?$',4,Account)
|
||||
| project Account_Name, Account_NTDomain, Account_UPNSuffix, Account_Aux_EventsData
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ query: |
|
|||
|
||||
let GetWireDataInboundWithHost = (v_Host_HostName:string){
|
||||
WireData
|
||||
| where Direction == 'Inbound'
|
||||
| where Direction == 'Inbound'
|
||||
| where Computer has v_Host_HostName
|
||||
| extend info = pack('Computer', Computer, 'LocalPortNumber', LocalPortNumber, 'RemoteIP', RemoteIP, 'Direction', Direction, 'ApplicationProtocol', ApplicationProtocol)
|
||||
| summarize Process_Aux_Min_SessionStartTime=min(SessionStartTime), count(), IP_Aux_info = makeset(info) by ProcessName , LocalIP, ProcessID
|
||||
|
|
|
@ -20,7 +20,7 @@ query: |
|
|||
|
||||
let GetWireDataOutboundWithHost = (v_Host_HostName:string){
|
||||
WireData
|
||||
| where Direction == 'Outbound'
|
||||
| where Direction == 'Outbound'
|
||||
| where Computer has v_Host_HostName
|
||||
| extend info = pack('Computer', Computer, 'LocalIP', LocalIP, 'LocalPortNumber', LocalPortNumber, 'Direction', Direction, 'ApplicationProtocol', ApplicationProtocol)
|
||||
| summarize Process_Aux_Min_SessionStartTime=min(SessionStartTime), count(), IP_Aux_info = makeset(info) by ProcessName, RemoteIP, ProcessID
|
||||
|
|
|
@ -23,7 +23,7 @@ query: |
|
|||
| where Computer has v_Host_HostName
|
||||
| extend info = pack('HostName', HostName, 'HostIP', HostIP)
|
||||
| summarize Process_Aux_StartTime=min(EventTime), Process_Aux_EndTime=max(EventTime), count(), Process_Aux_info = makeset(info) by Computer, ProcessName, ProcessID
|
||||
| top 10 by count_ asc nulls last
|
||||
| top 10 by count_ asc nulls last
|
||||
| project Process_Aux_StartTime, Process_Aux_EndTime, Process_Host_UnstructuredName=Computer, Process_ProcessId=ProcessID, Process_ImageFile_FullPath=ProcessName, Process_Aux_info
|
||||
};
|
||||
// change <HostName> value below
|
||||
|
|
|
@ -18,8 +18,8 @@ Tactics:
|
|||
query: |
|
||||
|
||||
let GetParentProcessesOnHost = (v_Host_HostName:string){
|
||||
SecurityEvent
|
||||
| where EventID == 4688
|
||||
SecurityEvent
|
||||
| where EventID == 4688
|
||||
| where isnotempty(ParentProcessName)
|
||||
// excluding well known processes, feel free to add more specific to the environment
|
||||
| where NewProcessName !contains ':\\Windows\\System32\\conhost.exe' and ParentProcessName !contains ':\\Windows\\System32\\conhost.exe'
|
||||
|
|
|
@ -18,7 +18,7 @@ Tactics:
|
|||
query: |
|
||||
|
||||
let GetActiveProcessesOnHost = (v_Host_HostName:string){
|
||||
SecurityEvent
|
||||
SecurityEvent
|
||||
| where EventID == 4688
|
||||
// excluding well known processes, feel free to add more specific to the environment
|
||||
| where NewProcessName !contains ':\\Windows\\System32\\conhost.exe' and ParentProcessName !contains ':\\Windows\\System32\\conhost.exe'
|
||||
|
|
|
@ -19,13 +19,13 @@ query: |
|
|||
let AccountActivity_byIP = (v_IP_Address:string){
|
||||
AzureActivity
|
||||
| where Caller != '' and CallerIpAddress =~ v_IP_Address
|
||||
| summarize Account_Aux_StartTime = min(TimeGenerated),
|
||||
Account_Aux_EndTime = max(TimeGenerated),
|
||||
Count = count() by
|
||||
| summarize Account_Aux_StartTime = min(TimeGenerated),
|
||||
Account_Aux_EndTime = max(TimeGenerated),
|
||||
Count = count() by
|
||||
Caller, TenantId
|
||||
| top 10 by Count asc nulls last
|
||||
| top 10 by Count asc nulls last
|
||||
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
|
||||
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
|
||||
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
|
||||
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
|
||||
};
|
||||
AccountActivity_byIP('<Address>')
|
||||
|
|
|
@ -19,13 +19,13 @@ query: |
|
|||
let AccountActivity_byIP = (v_IP_Address:string){
|
||||
AzureActivity
|
||||
| where Caller != '' and CallerIpAddress =~ v_IP_Address
|
||||
| summarize Account_Aux_StartTime = min(TimeGenerated),
|
||||
Account_Aux_EndTime = max(TimeGenerated),
|
||||
Count = count() by
|
||||
| summarize Account_Aux_StartTime = min(TimeGenerated),
|
||||
Account_Aux_EndTime = max(TimeGenerated),
|
||||
Count = count() by
|
||||
Caller, TenantId
|
||||
| top 10 by Count desc nulls last
|
||||
| top 10 by Count desc nulls last
|
||||
| extend UPN = iff(Caller contains '@', Caller, ''), Account_AadUserId = iff(Caller !contains '@', Caller,'')
|
||||
| extend Account_Name = split(UPN,'@')[0] , Account_UPNSuffix = split(UPN,'@')[1]
|
||||
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
|
||||
| project Account_Name, Account_UPNSuffix, Account_AadUserId, Account_AadTenantId=TenantId, Account_Aux_StartTime , Account_Aux_EndTime
|
||||
};
|
||||
AccountActivity_byIP('<Address>')
|
||||
|
|
|
@ -19,7 +19,7 @@ query: |
|
|||
let HostsReceivingDatafromIP = (v_IP_Address:string){
|
||||
WireData
|
||||
| parse Computer with HostName '.' Host_DnsDomain
|
||||
| where SessionState == 'Disconnected'
|
||||
| where SessionState == 'Disconnected'
|
||||
| where RemoteIP =~ v_IP_Address
|
||||
| extend Host_HostName = iff(Computer has '.', HostName, Computer)
|
||||
| summarize Host_Aux_BytesReceived = sum(ReceivedBytes), Host_Aux_LocalIPs=make_set(LocalIP) by Host_HostName, Host_DnsDomain
|
||||
|
|
|
@ -10,7 +10,7 @@ QueryPeriodBefore: 6h
|
|||
QueryPeriodAfter: 6h
|
||||
DataSources:
|
||||
- WindowsFirewall
|
||||
Tactics:
|
||||
Tactics:
|
||||
- Exfiltration
|
||||
- CommandAndControl
|
||||
- Collection
|
||||
|
|
|
@ -18,7 +18,7 @@ Tactics:
|
|||
query: |
|
||||
|
||||
let GetAllAccountByIP = (v_IP_Address:string){
|
||||
OfficeActivity
|
||||
OfficeActivity
|
||||
| where ClientIP =~ v_IP_Address
|
||||
| extend info = pack('ClientIP', ClientIP, 'UserType', UserType, 'Operation', Operation, 'OfficeWorkload', OfficeWorkload, 'ResultStatus', ResultStatus)
|
||||
| summarize min(TimeGenerated), max(TimeGenerated), Account_Aux_Count=count(), Account_Aux_info = makeset(info) by UserId
|
||||
|
|
|
@ -21,7 +21,7 @@ query: |
|
|||
|
||||
let GetWireDataInboundWithIp = (v_IPAddress:string){
|
||||
WireData
|
||||
| where Direction == 'Inbound'
|
||||
| where Direction == 'Inbound'
|
||||
| where RemoteIP has v_IPAddress
|
||||
| extend info = pack('LocalPortNumber', LocalPortNumber, 'RemoteIP', RemoteIP, 'Direction', Direction, 'ApplicationProtocol', ApplicationProtocol)
|
||||
| summarize Process_Aux_EarliestSessionStartTime=min(SessionStartTime), count(), IP_Aux_info = makeset(info) by Computer, ProcessName , LocalIP, ProcessID
|
||||
|
|
|
@ -17,7 +17,7 @@ Tactics:
|
|||
- Discovery
|
||||
- LateralMovement
|
||||
- Collection
|
||||
query: |
|
||||
query: |
|
||||
|
||||
let GetWireDataOutboundWithIp = (v_IP_Address:string){
|
||||
WireData
|
||||
|
|
|
@ -26,7 +26,7 @@ query: |
|
|||
| extend State = tostring(LocationDetails.state), City = tostring(LocationDetails.city)
|
||||
| extend info = pack('AppDisplayName', AppDisplayName, 'ClientAppUsed', ClientAppUsed, 'Browser', tostring(Browser), 'IPAddress', IPAddress, 'ResultType', ResultType, 'ResultDescription', ResultDescription, 'Location', Location, 'State', State, 'City', City, 'StatusCode', StatusCode, 'StatusDetails', StatusDetails)
|
||||
| summarize min(TimeGenerated), max(TimeGenerated), count(), Account_Aux_info = makeset(info) by RemoteHost , UserDisplayName, tostring(OS), UserPrincipalName, AADTenantId, UserId
|
||||
| top 10 by count_ asc nulls last
|
||||
| top 10 by count_ asc nulls last
|
||||
| project Account_Aux_StartTime = min_TimeGenerated, Account_Aux_EndTime = max_TimeGenerated, RemoteHost, UserDisplayName, OS, UserPrincipalName, AADTenantId, UserId, Account_Aux_info
|
||||
| project-rename Account_UnstructuredName=UserPrincipalName, Account_DisplayName=UserDisplayName, Account_AadTenantId=AADTenantId, Account_AadUserId=UserId, Account_Host_UnstructuredName=RemoteHost, Account_Host_OSVersion=OS
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ query: |
|
|||
| where HostIP has v_IP_Address
|
||||
| extend info = pack('HostIP', HostIP, 'ProcessName', ProcessName, 'SeverityLevel', SeverityLevel)
|
||||
| summarize min(EventTime), max(EventTime), count(), Host_Aux_info = makeset(info) by Computer
|
||||
| top 10 by count_ desc nulls last
|
||||
| top 10 by count_ desc nulls last
|
||||
| project Host_Aux_StartTime = min_EventTime, Host_Aux_EndTime = max_EventTime, Computer, Host_Aux_info
|
||||
| project-rename Host_UnstructuredName=Computer
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ query: |
|
|||
| extend State = tostring(LocationDetails.state), City = tostring(LocationDetails.city)
|
||||
| extend info = pack('AppDisplayName', AppDisplayName, 'ClientAppUsed', ClientAppUsed, 'Browser', tostring(Browser), 'IPAddress', IPAddress, 'ResultType', ResultType, 'ResultDescription', ResultDescription, 'Location', Location, 'State', State, 'City', City, 'StatusCode', StatusCode, 'StatusDetails', StatusDetails)
|
||||
| summarize min(TimeGenerated), max(TimeGenerated), count(), Account_Aux_info = makeset(info) by RemoteHost , UserDisplayName, tostring(OS), UserPrincipalName, AADTenantId, UserId
|
||||
| top 10 by count_ desc nulls last
|
||||
| top 10 by count_ desc nulls last
|
||||
| project Account_Aux_StartTimeUtc = min_TimeGenerated, Account_Aux_EndTimeUtc = max_TimeGenerated, RemoteHost, UserDisplayName, OS, UserPrincipalName, AADTenantId, UserId, Account_Aux_info
|
||||
| project-rename Account_UnstructuredName=UserPrincipalName, Account_DisplayName=UserDisplayName, Account_AadTenantId=AADTenantId, Account_AadUserId=UserId, Account_Host_UnstructuredName=RemoteHost, Account_Host_OSVersion=OS
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ query: |
|
|||
| where ProcessName has v_Process_ImageFile_FullPath
|
||||
| extend info = pack('HostName', HostName, 'HostIP', HostIP, 'ProcessName', ProcessName, 'SyslogMessage', SyslogMessage)
|
||||
| summarize min(EventTime), max(EventTime), count(), Host_Aux_info = makeset(info) by Computer
|
||||
| top 10 by count_ asc nulls last
|
||||
| top 10 by count_ asc nulls last
|
||||
| project Host_Aux_StartTime=min_EventTime, Host_Aux_EndTime=max_EventTime, Computer, Host_Aux_info
|
||||
| project-rename Host_UnstructuredName=Computer
|
||||
};
|
||||
|
|
|
@ -9,12 +9,12 @@ OutputEntityTypes:
|
|||
- Process
|
||||
QueryPeriodBefore: 30m
|
||||
QueryPeriodAfter: 30m
|
||||
DataSources:
|
||||
DataSources:
|
||||
- SecurityIoTRawEvent
|
||||
query: |
|
||||
|
||||
let Process_byIoTDevice = (v_IotDevice_DeviceId:string, v_IoTDevice_IoTHub:string){
|
||||
SecurityIoTRawEvent
|
||||
SecurityIoTRawEvent
|
||||
| where RawEventName =~ 'ProcessCreate'
|
||||
| where AssociatedResourceId =~ parse_json(v_IoTDevice_IoTHub)['ResourceId'] and DeviceId =~ v_IotDevice_DeviceId
|
||||
| extend Process_CommandLine = tostring(parse_json(EventDetails)['CommandLine'])
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: AuditLogs
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_NTDomain
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AADUserId
|
||||
- - Account_SID
|
||||
BaseQuery: |
|
||||
let GetAccountActions = (Account_Name:string, Account_NTDomain:string, Account_UPNSuffix:string, Account_AADUserId:string, Account_SID:string){
|
||||
union isfuzzy=true
|
||||
(
|
||||
AuditLogs
|
||||
| where tostring(bag_keys(InitiatedBy)[0]) == "user"
|
||||
| where OperationName in~ ('Delete user', 'Change user password', 'Reset user password', 'Change password (self-service)', 'Reset password (by admin)', 'Reset password (self-service)', 'Update user')
|
||||
| extend userPrincipalName = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
|
||||
| extend userName=tostring(split(userPrincipalName, '@')[0])
|
||||
| extend userUpnSuffix=tostring(split(userPrincipalName, '@')[1])
|
||||
| extend userId=tostring(parse_json(tostring(InitiatedBy.user)).id)
|
||||
| where (userName == Account_Name and userUpnSuffix == Account_UPNSuffix ) or (userId == Account_AADUserId )
|
||||
| extend Target_UPN = tostring(TargetResources[0].userPrincipalName)
|
||||
| extend Target_Name = tostring(split(Target_UPN, '@')[0])
|
||||
| extend Target_UPNSuffix = tostring(split(Target_UPN, '@')[1])
|
||||
| extend Target_AADUserId = tostring(TargetResources[0].id)
|
||||
| extend Action = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0])))
|
||||
| extend ModifiedProperty = parse_json(Action).displayName
|
||||
| extend ModifiedValue = parse_json(Action).newValue
|
||||
| extend DisableUser = iif(ModifiedProperty =~ 'AccountEnabled' and ModifiedValue =~ '[false]', 'True', 'False')
|
||||
),
|
||||
(
|
||||
SecurityEvent
|
||||
| where EventID in (4720, 4722, 4723, 4724, 4725, 4726, 4740)
|
||||
| where AccountType =~ "user" or isempty(AccountType)
|
||||
| extend userName=tostring(split(Account,"\\")[1]), userNTDomain=tostring(split(Account,"\\")[0]), userSid=SubjectUserSid
|
||||
| where (Account_Name==userName and userNTDomain==Account_NTDomain) or SubjectUserSid == Account_SID
|
||||
| extend OperationName = tostring(EventID)
|
||||
| extend Target_Name = TargetUserName, Target_NTDomain = TargetDomainName, Target_SID = TargetSid
|
||||
)
|
||||
};
|
||||
GetAccountActions('{{Account_Name}}', '{{Account_NTDomain}}', '{{Account_UPNSuffix}}', '{{Account_AADUserId}}', '{{Account_SID}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: d6d08c94-455f-4ea5-8f76-fc6c0c442cfa
|
||||
Title: "The user has created an account"
|
||||
Content: "The user has created the account {{Target_Name}} {{Count}} time(s)"
|
||||
Description: "This activity displays account creation events performed by the user"
|
||||
QueryDefinitions:
|
||||
Filter: "where OperationName in~ ('Add user', '4720')"
|
||||
SummarizeBy: "Target_Name, Target_NTDomain, Target_UPNSuffix"
|
||||
- Id: e0459780-ac9d-4b72-8bd4-fecf6b46a0a1
|
||||
Title: "The user has deleted an account"
|
||||
Content: "The user has deleted the account {{Target_Name}} {{Count}} time(s)"
|
||||
Description: "This activity displays account deletion events performed by the user"
|
||||
QueryDefinitions:
|
||||
Filter: "where OperationName in~ ('Delete user', '4726')"
|
||||
SummarizeBy: "Target_Name, Target_NTDomain, Target_UPNSuffix"
|
||||
- Id: ad1f4269-5418-4c46-a3b6-4ec01031de60
|
||||
Title: "The user has reset an account's password"
|
||||
Content: "The password for account {{Target_Name}} was reset by the user {{Count}} time(s)"
|
||||
Description: "This activity displays password reset events performed by the user"
|
||||
QueryDefinitions:
|
||||
Filter: "where OperationName in~ ('Change user password', 'Reset user password', 'Change password (self-service)', 'Reset password (by admin)', 'Reset password (self-service)', '4724', '4723')"
|
||||
SummarizeBy: "Target_Name, Target_NTDomain, Target_UPNSuffix"
|
|
@ -0,0 +1,126 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: AuditLogs
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_NTDomain
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AADUserId
|
||||
- - Account_SID
|
||||
BaseQuery: |
|
||||
let GetAccountActions = (v_Account_Name:string, v_Account_NTDomain:string, v_Account_UPNSuffix:string, v_Account_AADUserId:string, v_Account_SID:string){
|
||||
AuditLogs
|
||||
| where OperationName in~ ('Delete user', 'Change user password', 'Reset user password', 'Change password (self-service)', 'Reset password (by admin)', 'Reset password (self-service)', 'Update user')
|
||||
| extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName)
|
||||
| extend Account_Name = tostring(split(UserPrincipalName, '@')[0])
|
||||
| extend Account_UPNSuffix = tostring(split(UserPrincipalName, '@')[1])
|
||||
| extend Action = tostring(parse_json(tostring(parse_json(tostring(TargetResources[0].modifiedProperties))[0])))
|
||||
| extend ModifiedProperty = parse_json(Action).displayName
|
||||
| extend ModifiedValue = parse_json(Action).newValue
|
||||
| extend Account_AADUserId = tostring(TargetResources[0].id)
|
||||
| extend DisableUser = iif(ModifiedProperty =~ 'AccountEnabled' and ModifiedValue =~ '[false]', 'True', 'False')
|
||||
| union isfuzzy=true (
|
||||
SecurityEvent
|
||||
| where EventID in (4720, 4722, 4723, 4724, 4725, 4726, 4740)
|
||||
| extend OperationName = tostring(EventID)
|
||||
| where AccountType =~ "user" or isempty(AccountType)
|
||||
| extend Account_Name = TargetUserName, Account_NTDomain = TargetDomainName, Account_SID = TargetSid
|
||||
)
|
||||
| where (Account_Name =~ v_Account_Name and (Account_UPNSuffix =~ v_Account_UPNSuffix or Account_NTDomain =~ v_Account_NTDomain)) or Account_AADUserId =~ v_Account_AADUserId or Account_SID =~ v_Account_SID
|
||||
};
|
||||
GetAccountActions('{{Account_Name}}', '{{Account_NTDomain}}', '{{Account_UPNSuffix}}', '{{Account_AADUserId}}', '{{Account_SID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: 6db7f5d1-f41e-46c2-b935-230b36a569e6
|
||||
DisplayName: Actions on account
|
||||
Description: |
|
||||
Summary of actions taken on the specified account, grouped by action: password resets and changes, account lockouts (policy or admin), account creation and deletion, account enabled and disabled
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Action
|
||||
OutputType: String
|
||||
SupportDeepLink: false
|
||||
- Header: Most Recent
|
||||
OutputType: Date
|
||||
SupportDeepLink: false
|
||||
- Header: Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
|
||||
QueriesDefinitions:
|
||||
# Account Password Resets
|
||||
- Filter: "where OperationName in~ ('Change user password', 'Reset user password', 'Change password (self-service)', 'Reset password (by admin)', 'Reset password (self-service)', '4724', '4723')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Disabled by Policy
|
||||
- Filter: "where OperationName in~ ('Blocked from self-service password reset', '4740')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Disabled by Admin
|
||||
- Filter: "where OperationName == '4725' or (OperationName =~ 'Update user' and DisableUser =~ 'True')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Created
|
||||
- Filter: "where OperationName in~ ('Add user', '4720')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Deleted
|
||||
- Filter: "where OperationName in~ ('Delete user', '4726')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Deleted or Disabled
|
||||
- Filter: "where OperationName in~ ('4725', 'Blocked from self-service password reset', '4740') or (OperationName =~ 'Update user' and DisableUser =~ 'True')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Created or Enabled
|
||||
- Filter: "where OperationName in~ ('4722', '4767') or (OperationName =~ 'Update user' and DisableUser =~ 'False')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Account Setting Changed
|
||||
- Filter: "where OperationName in~ ('Update user','4738')"
|
||||
Summarize: "summarize MostRecent = max(TimeGenerated), Count = count() by OperationName"
|
||||
Project: "project Title = OperationName, MostRecent, Count"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Count
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Actions by type"
|
||||
DataSets:
|
||||
- Query: "summarize Count = count() by bin(TimeGenerated, 1h), OperationName"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "OperationName"
|
||||
Type: BarChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all account activity"
|
||||
Query: "project TimeGenerated, UserPrincipalName, Account_Name, OperationName, Activity, DisableUser, TargetSid, AADUserId, InitiatedBy, AADTenantId, AccountType, Computer, SubjectAccount, SubjectUserSid, EventData"
|
|
@ -0,0 +1,32 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: AuditLogs
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AadUserId
|
||||
BaseQuery: |-
|
||||
let UserConsentToApplication = (Account_Name:string, Account_UPNSuffix:string, Account_AadUserId:string){
|
||||
let account_upn = iff(Account_Name != "" and Account_UPNSuffix != ""
|
||||
, strcat(Account_Name,"@",Account_UPNSuffix)
|
||||
,"" );
|
||||
AuditLogs
|
||||
| where OperationName == "Consent to application"
|
||||
| extend Source_Account_UPNSuffix = tostring(todynamic(InitiatedBy) ["user"]["userPrincipalName"]), Source_Account_AadUserId = tostring(todynamic(InitiatedBy) ["user"]["id"])
|
||||
| where (account_upn != "" and account_upn =~ Source_Account_UPNSuffix)
|
||||
or (Account_AadUserId != "" and Account_AadUserId =~ Source_Account_AadUserId)
|
||||
| extend Target_CloudApplication_Name = tostring(todynamic(TargetResources)[0]["displayName"]), Target_CloudApplication_AppId = tostring(todynamic(TargetResources)[0]["id"])
|
||||
};
|
||||
UserConsentToApplication('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_AadUserId}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "The user consented to OAuth application"
|
||||
Content: "The user consented to the OAuth application named {{Target_CloudApplication_Name}} {{Count}} time(s)"
|
||||
Items:
|
||||
- Id: 5e9ecee5-e7a4-4a2a-94c4-9c0e22e1b673
|
||||
Description: This activity lists user's consents to an OAuth applications.
|
||||
QueryDefinitions:
|
||||
SummarizeBy: Target_CloudApplication_AppId, Target_CloudApplication_Name
|
|
@ -0,0 +1,33 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: AzureActivity
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AadUserId
|
||||
BaseQuery: |-
|
||||
let AzureRunProcess = (Account_Name:string, Account_UPNSuffix:string,Account_AadUserId:string){
|
||||
let upn = strcat(Account_Name,"@",Account_UPNSuffix);
|
||||
AzureActivity
|
||||
| where (isnotempty(Account_AadUserId) and Caller =~ Account_AadUserId) or Caller =~ upn
|
||||
| where OperationName contains "Run Command on Virtual Machine"
|
||||
or (OperationName == "List Storage Account Keys" and ActivityStatus == "Succeeded")
|
||||
or OperationName == "Create or Update Virtual Machine"
|
||||
or OperationName == "Create Deployment"
|
||||
or OperationName == "Create role assignment"
|
||||
| project-rename Target_AzureResource_ResourceId = _ResourceId, Source_IP_Address = CallerIpAddress
|
||||
| extend shortResourceId = tostring(split(ResourceId,'/')[-1])
|
||||
};
|
||||
AzureRunProcess('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_AadUserId}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "User performed operation on azure resource from IP"
|
||||
Content: "User performed operation {{OperationName}} on azure resource: {{shortResourceId}} from IP {{Source_IP_Address}} {{Count}} time(s)"
|
||||
Items:
|
||||
- Id: cab4058a-0707-4a02-b76f-cf96270823ed
|
||||
Description: This activity lists the user's activities on Azure.
|
||||
QueryDefinitions:
|
||||
SummarizeBy: Target_AzureResource_ResourceId, Source_IP_Address, shortResourceId, OperationName
|
|
@ -0,0 +1,88 @@
|
|||
SchemaVersion: 1.0
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
- DataType: Event
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_NTDomain
|
||||
- - Account_SID
|
||||
BaseQuery: |
|
||||
let UserClearedEventLog = (v_Account_Name:string, v_Account_NTDomain:string, v_Account_SID:string){
|
||||
SecurityEvent
|
||||
// 1102 - This event generates every time Windows Security audit log was cleared
|
||||
| where EventID == 1102 and EventSourceName =~ 'Microsoft-Windows-Eventlog'
|
||||
| parse EventData with * 'SubjectUserName>' SubjectUserName '<' *
|
||||
| parse EventData with * 'SubjectUserSid>' SubjectUserSid '<' *
|
||||
| parse EventData with * 'SubjectLogonId>' SubjectLogonId '<' *
|
||||
| parse EventData with * 'SubjectDomainName>' SubjectDomainName '<' *
|
||||
| parse EventData with * 'BackupPath>' BackupPath'<' *
|
||||
| extend ClearedLog = Type
|
||||
| extend Account_Name = SubjectUserName, Account_NTDomain = SubjectDomainName
|
||||
| union isfuzzy=true
|
||||
( Event
|
||||
// 104 - This event generates every time a Windows Event log was cleared
|
||||
| where EventID == 104 and Source == "Microsoft-Windows-Eventlog"
|
||||
| parse EventData with * 'SubjectUserName>' SubjectUserName '<' *
|
||||
| parse EventData with * 'SubjectUserSid>' SubjectUserSid '<' *
|
||||
| parse EventData with * 'SubjectLogonId>' SubjectLogonId '<' *
|
||||
| parse EventData with * 'SubjectDomainName>' SubjectDomainName '<' *
|
||||
| parse EventData with * 'BackupPath>' BackupPath'<' *
|
||||
| parse RenderedDescription with * 'The' ClearedLog 'log' *
|
||||
| extend Account_Name = SubjectUserName, Account_NTDomain = SubjectDomainName, Account_SID = SubjectUserSid
|
||||
)
|
||||
| project TimeGenerated, Computer, EventID, Account_Name, SubjectUserName, SubjectUserSid, Account_SID, SubjectLogonId, Account_NTDomain, SubjectDomainName, SourceComputerId, _ResourceId, ClearedLog, BackupPath
|
||||
| where (Account_Name =~ v_Account_Name and Account_NTDomain =~ v_Account_NTDomain) or (isnotempty(v_Account_SID) and Account_SID =~ v_Account_SID)
|
||||
};
|
||||
UserClearedEventLog('{{Account_Name}}', '{{Account_NTDomain}}', '{{Account_SID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: 5a70a68d-25d4-4012-b73e-4f302a16c06a
|
||||
DisplayName: Event Logs cleared by user
|
||||
Description: |
|
||||
Provides the datetime & count of number of times any event log was cleared by the user.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Cleared Log
|
||||
OutputType: String
|
||||
- Header: Times Cleared
|
||||
OutputType: Number
|
||||
- Header: Host Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: Host(s)
|
||||
OutputType: String
|
||||
QueriesDefinitions:
|
||||
# SecurityEvent cleared
|
||||
- Filter: "where ClearedLog =~ 'SecurityEvent'"
|
||||
Summarize: "summarize Hosts = makeset(Computer), HostCount = dcount(Computer), EventCount = count() by ClearedLog"
|
||||
Project: "project Title = ClearedLog, EventCount, HostCount, Hosts = case(array_length(Hosts) == 1, tostring(Hosts[0]), array_length(Hosts) > 1, 'Many', 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
# Other Event Cleared
|
||||
- Filter: "where ClearedLog !~ 'SecurityEvent'"
|
||||
Summarize: "summarize Hosts = makeset(Computer), HostCount = dcount(Computer), EventCount = count() by ClearedLog"
|
||||
Project: "project Title = ClearedLog, EventCount, HostCount, Hosts = case(array_length(Hosts) == 1, tostring(Hosts[0]), array_length(Hosts) > 1, 'Many', 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
ChartQuery:
|
||||
Title: "Log clear activity over time"
|
||||
DataSets:
|
||||
- Query: "summarize EventCount = count() by bin(TimeGenerated, 1d) | extend Legend = 'EventCount'"
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: EventCount
|
||||
LegendColumnName: Legend
|
||||
- Query: "summarize HostCount = dcount(Computer) by bin(TimeGenerated, 1d) | extend Legend = 'HostCount'"
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: HostCount
|
||||
LegendColumnName: Legend
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: See All Log Clear Activity
|
||||
Query: "project TimeGenerated, Computer, EventID, Account_Name, SubjectUserName, SubjectUserSid, SubjectLogonId, Account_NTDomain, SubjectDomainName, SourceComputerId, _ResourceId, ClearedLog, BackupPath"
|
|
@ -0,0 +1,163 @@
|
|||
SchemaVersion: 1.0
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_NTDomain
|
||||
- - Account_SID
|
||||
BaseQuery: |
|
||||
let WellKnownLocalSID = 'S-1-5-32-5[0-9][0-9]$';
|
||||
let WellKnownGroupSID = 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-5[0-9][0-9]$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1102$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1103$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-498$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1000$';
|
||||
let GetGroupAddForUser = (v_Account_Name:string, v_Account_NTDomain:string, v_Account_SID:string){
|
||||
SecurityEvent
|
||||
| where EventID in (4728, 4732, 4756)
|
||||
| where AccountType =~ 'User'
|
||||
| extend Account_Name = case(
|
||||
// Handles mixed use scenario of NTDomain\AccountName@UPNSuffix
|
||||
SubjectUserName has '@' and SubjectUserName has '\\', tostring(split(tostring(split(SubjectUserName, '\\')[1]),'@')[0]),
|
||||
SubjectUserName has '@', tostring(split(SubjectUserName, '@')[0]),
|
||||
SubjectUserName has '\\', tostring(split(SubjectUserName, '\\')[1]),
|
||||
SubjectUserName
|
||||
)
|
||||
| extend Account_NTDomain = case(
|
||||
SubjectDomainName has '\\', tostring(split(SubjectDomainName, '\\')[0]),
|
||||
// Handles UPN scenario of AccountName@UPNSuffix to pull potential NTDomain from
|
||||
SubjectDomainName has '@', tostring(split(tostring(split(SubjectDomainName, '@')[1]),'.')[0]),
|
||||
SubjectDomainName
|
||||
)
|
||||
| extend MemberAdded = case( MemberName has 'CN=', tostring(split(tostring(split(MemberName, ',')[0]),'CN=')[1]), MemberName == '-', MemberSid, MemberName)
|
||||
| extend MemberNameMatch = iff(isnotempty(v_Account_Name) and MemberAdded has v_Account_Name, true, false)
|
||||
| extend MemberNTDomainMatch = iff(isnotempty(v_Account_NTDomain) and MemberAdded has v_Account_NTDomain, true, false)
|
||||
| extend MemberSidMatch = iff(isnotempty(v_Account_SID) and MemberSid =~ v_Account_SID, true, false)
|
||||
| extend SubjectNameMatch = iff(isnotempty(v_Account_Name) and SubjectUserName =~ v_Account_Name, true, false)
|
||||
| extend SubjectNTDomainMatch = iff(isnotempty(v_Account_NTDomain) and SubjectDomainName =~ v_Account_NTDomain, true, false)
|
||||
| extend SubjectSidMatch = iff(isnotempty(v_Account_SID) and SubjectUserSid has v_Account_SID, true, false)
|
||||
| where (MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true or (SubjectNameMatch == true and SubjectNTDomainMatch == true) or SubjectSidMatch == true
|
||||
| project TimeGenerated, EventID, Activity, Computer, MemberName, MemberAdded, MemberSid, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectAccount, SubjectUserName, SubjectUserSid, WellKnownGroupSID, WellKnownLocalSID,
|
||||
MemberNameMatch, MemberNTDomainMatch, MemberSidMatch, SubjectNameMatch, SubjectNTDomainMatch, SubjectSidMatch
|
||||
| extend GroupName = TargetUserName, AddedBy = SubjectAccount
|
||||
//support for Activities
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = SubjectAccount
|
||||
};
|
||||
GetGroupAddForUser('{{Account_Name}}', '{{Account_NTDomain}}', '{{Account_SID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: bec9d204-c96c-4a34-8042-b49e89dbff89
|
||||
DisplayName: Group additions
|
||||
Description: |
|
||||
Summary of user additions to Groups for the specific user. Specifically, we provide the count of All Groups, [Privileged Groups](https://docs.microsoft.com/windows/security/identity-protection/access-control/active-directory-security-groups) and Remote Desktop Users Group.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Groups"
|
||||
OutputType: String
|
||||
SupportDeepLink: false
|
||||
- Header: GroupCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: HostCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
|
||||
# UserAddedToPrivilegedGroups
|
||||
- Filter: "where ((MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true) and (TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID)"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'Privileged', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserAddedToRemoteDesktopGroup
|
||||
- Filter: "where ((MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true) and TargetSid in ('S-1-5-32-555')"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'Remote Desktop', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserAddedToPrivilegedGroupsExcludeRDP
|
||||
- Filter: "where ((MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true) and (TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID) | where TargetSid !in ('S-1-5-32-555')"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'Privileged(non-RDP)', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserAddedToGroups
|
||||
- Filter: "where (MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'All Groups', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# GroupsAddedUsersTo
|
||||
- Filter: "where (SubjectNameMatch == true and SubjectNTDomainMatch == true) or SubjectSidMatch == true"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'Groups this user added users to', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UsersAddedToGroups
|
||||
- Filter: "where (SubjectNameMatch == true and SubjectNTDomainMatch == true) or SubjectSidMatch == true "
|
||||
Summarize: "summarize GroupCount = dcount(MemberAdded), HostCount = dcount(Computer) by SubjectAccount"
|
||||
Project: "project Title = 'Users this user added to Groups', GroupCount, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Group additions per hour"
|
||||
DataSets:
|
||||
- Query: "summarize Count = count() by bin(TimeGenerated, 1h) | extend Legend = 'Total'"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
Type: LineChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all group additions related to user"
|
||||
Query: "project TimeGenerated, EventID, Activity, Computer, MemberName, MemberAdded, MemberSid, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectAccount, SubjectUserName, SubjectUserSid, WellKnownGroupSID, WellKnownLocalSID | order by TimeGenerated desc"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: febba410-e7d6-4c63-8fe5-2b93f448b7a1
|
||||
Title: "The user has added an account to a privileged group"
|
||||
Content: "The user has added an account to the privileged group, {{TargetDomainName}}{{TargetUserName}}, {{Count}} time(s)"
|
||||
Description: "This activity displays the user that added an account and the account that was added to a privileged group"
|
||||
QueryDefinitions:
|
||||
Filter: "where ((MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true) and (TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID) | where TargetSid !in ('S-1-5-32-555')"
|
||||
SummarizeBy: "SubjectAccount, TargetUserName, TargetSid"
|
||||
- Id: 5ae2baf4-de7b-40f0-a861-8852266bfcd0
|
||||
Title: "The user has added an account to the Remote Desktop Users group"
|
||||
Content: "The user has added an account to the Remote Desktop Users group {{Count}} time(s)"
|
||||
Description: "This activity displays the account was added to Remote Desktop group"
|
||||
QueryDefinitions:
|
||||
Filter: "where ((MemberNameMatch == true and MemberNTDomainMatch == true) or MemberSidMatch == true) and TargetSid in ('S-1-5-32-555')"
|
||||
SummarizeBy: "SubjectAccount, TargetUserName, TargetSid"
|
||||
- Id: bf56473d-b9bd-4eb1-96d0-8569ec7a9003
|
||||
Title: "The user has added an account to a security group"
|
||||
Content: "The user has added {{SubjectAccount}} to the {{TargetDomainName}}\\{{TargetUserName}} group"
|
||||
Description: "This activity displays the user that added an account and the account that was added to a security group"
|
||||
QueryDefinitions:
|
||||
Filter: "where (SubjectNameMatch == true and SubjectNTDomainMatch == true) or SubjectSidMatch == true"
|
||||
SummarizeBy: "SubjectAccount, TargetUserName, TargetSid"
|
|
@ -0,0 +1,95 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: OfficeActivity
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
BaseQuery: |
|
||||
let AScoreThresh = 3;
|
||||
let maxAnomalies = 3;
|
||||
let BeforeRange = 14d;
|
||||
let EndTime = todatetime('{{End_Time_UTC}}');
|
||||
let StartTime = todatetime('{{Start_Time_UTC}}');
|
||||
let numDays = tolong((EndTime-StartTime)/1d);
|
||||
let userData = (v_Account_Name:string, v_Account_UPNSuffix:string) {
|
||||
OfficeActivity
|
||||
| extend splitUserId=split(UserId, '@')
|
||||
| extend Account_Name = tostring(splitUserId[0]), Account_UPNSuffix = tostring(splitUserId[1])
|
||||
| where Account_Name =~ v_Account_Name and Account_UPNSuffix =~ v_Account_UPNSuffix };
|
||||
userData('{{Account_Name}}', '{{Account_UPNSuffix}}')
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
Insights:
|
||||
Id: 0a5d7b14-b485-450a-a0ac-4100c860ac32
|
||||
DisplayName: Anomalously high office operation count
|
||||
Description: Highlight office operations of the user with anomalously high count compared to those observed in the preceding 14 days.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 1d
|
||||
AfterRange: 0d
|
||||
ReferenceTimeRange:
|
||||
BeforeRange: 14d
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Operation
|
||||
OutputType: String
|
||||
SupportDeepLink: true
|
||||
- Header: Expected Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
- Header: Actual Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
QueriesDefinitions:
|
||||
- Filter: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Operation
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost=maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore-maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
Summarize: take 1
|
||||
Project: project Operation, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Operation
|
||||
Query: |
|
||||
{{BaseQuery}}
|
||||
| where TimeGenerated between (StartTime .. EndTime)
|
||||
| where Operation == '{{RowValue_Operation}}'
|
||||
ChartQuery:
|
||||
Title: Anomalous operation timeline
|
||||
DataSets:
|
||||
- Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Operation
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost=maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore-maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,round(postExpectedCount,2))
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
| take 1
|
||||
| project Operation, TimeGenerated, count_
|
||||
| mvexpand TimeGenerated, count_ | project todatetime(TimeGenerated), toint(count_), Operation
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: count_
|
||||
LegendColumnName: Operation
|
||||
Type: LineChart
|
||||
AdditionalQuery:
|
||||
Text: Query all anomalous operations
|
||||
Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Operation
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh | order by maxAnomalyScorePost desc
|
||||
| project Operation, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
|
@ -0,0 +1,31 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: OfficeActivity
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_Sid
|
||||
BaseQuery: |-
|
||||
let TLQ_UserActedOnForeignMailbox = (Account_Name:string, Account_UPNSuffix:string, account_sid:string){
|
||||
let account_upn = iff(Account_Name!="" and Account_UPNSuffix != ""
|
||||
,strcat(Account_Name,"@",Account_UPNSuffix)
|
||||
,"");
|
||||
OfficeActivity
|
||||
| where RecordType == "ExchangeItem" and UserType =="Regular" and Operation !contains "InboxRule"
|
||||
| where LogonUserSid != MailboxOwnerSid
|
||||
| where ((account_sid != "" and LogonUserSid =~ account_sid)
|
||||
or ( account_upn != "" and UserId =~ account_upn ))
|
||||
};
|
||||
TLQ_UserActedOnForeignMailbox('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_Sid}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "The user acted on another accounts mailbox"
|
||||
Content: "The user acted on mailbox {{MailboxOwnerUPN}} {{Count}} time(s)"
|
||||
Items:
|
||||
- Id: 1f82f263-d694-469a-9717-1b3edf9d3bb2
|
||||
Description: This activity lists user's activities on others' mailbox
|
||||
QueryDefinitions:
|
||||
SummarizeBy: MailboxOwnerSid, MailboxOwnerUPN
|
|
@ -0,0 +1,32 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: OfficeActivity
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_Sid
|
||||
BaseQuery: |-
|
||||
let ruleChangeRecordTypes = dynamic( ["ExchangeAdmin", "ExchangeItem"]);
|
||||
let TLQ_UserModifiedinboxRules = (Account_Name: string, Account_UPNSuffix: string, Account_Sid: string){
|
||||
let upn = iff(Account_Name != "" and Account_UPNSuffix != ""
|
||||
, strcat(Account_Name, "@", Account_UPNSuffix)
|
||||
, "");
|
||||
OfficeActivity
|
||||
| where RecordType in~ (ruleChangeRecordTypes) and Operation contains "InboxRule"
|
||||
| where((Account_Sid != "" and LogonUserSid == Account_Sid)
|
||||
or(upn != "" and UserId == upn )
|
||||
)
|
||||
};
|
||||
TLQ_UserModifiedinboxRules('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_Sid}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "The user modified inbox rules on another accounts mailbox"
|
||||
Content: "User Modified {{Count}} inbox rules on {{MailboxOwnerUPN}}'s Mailbox"
|
||||
Items:
|
||||
- Id: e480efd0-016d-428e-b892-84b9d586d004
|
||||
Description: User modified inbox rules on a mailbox
|
||||
QueryDefinitions:
|
||||
SummarizeBy: MailboxOwnerSid, MailboxOwnerUPN
|
|
@ -0,0 +1,34 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: OfficeActivity
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
BaseQuery: |-
|
||||
let TLQ_UserUploadFiles = (Account_Name:string, Account_UPNSuffix:string){
|
||||
let upn = strcat(Account_Name,"@",Account_UPNSuffix);
|
||||
OfficeActivity
|
||||
| where RecordType =~ "SharePointFileOperation" and Operation in~ ("FileUploaded", "FileDownloaded")
|
||||
| where upn =~UserId
|
||||
| extend Subject_File_Directory = tostring(split(OfficeObjectId,SourceFileName)[0]), Op = iff (Operation != "FileUploaded", "uploaded", "downloaded")
|
||||
| project-rename Source_IP_Address = ClientIP
|
||||
};
|
||||
TLQ_UserUploadFiles('{{Account_Name}}', '{{Account_UPNSuffix}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "User {{Op}} files to SharePoint"
|
||||
Content: "User Uploaded {{Count}} File(s) To SharePoint From {{Source IPAddress}}"
|
||||
Items:
|
||||
- Id: 0eabec03-51e7-4909-b0cb-1adc76759e93
|
||||
Description: This activity lists the user's SharePoint uploads.
|
||||
QueryDefinitions:
|
||||
Filter: where Operation =~ "FileUploaded"
|
||||
SummarizeBy: Source_IP_Address, Op
|
||||
- Id: df564e7b-bf6d-4dc4-a32d-79b00bd2cc7b
|
||||
Description: This activity lists the user's SharePoint downloads.
|
||||
QueryDefinitions:
|
||||
Filter: where Operation =~ "FileDownloaded"
|
||||
SummarizeBy: Source_IP_Address, Op
|
|
@ -0,0 +1,75 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: OfficeActivity
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AADUserId
|
||||
BaseQuery: |
|
||||
let Operations = dynamic(["FileDownloaded", "FileUploaded"]);
|
||||
let UserOperationToSharePoint = (v_Account_Name:string, v_Account_UPNSuffix:string) {
|
||||
OfficeActivity
|
||||
// Select sharepoint activity that is relevant
|
||||
| where RecordType in~ ('SharePointFileOperation')
|
||||
| where Operation in~ (Operations)
|
||||
| extend Account_Name = tostring(split(UserId, '@')[0])
|
||||
| extend Account_UPNSuffix = tostring(split(UserId, '@')[1])
|
||||
| where Account_Name =~ v_Account_Name and Account_UPNSuffix =~ v_Account_UPNSuffix
|
||||
| project TimeGenerated, Account_Name, Account_UPNSuffix, UserId, OfficeId, RecordType, Operation, OrganizationId, UserType, UserKey, OfficeWorkload, OfficeObjectId, ClientIP, ItemType, UserAgent, Site_Url, SourceRelativeUrl, SourceFileName, SourceFileExtension , Start_Time , ElevationTime , TenantId, SourceSystem , Type
|
||||
};
|
||||
UserOperationToSharePoint ('{{Account_Name}}','{{Account_UPNSuffix}}')
|
||||
Insights:
|
||||
Id: e6cf68e6-1eca-4fbb-9fad-6280f2a9476e
|
||||
DisplayName: Resource access
|
||||
Description: |
|
||||
Provides the count and distinct resource accesses by a given user account
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Resource Type
|
||||
OutputType: String
|
||||
- Header: Distinct Resources
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: Total Resources
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: IPAddress(es)
|
||||
OutputType: string
|
||||
QueriesDefinitions:
|
||||
- Filter: "where Operation =~ 'FileUploaded'"
|
||||
Summarize: "summarize DistinctResources = dcount(SourceFileName), TotalResources = count(SourceFileName), IPAddresses = make_set(ClientIP) by Operation"
|
||||
Project: "project Title = Operation, DistinctResources, TotalResources, IPAddresses = case(array_length(IPAddresses) == 1, tostring(IPAddresses[0]), array_length(IPAddresses) > 1, 'Many', 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: DistinctResources
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: TotalResources
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- Filter: "where Operation =~ 'FileDownloaded'"
|
||||
Summarize: "summarize DistinctResources = dcount(SourceFileName), TotalResources = count(SourceFileName), IPAddresses = make_set(ClientIP) by Operation"
|
||||
Project: "project Title = Operation, DistinctResources, TotalResources, IPAddresses = case(array_length(IPAddresses) == 1, tostring(IPAddresses[0]), array_length(IPAddresses) > 1, 'Many', 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: DistinctResources
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: TotalResources
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
ChartQuery:
|
||||
Title: "Resource access over time"
|
||||
DataSets:
|
||||
- Query: "summarize DistinctResources = dcountif(Operation, Operation =~ 'FileUploaded'), TotalResources = countif(Operation =~ 'FileUploaded') by bin(TimeGenerated, 1h) | extend Legend = 'File Uploads'"
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: TotalResources
|
||||
LegendColumnName: Legend
|
||||
- Query: "summarize DistinctResources = dcountif(Operation, Operation =~ 'FileDownloaded'), TotalResources = countif(Operation =~ 'FileDownloaded') by bin(TimeGenerated, 1h) | extend Legend = 'File Downloads'"
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: TotalResources
|
||||
LegendColumnName: Legend
|
||||
Type: LineChart
|
||||
AdditionalQuery:
|
||||
Text: "See all resource activity"
|
||||
Query: "where Operation in~ (Operations)"
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SigninLogs
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
BaseQuery: |
|
||||
let AScoreThresh=3;
|
||||
let maxAnomalies=3;
|
||||
let BeforeRange = 14d;
|
||||
let EndTime=todatetime('{{End_Time_UTC}}');
|
||||
let StartTime = todatetime('{{Start_Time_UTC}}');
|
||||
let numDays = tolong((EndTime-StartTime)/1d);
|
||||
let userData = (v_Account_Name:string, v_Account_UPNSuffix:string, v_Account_AADUserId:string) {
|
||||
SigninLogs
|
||||
| where TimeGenerated between ((StartTime-BeforeRange) .. EndTime)
|
||||
| extend splitUserId=split(UserPrincipalName, '@')
|
||||
| extend Account_Name = tostring(splitUserId[0]), Account_UPNSuffix = tostring(splitUserId[1])
|
||||
| where (Account_Name =~ v_Account_Name and Account_UPNSuffix =~ v_Account_UPNSuffix) or UserId =~ v_Account_AADUserId };
|
||||
userData('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_AADUserId}}')
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AADUserId
|
||||
Insights:
|
||||
Id: cae8d0aa-aa45-4d53-8d88-17dd64ffd4e4
|
||||
DisplayName: Anomalously high Azure sign-in result count
|
||||
Description: Highlight Azure sign-in results by the user principal with anomalously high count compared to those observed in the preceding 14 days.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 1d
|
||||
AfterRange: 0d
|
||||
ReferenceTimeRange:
|
||||
BeforeRange: 14d
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Result Description
|
||||
OutputType: String
|
||||
SupportDeepLink: true
|
||||
- Header: Expected Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
- Header: Actual Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
QueriesDefinitions:
|
||||
- Filter: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by ResultDescription
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
Summarize: take 1
|
||||
Project: project ResultDescription, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: ResultDescription
|
||||
Query: |
|
||||
{{BaseQuery}}
|
||||
| where TimeGenerated between (StartTime .. EndTime)
|
||||
| where ResultDescription == '{{RowValue_ResultDescription}}'
|
||||
ChartQuery:
|
||||
Title: Anomalous sign-in result timeline
|
||||
DataSets:
|
||||
- Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by ResultDescription
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,round(postExpectedCount,2))
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
| take 1
|
||||
| project ResultDescription, TimeGenerated, count_
|
||||
| mvexpand TimeGenerated, count_
|
||||
| project todatetime(TimeGenerated), toint(count_), ResultDescription
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: count_
|
||||
LegendColumnName: ResultDescription
|
||||
Type: LineChart
|
||||
AdditionalQuery:
|
||||
Text: Query all anomalous sign-in results
|
||||
Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by ResultDescription
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
| project ResultDescription, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
|
@ -0,0 +1,28 @@
|
|||
SchemaVersion: 1.0
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
DataTypes:
|
||||
- DataType: SigninLogs
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_UPNSuffix
|
||||
- - Account_AadUserId
|
||||
BaseQuery: |-
|
||||
let SignInsByResource = (Account_Name:string, Account_UPNSuffix:string, Account_AadUserId:string){
|
||||
let acc_upn = iff(Account_Name != "" and Account_UPNSuffix != "" ,strcat(Account_Name,"@" ,Account_UPNSuffix),"");
|
||||
SigninLogs
|
||||
| where (acc_upn != "" and UserPrincipalName =~ acc_upn) or
|
||||
(Account_AadUserId != "" and Account_AadUserId =~ UserId) // UserPrincipalName, UserId
|
||||
| extend shortResourceId = tostring(split(ResourceId,"/")[-1])
|
||||
};
|
||||
SignInsByResource('{{Account_Name}}', '{{Account_UPNSuffix}}', '{{Account_AadUserId}}')
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "The user signed in to an Azure resource"
|
||||
Content: "The user signed in to {{shortResourceId}} {{Count}} time(s)"
|
||||
Items:
|
||||
- Id: 1f82f263-d694-469a-9717-1b3edf9d3bb2
|
||||
Description: This activity lists user's sign ins to Azure Resources
|
||||
QueryDefinitions:
|
||||
SummarizeBy: shortResourceId, ResourceId
|
|
@ -0,0 +1,272 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
RequiredInputFieldsSets:
|
||||
- - Account_Name
|
||||
- Account_NTDomain
|
||||
BaseQuery: |
|
||||
let GetAllLogonsForUser = (v_Account_Name:string, v_Account_NTDomain:string){
|
||||
let AllEvents = SecurityEvent
|
||||
| extend p_Account_Name = case(
|
||||
// Handles mixed use scenario of NTDomain\AccountName@UPNSuffix
|
||||
v_Account_Name has '@' and v_Account_Name has '\\', tostring(split(tostring(split(v_Account_Name, '\\')[1]),'@')[0]),
|
||||
v_Account_Name has '@', tostring(split(v_Account_Name, '@')[0]),
|
||||
v_Account_Name has '\\', tostring(split(v_Account_Name, '\\')[1]),
|
||||
v_Account_Name
|
||||
)
|
||||
| extend p_Account_NTDomain = case(
|
||||
v_Account_NTDomain has '\\', tostring(split(v_Account_NTDomain, '\\')[0]),
|
||||
// Handles UPN scenario of AccountName@UPNSuffix to pull potential NTDomain from
|
||||
v_Account_NTDomain has '@', tostring(split(tostring(split(v_Account_NTDomain, '@')[1]),'.')[0]),
|
||||
v_Account_NTDomain
|
||||
)
|
||||
| where EventID in (4624, 4625, 4672)
|
||||
| where AccountType =~ 'User'
|
||||
| where TargetUserName =~ p_Account_Name and TargetDomainName =~ p_Account_NTDomain
|
||||
| extend PassedInAccountName = p_Account_Name, PassedInNTDomain = p_Account_NTDomain, RelatedRowSet = 'AllEvents'
|
||||
| extend HourOfLogin = hourofday(TimeGenerated), DayNumberofWeek = dayofweek(TimeGenerated)
|
||||
| extend DayofWeek = case(
|
||||
DayNumberofWeek == "00:00:00", "Sunday",
|
||||
DayNumberofWeek == "1.00:00:00", "Monday",
|
||||
DayNumberofWeek == "2.00:00:00", "Tuesday",
|
||||
DayNumberofWeek == "3.00:00:00", "Wednesday",
|
||||
DayNumberofWeek == "4.00:00:00", "Thursday",
|
||||
DayNumberofWeek == "5.00:00:00", "Friday",
|
||||
DayNumberofWeek == "6.00:00:00", "Saturday","InvalidTimeStamp")
|
||||
// map the most common ntstatus codes
|
||||
| extend StatusDesc = case(
|
||||
Status =~ "0x80090302", "SEC_E_UNSUPPORTED_FUNCTION",
|
||||
Status =~ "0x80090308", "SEC_E_INVALID_TOKEN",
|
||||
Status =~ "0x8009030E", "SEC_E_NO_CREDENTIALS",
|
||||
Status =~ "0xC0000008", "STATUS_INVALID_HANDLE",
|
||||
Status =~ "0xC0000017", "STATUS_NO_MEMORY",
|
||||
Status =~ "0xC0000022", "STATUS_ACCESS_DENIED",
|
||||
Status =~ "0xC0000034", "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
Status =~ "0xC000005E", "STATUS_NO_LOGON_SERVERS",
|
||||
Status =~ "0xC000006A", "STATUS_WRONG_PASSWORD",
|
||||
Status =~ "0xC000006D", "STATUS_LOGON_FAILURE",
|
||||
Status =~ "0xC000006E", "STATUS_ACCOUNT_RESTRICTION",
|
||||
Status =~ "0xC0000073", "STATUS_NONE_MAPPED",
|
||||
Status =~ "0xC00000FE", "STATUS_NO_SUCH_PACKAGE",
|
||||
Status =~ "0xC000009A", "STATUS_INSUFFICIENT_RESOURCES",
|
||||
Status =~ "0xC00000DC", "STATUS_INVALID_SERVER_STATE",
|
||||
Status =~ "0xC0000106", "STATUS_NAME_TOO_LONG",
|
||||
Status =~ "0xC000010B", "STATUS_INVALID_LOGON_TYPE",
|
||||
Status =~ "0xC000015B", "STATUS_LOGON_TYPE_NOT_GRANTED",
|
||||
Status =~ "0xC000018B", "STATUS_NO_TRUST_SAM_ACCOUNT",
|
||||
Status =~ "0xC0000224", "STATUS_PASSWORD_MUST_CHANGE",
|
||||
Status =~ "0xC0000234", "STATUS_ACCOUNT_LOCKED_OUT",
|
||||
Status =~ "0xC00002EE", "STATUS_UNFINISHED_CONTEXT_DELETED",
|
||||
EventID == 4624 or EventID == 4672, "Success",
|
||||
"See - https://docs.microsoft.com/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
|
||||
)
|
||||
| extend SubStatusDesc = case(
|
||||
SubStatus =~ "0x80090325", "SEC_E_UNTRUSTED_ROOT",
|
||||
SubStatus =~ "0xC0000008", "STATUS_INVALID_HANDLE",
|
||||
SubStatus =~ "0xC0000022", "STATUS_ACCESS_DENIED",
|
||||
SubStatus =~ "0xC0000064", "STATUS_NO_SUCH_USER",
|
||||
SubStatus =~ "0xC000006A", "STATUS_WRONG_PASSWORD",
|
||||
SubStatus =~ "0xC000006D", "STATUS_LOGON_FAILURE",
|
||||
SubStatus =~ "0xC000006E", "STATUS_ACCOUNT_RESTRICTION",
|
||||
SubStatus =~ "0xC000006F", "STATUS_INVALID_LOGON_HOURS",
|
||||
SubStatus =~ "0xC0000070", "STATUS_INVALID_WORKSTATION",
|
||||
SubStatus =~ "0xC0000071", "STATUS_PASSWORD_EXPIRED",
|
||||
SubStatus =~ "0xC0000072", "STATUS_ACCOUNT_DISABLED",
|
||||
SubStatus =~ "0xC0000073", "STATUS_NONE_MAPPED",
|
||||
SubStatus =~ "0xC00000DC", "STATUS_INVALID_SERVER_STATE",
|
||||
SubStatus =~ "0xC0000133", "STATUS_TIME_DIFFERENCE_AT_DC",
|
||||
SubStatus =~ "0xC000018D", "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
|
||||
SubStatus =~ "0xC0000193", "STATUS_ACCOUNT_EXPIRED",
|
||||
SubStatus =~ "0xC0000380", "STATUS_SMARTCARD_WRONG_PIN",
|
||||
SubStatus =~ "0xC0000381", "STATUS_SMARTCARD_CARD_BLOCKED",
|
||||
SubStatus =~ "0xC0000382", "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED",
|
||||
SubStatus =~ "0xC0000383", "STATUS_SMARTCARD_NO_CARD",
|
||||
SubStatus =~ "0xC0000384", "STATUS_SMARTCARD_NO_KEY_CONTAINER",
|
||||
SubStatus =~ "0xC0000385", "STATUS_SMARTCARD_NO_CERTIFICATE",
|
||||
SubStatus =~ "0xC0000386", "STATUS_SMARTCARD_NO_KEYSET",
|
||||
SubStatus =~ "0xC0000387", "STATUS_SMARTCARD_IO_ERROR",
|
||||
SubStatus =~ "0xC0000388", "STATUS_DOWNGRADE_DETECTED",
|
||||
SubStatus =~ "0xC0000389", "STATUS_SMARTCARD_CERT_REVOKED",
|
||||
EventID == 4624 or EventID == 4672, "Success",
|
||||
"See - https://docs.microsoft.com/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
|
||||
)
|
||||
| project StartTime = TimeGenerated, DayofWeek, HourOfLogin, EventID, Activity, IpAddress, WorkstationName, Computer, TargetUserName, TargetDomainName, ProcessName, SubjectUserName, PrivilegeList, PassedInAccountName, PassedInNTDomain, LogonTypeName, StatusDesc, SubStatusDesc, RelatedRowSet
|
||||
;
|
||||
let UserSigninToSystems = AllEvents
|
||||
| where EventID == 4624
|
||||
| project-away StatusDesc, SubStatusDesc, PrivilegeList
|
||||
| summarize Total= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=max(StartTime), EndTime = min(StartTime), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), HostLoggedOn = make_set(Computer) by EventID, Activity, TargetDomainName, TargetUserName , ProcessName , LogonTypeName
|
||||
| extend RelatedRowSet = 'UserSigninToSystems' ;
|
||||
let UserFailedSigninToSystems = AllEvents
|
||||
| where EventID == 4625
|
||||
| project-away PrivilegeList
|
||||
| summarize Total= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=max(StartTime), EndTime = min(StartTime), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), HostLoggedOn = make_set(Computer) by EventID, Activity, TargetDomainName, TargetUserName , ProcessName , LogonTypeName
|
||||
| extend RelatedRowSet = 'UserFailedSigninToSystems' ;
|
||||
let UserSigninDuringAbnormalHours = AllEvents
|
||||
| where StartTime between (ago(14d)..ago(2d))
|
||||
| where EventID in (4624,4625)
|
||||
| where LogonTypeName in~ ('2 - Interactive','10 - RemoteInteractive')
|
||||
| summarize max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek) by TargetUserName
|
||||
| join kind= inner
|
||||
(
|
||||
AllEvents
|
||||
| where StartTime > ago(2d)
|
||||
| where LogonTypeName in~ ('2 - Interactive','10 - RemoteInteractive')
|
||||
)
|
||||
on TargetUserName
|
||||
| where HourOfLogin > max_HourOfLogin or HourOfLogin < min_HourOfLogin
|
||||
| extend historical_DayofWeek = tostring(historical_DayofWeek)
|
||||
| summarize Total= count(), max(HourOfLogin), min(HourOfLogin), current_DayofWeek =make_set(DayofWeek), StartTime=max(StartTime), EndTime = min(StartTime), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), HostLoggedOn = make_set(Computer) by EventID, Activity, TargetDomainName, TargetUserName , ProcessName , LogonTypeName, StatusDesc, SubStatusDesc, historical_DayofWeek
|
||||
| extend historical_DayofWeek = todynamic(historical_DayofWeek)
|
||||
| extend RelatedRowSet = 'UserSigninDuringAbnormalHour';
|
||||
let UserHadPrivilegedLogonSessions = AllEvents
|
||||
| where EventID == 4672
|
||||
| where PrivilegeList contains 'SeDebugPrivilege'
|
||||
| project-away StatusDesc, SubStatusDesc
|
||||
| summarize Total= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=max(StartTime), EndTime = min(StartTime), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), HostLoggedOn = make_set(Computer) by EventID, Activity, PrivilegeList
|
||||
// Notice! summarize removes the TimeGenerated field, which is required for Activities.
|
||||
| extend RelatedRowSet = 'UserHadPrivilegedLogonSessions' ;
|
||||
union isfuzzy=true AllEvents, UserSigninToSystems, UserFailedSigninToSystems, UserSigninDuringAbnormalHours, UserHadPrivilegedLogonSessions
|
||||
};
|
||||
// change {{Account_Name}} value below to the username you are interested in and {{Account_NTDomain}} to the domain of the user you are interested in
|
||||
GetAllLogonsForUser('{{Account_Name}}', '{{Account_NTDomain}}')
|
||||
Insights:
|
||||
Id: 8d209299-cb14-4f22-b5c5-6813f2d1ed2e
|
||||
DisplayName: Windows sign-in activity
|
||||
Description: |
|
||||
Summary of successful and failed sign-ins along with anamalous sign-in patterns for the specific user. Successful sign-ins currently only include interactive and limited to LogonType 2 and 10.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Signin Type"
|
||||
OutputType: String
|
||||
- Header: TotalLogons
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: HostCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
|
||||
# UserSigninToSystems
|
||||
- Filter: "where RelatedRowSet =~ 'UserSigninToSystems' | extend NumberOfHostsLoggedOn = array_length(HostLoggedOn) "
|
||||
Summarize: "summarize TotalLogons = sum(Total), HostCount = sum(NumberOfHostsLoggedOn)"
|
||||
Project: "project Title = 'Successful Signins', TotalLogons, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: TotalLogons
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserFailedSigninToSystems
|
||||
- Filter: "where RelatedRowSet =~ 'UserFailedSigninToSystems'| extend NumberOfHostsLoggedOn = array_length(HostLoggedOn) "
|
||||
Summarize: "summarize TotalLogons = sum(Total), HostCount = sum(NumberOfHostsLoggedOn)"
|
||||
Project: "project Title = 'Failed Signins', TotalLogons, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: TotalLogons
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserSigninDuringAbnormalHours
|
||||
- Filter: "where RelatedRowSet =~ 'UserSigninDuringAbnormalHour' | extend NumberOfHostsLoggedOn = array_length(HostLoggedOn)"
|
||||
Summarize: "summarize TotalLogons = sum(Total), HostCount = sum(NumberOfHostsLoggedOn)"
|
||||
Project: "project Title = 'Abnormal Time Signins', TotalLogons, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: TotalLogons
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserHadPrivilegedLogonSessions
|
||||
- Filter: "where RelatedRowSet =~ 'UserHadPrivilegedLogonSessions' | extend NumberOfHostsLoggedOn = array_length(HostLoggedOn) "
|
||||
Summarize: "summarize TotalLogons = sum(Total), HostCount = sum(NumberOfHostsLoggedOn)"
|
||||
Project: "project Title = 'Privileged Signins', TotalLogons, HostCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: TotalLogons
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: HostCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
|
||||
ChartQuery:
|
||||
Title: "Sign-ins over time"
|
||||
DataSets:
|
||||
- Query: "summarize Count=countif(EventID==4624) by Time = bin(StartTime, 1h) | extend Legend = 'Success'"
|
||||
XColumnName: "Time"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
- Query: "summarize Count=countif(EventID==4625) by Time = bin(StartTime, 1h) | extend Legend = 'Failed'"
|
||||
XColumnName: "Time"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
Type: LineChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all Windows sign-ins"
|
||||
Query: "where RelatedRowSet =~ 'AllEvents' | where EventID in (4624,4625,4672) | extend SubjectUserName = columnifexists('SubjectUserName', 'EventDoesNotContain') | summarize Total= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=max(StartTime), EndTime = min(StartTime), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), HostLoggedOn = make_set(Computer) by Activity, TargetDomainName, TargetUserName, ProcessName, LogonTypeName | extend NumberOfHostsLoggedOn = array_length(HostLoggedOn)"
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Title: "{{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Items:
|
||||
- Id: 0d4ec12e-e44a-40a4-bb87-3db84d2a8057
|
||||
Title: "{{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's interactive log-ins grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet == 'UserSigninToSystems' and LogonTypeName == '2 - Interactive' | extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: c9da5786-6c3c-45b5-9a46-53200ed9df09
|
||||
Title: "{{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's network log-ins, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet == 'UserSigninToSystems' and LogonTypeName == '3 - Network' | extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: 8a302bfc-00e3-43b3-a516-102fd0cb0dbc
|
||||
Title: "{{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's remote interactive log-ins, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet == 'UserSigninToSystems' and LogonTypeName == '10 - RemoteInteractive'| extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: ec87b066-17ad-4f9b-97c2-c2f2ee2d99e0
|
||||
Title: "{{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's log-ins with new credentials, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet == 'UserSigninToSystems' and LogonTypeName == '9 - NewCredentials'| extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: e1c4c03c-2b40-47cf-9b8c-49e0a37a6da6
|
||||
Title: "'Privileged log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's privileged log-ins, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet = 'UserHadPrivilegedLogonSessions'"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: a6fc3ad9-1a61-41f5-a5e2-bd1f5a6fe44d
|
||||
Title: "'Failed {{LogonTypeName}}' log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's failed interactive log-ins grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet =~ 'UserFailedSigninToSystems' and LogonTypeName == '2 - Interactive' | extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: 11449689-6542-4867-86dc-56264abbd90c
|
||||
Title: "'Failed {{LogonTypeName}}' log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's failed network log-ins, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet =~ 'UserFailedSigninToSystems' and LogonTypeName == '3 - Network' | extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
||||
- Id: 686cf7e8-87c7-4391-8898-25adf1033a54
|
||||
Title: "Failed {{LogonTypeName}} log-ins to a host"
|
||||
Content: "The user {{v_Account_Name}} failed to logged on to host {{WorkstationName}} {{Count}} time(s)"
|
||||
Description: "This activity lists the user's failed remote interactive log-ins, grouped by Host."
|
||||
QueryDefinitions:
|
||||
Filter: "where RelatedRowSet =~ 'UserFailedSigninToSystems' and LogonTypeName == '10 - RemoteInteractive' | extend TimeGenerated=StartTime"
|
||||
SummarizeBy: "Computer, WorkstationName, LogonTypeName"
|
|
@ -0,0 +1,136 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
let GetAccountActions = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string){
|
||||
SecurityEvent
|
||||
| where EventID in (4725, 4726, 4767, 4720, 4722, 4723, 4724)
|
||||
// parsing for Host to handle variety of conventions coming from data
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| project TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName, TargetDomainName, TargetSid, SubjectUserName, SubjectUserSid, _ResourceId, SourceComputerId
|
||||
| extend AddedBy = SubjectUserName
|
||||
// Future support for Activities
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = TargetAccount
|
||||
};
|
||||
GetAccountActions('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: e29ee1ef-7445-455e-85f1-269f2d536d61
|
||||
DisplayName: Actions on accounts
|
||||
Description: |
|
||||
Summary of actions taken on the specified host, grouped by action: password resets and changes, account lockouts (policy or admin), account creation and deletion, account enabled and disabled
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Action"
|
||||
OutputType: String
|
||||
SupportDeepLink: false
|
||||
- Header: "NameCount"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "SIDCount"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
# Account Created or Enabled
|
||||
- Filter: "where EventID in (4720, 4722)"
|
||||
Summarize: "summarize NameCount = dcount(TargetAccount), SIDCount = dcount(TargetSid)"
|
||||
Project: "project Title = 'Created or Enabled', NameCount, SIDCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: NameCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: SIDCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# Account Deleted or Disabled
|
||||
- Filter: "where EventID in (4725, 4726)"
|
||||
Summarize: "summarize NameCount = dcount(TargetAccount), SIDCount = dcount(TargetSid)"
|
||||
Project: "project Title = 'Deleted or Disabled', NameCount, SIDCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: NameCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: SIDCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# Account Password Changes
|
||||
- Filter: "where EventID in (4723, 4724)"
|
||||
Summarize: "summarize NameCount = dcount(TargetAccount), SIDCount = dcount(TargetSid)"
|
||||
Project: "project Title = 'Password Reset', NameCount, SIDCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: NameCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: SIDCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
|
||||
ChartQuery:
|
||||
Title: "Actions on accounts over time"
|
||||
DataSets:
|
||||
- Query: "where EventID in (4720, 4722) | summarize Count = dcount(TargetSid) by bin(TimeGenerated, 1h) | extend Legend = 'Created/Enabled'"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
- Query: "where EventID in (4725, 4726) | summarize Count = dcount(TargetSid) by bin(TimeGenerated, 1h) | extend Legend = 'Deleted/Disabled'"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
- Query: "where EventID in (4723, 4724) | summarize Count = dcount(TargetSid) by bin(TimeGenerated, 1h) | extend Legend = 'Reset'"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
Type: BarChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all actions on accounts"
|
||||
Query: "project TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName, TargetDomainName, TargetSid, SubjectUserName, SubjectUserSid, _ResourceId, SourceComputerId, timestamp, HostCustomEntity, AccountCustomEntity | order by TimeGenerated desc"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: 307c85ee-39a2-4da3-952e-4fd79aa46d3a
|
||||
Description: Account created on host
|
||||
Title: "An account was created on this host"
|
||||
Content: "On '{{Computer}}' the account '{{TargetAccount}}' was created by '{{AddedBy}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where EventID == 4720
|
||||
SummarizeBy: Computer
|
||||
- Id: 31529548-dbd2-4d5d-8270-710330cdcec7
|
||||
Description: Account deleted on host
|
||||
Title: "An account was deleted on this host"
|
||||
Content: "On '{{Computer}}' the account '{{TargetAccount}}' was deleted by '{{AddedBy}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where EventID == 4726
|
||||
SummarizeBy: Computer
|
|
@ -0,0 +1,180 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
- DataType: Event
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
let SystemAccount = datatable(AccountName:string)['NT AUTHORITY\\SYSTEM', 'NT AUTHORITY\\NETWORK SERVICE', 'NT AUTHORITY\\LOCAL SERVICE', 'NT AUTHORITY\\IUSR', 'NTAUTHORITY\\ANONYMOUS LOGON'];
|
||||
let SvcAcctList = dynamic(["Local SYSTEM","Local SERVICE","Network SERVICE","NT AUTHORITY"]);
|
||||
let ServiceAccount = SecurityEvent
|
||||
| where EventID == '4624' and LogonType == '5' and not(Account has_any (SvcAcctList))
|
||||
| extend AccountName = Account
|
||||
| distinct AccountName;
|
||||
let MachineAccount = SecurityEvent
|
||||
| where EventID == '4624' and AccountType == "Machine" and not(Account has_any (SvcAcctList))
|
||||
| extend AccountName = Account
|
||||
| distinct AccountName;
|
||||
let Accounts = union isfuzzy=true SystemAccount, ServiceAccount, MachineAccount;
|
||||
let source = 'Microsoft-Windows-Eventlog';
|
||||
let tableFunc = (tableName:string, event:int){
|
||||
table(tableName)
|
||||
| where EventID == event
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| extend SourceComputerId = column_ifexists("SourceComputerId", "NotAvailable"), EventOriginId = column_ifexists("EventOriginId", "NotAvailable")
|
||||
| parse EventData with * 'SubjectUserName>' SubjectUserName '<' *
|
||||
| parse EventData with * 'SubjectUserSid>' SubjectUserSid '<' *
|
||||
| parse EventData with * 'SubjectLogonId>' SubjectLogonId '<' *
|
||||
| parse EventData with * 'SubjectDomainName>' SubjectDomainName '<' *
|
||||
| extend SubjectAccount = strcat(SubjectDomainName, '\\', SubjectUserName)
|
||||
};
|
||||
let HostClearedEventLog = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string)
|
||||
{
|
||||
let Event104 = tableFunc('Event', event=104)
|
||||
| where Source =~ source
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| parse RenderedDescription with * 'The' LogName 'log' *
|
||||
| project TimeGenerated, Computer, EventID, SubjectAccount, SubjectUserName, SubjectDomainName, LogName, SubjectUserSid, SubjectLogonId, SourceComputerId, EventOriginId, _ResourceId
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = SubjectAccount, HostCustomEntity = Computer;
|
||||
let Event1102 = tableFunc('SecurityEvent', event=1102)
|
||||
| where EventSourceName == source
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| extend LogName = 'Security'
|
||||
| project TimeGenerated, Computer, EventID, SubjectAccount, SubjectUserName, SubjectDomainName, LogName, SubjectUserSid, SubjectLogonId, SourceComputerId, EventOriginId, _ResourceId
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = SubjectAccount, HostCustomEntity = Computer;
|
||||
union isfuzzy=true Event104, Event1102
|
||||
};
|
||||
HostClearedEventLog('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
Insights:
|
||||
Id: 9a70a72d-25d4-7212-b73e-4f302a90c06a
|
||||
DisplayName: Event Logs cleared on host
|
||||
Description: |
|
||||
'Provides the number of times event logs were cleared on the host.'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Cleared By"
|
||||
OutputType: String
|
||||
SupportDeepLink: false
|
||||
- Header: "Security Log"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "Other Logs"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "Total"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
|
||||
# LogCleared_User
|
||||
- Filter: "where SubjectUserName !in (Accounts)"
|
||||
Summarize: "summarize Security = countif(LogName =~ 'Security'), Other = countif(LogName !~ 'Security'), All = count() by SubjectAccount"
|
||||
Project: "project SubjectAccount, Security, Other, All"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Security
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName =~ 'Security'"
|
||||
- ProjectedName: Other
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName !~ 'Security'"
|
||||
- ProjectedName: All
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# LogCleared_System
|
||||
- Filter: "where AccountCustomEntity in (SystemAccount)"
|
||||
Summarize: "summarize Security = countif(LogName =~ 'Security'), Other = countif(LogName !~ 'Security'), All = count() by SubjectAccount"
|
||||
Project: "project SubjectAccount, Security, Other, All"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Security
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName =~ 'Security'"
|
||||
- ProjectedName: Other
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName !~ 'Security'"
|
||||
- ProjectedName: All
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# LogCleared_Service
|
||||
- Filter: "where AccountCustomEntity in (ServiceAccount)"
|
||||
Summarize: "summarize Security = countif(LogName =~ 'Security'), Other = countif(LogName !~ 'Security'), All = count() by SubjectAccount"
|
||||
Project: "project SubjectAccount, Security, Other, All"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Security
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName =~ 'Security'"
|
||||
- ProjectedName: Other
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName !~ 'Security'"
|
||||
- ProjectedName: All
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# LogCleared_Machine
|
||||
- Filter: "where AccountCustomEntity in (MachineAccount)"
|
||||
Summarize: "summarize Security = countif(LogName =~ 'Security'), Other = countif(LogName !~ 'Security'), All = count() by SubjectAccount"
|
||||
Project: "project SubjectAccount, Security, Other, All"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Security
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName =~ 'Security'"
|
||||
- ProjectedName: Other
|
||||
Query: "{{BaseQuery}} | {{RowFilter}} | where LogName !~ 'Security'"
|
||||
- ProjectedName: All
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Log clear activity over time"
|
||||
DataSets:
|
||||
- Query: "summarize LogClearOnHost = count() by Time = bin(TimeGenerated, 12h), SubjectAccount"
|
||||
XColumnName: Time
|
||||
YColumnName: LogClearOnHost
|
||||
LegendColumnName: SubjectAccount
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See all log clear activity"
|
||||
Query: "project TimeGenerated, LogName, Computer, SubjectAccount, SubjectUserName, SubjectDomainName, EventID, SubjectUserSid, SubjectLogonId, SourceComputerId, _ResourceId, EventOriginId"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: 2fcda698-9526-454f-8fe0-4a0fd7af13f2
|
||||
Description: Security Event log cleared by account
|
||||
Title: "Security Event log cleared by account on this host"
|
||||
Content: "On '{{Computer}}' the user '{{SubjectAccount}}' cleared the '{{LogName}}' log, EventID: '{{EventID}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where LogName =~ 'Security'
|
||||
SummarizeBy: SubjectAccount
|
||||
- Id: 3ff675ee-3052-4e0b-88ad-f34ed1732adc
|
||||
Description: Event logs cleared by account
|
||||
Title: "Event log(s) cleared by account on this host"
|
||||
Content: "On '{{Computer}}' the user '{{SubjectAccount}}' cleared the '{{LogName}}' log, EventID: '{{EventID}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where LogName !~ 'Security'
|
||||
SummarizeBy: SubjectAccount
|
|
@ -0,0 +1,149 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
let WellKnownLocalSID = 'S-1-5-32-5[0-9][0-9]$';
|
||||
let WellKnownGroupSID = 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-5[0-9][0-9]$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1102$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1103$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-498$|S-1-5-21-[0-9]*-[0-9]*-[0-9]*-1000$';
|
||||
let GetGroupAddForHost = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string){
|
||||
SecurityEvent
|
||||
| where EventID in (4728, 4732, 4756)
|
||||
// parsing for Host to handle variety of conventions coming from data
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| extend MemberAdded = case( MemberName has 'CN=', tostring(split(tostring(split(MemberName, ',')[0]),'CN=')[1]), MemberName == '-', MemberSid, MemberName)
|
||||
| project TimeGenerated, EventID, Activity, Computer, MemberAdded, MemberName, MemberSid, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectUserName, SubjectUserSid, WellKnownGroupSID, WellKnownLocalSID, _ResourceId, SourceComputerId
|
||||
| extend GroupName = TargetUserName, AddedBy = SubjectUserName
|
||||
//support for Activities
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer
|
||||
};
|
||||
GetGroupAddForHost('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: 44c9d0fe-c131-4080-a34f-da0e349da336
|
||||
DisplayName: Group additions
|
||||
Description: |
|
||||
Summary of user additions to Groups on the specific host. Specifically, we provide the count of All Groups, [Privileged Groups](https://docs.microsoft.com/windows/security/identity-protection/access-control/active-directory-security-groups) and Remote Desktop Users Group.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Groups"
|
||||
OutputType: String
|
||||
SupportDeepLink: false
|
||||
- Header: GroupCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: UsersAddedCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
|
||||
# UserAddedToPrivilegedGroups
|
||||
- Filter: "where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), UsersAddedCount = dcount(MemberAdded) by Computer"
|
||||
Project: "project Title = 'Privileged', GroupCount, UsersAddedCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UsersAddedCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserAddedToRemoteDesktopGroup
|
||||
- Filter: "where TargetSid in ('S-1-5-32-555')"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), UsersAddedCount = dcount(MemberAdded)"
|
||||
Project: "project Title = 'Remote Desktop', GroupCount, UsersAddedCount by Computer"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UserAddedToPrivilegedGroupsExcludeRDP
|
||||
- Filter: "where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID | where TargetSid !in ('S-1-5-32-555')"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), UsersAddedCount = dcount(MemberAdded) by Computer"
|
||||
Project: "project Title = 'Privileged(non-RDP)', GroupCount, UsersAddedCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# UsersAddedToGroups
|
||||
- Filter: "order by GroupName"
|
||||
Summarize: "summarize GroupCount = dcount(GroupName), UsersAddedCount = dcount(MemberAdded) by Comptuer"
|
||||
Project: "project Title = 'All', GroupCount, UsersAddedCount"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: GroupCount
|
||||
Query: "{{BaseQuery}}"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Group additions per hour"
|
||||
DataSets:
|
||||
- Query: "summarize Count = count() by bin(TimeGenerated, 1h) | extend Legend = 'Total'"
|
||||
XColumnName: "TimeGenerated"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
Type: LineChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all group additions"
|
||||
Query: "project TimeGenerated, EventID, Activity, Computer, MemberAdded, MemberName, MemberSid, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectUserName, SubjectUserSid, WellKnownGroupSID, WellKnownLocalSID, _ResourceId, SourceComputerId, timestamp, HostCustomEntity | order by TimeGenerated desc"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: b880ad94-f905-4ba8-8a3f-9088b19b12fa
|
||||
Description: Account added to local Administrators group
|
||||
Title: "An account was added to the local Administrators group"
|
||||
Content: "On '{{Computer}}' the user '{{MemberAdded}}' was added by '{{AddedBy}}' to group: '{{GroupName}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where TargetSid == 'S-1-5-32-544'
|
||||
SummarizeBy: Computer
|
||||
- Id: aaad22c3-be50-465f-b258-8570d629c3db
|
||||
Description: Account added to the Domain Admins group
|
||||
Title: "An account was added to the Domain Admins group"
|
||||
Content: "On '{{Computer}}' the user '{{MemberAdded}}' was added by '{{AddedBy}}' to group: '{{GroupName}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where TargetSid matches regex 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-512$'
|
||||
SummarizeBy: Computer
|
||||
- Id: cf3469b3-f64c-4ae2-9900-289617443d74
|
||||
Description: Account added to the Enterprise Admins group
|
||||
Title: "An account was added to the Enterprise Admins group"
|
||||
Content: "On '{{Computer}}' the user '{{MemberAdded}}' was added by '{{AddedBy}}' to group: '{{GroupName}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where TargetSid matches regex 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-519$'
|
||||
SummarizeBy: Computer
|
||||
- Id: 5ba7b064-c667-4bb9-b8ac-7e87872ae479
|
||||
Description: Account added to privileged group.
|
||||
Title: "Account added to a privileged group"
|
||||
Content: "On '{{Computer}}' the user '{{MemberAdded}}' was added by '{{AddedBy}}' to group: '{{GroupName}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where (TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID) and TargetSid != 'S-1-5-32-544' and not(TargetSid matches regex 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-512$') and not(TargetSid matches regex 'S-1-5-21-[0-9]*-[0-9]*-[0-9]*-519$')
|
||||
SummarizeBy: MemberAdded, AddedBy, GroupName
|
|
@ -0,0 +1,86 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: Syslog
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Linux
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- - Host_AzureID
|
||||
BaseQuery: |
|
||||
let AllUserEvents = (v_Host_Name:string, v_Host_AzureID:string) {
|
||||
Syslog
|
||||
| where Computer == v_Host_Name or v_Host_AzureID == _ResourceId
|
||||
| where Facility == 'authpriv'
|
||||
| where ProcessName in~ ('useradd','userdel')
|
||||
| where SyslogMessage startswith 'new user:' or SyslogMessage startswith 'delete user '
|
||||
| extend User = case(SyslogMessage startswith 'new user:', tostring(split(tostring(split(SyslogMessage, 'name=')[1]), ',')[0]),
|
||||
SyslogMessage startswith 'delete user ', tostring(split(SyslogMessage, "'")[1]),
|
||||
'Not Available')
|
||||
| extend Action = case( SyslogMessage startswith 'new user', 'new user', SyslogMessage startswith 'delete user', 'delete user', 'None')
|
||||
| project TimeGenerated, Computer, HostIP, User, Facility, ProcessName, Action, SyslogMessage, _ResourceId
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP, AccountCustomEntity = User
|
||||
};
|
||||
AllUserEvents('{{Host_HostName}}', '{{Host_AzureID}}')
|
||||
Insights:
|
||||
Id: e7144614-84b3-4884-bc14-cba1b9bac0de
|
||||
DisplayName: Linux new or deleted users on host
|
||||
Description: |
|
||||
'Summary of actions taken by Sudo on the specified host grouped by newly created or deleted users'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Action
|
||||
OutputType: String
|
||||
- Header: User
|
||||
OutputType: String
|
||||
- Header: UserCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: ActionCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
# UsersCreated
|
||||
- Filter: "where Action == 'new user'"
|
||||
Summarize: "summarize UserCount = dcount(User), NewUsers = makeset(User), ActionCount = count() by Computer, Action"
|
||||
Project: "project Action, User = case(UserCount == 1, tostring(NewUsers[0]), UserCount > 1, 'Many', 'None'), UserCount, ActionCount"
|
||||
# UsersDeleted
|
||||
- Filter: "where Action == 'delete user'"
|
||||
Summarize: "summarize UserCount = dcount(User), DelUsers = makeset(User), ActionCount = count() by Computer, Action"
|
||||
Project: "project Action, User = case(UserCount == 1, tostring(DelUsers[0]), UserCount > 1, 'Many', 'None'), UserCount, ActionCount"
|
||||
|
||||
ChartQuery:
|
||||
Title: "New or deleted users over time"
|
||||
DataSets:
|
||||
- Query: "summarize SudoUsage = count(User) by Time = bin(TimeGenerated, 1h), Action | extend Legend = Action"
|
||||
XColumnName: Time
|
||||
YColumnName: SudoUsage
|
||||
LegendColumnName: Legend
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See all new or deleted users"
|
||||
Query: "project TimeGenerated, Computer, HostIP, User, Facility, ProcessName, Action, SyslogMessage, _ResourceId, timestamp, HostCustomEntity, AccountCustomEntity, IPCustomEntity"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: 290032e9-c52e-4e66-841a-7428f0b356bb
|
||||
Description: Account created on Host
|
||||
Title: "An account was created on this host"
|
||||
Content: "On '{{Computer}}' the account '{{User}}' was created by sudo"
|
||||
QueryDefinitions:
|
||||
Filter: where Action == 'new user'
|
||||
SummarizeBy: Computer
|
||||
- Id: ce9e87c7-2ffa-42cb-92e5-f1a4f21f007a
|
||||
Description: Account deleted on Host
|
||||
Title: "An account was deleted on this host"
|
||||
Content: "On '{{Computer}}' the account '{{User}}' was deleted by sudo"
|
||||
QueryDefinitions:
|
||||
Filter: where Action == 'delete user'
|
||||
SummarizeBy: Computer
|
|
@ -0,0 +1,115 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: Syslog
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Linux
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- - Host_AzureID
|
||||
BaseQuery: |
|
||||
let AllUserEvents = (v_Host_Name:string, v_Host_AzureID:string) {
|
||||
Syslog
|
||||
| where Computer == v_Host_Name or v_Host_AzureID == _ResourceId
|
||||
| where Facility == 'authpriv'
|
||||
| where SyslogMessage !startswith "omsagent"
|
||||
| where SyslogMessage has 'COMMAND' or ProcessName in~ ('gpasswd', 'useradd', 'userdel')
|
||||
| parse SyslogMessage with * 'user ' User ' ' Verb ' by ' AcctMakingChange ' ' Preposition ' group ' Group
|
||||
| extend Group = case(
|
||||
SyslogMessage startswith 'removed group' or SyslogMessage startswith 'removed shadow group', tostring(split(SyslogMessage, "'")[1]),
|
||||
SyslogMessage startswith 'new group', tostring(split(tostring(split(SyslogMessage, '=')[1]),',')[0]),
|
||||
Group)
|
||||
| extend Action = case(
|
||||
isnotempty(Verb) or isnotempty(Preposition), strcat(Verb, ' ', Preposition),
|
||||
SyslogMessage startswith 'new group', 'new group',
|
||||
SyslogMessage startswith 'removed group', 'removed group',
|
||||
SyslogMessage startswith 'removed shadow group', 'removed shadow group',
|
||||
'None')
|
||||
| where isnotempty(Action) and Action != 'None' and isnotempty(Group)
|
||||
| project TimeGenerated, Computer, HostIP, User, Action, Group, Facility, ProcessName, AcctMakingChange, SyslogMessage, _ResourceId
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP, AccountCustomEntity = User
|
||||
};
|
||||
AllUserEvents('{{Host_HostName}}', '{{Host_AzureID}}')
|
||||
Insights:
|
||||
Id: f1607751-8784-4a69-a91b-45b56683bc77
|
||||
DisplayName: Linux group actions on host
|
||||
Description: |
|
||||
'Summary of additions or removals to groups by Sudo on the specified host, specifically the count for Sudo Group, Any group, Group creations and deletions'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Action
|
||||
OutputType: String
|
||||
- Header: Group(s)
|
||||
OutputType: String
|
||||
- Header: GroupCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: UserCount
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: User(s)
|
||||
OutputType: String
|
||||
QueriesDefinitions:
|
||||
# UsersAddedtoSudoGroup
|
||||
- Filter: "where Action =~ 'added to' and Group =~ 'sudo' | extend Action = strcat('users ', Action)"
|
||||
Summarize: "summarize UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = 'Sudo', GroupCount = 1, UserCount, Users = case(UserCount == 1, tostring(UsersAdded[0]), UserCount > 1, 'Many', 'None')"
|
||||
# UsersRemovedFromSudoGroup
|
||||
- Filter: "where Action =~ 'removed from' and Group =~ 'sudo' | extend Action = strcat('users ', Action)"
|
||||
Summarize: "summarize UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = 'Sudo', GroupCount = 1, UserCount, Users = case(UserCount == 1, tostring(UsersAdded[0]), UserCount > 1, 'Many', 'None')"
|
||||
# UsersAddedtoAnyGroup
|
||||
- Filter: "where Action =~ 'added to' | extend Action = strcat('users ', Action)"
|
||||
Summarize: "summarize GroupCount = dcount(Group), UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = 'Any', GroupCount, UserCount, Users = case(UserCount == 1, tostring(UsersAdded[0]), UserCount > 1, 'Many', 'None')"
|
||||
# UsersRemovedFromAnyGroup
|
||||
- Filter: "where Action =~ 'removed from' | extend Action = strcat('users ', Action)"
|
||||
Summarize: "summarize GroupCount = dcount(Group), UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = 'Any', GroupCount, UserCount, Users = case(UserCount == 1, tostring(UsersAdded[0]), UserCount > 1, 'Many', 'None')"
|
||||
# GroupAdded
|
||||
- Filter: "where Action =~ 'new group' | extend Action = strcat(Action, 's created')"
|
||||
Summarize: "summarize Groups = make_set(Group), GroupCount = dcount(Group), UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = case(GroupCount == 1, tostring(Groups[0]), GroupCount > 1, 'Many', 'None'), GroupCount, UserCount = 0, Users = 'None'"
|
||||
# GroupDeleted
|
||||
- Filter: "where Action =~ 'removed group'"
|
||||
Summarize: "summarize Groups = make_set(Group), GroupCount = dcount(Group), UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = case(GroupCount == 1, tostring(Groups[0]), GroupCount > 1, 'Many', 'None'), GroupCount, UserCount = 0, Users = 'None'"
|
||||
# ShadowGroupDeleted
|
||||
- Filter: "where Action =~ 'removed shadow group'"
|
||||
Summarize: "summarize Groups = make_set(Group), GroupCount = dcount(Group), UserCount = dcount(User), UsersAdded = makeset(User) by Computer, Action"
|
||||
Project: "project Action, Groups = case(GroupCount == 1, tostring(Groups[0]), GroupCount > 1, 'Many', 'None'), GroupCount, UserCount = 0, Users = 'None'"
|
||||
ChartQuery:
|
||||
Title: "Group actions over time"
|
||||
DataSets:
|
||||
- Query: "summarize SudoUsage = count(User) by Time = bin(TimeGenerated, 1h), Action | extend Legend = Action"
|
||||
XColumnName: Time
|
||||
YColumnName: SudoUsage
|
||||
LegendColumnName: Legend
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See all group actions"
|
||||
Query: "project TimeGenerated, Computer, HostIP, User, Action, Group, Facility, ProcessName, AcctMakingChange, SyslogMessage, _ResourceId, timestamp, HostCustomEntity, AccountCustomEntity, IPCustomEntity"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: 46aeae2d-187c-41f9-b8d6-9d75c43bce0a
|
||||
Description: Account added to the sudo group
|
||||
Title: "An account was added to the sudo group"
|
||||
Content: "On '{{Computer}}' the user '{{User}}' was added by '{{AcctMakingChange}}' to group: '{{Group}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where Action =~ 'added to' and Group =~ 'sudo'
|
||||
SummarizeBy: Computer
|
||||
- Id: e24dd437-c65e-40e1-8d59-cd303ad4496a
|
||||
Description: Account removed from sudo group
|
||||
Title: "An account was removed from the sudo group"
|
||||
Content: "On '{{Computer}}' the user '{{User}}' was added by '{{AcctMakingChange}}' to group: '{{Group}}'"
|
||||
QueryDefinitions:
|
||||
Filter: where Action =~ 'removed from' and Group =~ 'sudo'
|
||||
SummarizeBy: Computer
|
|
@ -0,0 +1,110 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: Syslog
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Linux
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- - Host_AzureID
|
||||
BaseQuery: |
|
||||
let SigninResults =
|
||||
Syslog
|
||||
| where Facility =~ 'auth'
|
||||
| where SyslogMessage startswith 'Accepted' or SyslogMessage startswith 'Failed';
|
||||
let AllSigninResults = (v_Host_Name:string, v_Host_AzureID:string)
|
||||
{
|
||||
let HostSpecificResults = SigninResults
|
||||
| where Computer == v_Host_Name or v_Host_AzureID == _ResourceId;
|
||||
let AcceptedAuth = HostSpecificResults
|
||||
| where SyslogMessage startswith 'Accepted';
|
||||
let LongAuth = AcceptedAuth
|
||||
| where SyslogMessage has ':'
|
||||
| parse SyslogMessage with * 'Accepted ' LogonMethod ' for ' User ' from ' ExternalIP ' port ' Port ' ' ConnectionType ':' TrimExtra
|
||||
| project TimeGenerated = EventTime, HostName, HostIP, User, LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result = 'SuccessfulSignin', _ResourceId;
|
||||
let ShortAuth = AcceptedAuth
|
||||
| where SyslogMessage !has ':'
|
||||
| parse SyslogMessage with * 'Accepted ' LogonMethod ' for ' User ' from ' ExternalIP ' port ' Port ' ' ConnectionType
|
||||
| project TimeGenerated = EventTime, HostName, HostIP, User, LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result = 'SuccessfulSignin', _ResourceId;
|
||||
let InitialFailedAuth = HostSpecificResults
|
||||
| where SyslogMessage startswith 'Failed';
|
||||
let FailedAuth = InitialFailedAuth
|
||||
| where SyslogMessage !has 'invalid'
|
||||
| parse SyslogMessage with * 'Failed ' LogonMethod ' for ' User ' from ' ExternalIP ' port ' Port ' ' ConnectionType
|
||||
| project TimeGenerated = EventTime, HostName, HostIP, User, LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result = 'FailedSignin', _ResourceId;
|
||||
let ShortInvalidAuth = InitialFailedAuth
|
||||
| where SyslogMessage has 'invalid user from'
|
||||
| parse SyslogMessage with * 'Failed ' LogonMethod ' for invalid user from ' ExternalIP ' port ' Port ' ' ConnectionType
|
||||
| project TimeGenerated = EventTime, HostName, HostIP, User = ' ', LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result = 'InvalidSignin', _ResourceId;
|
||||
let LongInvalidAuth = InitialFailedAuth
|
||||
| where SyslogMessage has 'invalid' and SyslogMessage !has ' user from'
|
||||
| parse SyslogMessage with * 'Failed ' LogonMethod ' for invalid user ' User ' from ' ExternalIP ' port ' Port ' ' ConnectionType
|
||||
| project TimeGenerated = EventTime, HostName, HostIP, User, LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result = 'InvalidSignin', _ResourceId;
|
||||
union isfuzzy=true LongAuth, ShortAuth, FailedAuth, ShortInvalidAuth, LongInvalidAuth
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = HostName, IPCustomEntity = HostIP, AccountCustomEntity = User
|
||||
};
|
||||
AllSigninResults('{{Host_HostName}}', '{{Host_AzureID}}')
|
||||
Insights:
|
||||
Id: d4ca45db-254b-46f0-98fa-d1d104c26e0c
|
||||
DisplayName: Linux sign-in activity
|
||||
Description: |
|
||||
'Summary of successful, failed or invalid signins, along with most frequent and least frequent signins.'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Signin Result"
|
||||
OutputType: String
|
||||
- Header: "Signin Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "User Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "User(s)"
|
||||
OutputType: String
|
||||
QueriesDefinitions:
|
||||
# UserSigninsToHost
|
||||
- Filter: "where Result == 'SuccessfulSignin'"
|
||||
Summarize: "summarize UserCount = dcount(User), Users = makeset(User), SigninCount = count() by HostName"
|
||||
Project: "project Title = 'Success', SigninCount = case(UserCount == 0, 0, isempty(SigninCount), 0, SigninCount), UserCount, Users = case(UserCount == 1, tostring(Users[0]), UserCount > 1, 'Many', 'None')"
|
||||
# UserFailedSigninsToHost
|
||||
- Filter: "where Result == 'FailedSignin'"
|
||||
Summarize: "summarize UserCount = dcount(User), Users = makeset(User), SigninCount = count() by HostName"
|
||||
Project: "project Title = 'Fail', SigninCount = case(UserCount == 0, 0, isempty(SigninCount), 0, SigninCount), UserCount, Users = case(UserCount == 1, tostring(Users[0]), UserCount > 1, 'Many', 'None')"
|
||||
# InvalidSigninsToHost
|
||||
- Filter: "where Result == 'InvalidSignin'"
|
||||
Summarize: "summarize UserCount = dcount(User), Users = makeset(User), SigninCount = count() by HostName"
|
||||
Project: "project Title = 'Invalid', SigninCount = case(UserCount == 0, 0, isempty(SigninCount), 0, SigninCount), UserCount, Users = case(UserCount == 1, tostring(Users[0]), UserCount > 1, 'Many', 'None')"
|
||||
# MostFrequent
|
||||
- Filter: "where Result == 'SuccessfulSignin'"
|
||||
Summarize: "summarize StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SigninCount = count() by User | top 1 by SigninCount desc"
|
||||
Project: "project Title = 'Most Frequent', SigninCount, UserCount = 1, Users = User"
|
||||
# LeastFrequent
|
||||
- Filter: "where Result == 'SuccessfulSignin'"
|
||||
Summarize: "summarize StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SigninCount = count() by User | top 1 by SigninCount asc"
|
||||
Project: "project Title = 'Least Frequent', SigninCount, UserCount = 1, Users = User"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Sign-ins over time"
|
||||
DataSets:
|
||||
- Query: "where Result == 'SuccessfulSignin' | summarize SigninCount = count() by Time = bin(TimeGenerated, 1h) | extend Legend = 'Success'"
|
||||
XColumnName: Time
|
||||
YColumnName: SigninCount
|
||||
LegendColumnName: Legend
|
||||
- Query: "where Result == 'FailedSignin' | summarize SigninCount = count() by Time = bin(TimeGenerated, 1h) | extend Legend = 'Fail'"
|
||||
XColumnName: Time
|
||||
YColumnName: SigninCount
|
||||
LegendColumnName: Legend
|
||||
- Query: "where Result == 'InvalidSignin' | summarize SigninCount = count() by Time = bin(TimeGenerated, 1h) | extend Legend = 'Invalid'"
|
||||
XColumnName: Time
|
||||
YColumnName: SigninCount
|
||||
LegendColumnName: Legend
|
||||
Type: LineChart
|
||||
AdditionalQuery:
|
||||
Text: "See all Linux sign-ins"
|
||||
Query: "summarize StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SigninCount = count() by HostName, HostIP, User, LogonMethod, ExternalIP, Port, ConnectionType, ProcessName, Result, _ResourceId, timestamp, HostCustomEntity, AccountCustomEntity, IPCustomEntity"
|
|
@ -0,0 +1,83 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: Syslog
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Linux
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- - Host_AzureID
|
||||
BaseQuery: |
|
||||
let AllUserEvents = (v_Host_Name:string, v_Host_AzureID:string) {
|
||||
Syslog
|
||||
| where Computer == v_Host_Name or v_Host_AzureID == _ResourceId
|
||||
| where Facility == "authpriv"
|
||||
| where SyslogMessage !startswith "omsagent"
|
||||
| where SyslogMessage has 'COMMAND'
|
||||
| parse SyslogMessage with User ' : TTY=' TTY ' PWD=' WorkingDirectory ' USER=' CmdRunAs ' COMMAND=' Commandline
|
||||
| where User != 'omsagent'
|
||||
| parse Commandline with Command ' ' *
|
||||
| extend Command = case(isempty(Command), Commandline, Command)
|
||||
| project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage, _ResourceId
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, IPCustomEntity = HostIP, AccountCustomEntity = User
|
||||
};
|
||||
AllUserEvents('{{Host_HostName}}', '{{Host_AzureID}}')
|
||||
Insights:
|
||||
Id: a9191fbe-ca33-400c-8036-18caac59271c
|
||||
DisplayName: Linux sudo usage on host
|
||||
Description: |
|
||||
'Sudo usage on host by users, most/least events by commands, most/least events by user'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 7d
|
||||
AfterRange: 7d
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Sudo Usage"
|
||||
OutputType: String
|
||||
- Header: User
|
||||
OutputType: String
|
||||
- Header: "Usage Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: Command(s)
|
||||
OutputType: String
|
||||
SupportDeepLink: true
|
||||
- Header: "Distinct Commands"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
# MostSudoEventsByCommand
|
||||
- Filter: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage"
|
||||
Summarize: "summarize User = make_set(User), UserCount = dcount(User), UsageCount = count() by Command | top 1 by UsageCount desc"
|
||||
Project: "project Title = 'Most by Command', User = case(UserCount == 1, tostring(User[0]), UserCount > 1, 'Many', 'None'), UsageCount = case(UsageCount == 0, 0, UsageCount), Commands = Command, CommandCount = 1"
|
||||
# LeastSudoEventsByCommand
|
||||
- Filter: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage"
|
||||
Summarize: "summarize User = make_set(User), UserCount = dcount(User), UsageCount = count() by Command | top 1 by UsageCount asc"
|
||||
Project: "project Title = 'Least by Command', User = case(UserCount == 1, tostring(User[0]), UserCount > 1, 'Many', 'None'), UsageCount = case(UsageCount == 0, 0, UsageCount), Commands = Command, CommandCount = 1"
|
||||
# MostSudoEventsByUser
|
||||
- Filter: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage"
|
||||
Summarize: "summarize CommandCount = dcount(Command), Commands = make_set(Command), UsageCount = count() by User | top 1 by UsageCount desc"
|
||||
Project: "project Title = 'Most by User', User = case(isnotempty(User), User, 'None'), UsageCount = case(UsageCount == 0, 0, UsageCount), Commands = case(CommandCount == 1, tostring(Commands[0]), CommandCount > 1, 'Many', 'None'), CommandCount = case(CommandCount == 0, 0, CommandCount)"
|
||||
# LeastSudoEventsByUser
|
||||
- Filter: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage"
|
||||
Summarize: "summarize CommandCount = dcount(Command), Commands = make_set(Command),UsageCount = count() by User | top 1 by UsageCount asc"
|
||||
Project: "project Title = 'Least by User', User = case(isnotempty(User), User, 'None'), UsageCount = case(UsageCount == 0, 0, UsageCount), Commands = case(CommandCount == 1, tostring(Commands[0]), CommandCount > 1, 'Many', 'None'), CommandCount = case(CommandCount == 0, 0, CommandCount)"
|
||||
# AllSudoEvents
|
||||
- Filter: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage"
|
||||
Summarize: "summarize CommandCount = dcount(Command), Commands = make_set(Command), User = make_set(User), UserCount = dcount(User), UsageCount = count() by Computer"
|
||||
Project: "project Title = 'All', User = case(UserCount == 1, tostring(User[0]), UserCount > 1, 'Many', 'None'), UsageCount = case(UsageCount == 0, 0, UsageCount), Commands = case(CommandCount == 1, tostring(Commands[0]), CommandCount > 1, 'Many', 'None'), CommandCount = case(CommandCount == 0, 0, CommandCount)"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Sudo usage over time"
|
||||
DataSets:
|
||||
- Query: "summarize UsageCount = count(User) by Time = bin(TimeGenerated, 1h), Command | extend Legend = Command"
|
||||
XColumnName: Time
|
||||
YColumnName: UsageCount
|
||||
LegendColumnName: Legend
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See all sudo usage"
|
||||
Query: "project TimeGenerated, Computer, HostIP, User, CmdRunAs, WorkingDirectory, Command, Commandline, SyslogMessage, _ResourceId, timestamp, HostCustomEntity, AccountCustomEntity, IPCustomEntity"
|
|
@ -0,0 +1,199 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
// exclude when over # of machines have the process
|
||||
let excludeThreshold = 10;
|
||||
// exclude when more than percent (default 10%)
|
||||
let ratioHighCount = 0.1;
|
||||
// exclude when less than percent (default 3%)
|
||||
let ratioMidCount = 0.03;
|
||||
// Process count limit in one day, perf improvement (default every minute for 24 hours - 60*24 = 1440)
|
||||
let procLimit = 60*24;
|
||||
let SecEvents = SecurityEvent
|
||||
| where EventID == 4688;
|
||||
let EntropyCalc = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string)
|
||||
{
|
||||
let Exclude = SecEvents
|
||||
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ExcludeCompCount = dcount(Computer), ExcludeProcCount = count() by Process
|
||||
// Removing general limit for noise in one day
|
||||
| extend timediff = iff(datetime_diff('day', EndTime, StartTime) > 0, datetime_diff('day', EndTime, StartTime), 1)
|
||||
// Default exclude of 1440 (1 per min) or more executions in 24 hours on a given machine
|
||||
| where ExcludeProcCount < procLimit*timediff
|
||||
// Removing noisy processes for an environment, adjust as needed
|
||||
| extend compRatio = ExcludeCompCount/toreal(ExcludeProcCount)
|
||||
| where compRatio == 0 or (ExcludeCompCount > excludeThreshold and compRatio < ratioHighCount) or (ExcludeCompCount between (2 .. excludeThreshold) and compRatio < ratioMidCount);
|
||||
let AllSecEvents = SecEvents
|
||||
// Removing general limit for noise in one day
|
||||
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), procCount = count() by Computer, Process
|
||||
| extend timediff = iff(datetime_diff('day', EndTime, StartTime) > 0, datetime_diff('day', EndTime, StartTime), 1)
|
||||
// Default include the 1440 (1 per min) or more executions in 24 hours on a given machine to remove them from overall comparison list
|
||||
| where procCount > procLimit*timediff
|
||||
| join kind= rightanti (
|
||||
SecEvents | project Computer, Process
|
||||
) on Computer, Process
|
||||
| project Computer, Process;
|
||||
// Removing noisy process from full list
|
||||
let Include = materialize(Exclude
|
||||
| join kind= rightanti (
|
||||
AllSecEvents
|
||||
) on Process);
|
||||
// Identifying prevalence for a given process in the environment
|
||||
let DCwPC = materialize(Include
|
||||
| summarize DistinctComputersWithProcessCount = dcount(Computer) by Process
|
||||
| join kind=inner (
|
||||
Include
|
||||
) on Process
|
||||
| distinct Computer, Process, DistinctComputersWithProcessCount);
|
||||
// Getting the Total process count on each host to use as the denominator in the entropy calc
|
||||
let TPCoH = materialize(Include
|
||||
| summarize TotalProcessCountOnHost = count(Process) by Computer
|
||||
| join kind=inner (
|
||||
Include
|
||||
) on Computer
|
||||
| distinct Computer, Process, TotalProcessCountOnHost
|
||||
//Getting a decimal value for later computation
|
||||
| extend TPCoHValue = todecimal(TotalProcessCountOnHost));
|
||||
// Need the count of each class in my bucket or also said as count of ProcName(Class) per Host(Bucket) for use in the entropy calc
|
||||
let PCoH = Include
|
||||
| summarize ProcessCountOnHost = count(Process) by Computer, Process
|
||||
| join kind=inner (
|
||||
Include
|
||||
) on Computer,Process
|
||||
| distinct Computer, Process, ProcessCountOnHost
|
||||
//Getting a decimal value for later computation
|
||||
| extend PCoHValue = todecimal(ProcessCountOnHost);
|
||||
let Combined = DCwPC
|
||||
| join (
|
||||
TPCoH
|
||||
) on Computer, Process
|
||||
| join (
|
||||
PCoH
|
||||
) on Computer, Process;
|
||||
let Results = Combined
|
||||
// Entropy calculation
|
||||
| extend ProcessEntropy = -log2(PCoHValue/TPCoHValue)*(PCoHValue/TPCoHValue)
|
||||
| extend NormalizedProcessEntropy = toreal(ProcessEntropy*10000)
|
||||
// Calculating Weight, see details in description
|
||||
| extend Weight = toreal((ProcessEntropy*10000)*ProcessCountOnHost*DistinctComputersWithProcessCount)
|
||||
// Remove or increase value to see processes with low entropy, meaning more common.
|
||||
| where Weight <= 1000
|
||||
| project Computer, Process, Weight , ProcessEntropy, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount, NormalizedProcessEntropy;
|
||||
// Join back full entry
|
||||
Results
|
||||
| join kind= inner (
|
||||
SecEvents
|
||||
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, AccountType, Process, NewProcessName, CommandLine, ParentProcessName, _ResourceId, SourceComputerId
|
||||
) on Computer, Process
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
// removing common items that may still show up in small environments, add here is you have additional exclusions
|
||||
| where NewProcessName !endswith ':\\Windows\\System32\\conhost.exe' and ParentProcessName !endswith ':\\Windows\\System32\\conhost.exe'
|
||||
| where ParentProcessName !endswith ':\\Windows\\System32\\wuauclt.exe' and NewProcessName !endswith ':\\Windows\\System32\\wuauclt.exe' and NewProcessName !startswith 'C:\\Windows\\SoftwareDistribution\\Download\\Install\\AM_Delta_Patch_'
|
||||
| where ParentProcessName !has ':\\WindowsAzure\\GuestAgent_' and NewProcessName !has ':\\WindowsAzure\\GuestAgent_'
|
||||
| where ParentProcessName !has ':\\WindowsAzure\\WindowsAzureNetAgent_' and NewProcessName !has ':\\WindowsAzure\\WindowsAzureNetAgent_'
|
||||
| where ParentProcessName !has ':\\ProgramData\\Microsoft\\Windows Defender\\platform\\'
|
||||
| where NewProcessName !has ':\\ProgramData\\Microsoft\\Windows Defender\\platform\\'
|
||||
| where NewProcessName !has ':\\Windows\\Microsoft.NET\\Framework' and not(NewProcessName endswith '\\ngentask.exe' or NewProcessName endswith '\\ngen.exe') and not(ParentProcessName endswith ':\\Windows\\System32\\taskhostw.exe')
|
||||
| where NewProcessName !endswith '\\MpSigStub.exe' and ParentProcessName !has ':\\Windows\\SoftwareDistribution\\Download\\Install\\'
|
||||
| where ParentProcessName !has ':\\Program Files\\Microsoft Monitoring Agent\\Agent\\MonitoringHost.exe'
|
||||
| where NewProcessName !endswith ':\\Windows\\servicing\\trustedinstaller.exe'
|
||||
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, AccountType, Weight, NormalizedProcessEntropy, FullDecimalProcessEntropy = ProcessEntropy,
|
||||
Process, NewProcessName, CommandLine, ParentProcessName, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount, _ResourceId, SourceComputerId
|
||||
| sort by Weight asc, NormalizedProcessEntropy asc, NewProcessName asc
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = Account
|
||||
};
|
||||
EntropyCalc('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
Insights:
|
||||
Id: 2b59ee1f-5098-4061-a868-e49c39ed2245
|
||||
DisplayName: Process rarity via entropy calculation
|
||||
Description: |
|
||||
Entropy calculation used to help identify Hosts where they have a high variety of processes(a high entropy process list on a given Host over time).
|
||||
This helps us identify rare processes on a given Host. Rare here means a process shows up on the Host relatively few times in the the last 7days.
|
||||
The Weight is calculated based on the Entropy, Process Count and Distinct Hosts with that Process. The lower the Weight/ProcessEntropy the, more interesting.
|
||||
The Weight calculation increases the Weight if the process executes more than once on the Host or has executed on more than 1 Hosts.
|
||||
In general, this should identify processes on a Host that are rare and rare for the environment. A Weight lower than 100 is considered very rare.
|
||||
References: https://medium.com/udacity/shannon-entropy-information-gain-and-picking-balls-from-buckets-5810d35d54b4
|
||||
https://en.wiktionary.org/wiki/Shannon_entropy
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 7d
|
||||
AfterRange: 7d
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Label
|
||||
OutputType: String
|
||||
- Header: "Rare Process(es)"
|
||||
OutputType: String
|
||||
- Header: "Entropy Weight"
|
||||
OutputType: Number
|
||||
- Header: "Process Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
QueriesDefinitions:
|
||||
|
||||
# TrulyRareProcess
|
||||
- Filter: "extend Weight = round(Weight, 3) | where Weight <= 75"
|
||||
Summarize: "summarize Weight = make_list(Weight), avgWeight = avg(round(Weight, 3)), Process = make_list(Process), ProcessCountOnHost = sum(ProcessCountOnHost) by Computer "
|
||||
Project: "project Title = 'Rare Process(es)', Process = case(array_length(Process) > 1, 'Many', array_length(Process) == 1, tostring(Process[0]), 'None'), EntropyWeight = case(array_length(Weight) > 1, strcat(tostring(round(avgWeight, 3)), ' avg'), array_length(Weight) == 1, tostring(Weight[0]), 'None'), ProcessCountOnHost = iff(isempty(Process), 0, ProcessCountOnHost)"
|
||||
|
||||
# LeastPrevalentProcesses
|
||||
- Filter: "extend Weight = round(Weight, 3)"
|
||||
Summarize: "sort by Weight asc, ProcessCountOnHost asc, TimeGenerated desc | take 3 | extend row = row_number()"
|
||||
Project: "project Title = case(row == 1, strcat(row,'st Least prevalent'), row == 2, strcat(row,'nd Least prevalent'), row == 3, strcat(row,'rd Least prevalent'), 'None'), Process = case(row == 1, Process, row == 2, Process, row == 3, Process, 'None'), EntropyWeight = tostring(case(row == 1, Weight, row == 2, Weight, row == 3, Weight, 0.000)), ProcessCountOnHost = case(row == 1, ProcessCountOnHost, row == 2, ProcessCountOnHost, row == 3, ProcessCountOnHost, 0) | sort by Title"
|
||||
|
||||
# ServiceParentProcesses
|
||||
- Filter: "where ParentProcessName has_any (':\\\\windows\\\\system32\\\\svchost.exe',':\\\\windows\\\\system32\\\\services.exe')"
|
||||
Summarize: "summarize Process = make_set(Process), Weight = make_set(round(Weight, 3)), avgWeight = avg(round(Weight, 3)), ProcessCountOnHost = count(Process) by Computer"
|
||||
Project: "project Title = 'Services as Parent', Process = case(array_length(Process) > 1, 'Many', array_length(Process) == 1, tostring(Process[0]), 'None'), EntropyWeight = case(array_length(Weight) > 1, strcat(tostring(round(avgWeight, 3)), ' avg'), array_length(Weight) == 1, tostring(Weight[0]), 'None'), ProcessCountOnHost = iff(isempty(Process), 0, ProcessCountOnHost)"
|
||||
|
||||
# MachineExecutions
|
||||
- Filter: "where AccountType =~ 'machine'"
|
||||
Summarize: "summarize Weight = make_set(round(Weight, 3)), avgWeight = avg(round(Weight, 3)), Process = make_set(Process), ProcessCountOnHost = count(Process) by Computer"
|
||||
Project: "project Title = 'Execution by Machine Accounts', Process = case(array_length(Process) > 1, 'Many', array_length(Process) == 1, tostring(Process[0]), 'None'), EntropyWeight = case(array_length(Weight) > 1, strcat(tostring(round(avgWeight, 3)), ' avg'), array_length(Weight) == 1, tostring(Weight[0]), 'None'), ProcessCountOnHost = iff(isempty(Process), 0, ProcessCountOnHost)"
|
||||
|
||||
# UserExecutions
|
||||
- Filter: "where AccountType =~ 'user'"
|
||||
Summarize: "summarize Weight = make_set(round(Weight, 3)), avgWeight = avg(round(Weight, 3)), Process = make_set(Process), ProcessCountOnHost = count(Process) by Computer"
|
||||
Project: "project Title = 'Execution by User Accounts', Process = case(array_length(Process) > 1, 'Many', array_length(Process) == 1, tostring(Process[0]), 'None'), EntropyWeight = case(array_length(Weight) > 1, strcat(tostring(round(avgWeight, 3)), ' avg'), array_length(Weight) == 1, tostring(Weight[0]), 'None'), ProcessCountOnHost = iff(isempty(Process), 0, ProcessCountOnHost)"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Rare processes over time (Weight <= 75)"
|
||||
DataSets:
|
||||
- Query: "where Weight <= 75 | summarize RareProcessCount = sum(ProcessCountOnHost) by Time = bin(TimeGenerated, 6h), Process"
|
||||
XColumnName: Time
|
||||
YColumnName: RareProcessCount
|
||||
LegendColumnName: Process
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See entropy weight levels 1000 and under"
|
||||
Query: "where Weight <= 1000"
|
|
@ -0,0 +1,124 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Provider: Sentinel
|
||||
Type: KQL
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
BaseQuery: |
|
||||
let AScoreThresh=3;
|
||||
let maxAnomalies=3;
|
||||
let BeforeRange = 14d;
|
||||
let EndTime = todatetime('{{End_Time_UTC}}');
|
||||
let StartTime = todatetime('{{Start_Time_UTC}}');
|
||||
let numDays = tolong((EndTime-StartTime)/1d);
|
||||
let computerData = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string) {
|
||||
SecurityEvent
|
||||
| extend SourceComputerId = column_ifexists("SourceComputerId", "NotAvailable"), _ResourceId = column_ifexists("_ResourceId", "NotAvailable")
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId };
|
||||
computerData('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
Insights:
|
||||
Id: 4191a4d7-e72b-4564-b2fb-25580630384b
|
||||
DisplayName: Anomalously high number of a security event
|
||||
Description: Highlight security events of the host with anomalously high count compared to those observed in the preceding 14 days.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 1d
|
||||
AfterRange: 0d
|
||||
ReferenceTimeRange:
|
||||
BeforeRange: 14d
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: Activity
|
||||
OutputType: String
|
||||
SupportDeepLink: true
|
||||
- Header: Expected Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
- Header: Actual Count
|
||||
OutputType: Number
|
||||
SupportDeepLink: false
|
||||
QueriesDefinitions:
|
||||
- Filter: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Activity
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
|
||||
Summarize: take 1
|
||||
Project: project Activity, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: Activity
|
||||
Query: |
|
||||
{{BaseQuery}}
|
||||
| where TimeGenerated between (StartTime .. EndTime)
|
||||
| where Activity == '{{RowValue_Activity}}'
|
||||
ChartQuery:
|
||||
Title: Anomalous activity timeline
|
||||
DataSets:
|
||||
- Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Activity
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,round(postExpectedCount,2))
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
| take 1
|
||||
| project Activity, TimeGenerated, count_
|
||||
| mvexpand TimeGenerated, count_
|
||||
| project todatetime(TimeGenerated), toint(count_), Activity
|
||||
|
||||
XColumnName: TimeGenerated
|
||||
YColumnName: count_
|
||||
LegendColumnName: Activity
|
||||
Type: LineChart
|
||||
AdditionalQuery:
|
||||
Text: Query all anomalous activities
|
||||
Query: |
|
||||
make-series count() default=0 on TimeGenerated from (StartTime - BeforeRange) to EndTime step 1d by Activity
|
||||
| extend (anomalies,anomalyScore, expectedCount)=series_decompose_anomalies(count_,AScoreThresh,7,'linefit',numDays, 'ctukey')
|
||||
| extend count1=count_, TimeGenerated1=TimeGenerated, anomalyScore1=anomalyScore
|
||||
| mv-apply count1 to typeof(long), TimeGenerated1 to typeof(datetime), anomalyScore1 to typeof(double), anomalies to typeof(long) on (summarize totAnomalies=sumif(abs(anomalies), TimeGenerated1 < StartTime), baseStd=stdevif(count1, TimeGenerated1 < StartTime), baseAvg=avgif(count1, TimeGenerated1 < StartTime), maxCountPost=maxif(count1,TimeGenerated1 >= StartTime), maxAnomalyScorePost = maxif(anomalyScore1, TimeGenerated1 >= StartTime))
|
||||
| extend count1=count_
|
||||
| mv-apply count1 to typeof(long), anomalyScore to typeof(double), expectedCount to typeof(double) on ( summarize (dummy, postExpectedCount, postActualCount)=arg_min(abs(anomalyScore - maxAnomalyScorePost), expectedCount, count1) )
|
||||
| where totAnomalies < maxAnomalies
|
||||
| extend postAnomalyScore=iff(baseStd == 0 and maxCountPost > tolong(count_[0]),1000.0,maxAnomalyScorePost), postExpectedCount=iff(postExpectedCount < 0,0.0,postExpectedCount)
|
||||
| where maxAnomalyScorePost > AScoreThresh
|
||||
| order by maxAnomalyScorePost desc
|
||||
| project Activity, expectedCount=round(postExpectedCount,2), actualCount=postActualCount, anomalyScore=round(postAnomalyScore,2)
|
|
@ -0,0 +1,121 @@
|
|||
SchemaVersion: '1.0'
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
|
||||
let excludeProc = dynamic([':\\Windows\\System32\\svchost.exe', ':\\Windows\\System32\\sppsvc.exe', ':\\Windows\\system32\\wbem\\WmiApSrv.exe', ':\\Windows\\System32\\conhost.exe', ':\\Windows\\System32\\wuauclt.exe', ':\\Windows\\SoftwareDistribution\\Download\\Install\\', ':\\WindowsAzure\\GuestAgent_', ':\\WindowsAzure\\WindowsAzureNetAgent_',
|
||||
':\\ProgramData\\Microsoft\\Windows Defender\\platform\\', ':\\Windows\\System32\\taskhostw.exe', '\\MpSigStub.exe',':\\Program Files\\Microsoft Monitoring Agent\\Agent\\MonitoringHost.exe', ':\\Windows\\servicing\\trustedinstaller.exe', ':\\Windows\\System32\\WerFault.exe', ':\\Windows\\CCM\\CcmExec.exe']);
|
||||
let starttime = todatetime('{{Start_Time_ISO}}');
|
||||
let endtime = todatetime('{{End_Time_ISO}}');
|
||||
let includeScope = 1d;
|
||||
let historicalScope = 14d;
|
||||
let Procs=(v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string){
|
||||
let processEvents=SecurityEvent
|
||||
| where TimeGenerated >= (starttime - historicalScope)
|
||||
| where EventID==4688
|
||||
// removing common items that may still show up in small environments, add here if you have additional exclusions
|
||||
| where not(NewProcessName has_any (excludeProc)) and not(ParentProcessName has_any (excludeProc))
|
||||
// parsing for Host to handle variety of conventions coming from data
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| project TimeGenerated, Computer, SubjectAccount, NewProcessName, Process, CommandLine, ParentProcessName, ParentProcess = tostring(split(ParentProcessName, '\\')[-1]), _ResourceId, SourceComputerId;
|
||||
let processes = materialize(processEvents
|
||||
| where TimeGenerated >= endtime - includeScope
|
||||
| join kind=leftanti (
|
||||
processEvents
|
||||
| where TimeGenerated between ((starttime - historicalScope) .. (endtime - includeScope))
|
||||
| distinct NewProcessName
|
||||
) on NewProcessName
|
||||
| extend ExecType = 'Processes');
|
||||
let parents = materialize(processEvents
|
||||
| where TimeGenerated >= endtime - includeScope
|
||||
| join kind=leftanti (
|
||||
processEvents
|
||||
| where TimeGenerated between ((starttime - historicalScope) .. (endtime - includeScope))
|
||||
| distinct ParentProcessName
|
||||
) on ParentProcessName
|
||||
| extend ExecType = 'Parents');
|
||||
union isfuzzy=true processes, parents
|
||||
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = SubjectAccount
|
||||
};
|
||||
Procs('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
Insights:
|
||||
Id: 37a420dc-8d03-4a1f-b579-d96d5c5f5fe4
|
||||
DisplayName: Windows process execution info
|
||||
Description: |
|
||||
'Identifies new processes and new parent processes, along with low or high execution counts.'
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
SingleValuesQuery: {}
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: ProcessType
|
||||
OutputType: String
|
||||
- Header: ProcessName
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: Executions
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: User(s)
|
||||
OutputType: String
|
||||
QueriesDefinitions:
|
||||
# NewProcessExec
|
||||
- Filter: "where ExecType == 'Processes'"
|
||||
Summarize: "summarize UserCount = dcount(SubjectAccount), Users = make_set(SubjectAccount), Processes = make_set(Process), ProcCount = count()"
|
||||
Project: "project Title = 'New Process(es)', Processes = case(array_length(Processes) == 1, tostring(Processes[0]), array_length(Processes) > 1, 'Many', 'None'), ProcCount, Users = case(array_length(Users) == 1, tostring(Users[0]), array_length(Users) > 1, 'Many', 'None')"
|
||||
# NewParentProcessExec
|
||||
- Filter: "where ExecType == 'Parents'"
|
||||
Summarize: "summarize UserCount = dcount(SubjectAccount), Users = make_set(SubjectAccount), Processes = make_set(ParentProcess), ProcCount = count()"
|
||||
Project: "project Title = 'New Parent(s)', Processes = case(array_length(Processes) == 1, tostring(Processes[0]), array_length(Processes) > 1, 'Many', 'None'), ProcCount, Users = case(array_length(Users) == 1, tostring(Users[0]), array_length(Users) > 1, 'Many', 'None')"
|
||||
# LeastNewProcessExecs
|
||||
- Filter: "where ExecType == 'Processes' | project TimeGenerated, Computer, SubjectAccount, Process"
|
||||
Summarize: "summarize UserCount = dcount(SubjectAccount), Users = make_set(SubjectAccount), ProcCount = count() by Process | order by ProcCount, UserCount asc | top 1 by ProcCount"
|
||||
Project: "project Title = 'Least New Process Executions', Processes = Process, ProcCount, Users = case(array_length(Users) == 1, tostring(Users[0]), array_length(Users) > 1, 'Many', 'None')"
|
||||
# MostNewProcessExecs
|
||||
- Filter: "where ExecType == 'Processes' | project TimeGenerated, Computer, SubjectAccount, Process"
|
||||
Summarize: "summarize UserCount = dcount(SubjectAccount), Users = make_set(SubjectAccount), ProcCount = count() by Process | order by ProcCount, UserCount desc | top 1 by ProcCount"
|
||||
Project: "project Title = 'Most New Process Executions', Processes = Process, ProcCount, Users = case(array_length(Users) == 1, tostring(Users[0]), array_length(Users) > 1, 'Many', 'None')"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Process executions over time"
|
||||
DataSets:
|
||||
- Query: "summarize ProcessCount = count() by Time = bin(TimeGenerated, 1h), Process | extend Legend = Process"
|
||||
XColumnName: Time
|
||||
YColumnName: ProcessCount
|
||||
LegendColumnName: Legend
|
||||
Type: BarChart
|
||||
AdditionalQuery:
|
||||
Text: "See all new process information"
|
||||
Query: "project TimeGenerated, Computer, SubjectAccount, NewProcessName, Process, CommandLine, ParentProcessName, ParentProcess, ExecType, _ResourceId, SourceComputerId, timestamp, HostCustomEntity, AccountCustomEntity"
|
|
@ -0,0 +1,271 @@
|
|||
SchemaVersion: 1.0
|
||||
DataTypes:
|
||||
- DataType: SecurityEvent
|
||||
Type: KQL
|
||||
Provider: Sentinel
|
||||
EntitiesFilter:
|
||||
Host_OsFamily:
|
||||
- Windows
|
||||
RequiredInputFieldsSets:
|
||||
- - Host_HostName
|
||||
- Host_NTDomain
|
||||
- - Host_HostName
|
||||
- Host_DnsDomain
|
||||
- - Host_AzureID
|
||||
- - Host_OMSAgentID
|
||||
BaseQuery: |
|
||||
let starttime = todatetime('{{Start_Time_ISO}}');
|
||||
let endtime = todatetime('{{End_Time_ISO}}');
|
||||
let includeScope = 2d;
|
||||
let historicalScope = 8d;
|
||||
let GetAllLogonsForHost = (v_Host_Name:string, v_Host_NTDomain:string, v_Host_DnsDomain:string, v_Host_AzureID:string, v_Host_OMSAgentID:string){
|
||||
let DomainAllowList = dynamic(["NT AUTHORITY", "NT SERVICE", "Font Driver Host", "Window Manager"]);
|
||||
let AccountAllowList = dynamic(["SYSTEM","NETWORK SERVICE", "LOCAL SERVICE"]);
|
||||
let AllEvents = SecurityEvent
|
||||
| where TimeGenerated >= (starttime - historicalScope)
|
||||
| where EventID in (4624, 4625, 4672)
|
||||
// parsing for Host to handle variety of conventions coming from data
|
||||
| extend Host_HostName = case(
|
||||
Computer has '@', tostring(split(Computer, '@')[0]),
|
||||
Computer has '\\', tostring(split(Computer, '\\')[1]),
|
||||
Computer has '.', tostring(split(Computer, '.')[0]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_NTDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', tostring(split(Computer, '.')[-2]),
|
||||
Computer
|
||||
)
|
||||
| extend Host_DnsDomain = case(
|
||||
Computer has '\\', tostring(split(Computer, '\\')[0]),
|
||||
Computer has '.', strcat_array(array_slice(split(Computer,'.'),-2,-1),'.'),
|
||||
Computer
|
||||
)
|
||||
| where (Host_HostName =~ v_Host_Name and Host_NTDomain =~ v_Host_NTDomain)
|
||||
or (Host_HostName =~ v_Host_Name and Host_DnsDomain =~ v_Host_DnsDomain)
|
||||
or v_Host_AzureID =~ _ResourceId
|
||||
or v_Host_OMSAgentID == SourceComputerId
|
||||
| extend RelatedRowSet = 'AllEvents'
|
||||
| extend HourOfLogin = hourofday(TimeGenerated), DayNumberofWeek = dayofweek(TimeGenerated)
|
||||
| extend DayofWeek = case(
|
||||
DayNumberofWeek == "00:00:00", "Sunday",
|
||||
DayNumberofWeek == "1.00:00:00", "Monday",
|
||||
DayNumberofWeek == "2.00:00:00", "Tuesday",
|
||||
DayNumberofWeek == "3.00:00:00", "Wednesday",
|
||||
DayNumberofWeek == "4.00:00:00", "Thursday",
|
||||
DayNumberofWeek == "5.00:00:00", "Friday",
|
||||
DayNumberofWeek == "6.00:00:00", "Saturday","InvalidTimeStamp")
|
||||
// map the most common ntstatus codes
|
||||
| extend StatusDesc = case(
|
||||
Status =~ "0x80090302", "SEC_E_UNSUPPORTED_FUNCTION",
|
||||
Status =~ "0x80090308", "SEC_E_INVALID_TOKEN",
|
||||
Status =~ "0x8009030E", "SEC_E_NO_CREDENTIALS",
|
||||
Status =~ "0xC0000008", "STATUS_INVALID_HANDLE",
|
||||
Status =~ "0xC0000017", "STATUS_NO_MEMORY",
|
||||
Status =~ "0xC0000022", "STATUS_ACCESS_DENIED",
|
||||
Status =~ "0xC0000034", "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
Status =~ "0xC000005E", "STATUS_NO_LOGON_SERVERS",
|
||||
Status =~ "0xC000006A", "STATUS_WRONG_PASSWORD",
|
||||
Status =~ "0xC000006D", "STATUS_LOGON_FAILURE",
|
||||
Status =~ "0xC000006E", "STATUS_ACCOUNT_RESTRICTION",
|
||||
Status =~ "0xC0000073", "STATUS_NONE_MAPPED",
|
||||
Status =~ "0xC00000FE", "STATUS_NO_SUCH_PACKAGE",
|
||||
Status =~ "0xC000009A", "STATUS_INSUFFICIENT_RESOURCES",
|
||||
Status =~ "0xC00000DC", "STATUS_INVALID_SERVER_STATE",
|
||||
Status =~ "0xC0000106", "STATUS_NAME_TOO_LONG",
|
||||
Status =~ "0xC000010B", "STATUS_INVALID_LOGON_TYPE",
|
||||
Status =~ "0xC000015B", "STATUS_LOGON_TYPE_NOT_GRANTED",
|
||||
Status =~ "0xC000018B", "STATUS_NO_TRUST_SAM_ACCOUNT",
|
||||
Status =~ "0xC0000224", "STATUS_PASSWORD_MUST_CHANGE",
|
||||
Status =~ "0xC0000234", "STATUS_ACCOUNT_LOCKED_OUT",
|
||||
Status =~ "0xC00002EE", "STATUS_UNFINISHED_CONTEXT_DELETED",
|
||||
EventID == 4624 or EventID == 4672, "Success",
|
||||
"See - https://docs.microsoft.com/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
|
||||
)
|
||||
| extend SubStatusDesc = case(
|
||||
SubStatus =~ "0x80090325", "SEC_E_UNTRUSTED_ROOT",
|
||||
SubStatus =~ "0xC0000008", "STATUS_INVALID_HANDLE",
|
||||
SubStatus =~ "0xC0000022", "STATUS_ACCESS_DENIED",
|
||||
SubStatus =~ "0xC0000064", "STATUS_NO_SUCH_USER",
|
||||
SubStatus =~ "0xC000006A", "STATUS_WRONG_PASSWORD",
|
||||
SubStatus =~ "0xC000006D", "STATUS_LOGON_FAILURE",
|
||||
SubStatus =~ "0xC000006E", "STATUS_ACCOUNT_RESTRICTION",
|
||||
SubStatus =~ "0xC000006F", "STATUS_INVALID_LOGON_HOURS",
|
||||
SubStatus =~ "0xC0000070", "STATUS_INVALID_WORKSTATION",
|
||||
SubStatus =~ "0xC0000071", "STATUS_PASSWORD_EXPIRED",
|
||||
SubStatus =~ "0xC0000072", "STATUS_ACCOUNT_DISABLED",
|
||||
SubStatus =~ "0xC0000073", "STATUS_NONE_MAPPED",
|
||||
SubStatus =~ "0xC00000DC", "STATUS_INVALID_SERVER_STATE",
|
||||
SubStatus =~ "0xC0000133", "STATUS_TIME_DIFFERENCE_AT_DC",
|
||||
SubStatus =~ "0xC000018D", "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
|
||||
SubStatus =~ "0xC0000193", "STATUS_ACCOUNT_EXPIRED",
|
||||
SubStatus =~ "0xC0000380", "STATUS_SMARTCARD_WRONG_PIN",
|
||||
SubStatus =~ "0xC0000381", "STATUS_SMARTCARD_CARD_BLOCKED",
|
||||
SubStatus =~ "0xC0000382", "STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED",
|
||||
SubStatus =~ "0xC0000383", "STATUS_SMARTCARD_NO_CARD",
|
||||
SubStatus =~ "0xC0000384", "STATUS_SMARTCARD_NO_KEY_CONTAINER",
|
||||
SubStatus =~ "0xC0000385", "STATUS_SMARTCARD_NO_CERTIFICATE",
|
||||
SubStatus =~ "0xC0000386", "STATUS_SMARTCARD_NO_KEYSET",
|
||||
SubStatus =~ "0xC0000387", "STATUS_SMARTCARD_IO_ERROR",
|
||||
SubStatus =~ "0xC0000388", "STATUS_DOWNGRADE_DETECTED",
|
||||
SubStatus =~ "0xC0000389", "STATUS_SMARTCARD_CERT_REVOKED",
|
||||
EventID == 4624 or EventID == 4672, "Success",
|
||||
"See - https://docs.microsoft.com/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
|
||||
)
|
||||
| project TimeGenerated, DayofWeek, HourOfLogin, EventID, Activity, IpAddress, WorkstationName, Computer, TargetAccount, TargetUserName, TargetDomainName, ProcessName, SubjectUserName, PrivilegeList, LogonTypeName, StatusDesc, SubStatusDesc, RelatedRowSet, _ResourceId, SourceComputerId
|
||||
;
|
||||
let HostSigninToSystems = materialize(AllEvents
|
||||
| where EventID == 4624
|
||||
| project-away StatusDesc, SubStatusDesc, PrivilegeList
|
||||
| summarize SigninCount= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName) by EventID, Activity, Computer, TargetAccount, TargetDomainName, TargetUserName , ProcessName , LogonTypeName, _ResourceId, SourceComputerId
|
||||
| extend RelatedRowSet = 'HostSigninToSystems', DomainAllowList = dynamic(["NT AUTHORITY", "NT SERVICE", "Font Driver Host", "Window Manager"]));
|
||||
let HostFailedSigninToSystems = materialize(AllEvents
|
||||
| where EventID == 4625
|
||||
| project-away PrivilegeList
|
||||
| summarize SigninCount= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName) by EventID, Activity, Computer, TargetAccount, TargetDomainName, TargetUserName , ProcessName , LogonTypeName, _ResourceId, SourceComputerId
|
||||
| extend RelatedRowSet = 'HostFailedSigninToSystems');
|
||||
let HostSigninDuringAbnormalHours = materialize(AllEvents
|
||||
| where TimeGenerated between ((starttime - historicalScope) .. (endtime - includeScope))
|
||||
| where EventID in (4624,4625)
|
||||
| where LogonTypeName in~ ('2 - Interactive','10 - RemoteInteractive')
|
||||
| summarize max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek) by Computer, _ResourceId, SourceComputerId
|
||||
| join kind= inner
|
||||
(
|
||||
AllEvents
|
||||
| where TimeGenerated > endtime - includeScope
|
||||
| where LogonTypeName in~ ('2 - Interactive','10 - RemoteInteractive')
|
||||
)
|
||||
on Computer
|
||||
| where HourOfLogin > max_HourOfLogin or HourOfLogin < min_HourOfLogin
|
||||
| extend historical_DayofWeek = tostring(historical_DayofWeek)
|
||||
| summarize SigninCount= count(), max(HourOfLogin), min(HourOfLogin), current_DayofWeek =make_set(DayofWeek), StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName) by EventID, Activity, Computer, TargetAccount, TargetDomainName, TargetUserName , ProcessName , LogonTypeName, StatusDesc, SubStatusDesc, historical_DayofWeek, _ResourceId, SourceComputerId
|
||||
| extend historical_DayofWeek = todynamic(historical_DayofWeek)
|
||||
| extend RelatedRowSet = 'HostSigninDuringAbnormalHour', DomainAllowList = dynamic(["NT AUTHORITY", "NT SERVICE", "Font Driver Host", "Window Manager"]));
|
||||
let HostHadPrivilegedLogonSessions = materialize(AllEvents
|
||||
| where EventID == 4672
|
||||
| where PrivilegeList contains 'SeDebugPrivilege'
|
||||
| project-away StatusDesc, SubStatusDesc
|
||||
| summarize SigninCount= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName) by EventID, Activity, Computer, TargetAccount, TargetDomainName, TargetUserName, PrivilegeList, _ResourceId, SourceComputerId
|
||||
| extend RelatedRowSet = 'HostHadPrivilegedLogonSessions', DomainAllowList = dynamic(["NT AUTHORITY", "NT SERVICE", "Font Driver Host", "Window Manager"]));
|
||||
union isfuzzy=true AllEvents, HostSigninToSystems, HostFailedSigninToSystems, HostSigninDuringAbnormalHours, HostHadPrivilegedLogonSessions
|
||||
| extend timestamp = StartTime, HostCustomEntity = Computer, AccountCustomEntity = TargetAccount
|
||||
};
|
||||
// change {{Host_HostName}} value below to the HostName you are interested in
|
||||
GetAllLogonsForHost('{{Host_HostName}}', '{{Host_NTDomain}}', '{{Host_DnsDomain}}', '{{Host_AzureID}}', '{{Host_OMSAgentID}}')
|
||||
# The queries for the insights.
|
||||
Insights:
|
||||
Id: 4ecc2229-5cbf-4b04-a2ab-0842c5e4d1cd
|
||||
DisplayName: Windows sign-in activity
|
||||
Description: |
|
||||
Summary of successful and failed sign-ins along with anamalous sign-in patterns for the specific host. Successful sign-ins currently only include interactive and limited to LogonType 2 and 10.
|
||||
DefaultTimeRange:
|
||||
BeforeRange: 12h
|
||||
AfterRange: 12h
|
||||
TableQuery:
|
||||
ColumnsDefinitions:
|
||||
- Header: "Signin Type"
|
||||
OutputType: String
|
||||
- Header: "Signin Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "User Count"
|
||||
OutputType: Number
|
||||
SupportDeepLink: true
|
||||
- Header: "User(s)"
|
||||
OutputType: String
|
||||
QueriesDefinitions:
|
||||
|
||||
# HostSigninToSystems
|
||||
- Filter: "where RelatedRowSet =~ 'HostSigninToSystems' and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager')"
|
||||
Summarize: "summarize SigninCount = sum(SigninCount), UserCount = dcount(TargetUserName), Users = make_set(TargetUserName)"
|
||||
Project: "project Title = 'Successful', SigninCount, UserCount, Users = case(array_length(Users) > 1, 'Many', array_length(Users) == 1, tostring(Users[0]), 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# HostFailedSigninToSystems
|
||||
- Filter: "where RelatedRowSet =~ 'HostFailedSigninToSystems'"
|
||||
Summarize: "summarize SigninCount= sum(SigninCount), UserCount = dcount(TargetUserName), Users = make_set(TargetUserName)"
|
||||
Project: "project Title = 'Failed', SigninCount, UserCount, Users = case(array_length(Users) > 1, 'Many', array_length(Users) == 1, tostring(Users[0]), 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# HostSigninDuringAbnormalHours
|
||||
- Filter: "where RelatedRowSet =~ 'HostSigninDuringAbnormalHour' and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager')"
|
||||
Summarize: "summarize SigninCount = sum(SigninCount), UserCount = dcount(TargetUserName), Users = make_set(TargetUserName)"
|
||||
Project: "project Title = 'Abnormal Time', SigninCount, UserCount, Users = case(array_length(Users) > 1, 'Many', array_length(Users) == 1, tostring(Users[0]), 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# HostHadPrivilegedLogonSessions
|
||||
- Filter: "where RelatedRowSet =~ 'HostHadPrivilegedLogonSessions' "
|
||||
Summarize: "summarize SigninCount = sum(SigninCount), UserCount = dcount(TargetUserName), Users = make_set(TargetUserName)"
|
||||
Project: "project Title = 'Privileged', SigninCount, UserCount, Users = case(array_length(Users) > 1, 'Many', array_length(Users) == 1, tostring(Users[0]), 'None')"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# MostFrequent
|
||||
- Filter: "where RelatedRowSet =~ 'AllEvents' | where EventID == 4624 and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager')"
|
||||
Summarize: "summarize StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SigninCount = count() by TargetDomainName, TargetUserName, EventID | top 1 by SigninCount desc"
|
||||
Project: "project Title = 'Most Frequent', SigninCount, UserCount = 1, Users = TargetUserName"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
# LeastFrequent
|
||||
- Filter: "where RelatedRowSet =~ 'AllEvents' | where EventID == 4624 and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager')"
|
||||
Summarize: "summarize StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SigninCount = count() by TargetDomainName, TargetUserName, EventID | top 1 by SigninCount asc"
|
||||
Project: "project Title = 'Least Frequent', SigninCount, UserCount = 1, Users = TargetUserName"
|
||||
LinkColumnsDefinitions:
|
||||
- ProjectedName: SigninCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
- ProjectedName: UserCount
|
||||
Query: "{{BaseQuery}} | {{RowFilter}}"
|
||||
|
||||
ChartQuery:
|
||||
Title: "Sign-ins over time"
|
||||
DataSets:
|
||||
- Query: "where RelatedRowSet =~ 'AllEvents' and EventID == 4624 and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager') | summarize Count=count() by Time = bin(TimeGenerated, 1h) | extend Legend = 'Success'"
|
||||
XColumnName: "Time"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
- Query: "where RelatedRowSet =~ 'AllEvents' and EventID == 4625 | summarize Count=count() by Time = bin(TimeGenerated, 1h) | extend Legend = 'Failed'"
|
||||
XColumnName: "Time"
|
||||
YColumnName: "Count"
|
||||
LegendColumnName: "Legend"
|
||||
Type: LineChart
|
||||
|
||||
AdditionalQuery:
|
||||
Text: "See all windows sign-ins"
|
||||
Query: "where RelatedRowSet =~ 'AllEvents' | extend SubjectUserName = columnifexists('SubjectUserName', 'EventDoesNotContain') | summarize SigninCount= count(), max(HourOfLogin), min(HourOfLogin), historical_DayofWeek=make_set(DayofWeek), StartTime=min(TimeGenerated), EndTime = max(TimeGenerated), SourceIP = make_set(IpAddress), SourceHost = make_set(WorkstationName), SubjectUserName = make_set(SubjectUserName), UserCount = dcount(TargetUserName), Users = make_set(TargetUserName) by Activity, TargetDomainName, TargetUserName, ProcessName, LogonTypeName, timestamp, HostCustomEntity, AccountCustomEntity"
|
||||
|
||||
Activities:
|
||||
EnabledByDefault: true
|
||||
Items:
|
||||
- Id: cfba48ac-49dd-4d8a-8a65-70eaf4aafb61
|
||||
Description: Privileged logon by account
|
||||
Title: "Privileged logon by account on this host"
|
||||
Content: "On '{{Computer}}' the user '{{TargetUserName}}' logged on at least once with the SeDebugPrivilege"
|
||||
QueryDefinitions:
|
||||
Filter: where RelatedRowSet =~ 'HostHadPrivilegedLogonSessions'
|
||||
SummarizeBy: TargetUserName
|
||||
- Id: 8d639acf-f55f-40cd-8ada-bf81b277bb73
|
||||
Description: Logon outside normal hours
|
||||
Title: "An account has logged on outside of their normal hours on this host"
|
||||
Content: "On '{{Computer}}' the user '{{TargetUserName}}' logged on at least once outside of their normal logon hours"
|
||||
QueryDefinitions:
|
||||
Filter: where RelatedRowSet =~ 'HostSigninDuringAbnormalHour' and TargetDomainName !in ('NT AUTHORITY', 'NT SERVICE', 'Font Driver Host', 'Window Manager')
|
||||
SummarizeBy: TargetUserName
|
|
@ -0,0 +1 @@
|
|||
Insights feature preview
|
Загрузка…
Ссылка в новой задаче