tldr: I have an event of interest, and I want to find the next qualified event after it, but without specifically using a time qualifier since it is oftentimes unpredictable. Think "grep -A1" but the next event has an association with the first as seen in the longer version.
Longer version:
I have logs where a malicious download event happened between two IPs. Immediately afterwards, the source IP reaches out to another nefarious IP. I'm looking for the best way of easily searching for this, but in a way that isn't dependent upon specific time range.
Initial infection vector=
"id.orig_h"="192.168.1.1" "id.resp_h"="10.1.1.1" proto=tcp
To find the following log I'm currently removing the id.resp_h from the search but then ctrl+f for 10.1.1.1 to manually find the log that occurs immediately after (10.2.2.2)
It sounds simple and I'm sure it probably is. I've tried creating an "earliest" time based on the original download event, but it is unpredictable and nonfunctional in my case due to lack of milliseconds in _time (and in production it could have duplicates). Had this worked, I could have used it in a subsearch and followed it up with a tail -2. I've also tried using transaction, but that doesn't allow me to be specific about the second qualifier to the point where it needs to be (unless i've done it incorrectly).
"id.orig_h"="192.168.1.1" id.resp_h=* proto=tcp
[ search "id.orig_h"="192.168.1.1" "id.resp_h"="10.1.1.1" proto=tcp
| head 1
| eval timestamp_epoch = strptime(ts, "%Y-%m-%dT%H:%M:%S.%6N")
| eval earliest=timestamp_epoch
| table earliest
| format]
| tail 1
To explain; the subsearch is looking for the initial download. I take the very last event related to the ip pair. It then takes the "ts" field, which is a fine grained timestamp in the logs and it converts it to a timestamp that can be assigned to "earliest". That is then used in the main search that looks for the first thing AFTER that event in which the event must have a id.resp_h value.
This seems to satisfy what I needed, although I'd be curious to know if there is an easier or more streamlined version. I was thinking something similar to either transaction (but with next events instead of time windows) or using something similar to localize|map.
"id.orig_h"="192.168.1.1" id.resp_h=* proto=tcp
[ search "id.orig_h"="192.168.1.1" "id.resp_h"="10.1.1.1" proto=tcp
| head 1
| eval timestamp_epoch = strptime(ts, "%Y-%m-%dT%H:%M:%S.%6N")
| eval earliest=timestamp_epoch
| table earliest
| format]
| tail 1
To explain; the subsearch is looking for the initial download. I take the very last event related to the ip pair. It then takes the "ts" field, which is a fine grained timestamp in the logs and it converts it to a timestamp that can be assigned to "earliest". That is then used in the main search that looks for the first thing AFTER that event in which the event must have a id.resp_h value.
This seems to satisfy what I needed, although I'd be curious to know if there is an easier or more streamlined version. I was thinking something similar to either transaction (but with next events instead of time windows) or using something similar to localize|map.