Updating entities and putting in YAML format

This commit is contained in:
Shain Wray (MSTIC) 2019-09-03 15:10:13 -07:00
Родитель 2f8bbb1e7f
Коммит 0b92a4bf6a
46 изменённых файлов: 932 добавлений и 768 удалений

Просмотреть файл

@ -1,80 +0,0 @@
// Name: VIP account more than 6 failed logons in 10
// Description: VIP Account with more than 6 failed logon attempts in 10 minutes, include your own VIP list in the table below
// NTSTATUS codes - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
//
// Id: 892cd37e-f9e1-49c3-b0b2-d74f52ac7b71
//
// DataSource: #SecurityEvent
//
// Tactics: #Discovery, #LateralMovement
//
// Create DataTable with your own values, example below shows dummy usernames and domain
let List = datatable(VIPUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN", "administrator", ""];
let timeframe = 10m;
List
| project TargetUserName = tolower(VIPUser), TargetDomainName = toupper(Domain)
| join kind= rightsemi (
SecurityEvent
| where TimeGenerated > ago(2*timeframe)
| where EventID == "4625"
| where AccountType == "User"
) on TargetUserName, TargetDomainName
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FailedVIPLogons = count() by EventID, Activity, WorkstationName, Account, TargetAccount, TargetUserName, TargetDomainName, LogonType, LogonTypeName, LogonProcessName, Status, SubStatus
| where FailedVIPLogons >= 6
// 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",
"See - https://docs.microsoft.com/en-us/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",
"See - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
)
| project StartTimeUtc, EndTimeUtc, FailedVIPLogons, EventID, Activity, WorkstationName, Account, TargetAccount, TargetUserName, TargetDomainName, LogonType, LogonTypeName, LogonProcessName, Status, StatusDesc, SubStatus, SubStatusDesc
| extend AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,83 @@
id: 892cd37e-f9e1-49c3-b0b2-d74f52ac7b71
name: VIP account more than 6 failed logons in 10
description: |
'VIP Account with more than 6 failed logon attempts in 10 minutes, include your own VIP list in the table below
NTSTATUS codes - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
query: |
// Create DataTable with your own values, example below shows dummy usernames and domain
let List = datatable(VIPUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN", "administrator", ""];
let timeframe = 10m;
List
| project TargetUserName = tolower(VIPUser), TargetDomainName = toupper(Domain)
| join kind= rightsemi (
SecurityEvent
| where TimeGenerated > ago(2*timeframe)
| where EventID == "4625"
| where AccountType == "User"
) on TargetUserName, TargetDomainName
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FailedVIPLogons = count() by EventID, Activity, WorkstationName, Account, TargetAccount, TargetUserName, TargetDomainName, LogonType, LogonTypeName, LogonProcessName, Status, SubStatus
| where FailedVIPLogons >= 6
// 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",
"See - https://docs.microsoft.com/en-us/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",
"See - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55"
)
| project StartTimeUtc, EndTimeUtc, FailedVIPLogons, EventID, Activity, WorkstationName, Account, TargetAccount, TargetUserName, TargetDomainName, LogonType, LogonTypeName, LogonProcessName, Status, StatusDesc, SubStatus, SubStatusDesc
| extend timestamp = StartTimeUtc, AccountCustomEntity = Account

Просмотреть файл

@ -1,26 +0,0 @@
// Name: Summary of failed user logons by reason of failure
// Description: A summary of failed logons can be used to infer lateral movement with the intention of discovering credentials and sensitive data
//
// Id: e7642e6e-cf27-46ec-a4b9-e4475228fead
//
// Tactics: #InitialAccess, #LateralMovement, #Persistence
//
SecurityEvent|
where AccountType == 'User' and EventID == 4625
| extend Reason = case(
SubStatus == '0xc000005e', 'No logon servers available to service the logon request',
SubStatus == '0xc0000062', 'Account name is not properly formatted',
SubStatus == '0xc0000064', 'Account name does not exist',
SubStatus == '0xc000006a', 'Incorrect password', SubStatus == '0xc000006d', 'Bad user name or password',
SubStatus == '0xc000006f', 'User logon blocked by account restriction',
SubStatus == '0xc000006f', 'User logon outside of restricted logon hours',
SubStatus == '0xc0000070', 'User logon blocked by workstation restriction',
SubStatus == '0xc0000071', 'Password has expired',
SubStatus == '0xc0000072', 'Account is disabled',
SubStatus == '0xc0000133', 'Clocks between DC and other computer too far out of sync',
SubStatus == '0xc000015b', 'The user has not been granted the requested logon right at this machine',
SubStatus == '0xc0000193', 'Account has expirated',
SubStatus == '0xc0000224', 'User is required to change password at next logon',
SubStatus == '0xc0000234', 'Account is currently locked out',
strcat('Unknown reason substatus: ', SubStatus))
| summarize count() by Reason

Просмотреть файл

@ -0,0 +1,38 @@
id: e7642e6e-cf27-46ec-a4b9-e4475228fead
name: Summary of failed user logons by reason of failure
description: |
'A summary of failed logons can be used to infer lateral movement with the intention of discovering credentials and sensitive data'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1110
query: |
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where AccountType == 'User' and EventID == 4625
| extend Reason = case(
SubStatus == '0xc000005e', 'No logon servers available to service the logon request',
SubStatus == '0xc0000062', 'Account name is not properly formatted',
SubStatus == '0xc0000064', 'Account name does not exist',
SubStatus == '0xc000006a', 'Incorrect password', SubStatus == '0xc000006d', 'Bad user name or password',
SubStatus == '0xc000006f', 'User logon blocked by account restriction',
SubStatus == '0xc000006f', 'User logon outside of restricted logon hours',
SubStatus == '0xc0000070', 'User logon blocked by workstation restriction',
SubStatus == '0xc0000071', 'Password has expired',
SubStatus == '0xc0000072', 'Account is disabled',
SubStatus == '0xc0000133', 'Clocks between DC and other computer too far out of sync',
SubStatus == '0xc000015b', 'The user has not been granted the requested logon right at this machine',
SubStatus == '0xc0000193', 'Account has expirated',
SubStatus == '0xc0000224', 'User is required to change password at next logon',
SubStatus == '0xc0000234', 'Account is currently locked out',
strcat('Unknown reason substatus: ', SubStatus))
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by Reason
| extend timestamp = StartTimeUtc

Просмотреть файл

@ -1,39 +0,0 @@
// Name: Group added to Built in Domain Local or Global Group
// Description: A Group created in the last 7 days was added to a privileged built in domain local group or global group such as the Enterprise Admins, Cert Publishers or DnsAdmins
// Be sure to verify this is an expected addition
//
// Id: cb47a115-2616-4d56-890d-b28c14bc83e4
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement, #Collection, #PrivilegeEscalation
//
let timeframe = 7d;
// For AD SID mappings - https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
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";
let GroupAddition = SecurityEvent
| where TimeGenerated > ago(timeframe)
// 4728 - A member was added to a security-enabled global group
// 4732 - A member was added to a security-enabled local group
// 4756 - A member was added to a security-enabled universal group
| where EventID in ("4728", "4732", "4756")
| where AccountType == "User" and MemberName == "-"
// Exclude Remote Desktop Users group: S-1-5-32-555
| where TargetSid !in ("S-1-5-32-555")
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| project GroupAddTime = TimeGenerated, GroupAddEventID = EventID, GroupAddActivity = Activity, GroupAddComputer = Computer, GroupAddTargetUserName = TargetUserName, GroupAddTargetDomainName = TargetDomainName, GroupAddTargetSid = TargetSid, GroupAddSubjectUserName = SubjectUserName, GroupAddSubjectUserSid = SubjectUserSid, GroupSid = MemberSid, Account, Computer
| extend AccountCustomEntity = Account, HostCustomEntity = Computer;
let GroupCreated = SecurityEvent
| where TimeGenerated > ago(timeframe)
// 4727 - A security-enabled global group was created
// 4731 - A security-enabled local group was created
// 4754 - A security-enabled universal group was created
| where EventID in ("4727", "4731", "4754")
| where AccountType == "User"
| project GroupCreateTime = TimeGenerated, GroupCreateEventID = EventID, GroupCreateActivity = Activity, GroupCreateComputer = Computer, GroupCreateTargetUserName = TargetUserName, GroupCreateTargetDomainName = TargetDomainName, GroupCreateSubjectUserName = SubjectUserName, GroupCreateSubjectDomainName = SubjectDomainName, GroupCreateSubjectUserSid = SubjectUserSid, GroupSid = TargetSid, Account, Computer
| extend AccountCustomEntity = Account, HostCustomEntity = Computer;
GroupCreated
| join (
GroupAddition
) on GroupSid

