Dont know if this will trigger or not, im a little bit lost.
Thanks!
Best answer by AbdElHafez
@MikelSA Please look at and try this version, this is as close as I managed to get.
Currently there is no IF in Yara-L events section, so I moved HasPrivilegedPort to outcome.
The $PrivilegedPortCount will get the count of ALL privileged ports in the repeated port array, so if the array is [80,80,2000] ; KQL will yield HasPrivilegedPort =1 for port 80 , but Yara-L has no current way to do more than 1 aggregated operator in outcome, so HasPrivilegedPort will be = 2 (80 x 2) which is the main difference in the logic.
$SourceHostName != "" cast.as_string($DestinationPort) != "" //isnotempty(DestinationPort) $DestinationIP = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/ //isnotempty(DestinationIP) AND where (ipv4_is_private(DestinationIP)) merged into 1 condition
match: $SourceIP, $SourceHostName, $DestinationIP over 1h //summarize....by SourceIP, SourceHostName, DestinationIP, bin(TimeGenerated, 1h) We used $event_timestamp which is the default time field for UDM //$SourceIP over 12h //For Testing outcome: $arr = array($DestinationPort) //Optional,used just to display the intermediate repeated variable $arr_distinct = array_distinct($DestinationPort) //Optional,used just to display the intermediate non-repeated variable $Port_count = count($DestinationPort) //Optional, not used $TotalPortCount = count_distinct($DestinationPort) //TotalPortCount = dcount(DestinationPort)
The rule will trigger but I have some suggestions ;
You should use a data table in the exclusions instead of the regex.
It is possible to split the rule into host-based and port-based scanning, one for the unique_ports only, and another with unique_hosts only. Port scanning will usually target multiple ports on the same host , OR same port with multiple targets, so 250 different hosts+250 different ports within 30 minutes will trigger for very aggressive scans in my opinion, OR you could use the summation unique_hosts + unique_ports = 250 instead.
Also 250 is a high threshold number, I would suggest you would use lesser thresholds just for testing, or run it in a dashboard to see your environment trend first.
Your code is similar to SQL ; Select IP, count (distinct(port)), count(distinct(host)) Group by IP Having count(distinct(port))>250 and count(distinct(port))>250
The rule will trigger but I have some suggestions ;
You should use a data table in the exclusions instead of the regex.
It is possible to split the rule into host-based and port-based scanning, one for the unique_ports only, and another with unique_hosts only. Port scanning will usually target multiple ports on the same host , OR same port with multiple targets, so 250 different hosts+250 different ports within 30 minutes will trigger for very aggressive scans in my opinion, OR you could use the summation unique_hosts + unique_ports = 250 instead.
Also 250 is a high threshold number, I would suggest you would use lesser thresholds just for testing, or run it in a dashboard to see your environment trend first.
Your code is similar to SQL ; Select IP, count (distinct(port)), count(distinct(host)) Group by IP Having count(distinct(port))>250 and count(distinct(port))>250
Thank you so much for the answer, I will take the suggestions. Thanks!!!
Give me two hours and I will get sometime to look at the exact context of the KQL rule to translate it properly, from what I can pick up right now ;
1. The is_private() function could be done using regex but I will need to write a pattern for the RFC1918 IPs. 2. The “by” operator will be translated to match: sourceIP, sourceHostName while the destination port should be either added to the match or be an array_distinct() in the outcome depending on the rule context I need to examine. 3. “where” operator conditions are the conditions in the Yara-L “condition” section ; $e and count_port >=90 ,...etc but the count variables will need to be defined in the outcome.
@MikelSA Please look at and try this version, this is as close as I managed to get.
Currently there is no IF in Yara-L events section, so I moved HasPrivilegedPort to outcome.
The $PrivilegedPortCount will get the count of ALL privileged ports in the repeated port array, so if the array is [80,80,2000] ; KQL will yield HasPrivilegedPort =1 for port 80 , but Yara-L has no current way to do more than 1 aggregated operator in outcome, so HasPrivilegedPort will be = 2 (80 x 2) which is the main difference in the logic.
$SourceHostName != "" cast.as_string($DestinationPort) != "" //isnotempty(DestinationPort) $DestinationIP = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/ //isnotempty(DestinationIP) AND where (ipv4_is_private(DestinationIP)) merged into 1 condition
match: $SourceIP, $SourceHostName, $DestinationIP over 1h //summarize....by SourceIP, SourceHostName, DestinationIP, bin(TimeGenerated, 1h) We used $event_timestamp which is the default time field for UDM //$SourceIP over 12h //For Testing outcome: $arr = array($DestinationPort) //Optional,used just to display the intermediate repeated variable $arr_distinct = array_distinct($DestinationPort) //Optional,used just to display the intermediate non-repeated variable $Port_count = count($DestinationPort) //Optional, not used $TotalPortCount = count_distinct($DestinationPort) //TotalPortCount = dcount(DestinationPort)
@MikelSA Please look at and try this version, this is as close as I managed to get.
Currently there is no IF in Yara-L events section, so I moved HasPrivilegedPort to outcome.
The $PrivilegedPortCount will get the count of ALL privileged ports in the repeated port array, so if the array is [80,80,2000] ; KQL will yield HasPrivilegedPort =1 for port 80 , but Yara-L has no current way to do more than 1 aggregated operator in outcome, so HasPrivilegedPort will be = 2 (80 x 2) which is the main difference in the logic.
$SourceHostName != "" cast.as_string($DestinationPort) != "" //isnotempty(DestinationPort) $DestinationIP = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/ //isnotempty(DestinationIP) AND where (ipv4_is_private(DestinationIP)) merged into 1 condition
match: $SourceIP, $SourceHostName, $DestinationIP over 1h //summarize....by SourceIP, SourceHostName, DestinationIP, bin(TimeGenerated, 1h) We used $event_timestamp which is the default time field for UDM //$SourceIP over 12h //For Testing outcome: $arr = array($DestinationPort) //Optional,used just to display the intermediate repeated variable $arr_distinct = array_distinct($DestinationPort) //Optional,used just to display the intermediate non-repeated variable $Port_count = count($DestinationPort) //Optional, not used $TotalPortCount = count_distinct($DestinationPort) //TotalPortCount = dcount(DestinationPort)