Friday 7 October 2016

Mickeys SVIP - Tip 3 - Monitoring Insider Threat and Potential Credential Misuse with Levenshtein Algorithm

Hi All,

Leading up to .conf I was a little quiet on the blogging front so thought I would come out and do another one now I have a little bit of spare time.  So lets get straight into this.  

What is the problem we are trying to solve?
My favourite question to ask :).  If we are not solving a problem then there is no point in wasting our time.  

So what is the problem? Credential theft and misuse.  The best way to explain is in a scenario.

You have an administrator who has access to 3 accounts.  Lets say his name is Joe Bloggs.  His accounts are

jbloggs - Standard User Account
a_jbloggs - Administrator Account
svc_backup - A backup account that is able to make changes to any server and has super privileges.

So again going back to the problem.  Could Joe Bloggs have used his standard admin account to do the work? Yes he probably could have.  So why does he need to use the svc_backup account? Well who knows, we will let you find that out in your own network.   But knowing, lets you ask that question.  In security its about being able to ask those questions which helps us improve and understand.

How do we solve the problem?
TLDR version down the bottom with the search.
We have two awesome functions at our disposal.  
Event Code 4648 - A logon was attempted using explicit credentials
Levenshtein distance - A way to measure the distance in characters between two strings using substitution,deletion or addition of a string.

Lets go into Event Code 4648 this is straight from the windows event log record
This event is generated when a process attempts to log on an account by explicitly specifying that account’s credentials.  This most commonly occurs in batch-type configurations such as scheduled tasks, or when using the RUNAS command.

Levenshtein Distance
Some details here - https://en.wikipedia.org/wiki/Levenshtein_distance
and here - http://www.levenshtein.net

TLDR version of the above is that if you have 2 strings lets say jbloggs and a_jbloggs there is a 2 character distance between the two (addition of 2 letters).  Between jbloggs and svc_backup is 9.
Why 9 when there are 10 characters well because the b in backup and the b in bloggs line up. Here is a pic to demonstrate.


Now lets look at an example log.



Looking at the above you can see it clearly that they are two different people.  So what do we need to get this in our data sources.  Windows event log and URL Toolbox (link below)

URL Toolbox - https://splunkbase.splunk.com/app/2734/

URL toolbox not only has levenshtein but also has other cool string analysis tools such as entropy and can help you parse URLs.

TLDR Splunk Search
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4648 NOT Account_Name=*$ 
| eval srcAccount=mvindex(Security_ID,0) 
| eval destAccount = mvindex(Account_Domain,1) +"\\" + mvindex(Account_Name,1) 
| `ut_levenshtein(srcAccount,destAccount)` 
| table _time,srcAccount,destAccount,ut_levenshtein

1st Line - Searches for Windows event code 4648 and excludes local system
2nd Line - Gets the first item in the array of Security_ID field and creates a new field srcAccount
3rd Line - Joins the 2nd item in the array of Account_Domain field and the 2nd item in the array of Security_ID and creates a new field called destAccount
4th Line - Calculates the levenshtein distance between  srcAccount and destAccount.
5th Line - Nicely formats.

Filtering out names that are the same / users that are using their own admin account.
The above search will need a where clause depending on how you name your admin accounts.  i.e if you name your admin accounts like 'a_' then create the clause as 'where ut_levenshtein>2' so that a_jbloggs is not picked up.

So looking like this.
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4648 NOT Account_Name=*$ 
| eval srcAccount=mvindex(Security_ID,0) 
| eval destAccount = mvindex(Account_Domain,1) +"\\" + mvindex(Account_Name,1) 
| `ut_levenshtein(srcAccount,destAccount)` 
| where ut_levenshtein>2
| table _time,srcAccount,destAccount,ut_levenshtein

If you want to only compare Account Names and not the whole domain then do the following (this can be helpful when you have multiple Active Directory domains).

index=wineventlog sourcetype="WinEventLog:Security" EventCode=4648 NOT Account_Name=*$ 
| eval srcAccount=mvindex(Account_Name,0) 
| eval destAccount = mvindex(Account_Name,1)  
| `ut_levenshtein(srcAccount,destAccount)` 
| where ut_levenshtein>2
| table _time,srcAccount,destAccount,ut_levenshtein


Pic of this search