Просмотреть файл

@ -0,0 +1,51 @@
id: cb47a115-2616-4d56-890d-b28c14bc83e4
name: Group added to Built in Domain Local or Global Group
description: |
'A Group created in the last 7 days was added to a privileged built in domain local group or global group such as the
Enterprise Admins, Cert Publishers or DnsAdmins. Be sure to verify this is an expected addition'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
let timeframe = 7d;
// For AD SID mappings - https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
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";
let GroupAddition = SecurityEvent
| where TimeGenerated > ago(timeframe)
// 4728 - A member was added to a security-enabled global group
// 4732 - A member was added to a security-enabled local group
// 4756 - A member was added to a security-enabled universal group
| where EventID in ("4728", "4732", "4756")
| where AccountType == "User" and MemberName == "-"
// Exclude Remote Desktop Users group: S-1-5-32-555
| where TargetSid !in ("S-1-5-32-555")
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
| project GroupAddTime = TimeGenerated, GroupAddEventID = EventID, GroupAddActivity = Activity, GroupAddComputer = Computer,
GroupAddTargetUserName = TargetUserName, GroupAddTargetDomainName = TargetDomainName, GroupAddTargetSid = TargetSid,
GroupAddSubjectUserName = SubjectUserName, GroupAddSubjectUserSid = SubjectUserSid, GroupSid = MemberSid, Account, Computer
| extend AccountCustomEntity = Account, HostCustomEntity = Computer;
let GroupCreated = SecurityEvent
| where TimeGenerated > ago(timeframe)
// 4727 - A security-enabled global group was created
// 4731 - A security-enabled local group was created
// 4754 - A security-enabled universal group was created
| where EventID in ("4727", "4731", "4754")
| where AccountType == "User"
| project GroupCreateTime = TimeGenerated, GroupCreateEventID = EventID, GroupCreateActivity = Activity, GroupCreateComputer = Computer,
GroupCreateTargetUserName = TargetUserName, GroupCreateTargetDomainName = TargetDomainName, GroupCreateSubjectUserName = SubjectUserName,
GroupCreateSubjectDomainName = SubjectDomainName, GroupCreateSubjectUserSid = SubjectUserSid, GroupSid = TargetSid, Account, Computer;
GroupCreated
| join (
GroupAddition
) on GroupSid
| extend timestamp = GroupCreateTime, AccountCustomEntity = Account, HostCustomEntity = Computer

Просмотреть файл

@ -1,32 +0,0 @@
// Name: Hosts with new logons
// Description: Shows new accounts that have logged onto a host for the first time - this may clearly be benign activity but an account logging onto multiple hosts for the first time can also be used to look for evidence of that account being used to move laterally across a network.
//
// Id: 62e2df59-1535-4c8e-ac6c-c91faeed0179
//
// Tactics: #LateralMovement,
//
let LogonEvents=() {
let logonSuccess=SecurityEvent
| where EventID==4624
| project TimeGenerated, ComputerName=Computer, AccountName=TargetUserName, AccountDomain=TargetDomainName, IpAddress, ActionType='Logon';
let logonFail=SecurityEvent
| where EventID==4625
| project TimeGenerated, ComputerName=Computer, AccountName=TargetUserName, AccountDomain=TargetDomainName, IpAddress, ActionType='LogonFailure';
logonFail
| union logonSuccess
};
LogonEvents
| where TimeGenerated >= ago(14d)
| where ActionType == 'Logon'
| summarize count() by ComputerName, AccountName
| join kind=leftouter (
LogonEvents
| where TimeGenerated >= ago(1d)
| where ActionType == 'Logon'
| summarize count() by ComputerName, AccountName
) on ComputerName
| where AccountName != AccountName1
| summarize HostCount=dcount(ComputerName) by AccountName1
| extend Name = AccountName1
| project Name , HostCount
| extend AccountCustomEntity = Name

Просмотреть файл

@ -0,0 +1,42 @@
id: 62e2df59-1535-4c8e-ac6c-c91faeed0179
name: Hosts with new logons
description: |
'Shows new accounts that have logged onto a host for the first time - this may clearly be benign activity but an account
logging onto multiple hosts for the first time can also be used to look for evidence of that account being used to move
laterally across a network.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1110
query: |
let starttime = 7d;
let endtime = 1d;
let LogonEvents=() {
let logonSuccess=SecurityEvent
| where EventID==4624
| project TimeGenerated, ComputerName=Computer, AccountName=TargetUserName, AccountDomain=TargetDomainName, IpAddress, ActionType='Logon';
let logonFail=SecurityEvent
| where EventID==4625
| project TimeGenerated, ComputerName=Computer, AccountName=TargetUserName, AccountDomain=TargetDomainName, IpAddress, ActionType='LogonFailure';
logonFail
| union logonSuccess
};
LogonEvents
| where TimeGenerated >= ago(endtime)
| where ActionType == 'Logon'
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by ComputerName, AccountName
| join kind=leftanti (
LogonEvents
| where TimeGenerated >= ago(starttime)
| where ActionType == 'Logon'
| summarize count() by ComputerName, AccountName
) on ComputerName
| summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), HostCount=dcount(ComputerName) by AccountName
| extend timestamp = StartTimeUtc, AccountCustomEntity = AccountName

Просмотреть файл

@ -1,20 +0,0 @@
// Name: Least Common Parent And Child Process Pairs
// Id: 3712595d-6f47-416b-963a-605201ed2764
// Description: Looks across your environment for least common Parent/Child process combinations. Will possibly find some malicious activity disguised as well known process names. By ZanCo
// DataSource: #SecurityEvent
// Tactics: #InitialAccess, #Execution, #Persistence
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 5;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration) and isnotnull(ParentProcessName)
| extend ProcArray = split(NewProcessName, '\\'), ParentProcArray = split(ParentProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray), ParentProcArrayLength = arraylength(ParentProcArray)
| extend LastIndex = ProcArrayLength - 1, ParentLastIndex = ParentProcArrayLength - 1
| extend Proc = ProcArray[LastIndex], ParentProc = ParentProcArray[ParentLastIndex]
| where Proc !in (Whitelist)
| extend ParentChildPair = strcat(ParentProc , ' > ', Proc)
| summarize TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by ParentChildPair
| where TimesSeen < Sensitivity

Просмотреть файл

