In my previous post I introduced the concept of bitmap hunting. Today I will show another example that helps to find a sequence of more than 2 events.
Consider this artificially generated sequence of events:
| makeresults | eval _time=_time + 01 | eval evt="Run" | eval program="outlook.exe" | append [| makeresults | eval _time=_time + 02 | eval evt="Run" | eval program="firefox.exe"] | append [| makeresults | eval _time=_time + 03 | eval evt="Run" | eval program="firefox.exe"] | append [| makeresults | eval _time=_time + 04 | eval evt="File" | eval file="...\invoice.lnk" ] | append [| makeresults | eval _time=_time + 05 | eval evt="Run" | eval program="cscript.exe"] | append [| makeresults | eval _time=_time + 06 | eval evt="Run" | eval program="powershell.exe"] | append [| makeresults | eval _time=_time + 07 | eval evt="Run" | eval program="mshta.exe"] | append [| makeresults | eval _time=_time + 08 | eval evt="File" | eval file="...\bar.tmp" ] | append [| makeresults | eval _time=_time + 09 | eval evt="Run" | eval program="svchost.exe"] | append [| makeresults | eval _time=_time + 10 | eval evt="Run" | eval program="outlook.exe"] | append [| makeresults | eval _time=_time + 11 | eval evt="Run" | eval program="dllhost.exe"] | append [| makeresults | eval _time=_time + 12 | eval evt="File" | eval file="...\foo.tmp" ] | append [| makeresults | eval _time=_time + 13 | eval evt="Run" | eval program="cscript.exe"] | append [| makeresults | eval _time=_time + 14 | eval evt="Run" | eval program="powershell.exe"] | append [| makeresults | eval _time=_time + 15 | eval evt="Run" | eval program="mshta.exe"]
giving us this data:
It’s completely fictional, but you can see that we have two clusters of cscript, powershell, mshta program executions, with one following the file creation event where the file was a shortcut file using the file extension LNK (often used by malware).
Let’s say we want to find all the clusters of these 3 programs being executed AFTER the LNK file was created, and ignore the others.
We can first create a bitmap by adding the following code:
| eval b= case ( evt="Run" and program="cscript.exe", "c", evt="Run" and program="powershell.exe", "p", evt="Run" and program="mshta.exe", "m", evt="File" and like (file, "%.lnk"), "l", 1=1," " ) | eventstats list(b) as allb | eval allb_bitmap = mvjoin(allb,"") | table _time, allb_bitmap, b, evt, file, program
giving us this:
We can clearly see 2 interesting clusters, but only one fitting our criteria.
We can obviously exclude the rows where the b is empty, but we still need to split these lcpm and cpm clusters into separate buckets.
And the bucket is the word!
Modifying our earlier code a bit:
| eval b= case ( evt="Run" and program="cscript.exe", "c", evt="Run" and program="powershell.exe", "p", evt="Run" and program="mshta.exe", "m", evt="File" and like (file, "%.lnk"), "l", 1=1," " ) | bucket _time span=10s | eventstats list(b) as allb by _time | eval allb_bitmap = mvjoin(allb,"") | where b!=" " and like(allb_bitmap, "%lcpm%") | table _time, allb_bitmap, evt, file, program
we get this:
We can now start the triage!
Of course, there is a manual effort to this exercise and it may not be always possible to fully automate it, but I hope you can see the potential of this technique.
And see how cheap that is! There are no summary indexes, nested queries, complex statistics involved – it’s just a simple exercise of putting interesting events on a one-dimensional map, and then breaking them down into manageable clusters.