This commit is contained in:
Pete Bryan 2021-08-06 13:39:23 -07:00
Родитель b1faf7dc83
Коммит 8c900dafa2
11 изменённых файлов: 73 добавлений и 74 удалений

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

@ -1,7 +1,7 @@
id: d0ae35df-0eaf-491f-b23e-8190e4f3ffe9
name: Rare process running on a Linux host
description: |
'Looks for rare processes that are running on Linux hosts. Looks for process seen less than 14 times in last 7 days,
'Looks for rare processes that are running on Linux hosts. Looks for process seen less than 14 times in last 7 days,
or observed rate is less than 1% of of the average for the environment and fewer than 100.'
requiredDataConnectors:
- connectorId: Syslog
@ -15,26 +15,27 @@ relevantTechniques:
- T1053
- T1037
query: |
let starttime = 7d;
let endtime = 1m;
let lookback = 30d;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = starttime - 14d;
let count_threshold = 100;
let perc_threshold = 0.01;
let host_threshold = 14;
let basic=materialize(
Syslog
| where TimeGenerated >= ago(lookback)
| where TimeGenerated >= lookback
| summarize FullCount = count()
, Count= countif(TimeGenerated between (ago(starttime) .. ago(endtime)))
, Count= countif(TimeGenerated between (starttime .. endtime))
, min_TimeGenerated=min(TimeGenerated)
, max_TimeGenerated=max(TimeGenerated)
, max_TimeGenerated=max(TimeGenerated)
by Computer, ProcessName
| where Count > 0 and Count < count_threshold);
let basic_avg = basic
| summarize Avg = avg(FullCount) by ProcessName;
basic | project-away FullCount
| join kind=inner
basic_avg
| join kind=inner
basic_avg
on ProcessName | project-away ProcessName1
| where Count < host_threshold or (Count <= Avg*perc_threshold and Count < count_threshold)
| where Count < host_threshold or (Count <= Avg*perc_threshold and Count < count_threshold)
| extend HostCustomEntity=Computer

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

@ -1,8 +1,8 @@
id: 959fe0f0-7ac0-467c-944f-5b8c6fdc9e72
name: Disabled accounts using Squid proxy
description: |
'Look for accounts that have a been recorded as disabled by AD in the previous week but are still using the proxy during
the current week. This query presumes the default squid log format is being used. http://www.squid-cache.org/Doc/config/access_log/'
'Look for accounts that have a been recorded as disabled by AD in the previous time period but are still using the proxy during
the current time period. This query presumes the default squid log format is being used. http://www.squid-cache.org/Doc/config/access_log/'
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
@ -13,17 +13,18 @@ relevantTechniques:
- T1110
query: |
let starttime = 14d;
let endtime = 7d;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = startime - 14d;
let disabledAccounts = (){
SigninLogs
| where TimeGenerated between(ago(starttime) .. ago(endtime))
| where TimeGenerated between(lookback..starttime))
| where ResultType == 50057
| where ResultDescription =~ "User account is disabled. The account has been disabled by an administrator."
};
let proxyEvents = (){
Syslog
| where TimeGenerated > ago(endtime)
| where TimeGenerated between(starttime..endtime)
| where ProcessName contains "squid"
| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),

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