@ -0,0 +1,31 @@
id: 3712595d-6f47-416b-963a-605201ed2764
name: Least Common Parent And Child Process Pairs
description: |
'Looks across your environment for least common Parent/Child process combinations.
Will possibly find some malicious activity disguised as well known process names.
By ZanCo'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 5;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration) and isnotnull(ParentProcessName)
| extend ProcArray = split(NewProcessName, '\\'), ParentProcArray = split(ParentProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray), ParentProcArrayLength = arraylength(ParentProcArray)
| extend LastIndex = ProcArrayLength - 1, ParentLastIndex = ParentProcArrayLength - 1
| extend Proc = ProcArray[LastIndex], ParentProc = ParentProcArray[ParentLastIndex]
| where Proc !in (Whitelist)
| extend ParentChildPair = strcat(ParentProc , ' > ', Proc)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by ParentChildPair
| where TimesSeen < Sensitivity
| extend timestamp = StartTimeUtc

Просмотреть файл

@ -1,19 +0,0 @@
// Name: Least Common Processes by Command Line
// Id: 088d30e9-c02b-46b1-bd1f-d5b6d6b782f0
// Description: Looks across your environment for least common Process Command Lines, may be noisy and require whitelisting. By ZanCo
// DataSource: #SecurityEvent
// Tactics: #InitialAccess, #Execution, #Persistence
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 5;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration) and NewProcessName !endswith 'conhost.exe'
| extend ProcArray = split(NewProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray)
| extend LastIndex = ProcArrayLength - 1
| extend Proc = ProcArray[LastIndex]
| where Proc !in (Whitelist)
| summarize TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by CommandLine
| where TimesSeen < Sensitivity

Просмотреть файл

@ -0,0 +1,28 @@
id: 088d30e9-c02b-46b1-bd1f-d5b6d6b782f0
name: Least Common Processes by Command Line
description: |
'Looks across your environment for least common Process Command Lines, may be noisy and require whitelisting. By ZanCo'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 5;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration) and NewProcessName !endswith 'conhost.exe'
| extend ProcArray = split(NewProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray)
| extend LastIndex = ProcArrayLength - 1
| extend Proc = ProcArray[LastIndex]
| where Proc !in (Whitelist)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by CommandLine
| where TimesSeen < Sensitivity
| extend timestamp = StartTimeUtc

Просмотреть файл

@ -1,26 +0,0 @@
// Name: Least Common Processes Including Folder Depth
//
// Id: 6d04a1ef-1b4d-4ff8-a76c-ad7d1a396136
//
// Description: Looks across your environment for least commonly occurring Processes. To observe for commonly known process names, this hunting query will also look at the process' depth from the root. We don't compare full process path due to some processes having dynamic folder names. By ZanCo
//
// DataSource: #SecurityEvent
//
// Tactics: #InitialAccess, #Execution, #Persistence
//
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 15;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration)
| extend ProcArray = split(NewProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray)
| extend LastIndex = ProcArrayLength - 1
| extend Proc = ProcArray[LastIndex]
| where Proc !in (Whitelist)
// ProcArray[0] is the proc's Drive
| extend DriveDepthProc = strcat(ProcArray[0], '-', ProcArrayLength, '-', Proc)
| summarize TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by DriveDepthProc
| where TimesSeen < Sensitivity

Просмотреть файл

@ -0,0 +1,29 @@
id: 6d04a1ef-1b4d-4ff8-a76c-ad7d1a396136
name: Least Common Processes Including Folder Depth
description: |
'Looks across your environment for least common Process Command Lines, may be noisy and require whitelisting. By ZanCo'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let Whitelist = dynamic (['foo.exe', 'baz.exe']);
let Sensitivity = 15;
let StartDate = ago(7d);
let Duration = 7d;
SecurityEvent
| where EventID == 4688 and TimeGenerated > StartDate and TimeGenerated < (StartDate + Duration)
| extend ProcArray = split(NewProcessName, '\\')
// ProcArrayLength is Folder Depth
| extend ProcArrayLength = arraylength(ProcArray)
| extend LastIndex = ProcArrayLength - 1
| extend Proc = ProcArray[LastIndex]
| where Proc !in (Whitelist)
// ProcArray[0] is the proc's Drive
| extend DriveDepthProc = strcat(ProcArray[0], '-', ProcArrayLength, '-', Proc)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), TimesSeen = count(), HostCount = dcount(Computer), Hosts = makeset(Computer), UserCount = dcount(SubjectUserName), Users = makeset(SubjectUserName) by DriveDepthProc
| where TimesSeen < Sensitivity
| extend timestamp = StartTimeUtc

Просмотреть файл

@ -1,76 +0,0 @@
// Name: Entropy for Processes for a given Host
// 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.
//
// References: https://medium.com/udacity/shannon-entropy-information-gain-and-picking-balls-from-buckets-5810d35d54b4
// https://en.wiktionary.org/wiki/Shannon_entropy
//
// Id: 05208917-82de-46f7-a190-a65739a690f4
//
// DataSource: ##SecurityEvent
//
// Tactics: #Execution
//
// May need to reduce the number of days if the environment is very large. Try 3-5 days, but less accuracy.
let end = startofday(now());
let start = end - 7d;
let Exclude = SecurityEvent
// Timeframe is set so that results do not change during the same day (UTC time)
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| summarize count() by Process
// Removing noisy processes for an environment, adjust as needed
| where count_ >= 100000;
let AllSecEvents = SecurityEvent
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| where Process !in~ ("conhost.exe")
| project Computer, Process;
// Removing noisy process from full list
let Include = Exclude | join kind= rightanti (
AllSecEvents
) on Process;
// Identifying prevalence for a given process in the environment
let DCwPC = 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 = 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 = TotalProcessCountOnHost*1.0;
// 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 = ProcessCountOnHost*1.0;
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)
// Calculating Weight, see details in description
| extend Weight = toreal((ProcessEntropy*100000)*ProcessCountOnHost*DistinctComputersWithProcessCount)
| where Weight <= 100
| project Computer, Process, Weight , ProcessEntropy, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount;
// Join back full entry
Results | join kind= inner (
SecurityEvent
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| where Process !in~ ("conhost.exe")
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, AccountType, Process, NewProcessName, CommandLine, ParentProcessName
) on Computer, Process
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, Weight, ProcessEntropy, Process, NewProcessName, CommandLine, ParentProcessName, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount
| sort by Weight asc, ProcessEntropy asc, NewProcessName asc
| extend HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,79 @@
id: 05208917-82de-46f7-a190-a65739a690f4
name: Entropy for Processes for a given Host
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.
References: https://medium.com/udacity/shannon-entropy-information-gain-and-picking-balls-from-buckets-5810d35d54b4
https://en.wiktionary.org/wiki/Shannon_entropy'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
// May need to reduce the number of days if the environment is very large. Try 3-5 days, but less accuracy.
let end = startofday(now());
let start = end - 7d;
let Exclude = SecurityEvent
// Timeframe is set so that results do not change during the same day (UTC time)
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| summarize count() by Process
// Removing noisy processes for an environment, adjust as needed
| where count_ >= 100000;
let AllSecEvents = SecurityEvent
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| where Process !in~ ("conhost.exe")
| project Computer, Process;
// Removing noisy process from full list
let Include = Exclude | join kind= rightanti (
AllSecEvents
) on Process;
// Identifying prevalence for a given process in the environment
let DCwPC = 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 = 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 = TotalProcessCountOnHost*1.0;
// 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 = ProcessCountOnHost*1.0;
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)
// Calculating Weight, see details in description
| extend Weight = toreal((ProcessEntropy*100000)*ProcessCountOnHost*DistinctComputersWithProcessCount)
| where Weight <= 100
| project Computer, Process, Weight , ProcessEntropy, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount;
// Join back full entry
Results | join kind= inner (
SecurityEvent
| where TimeGenerated >= start and TimeGenerated <= end
| where EventID == 4688
| where Process !in~ ("conhost.exe")
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, AccountType, Process, NewProcessName, CommandLine, ParentProcessName
) on Computer, Process
| project TimeGenerated, EventID, Computer, SubjectUserSid, Account, Weight, ProcessEntropy, Process, NewProcessName, CommandLine, ParentProcessName, TotalProcessCountOnHost, ProcessCountOnHost, DistinctComputersWithProcessCount
| sort by Weight asc, ProcessEntropy asc, NewProcessName asc
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -1,64 +0,0 @@
// Name: Rare processes run by Service accounts
// Description: Service accounts normally are supposed to perform a limited set of tasks in a stable environment.
// The query collects a list of service account and then joins them with rare processes in an environment to detect anomalous behaviours.
//
//Id: af02987c-949d-47d5-b0ae-64d8e1b674e2
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement , #InitialAccess, #Execution,
//
let List1 = datatable(AccountName:string)["MSSQLSERVER", "ReportServer", "MSDTSServer100", "IUSR"];
// Provide a list of service account/ built-in accounts in an environment.
let List2 = SecurityEvent
// Self generating a list of Service account using event Id :4624
| where TimeGenerated >= ago(1d)
| where EventID == "4624"
| where Account !contains "$" and Account !contains "Local SYSTEM" and Account !contains "Local SERVICE" and Account !contains "Network SERVICE" and Account !contains "NT AUTHORITY" and Account !contains "NT-AUTORITÄT"
| where LogonType == "5"
| extend AccountName = Account
| distinct AccountName;
let Accounts = List1 | union (List2 | distinct AccountName);
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where TimeGenerated >= ago(1d)
| where EventID==4688
// filter out common randomly named files related to MSI installers and browsers
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{4}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{4}\.tmp")
| project TimeGenerated,
ComputerName=Computer,
AccountName=SubjectUserName,
AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,
InitiatingProcessCommandLine="",
InitiatingProcessParentFileName="";
processEvents;
};
let normalizedProcesses = ProcessCreationEvents
// normalize guids
| project TimeGenerated, AccountName, FileName = replace("[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}", "<guid>", FileName)
// normalize digits away
| project TimeGenerated, AccountName, FileName=replace(@'\d', 'n', FileName);
let freqs = normalizedProcesses
| summarize frequency = count() by FileName
| join kind= leftouter (
normalizedProcesses
| summarize Since=min(TimeGenerated), LastSeen=max(TimeGenerated) by FileName, AccountName
) on FileName;
let Finalfreqs = freqs
| where frequency <= toscalar( freqs | serialize | project frequency | summarize percentiles(frequency, 10))
| order by frequency asc
| project FileName, frequency, Since, LastSeen , AccountName
// restrict results to unusual processes seen in last day
| where LastSeen >= ago(1d);
Accounts
| join kind= inner (
Finalfreqs
) on AccountName
| where frequency < 10
| project-away AccountName1

