2016-06-23 05:23 AM
Great Esper capabilities which is heart of ESA allows to create complex rules. In this article I describe how to build a profile of some activity and fire an alert when there is deviations from usual activity. Lets take domain administrators accounts or other critical accounts as an example. Most of times such accounts are used from particular sources therefore we can build a profile which will contains: user_src, ip_src, count of logins. After some time profile of one of accounts will looks like this:
X axis - is a ip_src (I replaced it with numbers), Y axis - number of login attempts.
ip_src with number 6 and 7 are looks like ok but 1,2,3 needs to be investigated. Next we can calculate probability of every source simply divide count of logins from particular ip_src by total login attempts. Next formula will estimate probability (in %) that particular source is NON usual for this user:
100-100*CountForIPForUser/TotalForUser
The result of this estimation will looks like this.
So we can say that if calculated value will be grater than defined threshold (say 97%) this is abnormal and must be investigated. Hope you understood idea
To implement this approach in ESA we well create two rules (modules in terms of Esper).
/*-------------------------*/
module profileBuilder;
@RSAPersist /*to save and restore during rebooting*/
create window profile.win:keepall() (name string,key string, value string, cnt long, date long);
create constant variable long profileDurationHoursMultiplier = 24L;
create constant variable long profileDuration = 30L;
create constant variable long profileRewiewIntervalHour = 12;
@Name('ClearProfileTimer') /* delete old records */
on pattern [every timer:interval(profileRewiewIntervalHour hour)]
delete from profile
where Math.round(current_timestamp/1000/60/60/profileDurationHoursMultiplier)-date>=profileDuration;
/*-------------------------*/
We created named window "profile" which will hold:
There are several constants which defines how wide in time our profile window will be (measures in hours).
So in my example profileDurationHoursMultiplier*profileDuration = 24 hours * 30 = 720 hours = 30 days
profileRewiewIntervalHour defines how often old records (based on date field) will be reviewed and deleted. You can change this constants according your needs.
/*-------------------------------------------*/
module domainAdmin;
uses profileBuilder;
create constant variable string profileName = 'domainAdmins';
create constant variable int minScore = 95;
create constant variable string[] adminList= {'superadmin1','superadmin2'};
create constant variable string[] whiteListIP={'10.0.0.1','192.168.0.1'};
/*using afreemaker variable which will be injected to Esper rule during deployment stage*/
<#assign myFilter="device_type in ('msacs','winevent_nic') AND reference_id in ('4624','4625','4768','4776')
AND adminList.anyOf(v=>v=user_src.toLowerCase()) and ip_src IS NOT NULL
AND NOT whiteListIP.anyOf(v=>v=ip_src.toLowerCase()) " >
@Name('Fill domain admins profile')
on Event(${myFilter}) e
merge profile p
where p.key =e.user_src.toLowerCase() and p.value=e.ip_src and p.date = Math.round(current_timestamp/1000/60/60/profileDurationHoursMultiplier )
when matched
then update set cnt = cnt+1L
when not matched
then insert select profileName as name, ip_src as value, user_src.toLowerCase() as key, 1L as cnt,Math.round(current_timestamp/1000/60/60/profileDurationHoursMultiplier) as date;
create expression pCount { x =>
(select case sum(cnt)
when NULL then 1L
else sum(cnt) end
from profile p where p.name = profileName AND (x.user_src).toLowerCase() = p.key and x.ip_src = p.value)
} ;
create expression pTotal { x =>
(select case sum(cnt)
when NULL then 1L
else sum(cnt) end
from profile p where p.name = profileName AND p.key=(x.user_src).toLowerCase()) } ;
create expression pScore{ x=>
Math.round(100 - 100*pCount(x)/pTotal(x))
};
@Name('DomainAdminsProfile')
@RSAAlert
select e.ip_src as ip_src,e.user_src as user_src, pCount(e) as count,
pTotal(e) as total,
pScore(e) as score
from Event( ${myFilter} ).std:lastevent() e
where pScore(e)>=minScore
group by e.user_src,e.ip_src
output first every 1 hour;
/*-------------------------------------------*/
At the beginning we define some constants
we also use freemaker variable myFilter which will be injected to esper statements by freemaker engine.
Next we fill our profile with upsert operation which is implemented with merge instruction. If username and ip_src is already in profile for this date then increase counter by 1 or simply create new record.
pCount,pTotal, pScore is expressions which will be used in the last rule for calculating count of login attempts from particular IP for a user, total number of logins attempts for user and score - the probability that ip is non usual for this user.
The latest rule will calculate probability and compare it with constant we defined at the beginning.
Example of alert looks like this:
So features of my approach are:
Note: rule can generate false positives especially during firs days of working. To modify while list of IP rule must be changed and redeployed.
I hope that RSA team will create some sort of interface to create on-demand queries with such functionality it will be possible to mannualy select, delete, update values in named windows to adjust profile by hands.
2016-07-02 01:07 AM
Hi Nikolay,
Thank you for this excellent post. The RSA Live content team has been experimenting with rules very similar to the example you have provided. Do you have any additional anomaly detection use cases you would find valuable. Providing a mix of aggregated feature based models and content is a critical part of our threat detection strategy.
2016-07-04 02:15 AM
Hi, Mark! I also have rule to detect beaconing activity to some web host which is similar to ATD feature but works well with logs (not packets). But this rule is under debugging stage. I need to upgrade to 10.6.0.2 (currently at 10.6.0.1) to support tables to store statistics to solve some issues.