@ -2,11 +2,11 @@ id: e472c490-4792-4f12-8b6b-6ab3e0404d35
name: Squid data volume timeseries anomalies
description: |
'Malware infections or data exfiltration activity often leads to anomalies in network data volume
this hunting query looks for anomalies in the volume of bytes traversing a squid proxy. Anomalies require further
this hunting query looks for anomalies in the volume of bytes traversing a squid proxy. Anomalies require further
investigation to determine cause. This query presumes the default squid log format is being used.'
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
dataTypes:
- Syslog
tactics:
- CommandAndControl
@ -16,16 +16,12 @@ relevantTechniques:
- T1030
query: |
let starttime = 14d;
let endtime = 1d;
let timeframe = 1h;
let TimeSeriesData =
let TimeSeriesData =
Syslog
| where TimeGenerated between (startofday(ago(starttime))..startofday(ago(endtime)))
| where ProcessName contains "squid"
| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
| extend URL = extract("(([A-Z]+ [a-z]{4,5}:\\/\\/)|[A-Z]+ )([^ :]*)",3,SyslogMessage),
SourceIP = extract("([0-9]+ )(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3}))",2,SyslogMessage),
Status = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))",1,SyslogMessage),
HTTP_Status_Code = extract("(TCP_(([A-Z]+)(_[A-Z]+)*)|UDP_(([A-Z]+)(_[A-Z]+)*))/([0-9]{3})",8,SyslogMessage),
User = extract("(CONNECT |GET )([^ ]* )([^ ]+)",3,SyslogMessage),
RemotePort = extract("(CONNECT |GET )([^ ]*)(:)([0-9]*)",4,SyslogMessage),
@ -39,4 +35,3 @@ query: |
| extend (anomalies, score, baseline) = series_decompose_anomalies(TotalBytesSent,3, -1, 'linefit')
| extend timestamp = TimeGenerated
| render timechart with (title="Squid Time Series anomalies")

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

@ -17,14 +17,15 @@ tactics:
- Impact
query: |
let dt_lookBack = 1h;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
| where Active == true
| where isnotempty(FileName)
| join (
OfficeActivity| where TimeGenerated >= ago(dt_lookBack)
OfficeActivity| where TimeGenerated between(starttime..endtime)
| where isnotempty(SourceFileName)
| extend OfficeActivity_TimeGenerated = TimeGenerated
)

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