Просмотреть файл

@ -0,0 +1,69 @@
id: af02987c-949d-47d5-b0ae-64d8e1b674e2
name: Rare processes run by Service accounts
description: |
'Service accounts normally are supposed to perform a limited set of tasks in a stable environment.
The query collects a list of service account and then joins them with rare processes in an environment to detect anomalous behaviours.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let timeframe = 1d;
let List1 = datatable(AccountName:string)["MSSQLSERVER", "ReportServer", "MSDTSServer100", "IUSR"];
// Provide a list of service account/ built-in accounts in an environment.
let List2 = SecurityEvent
// Self generating a list of Service account using event Id :4624
| where TimeGenerated >= ago(timeframe)
| where EventID == "4624"
| where Account !contains "$" and Account !contains "Local SYSTEM" and Account !contains "Local SERVICE" and Account !contains "Network SERVICE" and Account !contains "NT AUTHORITY" and Account !contains "NT-AUTORITÄT"
| where LogonType == "5"
| extend AccountName = Account
| distinct AccountName;
let Accounts = List1 | union (List2 | distinct AccountName);
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID==4688
// filter out common randomly named files related to MSI installers and browsers
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{4}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{4}\.tmp")
| project TimeGenerated,
ComputerName=Computer,
AccountName=SubjectUserName,
AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,
InitiatingProcessCommandLine="",
InitiatingProcessParentFileName="";
processEvents;
};
let normalizedProcesses = ProcessCreationEvents
// normalize guids
| project TimeGenerated, AccountName, FileName = replace("[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}", "<guid>", FileName)
// normalize digits away
| project TimeGenerated, AccountName, FileName=replace(@'\d', 'n', FileName);
let freqs = normalizedProcesses
| summarize frequency = count() by FileName
| join kind= leftouter (
normalizedProcesses
| summarize Since=min(TimeGenerated), LastSeen=max(TimeGenerated) by FileName, AccountName
) on FileName;
let Finalfreqs = freqs
| where frequency <= toscalar( freqs | serialize | project frequency | summarize percentiles(frequency, 10))
| order by frequency asc
| project FileName, frequency, Since, LastSeen , AccountName
// restrict results to unusual processes seen in last day
| where LastSeen >= ago(timeframe);
Accounts
| join kind= inner (
Finalfreqs
) on AccountName
| where frequency < 10
| project-away AccountName1
| extend AccountCustomEntity = AccountName

Просмотреть файл

@ -1,11 +0,0 @@
// Name: Summary of user logons by logon type
// Description: Comparing succesful and nonsuccessful logon attempts can be used to identify attempts to move laterally within the environment with the intention of discovering credentials and sensitive data.
//
// Id: d0f13bb9-e713-4f89-b610-1806326a1dea
//
// Tactics: #InitialAccess, #LateralMovement, #Persistence
//
SecurityEvent
| where EventID in (4624, 4625)
| where AccountType == 'User'
| summarize Amount = count() by LogonTypeName

Просмотреть файл

@ -0,0 +1,23 @@
id: d0f13bb9-e713-4f89-b610-1806326a1dea
name: Summary of user logons by logon type
description: |
'Comparing succesful and nonsuccessful logon attempts can be used to identify attempts to move laterally within the
environment with the intention of discovering credentials and sensitive data.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1110
query: |
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID in (4624, 4625)
| where AccountType == 'User'
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), Amount = count() by LogonTypeName
| extend timestamp = StartTimeUtc

Просмотреть файл

@ -1,26 +0,0 @@
// Name: User Account added to Built in Domain Local or Global Group over last 10 days
// Description: User account was added to a privileged built in domain local group or global group such as the Enterprise Adminis, Cert Publishers or DnsAdmins
// Be sure to verify this is an expected addition
//
// Id: 8d69a665-074a-443b-aae6-5dd9bdd5cfb1
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement, #Collection
//
let timeframe = 10d;
// For AD SID mappings - https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
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";
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where AccountType == "User"
// 4728 - A member was added to a security-enabled global group
// 4732 - A member was added to a security-enabled local group
// 4756 - A member was added to a security-enabled universal group
| where EventID in ("4728", "4732", "4756")
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
// Exclude Remote Desktop Users group: S-1-5-32-555
| where TargetSid !in ("S-1-5-32-555")
| project StartTimeUtc = TimeGenerated, EventID, Activity, Computer, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectUserName, SubjectUserSid
| extend HostCustomEntity = Computer, AccountCustomEntity = UserPrincipalName

Просмотреть файл

@ -0,0 +1,34 @@
id: 8d69a665-074a-443b-aae6-5dd9bdd5cfb1
name: User Account added to Built in Domain Local or Global Group
description: |
'User account was added to a privileged built in domain local group or global group such as the Enterprise Adminis, Cert Publishers or DnsAdmins
Be sure to verify this is an expected addition.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
let timeframe = 10d;
// For AD SID mappings - https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
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";
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where AccountType == "User"
// 4728 - A member was added to a security-enabled global group
// 4732 - A member was added to a security-enabled local group
// 4756 - A member was added to a security-enabled universal group
| where EventID in ("4728", "4732", "4756")
| where TargetSid matches regex WellKnownLocalSID or TargetSid matches regex WellKnownGroupSID
// Exclude Remote Desktop Users group: S-1-5-32-555
| where TargetSid !in ("S-1-5-32-555")
| project StartTimeUtc = TimeGenerated, EventID, Activity, Computer, TargetUserName, TargetDomainName, TargetSid, UserPrincipalName, SubjectUserName, SubjectUserSid
| extend timestamp = StartTimeUtc, HostCustomEntity = Computer, AccountCustomEntity = UserPrincipalName

