{"id":9025,"date":"2024-01-01T17:23:21","date_gmt":"2024-01-01T17:23:21","guid":{"rendered":"https:\/\/www.hexacorn.com\/blog\/?p=9025"},"modified":"2024-01-02T10:03:09","modified_gmt":"2024-01-02T10:03:09","slug":"bitmap-hunting-in-spl","status":"publish","type":"post","link":"https:\/\/www.hexacorn.com\/blog\/2024\/01\/01\/bitmap-hunting-in-spl\/","title":{"rendered":"Bitmap Hunting in SPL"},"content":{"rendered":"\n<p>One of the most annoying hunting exercises is detecting a sequence of failures followed by a success. Brute-force attacks, dictionary attacks, and finally password spray attacks have all this in common: lots of failures, sometimes followed by a success.<\/p>\n\n\n\n<p>The problem is stated clearly, but there is no easy solution.<\/p>\n\n\n\n<p>Why?<\/p>\n\n\n\n<p>Most of logs are stateless. Every log row describes an event, and every row is detached from the others. Combing them, combining them, clustering them and extracting some juice from them is a detection engineering art on its own&#8230;<\/p>\n\n\n\n<p>So, yes&#8230; it&#8217;s actually hard to detect these types of sequences and it&#8217;s usually very expensive f.ex. if you use Splunk it offers its <em>transaction<\/em> command for situations like this, but it&#8217;s a very very bad choice: it affects performance too much.<\/p>\n\n\n\n<p>There is a more elegant solution out there though&#8230; and I call it <em>bitmap hunting<\/em>.<\/p>\n\n\n\n<p>Instead of getting fixated on the sequence of the events that fit our narrative (set of failures followed by a success in the above example), we focus on building a bitmap of ALL states registered by the respective telemetry, the one that we can always group by the endpoint name, user, time\/time bucket, etc..<\/p>\n\n\n\n<p>Let&#8217;s look at an example:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">| makeresults | eval endpoint=\"sys01\" | eval username=\"test\" | eval status = 0  \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"foo\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"bar\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"abc\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"nimda\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"root\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"r00t\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys01\" | eval username=\"john.doe\" | eval status = 1]\n| append [| makeresults | eval endpoint=\"sys02\" | eval username=\"jane.doe\" | eval status = 0] \n| append [| makeresults | eval endpoint=\"sys02\" | eval username=\"jane.doe\" | eval status = 1]\n| table _time, endpoint, username, status<\/pre>\n\n\n\n<p>These SPL commands build a list of fake events for us, where 2 endpoints <em>sys01 <\/em>and <em>sys02 <\/em>register their logon events, where the <em>endpoint<\/em>, <em>username<\/em>, and <em>status<\/em> fields\/columns include all the info about the set of events occurring. In essence, this is how it looks like (ignore the <em>_time<\/em> as I didn&#8217;t want to clutter the commands above even more):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1.png\"><img decoding=\"async\" loading=\"lazy\" width=\"954\" height=\"351\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1.png\" alt=\"\" class=\"wp-image-9036\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1.png 954w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1-300x110.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1-768x283.png 768w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2024\/01\/spl_bitmap2-1-500x184.png 500w\" sizes=\"(max-width: 954px) 100vw, 954px\" \/><\/a><\/figure>\n\n\n\n<p>We can use the status of all events (success=1, failure=0) to build a bitmap of all of them by grouping them all together by the <em>endpoint<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">| stats list(status) as allstatuses, list(username) as allusernames by endpoint\n| eval allstatuses_bitmap = mvjoin(allstatuses,\"\")\n| table endpoint, allstatuses_bitmap, allusernames<\/pre>\n\n\n\n<p>The result gives us this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2023\/12\/spl_bitmap1.png\"><img decoding=\"async\" loading=\"lazy\" width=\"752\" height=\"339\" src=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2023\/12\/spl_bitmap1.png\" alt=\"\" class=\"wp-image-9026\" srcset=\"https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2023\/12\/spl_bitmap1.png 752w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2023\/12\/spl_bitmap1-300x135.png 300w, https:\/\/www.hexacorn.com\/blog\/wp-content\/uploads\/2023\/12\/spl_bitmap1-500x225.png 500w\" sizes=\"(max-width: 752px) 100vw, 752px\" \/><\/a><\/figure>\n\n\n\n<p>As you can clearly see, it&#8217;s pretty easy now to &#8216;guess&#8217; that <code>sys02<\/code> user <code>Jane.doe<\/code> is just a possible typo or otherwise minor issue that led the user account to be logged in after the first failure, while the <code>sys01<\/code> system experienced a barrage of logon attempts with different user names that eventually led to a successful logon. The <code>sys01<\/code> should be definitely investigated.<\/p>\n\n\n\n<p>Looking at the bitmap created by all the logon statuses we can quickly devise a logic to detect f.ex. successful password spray\/brute force\/dictionary attacks:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">| stats list(status) as allstatuses, list(username) as allusernames by endpoint<br>| eval allstatuses_bitmap = mvjoin(allstatuses,\"\")<br>| where like(allstatuses_bitmap, \"%0001\")<br>| table endpoint, allstatuses_bitmap, allusernames<\/pre>\n\n\n\n<p>In the example above, we detect at least 3 failed logons before a successful logon.<\/p>\n\n\n\n<p>And yes, it will hit False Positives too (legitimate logons will be amongst the malicious ones), but number of failed logons will be usually high enough and as such, a good indicator of badness, plus at least we now have something to triage&#8230;<\/p>\n\n\n\n<p>p.s. logon events are just one example, but you can convert any condition into a bitmap &#8212; as such, you can build more complex conditions too (f.ex. more than two specific events present in a sequence of events)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the most annoying hunting exercises is detecting a sequence of failures followed by a success. Brute-force attacks, dictionary attacks, and finally password spray attacks have all this in common: lots of failures, sometimes followed by a success. The &hellip; <a href=\"https:\/\/www.hexacorn.com\/blog\/2024\/01\/01\/bitmap-hunting-in-spl\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[86,79],"tags":[],"_links":{"self":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9025"}],"collection":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/comments?post=9025"}],"version-history":[{"count":6,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9025\/revisions"}],"predecessor-version":[{"id":9041,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/posts\/9025\/revisions\/9041"}],"wp:attachment":[{"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/media?parent=9025"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/categories?post=9025"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hexacorn.com\/blog\/wp-json\/wp\/v2\/tags?post=9025"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}