@ -17,14 +17,15 @@ tactics:
- Impact
query: |
let dt_lookBack = 1h;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
| where Active == true
| where isnotempty(FileName)
| join (
SecurityEvent | where TimeGenerated >= ago(dt_lookBack)
SecurityEvent | where TimeGenerated between(starttime..endtime)
| where EventID in ("4688","8002","4648","4673")
| where isnotempty(Process)
| extend SecurityEvent_TimeGenerated = TimeGenerated, Event = EventID

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

@ -17,7 +17,8 @@ tactics:
- Impact
query: |
let dt_lookBack = 1h;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
@ -25,7 +26,7 @@ query: |
| where isnotempty(FileName)
| extend TI_ProcessEntity = tostring(split(FileName, ".")[-2])
| join (
Syslog | where TimeGenerated >= ago(dt_lookBack)
Syslog | where TimeGenerated between(starttime..endtime)
| where isnotempty(ProcessName)
| extend Syslog_TimeGenerated = TimeGenerated
)

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

@ -17,7 +17,8 @@ tactics:
- Impact
query: |
let dt_lookBack = 1h;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
@ -25,7 +26,7 @@ query: |
| where isnotempty(FileName)
| extend TI_ProcessEntity = tostring(split(FileName, ".")[-2])
| join (
VMConnection | where TimeGenerated >= ago(dt_lookBack)
VMConnection | where TimeGenerated between(starttime..endtime)
| where isnotempty(ProcessName)
| extend VMConnection_TimeGenerated = TimeGenerated
)

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

@ -17,14 +17,15 @@ tactics:
- Impact
query: |
let dt_lookBack = 1h;
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
| where Active == true
| where isnotempty(FileName)
| join (
WireData | where TimeGenerated >= ago(dt_lookBack)
WireData | where TimeGenerated between(starttime..endtime)
| where isnotempty(ProcessName)
| extend Process =reverse(substring(reverse(ProcessName), 0, indexof(reverse(ProcessName), "\\")))
| extend WireData_TimeGenerated = TimeGenerated

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

@ -2,8 +2,8 @@ id: e0c947c3-fe83-46ff-bbda-a43224a785fd
name: Web Shell Activity
description: |
'Web shells are scripts that, when uploaded to a web server, can be used to provide a backdoor to a compromised network.
Attackers can use this entry point to leave malicious implants, such as obtaining unauthorized access, escalating privilege, and further compromising the environment.
Attackers can use this entry point to leave malicious implants, such as obtaining unauthorized access, escalating privilege, and further compromising the environment.
This query hunts for web shells by analysing the distribution of commonly-used web shell scripts against regular scripts for those public client IPs which have not observed any W3CIIS activity in a fixed lookback period.
The results obtained summarise the public client IPs, user agents, and the distribution of the above scripts between the start and end time.'
requiredDataConnectors:
@ -15,25 +15,25 @@ tactics:
- InitialAccess
relevantTechniques:
- T1100
query: |
query: |
let end_time = now();
let start_time = end_time - timespan(1d);
let lookback_time_from_start_time = timespan(3d);
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = starttime - (3d);
let script_extensions = dynamic([".asp", ".aspx", ".armx", ".asax", ".ashz", ".asmx", ".axd", ".cshtml", ".php", ".phps", ".php3", ".php4", ".php5", ".php7", ".jsp", ".jspx", ".cfm", ".cfml", ".phtml"]);
let ignore_uristems = dynamic(["/ews/exchange.asmx"]);
let lookback_period = (
W3CIISLog
| where TimeGenerated between ((start_time - lookback_time_from_start_time).. start_time)
W3CIISLog
| where TimeGenerated between (lookback .. starttime)
| where not(ipv4_is_private(cIP)) and cIP != "127.0.0.1"
| summarize count() by cIP, csUserAgent
| project cIP, csUserAgent
);
let potential_webshell_activity = (W3CIISLog
| where TimeGenerated between (start_time .. end_time)
| where TimeGenerated between (starttime .. endtime)
| extend csUriStem = tolower(csUriStem)
| where csUriStem matches regex "\\.[a-zA-Z][a-zA-Z0-9]+$"
| where csUriStem !in~ (ignore_uristems) // Remove noisy uri stems in the final results by editing the ignore_uristems variable
| where csUriStem !in~ (ignore_uristems) // Remove noisy uri stems in the final results by editing the ignore_uristems variable
| extend suffix = strcat(".", split(split(csUriStem, "/")[-1], ".")[-1])
| extend is_script = iff(suffix in (script_extensions), 1, 0)
| where not(ipv4_is_private(cIP)) and cIP != "127.0.0.1"

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

@ -2,10 +2,10 @@ id: 33aa0e01-87e2-43ea-87f9-2f7e3ff1d532
name: Detect beacon like pattern based on repetitive time intervals in Wire Data Traffic
description: |
'This query will identify beaconing patterns from Wire Data logs based on timedelta patterns. The query leverages various KQL functions
to calculate time delta and then compare it with total events observed in a day to find percentage of beaconing.
to calculate time delta and then compare it with total events observed in a day to find percentage of beaconing.
Results of such beaconing patterns to untrusted public networks can be a good starting point for investigation.
References: Blog about creating dataset to identify network beaconing via repetitive time intervals seen against total traffic
between same source-destination pair.
References: Blog about creating dataset to identify network beaconing via repetitive time intervals seen against total traffic
between same source-destination pair.
http://www.austintaylor.io/detect/beaconing/intrusion/detection/system/command/control/flare/elastic/stack/2017/06/10/detect-beaconing-with-flare-elasticsearch-and-intrusion-detection-systems/'
requiredDataConnectors:
- connectorId: AzureMonitor(WireData)
@ -18,30 +18,28 @@ relevantTechniques:
- T1065
query: |
let starttime = 7d;
let endtime = 1d;
let TimeDeltaThreshold = 10;
let TotalEventsThreshold = 15;
let PercentBeaconThreshold = 95;
let PrivateIPregex = @'^127\.|^10\.|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-1]\.|^192\.168\.';
let lookback = 1d;
let TimeDeltaThreshold = 10;
let TotalEventsThreshold = 15;
let PercentBeaconThreshold = 95;
let PrivateIPregex = @'^127\.|^10\.|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-1]\.|^192\.168\.';
WireData
| where TimeGenerated between (ago(starttime)..ago(endtime))
| extend RemoteIPType = iff(RemoteIP matches regex PrivateIPregex,"private" ,"public" )
| where RemoteIPType =="public"
| project TimeGenerated , LocalIP , LocalPortNumber , RemoteIP, RemotePortNumber, ReceivedBytes, SentBytes
| sort by LocalIP asc,TimeGenerated asc, RemoteIP asc, RemotePortNumber asc
| where TimeGenerated > lookback
| extend RemoteIPType = iff(RemoteIP matches regex PrivateIPregex,"private" ,"public" )
| where RemoteIPType =="public"
| project TimeGenerated , LocalIP , LocalPortNumber , RemoteIP, RemotePortNumber, ReceivedBytes, SentBytes
| sort by LocalIP asc,TimeGenerated asc, RemoteIP asc, RemotePortNumber asc
| serialize
| extend nextTimeGenerated = next(TimeGenerated, 1), nextLocalIP = next(LocalIP, 1)
| extend TimeDeltainSeconds = datetime_diff('second',nextTimeGenerated,TimeGenerated)
| where LocalIP == nextLocalIP
//Whitelisting criteria/ threshold criteria
| where TimeDeltainSeconds > TimeDeltaThreshold
| extend nextTimeGenerated = next(TimeGenerated, 1), nextLocalIP = next(LocalIP, 1)
| extend TimeDeltainSeconds = datetime_diff('second',nextTimeGenerated,TimeGenerated)
| where LocalIP == nextLocalIP
//Whitelisting criteria/ threshold criteria
| where TimeDeltainSeconds > TimeDeltaThreshold
| where RemotePortNumber != "0"
| project TimeGenerated, TimeDeltainSeconds, LocalIP, LocalPortNumber,RemoteIP,RemotePortNumber, ReceivedBytes, SentBytes
| summarize count(), sum(ReceivedBytes), sum(SentBytes), make_list(TimeDeltainSeconds) by TimeDeltainSeconds, bin(TimeGenerated, 1h), LocalIP, RemoteIP, RemotePortNumber
| summarize (MostFrequentTimeDeltaCount, MostFrequentTimeDeltainSeconds)=arg_max(count_, TimeDeltainSeconds), TotalEvents=sum(count_), TotalSentBytes=sum(sum_SentBytes),TotalReceivedBytes=sum(sum_ReceivedBytes) by bin(TimeGenerated, 1h), LocalIP, RemoteIP, RemotePortNumber
| where TotalEvents > TotalEventsThreshold
| extend BeaconPercent = MostFrequentTimeDeltaCount/toreal(TotalEvents) * 100
| project TimeGenerated, TimeDeltainSeconds, LocalIP, LocalPortNumber,RemoteIP,RemotePortNumber, ReceivedBytes, SentBytes
| summarize count(), sum(ReceivedBytes), sum(SentBytes), make_list(TimeDeltainSeconds) by TimeDeltainSeconds, bin(TimeGenerated, 1h), LocalIP, RemoteIP, RemotePortNumber
| summarize (MostFrequentTimeDeltaCount, MostFrequentTimeDeltainSeconds)=arg_max(count_, TimeDeltainSeconds), TotalEvents=sum(count_), TotalSentBytes=sum(sum_SentBytes),TotalReceivedBytes=sum(sum_ReceivedBytes) by bin(TimeGenerated, 1h), LocalIP, RemoteIP, RemotePortNumber
| where TotalEvents > TotalEventsThreshold
| extend BeaconPercent = MostFrequentTimeDeltaCount/toreal(TotalEvents) * 100
| where BeaconPercent > PercentBeaconThreshold
| extend timestamp = TimeGenerated, IPCustomEntity = RemoteIP

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

@ -9,14 +9,13 @@ relevantTechniques:
- T1078
query: |
let hunt_time = 1d;
let previous_tz = (
ZoomLogs
| where Event =~ "meeting.participant_joined"
| extend TimeZone = columnifexists('payload_object_timezone_s', "")
| summarize by TimeZone
);
ZoomLogs
ZoomLogs
| where Event =~ "meeting.participant_joined"
| extend TimeZone = columnifexists('payload_object_timezone_s', "")
| where isnotempty(TimeZone) and TimeZone in (previous_tz)