Просмотреть файл

@ -1,32 +0,0 @@
// Name: User Account Created and Deleted within 10mins
// Description: User account created and then deleted within 10 minutes across last 30 days
//
// Id: 6135a90e-ba30-4f36-9b6a-3a350050704b
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement, #Collection
//
// TimeFrame is the number of lookback days, default is last 14 days
let timeframe = 14d;
// TimeDelta is the difference between when the account was created and when it was deleted, default is set to 10min or less
let timedelta = 10m;
SecurityEvent
| where TimeGenerated > ago(timeframe)
// A user account was created
| where EventID == "4720"
| where AccountType == "User"
| project creationTime = TimeGenerated, CreateEventID = EventID, Activity, Computer, TargetUserName, UserPrincipalName, AccountUsedToCreate = SubjectUserName, TargetSid, SubjectUserSid
| join kind= inner (
SecurityEvent
| where TimeGenerated > ago(timeframe)
// A user account was deleted
| where EventID == "4726"
| where AccountType == "User"
| project deletionTime = TimeGenerated, DeleteEventID = EventID, Activity, Computer, TargetUserName, UserPrincipalName, AccountUsedToDelete = SubjectUserName, TargetSid, SubjectUserSid
) on Computer, TargetUserName
| where deletionTime - creationTime < 10m
| extend TimeDelta = deletionTime - creationTime
| where tolong(TimeDelta) >= 0
| project TimeDelta, creationTime, CreateEventID, Computer, TargetUserName, UserPrincipalName, AccountUsedToCreate, deletionTime, DeleteEventID, AccountUsedToDelete
| extend HostCustomEntity = Computer, AccountCustomEntity = UserPrincipalName

Просмотреть файл

@ -0,0 +1,43 @@
id: 6135a90e-ba30-4f36-9b6a-3a350050704b
name: Long lookback User Account Created and Deleted within 10mins
description: |
'User account created and then deleted within 10 minutes across last 14 days'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
// TimeFrame is the number of lookback days, default is last 14 days
let timeframe = 14d;
// TimeDelta is the difference between when the account was created and when it was deleted, default is set to 10min or less
let timedelta = 10m;
SecurityEvent
| where TimeGenerated > ago(timeframe)
// A user account was created
| where EventID == "4720"
| where AccountType == "User"
| project creationTime = TimeGenerated, CreateEventID = EventID, Activity, Computer, TargetUserName, UserPrincipalName,
AccountUsedToCreate = SubjectUserName, TargetSid, SubjectUserSid
| join kind= inner (
SecurityEvent
| where TimeGenerated > ago(timeframe)
// A user account was deleted
| where EventID == "4726"
| where AccountType == "User"
| project deletionTime = TimeGenerated, DeleteEventID = EventID, Activity, Computer, TargetUserName, UserPrincipalName,
AccountUsedToDelete = SubjectUserName, TargetSid, SubjectUserSid
) on Computer, TargetUserName
| where deletionTime - creationTime < timedelta
| extend TimeDelta = deletionTime - creationTime
| where tolong(TimeDelta) >= 0
| project TimeDelta, creationTime, CreateEventID, Computer, TargetUserName, UserPrincipalName, AccountUsedToCreate,
deletionTime, DeleteEventID, AccountUsedToDelete
| extend timestamp = creationTime, HostCustomEntity = Computer, AccountCustomEntity = UserPrincipalName

Просмотреть файл

