I have an alert designed to examine Windows event logs (event 560 or 4663) for file access by unauthorized users. The search works by slowly checking the file location, files that aren't examined, and then finally the user's group membership compared to that allowed for the file. The final comparison is where I have problems, as I need to look to see that at least one of the user's groups is in the list of groups allowed access to the file. Because of that, and how it is structured as a reductionary search, I can't explode using mvexpand, because anything remaining would be indicative of an alert, so there would have to be 100% match as opposed to the !=0% match I am looking for. That is why solutions such as those found at https://answers.splunk.com/answers/11287/comparing-multivalue-fields.html won't work. Right now the search below will always alert if a sensitive file is accessed, but it is not appropriately skipping users who have permission.
Search:
index=wineventlog sourcetype=WinEventLog:Security EventCode=560 OR EventCode=4663 OR EventCode=5145 NOT user=$ folder_name!=null
| lookup lookup_wild_folder folder_lookup AS folder_name, server AS host OUTPUT group_lookup user_lookup file_exceptions
| eval user=if(isnull(Object_Name),null,user)
| eval user=if(match(Object_Name,folder_lookup),null,user)
| eval user=if(match(user_lookup,user),null,user)
| eval user=if(match(Object_Name,file_exceptions),null,user)
| table user host folder_lookup folder_name Object_Name Message group_lookup
| ldapfilter domain=default search="(sAMAccountName=$user$)" attrs="memberOf"
| rex field=memberOf "^CN=(?[^,]+)"
| eval allowed_groups=split(group_lookup,"::")
| **eval user=if(match(group_name,allowed_groups),null,user)*
| search user!=null
| table user host Object_Name allowed_groups group_name
lookup_wild_folder CSV structure
server,folder_lookup,folder_out,group_lookup,user_lookup,file_exceptions
SERVERNAME,D:\Docstore*,D:\Docstore,GROUP1::Group2,::USER1::USER2::USER3::,
Try something like this
Your current search till | eval allowed_groups=split(group_lookup,"::")
| eval temp=mvdedup(mvappend(group_name,allowed_groups))
| eval user=if(mvcount(temp)=(mvcount(group_name) + mvcount(allowed_groups)),user,null)
| search user!=null
| table user host Object_Name allowed_groups group_name
Explanation:
a) Created a field temp in which I merged group_name and allowed_groups mv fields and removed duplicates from merged field.
b) If there is no common group between group_name (user's groups) and allowed_groups, then no of values in temp will exactly as sum of no of values in each of group_name and allowed_groups field. So, unauthorized access.
Try something like this
Your current search till | eval allowed_groups=split(group_lookup,"::")
| eval temp=mvdedup(mvappend(group_name,allowed_groups))
| eval user=if(mvcount(temp)=(mvcount(group_name) + mvcount(allowed_groups)),user,null)
| search user!=null
| table user host Object_Name allowed_groups group_name
Explanation:
a) Created a field temp in which I merged group_name and allowed_groups mv fields and removed duplicates from merged field.
b) If there is no common group between group_name (user's groups) and allowed_groups, then no of values in temp will exactly as sum of no of values in each of group_name and allowed_groups field. So, unauthorized access.
Unfortunately this is still showing false positives. I had tried something similar to this on an initial version of the search but never was able to make it work.
Can you provide some sample data, which will available after | eval allowed_groups=split...
?
This sanitized event will be a false positive, triggering the alert when it should not:
03/01/2016 10:42:26 AM
LogName=Security
SourceName=Microsoft Windows security auditing.
EventCode=4663
EventType=0
Type=Information
ComputerName=SERVER.DOMAIN.COM
TaskCategory=File System
OpCode=Info
RecordNumber=100125574
Keywords=Audit Success
Message=An attempt was made to access an object.
Subject:
Security ID: DOMAIN\USERNAME
Account Name: USERNAME
Account Domain: DOMAIN
Logon ID: 0xc93b0ff0
Object:
Object Server: Security
Object Type: File
Object Name: D:\FOLDER\PROTECTED.XLSX
Handle ID: 0x1418
Process Information:
Process ID: 0x4
Process Name:
Access Request Information:
Accesses: ReadData (or ListDirectory)
Access Mask: 0x1
It triggers with the following statistics:
user: USERNAME
host: SERVER.DOMAIN.COM
Object_Name: D:\FOLDER\PROTECTED.XLSX
allowed_groups: ALLOWED_GROUP1
ALLOWED_GROUP2
group_names: RANDOM_GROUP1
RANDOM_GROUP2
ALLOWED_GROUP2
RANDOM_GROUP3
You can see that the user is a member of ALLOWED_GROUP2, which is one of the two allowed groups to this file.
Can you also post what is the value of field temp after executing this. Since the check for alert is based on temp, value in this will help determine what's going on.
| eval temp=mvdedup(mvappend(group_name,allowed_groups))
Actually, after recopying your suggestion into the search, it appears to be working correctly. Its possible that I somehow botched the search syntax the first time. For the record, this is the search that is working now:
index=wineventlog sourcetype=WinEventLog:Security EventCode=560 OR EventCode=4663 OR EventCode=5145 NOT user=*$
| lookup lookup_wild_folder folder_lookup AS folder_name, server AS host OUTPUT group_lookup user_lookup file_exceptions
| eval user=if(isnull(Object_Name),null,user)
| eval user=if(match(Object_Name,folder_lookup),null,user)
| eval user=if(match(user_lookup,user),null,user)
| eval user=if(match(Object_Name,file_exceptions),null,user)
| table user host folder_lookup folder_name Object_Name Message group_lookup
| ldapfilter domain=default search="(sAMAccountName=$user$)" attrs="memberOf"
| rex field=memberOf "^CN=(?[^,]+)"
| eval allowed_groups=split(group_lookup,"::")
| eval temp=mvdedup(mvappend(group_name,allowed_groups))
| eval user=if(mvcount(temp)=(mvcount(group_name) + mvcount(allowed_groups)),user,null)
| search user!=null
| table user host Object_Name allowed_groups group_name temp
The lookup_wild_folder CSV structure is still the same:
server,folder_lookup,folder_out,group_lookup,user_lookup,file_exceptions
SERVERNAME,D:Docstore*,D:Docstore,GROUP1::Group2,::USER1::USER2::USER3::,