Deployment Architecture

Stdev against Null Time Bucket- How to create a rule alert on internal IP scanning?

ericl42
Path Finder

I've read quite a few forum posts about this but honestly didn't find a great solution for my use case (note that is probably because I didn't fully understand some of the items going on).

I'm trying to create a rule to alert on internal IP scanning. Below is my current logic.

index="firewall*"
| bucket _time span=15m@m
| stats dc(dest_ip) as num_dest_ips values(dest_ip) as dest_ips dc(dest_port) as num_dest_ports values(dest_port) as dest_ports count by src_ip, _time
| eventstats avg(num_dest_ips) as avg stdev(num_dest_ips) as stdev by src_ip | eval avg = round(avg,2) | eval stdev = round(stdev,2)
| lookup dnslookup clientip AS src_ip OUTPUT clienthost as src_dns
| eval temp=split(src_dns,".") | eval src_dns=mvindex(temp,0)
| eval src_system = coalesce(src_dns, src_ip)
| search NOT src_system=dns*
| eval lower_bound = avg-(stdev*.3) | eval lower_bound = round(lower_bound,2)
| eval upper_bound = avg+(stdev*.3) | eval upper_bound = round(upper_bound,2)
| eval isOutlier = if(dest_ips>upper_bound OR dest_ips<lower_bound,1,0)
| eval difference = upper_bound-lower_bound | eval different = round(different,2)
| table _time, src_ip, src_system, dest_ips, num_dest_ips, dest_ports, num_dest_ports, count, avg, lower_bound, upper_bound, difference, stdev, isOutlier

This works very well but as I mentioned, if 192.168.1.1 has no entries during a 15 min time bucket, then no entries will show up and therefore the math won't occur.

I've seen some posts where they append data and make the bucket command work but this is a fairly large index and I'd prefer not to double up on the search. I've seen others that recommend using timechart but my confusion on that front is how I would get all of my various stats commands to work with it.

Overall, I Just want something like the table below to occur and the 9:15 line be populated to zero if there are truly no entries. Unless there is a better way to do this entire setup, which I'm definitely open to.

_time src_ip num_dest_ips avg
9:00 192.168.1.1 30 15
9:15 192.168.1.1 0 15

Any hep would be greatly appreciated.

Below is what I would like to see.alt text

Tags (1)
0 Karma
1 Solution

ericl42
Path Finder

I finally found a solution using a combination of timechart which instantly adds 0 to empty bucks and then untabling it so I can then format it however I want.

| timechart span=5m limit=0 dc(dest_ip) as num_dest_ips by src_ip
| untable _time, src_ip, num_dest_ips
| eventstats avg(num_dest_ips) as avg stdev(num_dest_ips) as stdev by src_ip | eval avg = round(avg,2) | eval stdev = round(stdev,2)

View solution in original post

ericl42
Path Finder

I finally found a solution using a combination of timechart which instantly adds 0 to empty bucks and then untabling it so I can then format it however I want.

| timechart span=5m limit=0 dc(dest_ip) as num_dest_ips by src_ip
| untable _time, src_ip, num_dest_ips
| eventstats avg(num_dest_ips) as avg stdev(num_dest_ips) as stdev by src_ip | eval avg = round(avg,2) | eval stdev = round(stdev,2)

ErikA
Engager

Yes, this worked for me as well. Thanks!

0 Karma

to4kawa
Ultra Champion
 index="firewall*"
 | bucket _time span=15m@m
 | timechart limit=0 cont=f dc(dest_ip) as num_dest_ips values(dest_ip) as dest_ips dc(dest_port) as num_dest_ports values(dest_port) as dest_ports count by src_ip
 | eventstats avg(num_dest_ips) as avg stdev(num_dest_ips) as stdev by src_ip | eval avg = round(avg,2) | eval stdev = round(stdev,2)
 | lookup dnslookup clientip AS src_ip OUTPUT clienthost as src_dns
 | eval temp=split(src_dns,".") | eval src_dns=mvindex(temp,0)
 | eval src_system = coalesce(src_dns, src_ip)
 | search NOT src_system=dns*
 | eval lower_bound = avg-(stdev*.3) | eval lower_bound = round(lower_bound,2)
 | eval upper_bound = avg+(stdev*.3) | eval upper_bound = round(upper_bound,2)
 | eval isOutlier = if(dest_ips>upper_bound OR dest_ips<lower_bound,1,0)
 | eval difference = upper_bound-lower_bound | eval different = round(different,2)
 | table _time, src_ip, src_system, dest_ips, num_dest_ips, dest_ports, num_dest_ports, count, avg, lower_bound, upper_bound, difference, stdev, isOutlier

I changed it a little.

0 Karma

ericl42
Path Finder

Thanks for your response. I tried your query and the only thing I got was two rows that did a 9:00 and 9:15 bucket but no other data is in any of the other columns.

Attaching a screenshot to my initial question of what I would like to see and then I'll only alert where outLier>0.

0 Karma

to4kawa
Ultra Champion

Standard_deviation

the time has only one ip, so stdev is 0.
It can't be helped.

  | eval isOutlier = if(stdev !=0 AND (dest_ips>upper_bound OR dest_ips<lower_bound),1,0)

How about changing the conditions?

0 Karma

ericl42
Path Finder

I like the concept of potentially changing the logic if I can't make the other time show num_dest_ips as 0. However, I could see there being issues where if two hosts have the same amount of IPs during both buckets (e.g. 20), then the stdev is going to be 0 there too.

0 Karma
Got questions? Get answers!

Join the Splunk Community Slack to learn, troubleshoot, and make connections with fellow Splunk practitioners in real time!

Meet up IRL or virtually!

Join Splunk User Groups to connect and learn in-person by region or remotely by topic or industry.

Get Updates on the Splunk Community!

Index This | What travels the world but is also stuck in place?

April 2026 Edition  Hayyy Splunk Education Enthusiasts and the Eternally Curious!   We’re back with this ...

Discover New Use Cases: Unlock Greater Value from Your Existing Splunk Data

Realizing the full potential of your Splunk investment requires more than just understanding current usage; it ...

Continue Your Journey: Join Session 2 of the Data Management and Federation Bootcamp ...

As data volumes continue to grow and environments become more distributed, managing and optimizing data ...