@ -1,21 +0,0 @@
// Name: User account added or removed from a security group by an unauthorized user
// Description: User account added or removed from a security group by an unauthorized user, pass in a list
//
// Id: d57f675c-ad6c-44d0-95fb-3bf707e70155
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement, #PrivilegeEscalation
//
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(AuthorizedUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID in (4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762)
| join kind= leftanti (
List
| project SubjectUserName = tolower(AuthorizedUser), SubjectDomainName = toupper(Domain)
) on SubjectUserName, SubjectDomainName
| project TimeGenerated, Computer, Account, SubjectUserName, SubjectDomainName, TargetAccount, EventID, Activity
| extend HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,29 @@
id: d57f675c-ad6c-44d0-95fb-3bf707e70155
name: User account added or removed from a security group by an unauthorized user
description: |
'User account added or removed from a security group by an unauthorized user, pass in a list'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(AuthorizedUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID in (4728, 4729, 4732, 4733, 4746, 4747, 4751, 4752, 4756, 4757, 4761, 4762)
| join kind= leftanti (
List
| project SubjectUserName = tolower(AuthorizedUser), SubjectDomainName = toupper(Domain)
) on SubjectUserName, SubjectDomainName
| project TimeGenerated, Computer, Account, SubjectUserName, SubjectDomainName, TargetAccount, EventID, Activity
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -1,22 +0,0 @@
// Name: User created by unauthorized user
// Description: User account created by an unauthorized user, pass in a list
//
// Id: 42ae9690-89ce-4063-9a90-465badad5395
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence, #Discovery, #LateralMovement, #PrivilegeEscalation
//
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(AuthorizedUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID == 4720
| where AccountType == "User"
| join kind= leftanti (
List
| project SubjectUserName = tolower(AuthorizedUser), SubjectDomainName = toupper(Domain)
) on SubjectUserName, SubjectDomainName
| project TimeGenerated, Computer, Account, SubjectUserName, SubjectDomainName, TargetAccount, EventID, Activity
| extend HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,30 @@
id: 42ae9690-89ce-4063-9a90-465badad5395
name: User created by unauthorized user
description: |
'User account created by an unauthorized user, pass in a list'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(AuthorizedUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID == 4720
| where AccountType == "User"
| join kind= leftanti (
List
| project SubjectUserName = tolower(AuthorizedUser), SubjectDomainName = toupper(Domain)
) on SubjectUserName, SubjectDomainName
| project TimeGenerated, Computer, Account, SubjectUserName, SubjectDomainName, TargetAccount, EventID, Activity
| extend timestamp = TimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -1,22 +0,0 @@
// Name: VIP account more than 6 failed logons in 10
// Description: VIP Account with more than 6 failed logon attempts in 10 minutes, include your own VIP list in the table below
//
// Id: e8d36582-c403-4466-bd44-ebede5b6fa6e
//
// DataSource: #SecurityEvent
//
// Tactics: #Discovery, #LateralMovement
//
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(VIPUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 10m;
List | extend Account = strcat(Domain,"\\",VIPUser) | join kind= inner (
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where EventID == "4625"
| where AccountType == "User"
| where LogonType == "2" or LogonType == "3"
) on Account
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FailedVIPLogons = count() by LogonType, Account
| where FailedVIPLogons >= 6
| extend AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,28 @@
id: e8d36582-c403-4466-bd44-ebede5b6fa6e
name: VIP account more than 6 failed logons in 10
description: |
'VIP Account with more than 6 failed logon attempts in 10 minutes, include your own VIP list in the table below'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
relevantTechniques:
- T1110
query: |
// Create DataTable with your own values, example below shows dummy usernames that are authorized and for what domain
let List = datatable(VIPUser:string, Domain:string)["Bob", "Domain", "joe", "domain", "MATT", "DOMAIN"];
let timeframe = 10m;
List | extend Account = strcat(Domain,"\\",VIPUser) | join kind= inner (
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where EventID == "4625"
| where AccountType == "User"
| where LogonType == "2" or LogonType == "3"
) on Account
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FailedVIPLogons = count() by LogonType, Account
| where FailedVIPLogons >= 6
| extend timestamp = StartTimeUtc, AccountCustomEntity = Account

Просмотреть файл

@ -1,32 +0,0 @@
// Name: Cscript script daily summary breakdown
//
// Id: 36abe031-962d-482e-8e1e-a556ed99d5a3
//
// Description: breakdown of scripts running in the environment
//
// DataSource: #SecurityEvent
//
// Tactics: #Execution
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project EventTime=TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents;
};
// Daily summary of cscript activity – extracting script name and parameters from commandline:
ProcessCreationEvents | where FileName =~ "cscript.exe"
| project removeSwitches = replace(@"/+[a-zA-Z0-9:]+", "", ProcessCommandLine) // remove commandline switches
| project CommandLine = trim(@"[a-zA-Z0-9\\:""]*cscript(.exe)?("")?(\s)+", removeSwitches) // remove the leading cscript.exe process name
// extract the script name:
| project ScriptName= iff(CommandLine startswith @"""",
extract(@"([:\\a-zA-Z_\-\s0-9\.()]+)(""?)", 0, CommandLine), // handle case where script name is enclosed in " characters
extract(@"([:\\a-zA-Z_\-0-9\.()]+)(""?)", 0, CommandLine)) // handle case where script name is not enclosed in quotes
, CommandLine
| project ScriptName=trim(@"""", ScriptName) , ScriptNameLength=strlen(ScriptName), CommandLine
// extract remainder of commandline as script parameters:
| project ScriptName, ScriptParams = iff(ScriptNameLength < strlen(CommandLine), substring(CommandLine, ScriptNameLength +1), "")
| summarize by ScriptName, ScriptParams

Просмотреть файл

@ -0,0 +1,41 @@
id: 36abe031-962d-482e-8e1e-a556ed99d5a3
name: Cscript script daily summary breakdown
description: |
'breakdown of scripts running in the environment'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let timeframe = 1d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project EventTime=TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]), ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents;
};
// Daily summary of cscript activity – extracting script name and parameters from commandline:
ProcessCreationEvents
| where EventTime >= ago(timeframe)
| where FileName =~ "cscript.exe"
// remove commandline switches
| project EventTime, ComputerName, AccountName, removeSwitches = replace(@"/+[a-zA-Z0-9:]+", "", ProcessCommandLine)
// remove the leading cscript.exe process name
| project EventTime, ComputerName, AccountName, CommandLine = trim(@"[a-zA-Z0-9\\:""]*cscript(.exe)?("")?(\s)+", removeSwitches)
// extract the script name:
| project EventTime, ComputerName, AccountName,
// handle case where script name is enclosed in " characters or is not enclosed in quotes
ScriptName= iff(CommandLine startswith @"""",
extract(@"([:\\a-zA-Z_\-\s0-9\.()]+)(""?)", 0, CommandLine),
extract(@"([:\\a-zA-Z_\-0-9\.()]+)(""?)", 0, CommandLine)), CommandLine
| project EventTime, ComputerName, AccountName, ScriptName=trim(@"""", ScriptName) , ScriptNameLength=strlen(ScriptName), CommandLine
// extract remainder of commandline as script parameters:
| project EventTime, ComputerName, AccountName, ScriptName, ScriptParams = iff(ScriptNameLength < strlen(CommandLine), substring(CommandLine, ScriptNameLength +1), "")
| summarize min(EventTime), count() by ComputerName, AccountName, ScriptName, ScriptParams
| order by count_ asc nulls last
| extend timestamp = min_EventTime, HostCustomEntity = ComputerName, AccountCustomEntity = AccountName

Просмотреть файл

@ -1,27 +0,0 @@
// Name: Enumeration of users and groups
//
// Id: a1e993de-770a-4434-83e9-9e3b47a6e470
//
// Description: Finds attempts to list users or groups using the built-in Windows 'net' tool
//
// DataSource: #SecurityEvent
//
// Tactics: #Discovery
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
FolderPath = "",
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
ProcessCreationEvents
| where FileName == 'net.exe' and AccountName != "" and ProcessCommandLine !contains '\\' and ProcessCommandLine !contains '/add'
| where (ProcessCommandLine contains ' user ' or ProcessCommandLine contains ' group ') and (ProcessCommandLine endswith ' /do' or ProcessCommandLine endswith ' /domain')
| extend Target = extract("(?i)[user|group] (\"*[a-zA-Z0-9-_ ]+\"*)", 1, ProcessCommandLine) | filter Target != ''
| summarize minTimeGenerated=min(TimeGenerated), maxTimeGenerated=max(TimeGenerated), count() by AccountName, Target, ProcessCommandLine, ComputerName
| project minTimeGenerated, maxTimeGenerated, count_, AccountName, Target, ProcessCommandLine, ComputerName
| sort by AccountName, Target
| extend AccountCustomEntity = AccountName, HostCustomEntity = ComputerName

Просмотреть файл

@ -0,0 +1,32 @@
id: a1e993de-770a-4434-83e9-9e3b47a6e470
name: Enumeration of users and groups
description: |
'Finds attempts to list users or groups using the built-in Windows 'net' tool '
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Discovery
query: |
let timeframe = 1d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
FolderPath = "",
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
ProcessCreationEvents
| where TimeGenerated >= ago(timeframe)
| where FileName == 'net.exe' and AccountName != "" and ProcessCommandLine !contains '\\' and ProcessCommandLine !contains '/add'
| where (ProcessCommandLine contains ' user ' or ProcessCommandLine contains ' group ') and (ProcessCommandLine endswith ' /do' or ProcessCommandLine endswith ' /domain')
| extend Target = extract("(?i)[user|group] (\"*[a-zA-Z0-9-_ ]+\"*)", 1, ProcessCommandLine) | filter Target != ''
| summarize minTimeGenerated=min(TimeGenerated), maxTimeGenerated=max(TimeGenerated), count() by AccountName, Target, ProcessCommandLine, ComputerName
| project minTimeGenerated, maxTimeGenerated, count_, AccountName, Target, ProcessCommandLine, ComputerName
| sort by AccountName, Target
| extend timestamp = minTimeGenerated, AccountCustomEntity = AccountName, HostCustomEntity = ComputerName

Просмотреть файл

@ -1,22 +0,0 @@
// Name: masquerading files.
//
// Id: 60304ebf-ebdd-4869-a702-e0216d90ab46
//
// Description: Malware writers often use windows system process names for their malicious process names to make them blend
// in with other legitimate commands that the Windows system executes.
// An analyst can create a simple query looking for a process named svchost.exe.
// It is recommended to filter out well-known security identifiers (SIDs) that are used to launch the legitimate svchost.exe process.
// The query also filters out the legitimate locations from which svchost.exe is launched.
//
// DataSource: #SecurityEvent
//
// Tactics: #Execution, #DefenseEvasion
//
SecurityEvent
| where NewProcessName endswith "\\svchost.exe"
| where SubjectUserSid !in ("S-1-5-18", "S-1-5-19", "S-1-5-20")
| where NewProcessName !contains ":\\Windows\\System32"
| where NewProcessName !contains ":\\Windows\\Syswow64"
| summarize minTimeGenerated=min(TimeGenerated), maxTimeGenerated=max(TimeGenerated), count() by Computer, SubjectUserName, NewProcessName, CommandLine, Account
| project minTimeGenerated , maxTimeGenerated , count_ , Computer , SubjectUserName , NewProcessName , CommandLine, Account
| extend HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -0,0 +1,27 @@
id: 60304ebf-ebdd-4869-a702-e0216d90ab46
name: Masquerading files
description: |
'Malware writers often use windows system process names for their malicious process names to make them blend
in with other legitimate commands that the Windows system executes.
An analyst can create a simple query looking for a process named svchost.exe.
It is recommended to filter out well-known security identifiers (SIDs) that are used to launch the legitimate svchost.exe process.
The query also filters out the legitimate locations from which svchost.exe is launched.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where NewProcessName endswith "\\svchost.exe"
| where SubjectUserSid !in ("S-1-5-18", "S-1-5-19", "S-1-5-20")
| where NewProcessName !contains ":\\Windows\\System32"
| where NewProcessName !contains ":\\Windows\\Syswow64"
| summarize minTimeGenerated=min(TimeGenerated), maxTimeGenerated=max(TimeGenerated), count() by Computer, SubjectUserName, NewProcessName, CommandLine, Account
| project minTimeGenerated , maxTimeGenerated , count_ , Computer , SubjectUserName , NewProcessName , CommandLine, Account
| extend timestamp = minTimeGenerated, HostCustomEntity = Computer, AccountCustomEntity = Account

Просмотреть файл

@ -1,28 +0,0 @@
// Name: New processes observed in last 24 hours
//
// Id: 513e3a11-e1bb-4cfc-8af9-451da0407e6b
//
// Description: These new processes could be benign new programs installed on hosts; however, especially in normally stable environments,
// these new processes could provide an indication of an unauthorized/malicious binary that has been installed and run.
// Reviewing the wider context of the logon sessions in which these binaries ran can provide a good starting point for identifying possible attacks.
//
// DataSource: #SecurityEvent
//
// Tactics: #Execution
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| where TimeGenerated >= ago(14d)
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName, FileName=tostring(split(NewProcessName, @'')[(-1)]), ProcessCommandLine = CommandLine, InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine='',InitiatingProcessParentFileName='';
processEvents};
ProcessCreationEvents
| where TimeGenerated < ago(1d)
| where TimeGenerated >= ago(14d)
| summarize HostCount=dcount(ComputerName) by tostring(FileName)
| join kind=rightanti (
ProcessCreationEvents
| where TimeGenerated >= ago(1d)
| summarize HostCount=dcount(ComputerName) by tostring(FileName)
) on FileName
| project HostCount, FileName

Просмотреть файл

@ -0,0 +1,32 @@
id: 513e3a11-e1bb-4cfc-8af9-451da0407e6b
name: New processes observed in last 24 hours
description: |
'These new processes could be benign new programs installed on hosts; however, especially in normally stable environments,
these new processes could provide an indication of an unauthorized/malicious binary that has been installed and run.
Reviewing the wider context of the logon sessions in which these binaries ran can provide a good starting point for identifying possible attacks.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let starttime = 14d;
let endtime = 1d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| where TimeGenerated >= ago(starttime)
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName, FileName=tostring(split(NewProcessName, @'')[(-1)]), ProcessCommandLine = CommandLine, InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine='',InitiatingProcessParentFileName='';
processEvents};
ProcessCreationEvents
| where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
| summarize HostCount=dcount(ComputerName) by tostring(FileName)
| join kind=rightanti (
ProcessCreationEvents
| where TimeGenerated >= ago(endtime)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), Computers = makeset(ComputerName) , HostCount=dcount(ComputerName) by tostring(FileName)
) on FileName
| project StartTimeUtc, Computers, HostCount, FileName

Просмотреть файл

@ -1,29 +0,0 @@
// Name: Summary of users created using uncommon & undocumented commandline switches
//
// Id: 5e76eaf9-79a7-448c-bace-28e5b53b8396
//
// Description: Summarizes uses of uncommon & undocumented commandline switches to create persistence
// User accounts may be created to achieve persistence on a machine.
// Read more here: https://attack.mitre.org/wiki/Technique/T1136
// Query for users being created using "net user" command
// "net user" commands are noisy, so needs to be joined with another signal -
// e.g. in this example we look for some undocumented variations (e.g. /ad instead of /add)
//
// DataSource: #SecurityEvent
//
// Tactics: #Persistence
//
SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName,
AccountDomain=SubjectDomainName, FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
FolderPath = "", InitiatingProcessFileName=ParentProcessName,
InitiatingProcessCommandLine="",InitiatingProcessParentFileName=""
| where FileName in~ ("net.exe", "net1.exe")
| parse kind=regex flags=iU ProcessCommandLine with * "user " CreatedUser " " * "/ad"
| where not(FileName =~ "net1.exe" and InitiatingProcessFileName =~ "net.exe" and replace("net", "net1", InitiatingProcessCommandLine) =~ ProcessCommandLine)
| extend CreatedOnLocalMachine=(ProcessCommandLine !contains "/do")
| where ProcessCommandLine contains "/add" or (CreatedOnLocalMachine == 0 and ProcessCommandLine !contains "/domain")
| summarize MachineCount=dcount(ComputerName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| extend AccountCustomEntity = CreatedUser

Просмотреть файл

@ -0,0 +1,37 @@
id: 5e76eaf9-79a7-448c-bace-28e5b53b8396
name: Summary of users created using uncommon/undocumented commandline switches
description: |
'Summarizes uses of uncommon & undocumented commandline switches to create persistence
User accounts may be created to achieve persistence on a machine.
Read more here: https://attack.mitre.org/wiki/Technique/T1136
Query for users being created using "net user" command
"net user" commands are noisy, so needs to be joined with another signal -
e.g. in this example we look for some undocumented variations (e.g. /ad instead of /add)'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1110
query: |
let timeframe = 1d;
SecurityEvent
| where TimeGenerated >= ago(timeframe)
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName,
AccountDomain=SubjectDomainName, FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
FolderPath = "", InitiatingProcessFileName=ParentProcessName,
InitiatingProcessCommandLine="",InitiatingProcessParentFileName=""
| where FileName in~ ("net.exe", "net1.exe")
| parse kind=regex flags=iU ProcessCommandLine with * "user " CreatedUser " " * "/ad"
| where not(FileName =~ "net1.exe" and InitiatingProcessFileName =~ "net.exe" and replace("net", "net1", InitiatingProcessCommandLine) =~ ProcessCommandLine)
| extend CreatedOnLocalMachine=(ProcessCommandLine !contains "/do")
| where ProcessCommandLine contains "/add" or (CreatedOnLocalMachine == 0 and ProcessCommandLine !contains "/domain")
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), MachineCount=dcount(ComputerName) by CreatedUser, CreatedOnLocalMachine, InitiatingProcessFileName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
| extend timestamp = StartTimeUtc, AccountCustomEntity = CreatedUser

Просмотреть файл

@ -1,29 +0,0 @@
// Name: powershell downloads
//
// Id: d83f40fc-bbcc-4020-8d45-ad2d82355cb2
//
// Description: Finds PowerShell execution events that could involve a download
//
// DataSource: #SecurityEvent
//
// Tactics: #InitialAccess, #Execution, #Persistence
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
ProcessCreationEvents
| where FileName in~ ("powershell.exe", "powershell_ise.exe")
| where ProcessCommandLine has "Net.WebClient"
or ProcessCommandLine has "DownloadFile"
or ProcessCommandLine has "Invoke-WebRequest"
or ProcessCommandLine has "Invoke-Shellcode"
or ProcessCommandLine contains "http:"
| project TimeGenerated, ComputerName, InitiatingProcessFileName, FileName, ProcessCommandLine
| top 100 by TimeGenerated
| extend TimestampCustomEntity = TimeGenerated
| extend HostCustomEntity = ComputerName

Просмотреть файл

@ -0,0 +1,34 @@
id: d83f40fc-bbcc-4020-8d45-ad2d82355cb2
name: PowerShell downloads
description: |
'Finds PowerShell execution events that could involve a download'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
- CommandAndControl
query: |
let timeframe = 1d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
ProcessCreationEvents
| where TimeGenerated >= ago(timeframe)
| where FileName in~ ("powershell.exe", "powershell_ise.exe")
| where ProcessCommandLine has "Net.WebClient"
or ProcessCommandLine has "DownloadFile"
or ProcessCommandLine has "Invoke-WebRequest"
or ProcessCommandLine has "Invoke-Shellcode"
or ProcessCommandLine contains "http:"
| project TimeGenerated, ComputerName, InitiatingProcessFileName, FileName, ProcessCommandLine
| top 100 by TimeGenerated
| extend timestamp = TimeGenerated, HostCustomEntity = ComputerName

Просмотреть файл

@ -1,35 +0,0 @@
// Name: new powershell scripts encoded on the commandline
//
// Id: 4e78daf1-8bba-4b5d-8a8b-c75fe9bbc2d9
//
// Description: Identify and decode new encoded powershell scripts this week versus previous fortnight
//
// DataSource: #SecurityEvent
//
// Tactics: #InitialAccess, #Execution, #Persistence
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName,AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
let encodedPSScripts =
ProcessCreationEvents | where TimeGenerated >= ago(14d)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine contains "-encodedCommand";
encodedPSScripts
| where TimeGenerated > ago(7d)
| summarize count() by ProcessCommandLine
| parse ProcessCommandLine with * "-EncodedCommand " encodedCommand
| project decodedCommand=base64_decodestring(substring(encodedCommand, 0,
strlen(encodedCommand) - (strlen(encodedCommand) %8))), encodedCommand
| join kind=anti (encodedPSScripts
| where TimeGenerated between(ago(21d)..ago(7d))
| summarize count() by ProcessCommandLine
| parse ProcessCommandLine with * "-EncodedCommand " encodedCommand
| project decodedCommand=base64_decodestring(substring(encodedCommand, 0,
strlen(encodedCommand) - (strlen(encodedCommand) %8))), encodedCommand
) on encodedCommand, decodedCommand

Просмотреть файл

@ -0,0 +1,43 @@
id: 4e78daf1-8bba-4b5d-8a8b-c75fe9bbc2d9
name: New PowerShell scripts encoded on the commandline
description: |
'Identify and decode new encoded powershell scripts this week versus previous 14 days'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
- CommandAndControl
query: |
let starttime = 21d;
let midtime = 14d;
let endtime = 7d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
| project TimeGenerated, ComputerName=Computer,AccountName=SubjectUserName,AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,InitiatingProcessCommandLine="",InitiatingProcessParentFileName="";
processEvents};
let encodedPSScripts =
ProcessCreationEvents
| where TimeGenerated >= ago(midtime)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine contains "-encodedCommand";
encodedPSScripts
| where TimeGenerated > ago(endtime)
| summarize count() by ProcessCommandLine
| parse ProcessCommandLine with * "-EncodedCommand " encodedCommand
| project decodedCommand=base64_decodestring(substring(encodedCommand, 0,
strlen(encodedCommand) - (strlen(encodedCommand) %8))), encodedCommand
| join kind=anti (encodedPSScripts
| where TimeGenerated between(ago(starttime)..ago(endtime))
| summarize count() by ProcessCommandLine
| parse ProcessCommandLine with * "-EncodedCommand " encodedCommand
| project decodedCommand=base64_decodestring(substring(encodedCommand, 0,
strlen(encodedCommand) - (strlen(encodedCommand) %8))), encodedCommand
) on encodedCommand, decodedCommand

Просмотреть файл

@ -1,50 +0,0 @@
// Name: Uncommon processes - bottom 5%
//
// Id: 2ff4b10c-7056-4898-83fd-774104189fd5
//
// Description:
// Shows the rarest processes seen running for the first time. (Performs best over longer time ranges - eg 3+ days rather than 24 hours!)
// These new processes could be benign new programs installed on hosts;
// However, especially in normally stable environments, these new processes could provide an indication of an unauthorized/malicious binary that has been installed and run.
// Reviewing the wider context of the logon sessions in which these binaries ran can provide a good starting point for identifying possible attacks.
//
// DataSource: #SecurityEvent
//
// Tactics: #InitialAccess, #Execution, #Persistence, #PrivilegeEscalation, #CredentialAccess, #Discovery, #LateralMovement, #Collection, #Exfiltration, #CommandAndControl
//
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
// filter out common randomly named files related to MSI installers and browsers
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{4}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{4}\.tmp")
| project TimeGenerated,
ComputerName=Computer,
AccountName=SubjectUserName,
AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]),
ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName,
InitiatingProcessCommandLine="",
InitiatingProcessParentFileName="";
processEvents;
};
let normalizedProcesses = ProcessCreationEvents
// normalize guids
| project TimeGenerated, FileName = replace("[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}", "<guid>", FileName)
// normalize digits away
| project TimeGenerated, FileName=replace(@'\d', 'n', FileName);
let freqs = normalizedProcesses
| summarize frequency=count() by FileName
| join kind= leftouter (
normalizedProcesses
| summarize Since=min(TimeGenerated), LastSeen=max(TimeGenerated) by FileName
) on FileName;
freqs
| where frequency <= toscalar( freqs | serialize | project frequency | summarize percentiles(frequency, 5))
| order by frequency asc
| project FileName, frequency, Since, LastSeen
// restrict results to unusual processes seen in last day
| where LastSeen >= ago(1d)

Просмотреть файл

@ -0,0 +1,49 @@
id: 2ff4b10c-7056-4898-83fd-774104189fd5
name: Uncommon processes - bottom 5%
description: |
'Shows the rarest processes seen running for the first time. (Performs best over longer time ranges - eg 3+ days rather than 24 hours!)
These new processes could be benign new programs installed on hosts;
However, especially in normally stable environments, these new processes could provide an indication of an unauthorized/malicious binary that has been installed and run.
Reviewing the wider context of the logon sessions in which these binaries ran can provide a good starting point for identifying possible attacks.'
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
query: |
let timeframe = 1d;
let ProcessCreationEvents=() {
let processEvents=SecurityEvent
| where EventID==4688
// filter out common randomly named files related to MSI installers and browsers
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"\\TRA[0-9A-Fa-f]{4}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{3}\.tmp")
| where not(NewProcessName matches regex @"Installer\\MSI[0-9A-Fa-f]{4}\.tmp")
| project TimeGenerated, ComputerName=Computer, AccountName=SubjectUserName, AccountDomain=SubjectDomainName,
FileName=tostring(split(NewProcessName, '\\')[-1]), ProcessCommandLine = CommandLine,
InitiatingProcessFileName=ParentProcessName, InitiatingProcessCommandLine="", InitiatingProcessParentFileName="";
processEvents;
};
let normalizedProcesses = ProcessCreationEvents
| where TimeGenerated >= ago(timeframe)
// normalize guids
| project TimeGenerated, FileName = replace("[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}", "<guid>", FileName)
// normalize digits away
| project TimeGenerated, FileName=replace(@'\d', 'n', FileName);
let freqs = normalizedProcesses
| summarize frequency=count() by FileName
| join kind= leftouter (
normalizedProcesses
| summarize Since=min(TimeGenerated), LastSeen=max(TimeGenerated) by FileName
) on FileName;
freqs
| where frequency <= toscalar( freqs | serialize | project frequency | summarize percentiles(frequency, 5))
| order by frequency asc
| project FileName, frequency, Since, LastSeen
// restrict results to unusual processes seen in last day
| where LastSeen >= ago(1d)
| extend timestamp = LastSeen