In one of my previous posts (Shannon. Have you seen my Entropy?) I touched on using a custom Java entropy calculator within the ESA to calculate the entropy values for domains to assist with detecting Domain Generation Algorithms (DGA's); the post was more theory than practical so I decided to implement and test it in my lab so I could share the implementation with you all.
The basic principle behind this form of DGA detection is to calculate an entropy value for each domain seen and store this value in an ESA window. We can then use the values in the ESA window to calculate an average entropy for the domains seen within an environment, this subsequently allows an alert to be generated if any domains exceed the average entropy by 1.3x.
As an example, let's take the following four domains from the Alexa top 100 (this will be what we use as an example baseline, the rule attached to this post would actually monitor your network for what is normal):-
Running these each through the entropy calculator we receive the following values:-
Using this average as our baseline, we can then say that anything that is greater than 1.3x (3.87861981324625) this average, let me know about it as this is a high entropy value.
Taking the following values from Zeus tracker and calculating their entropy values, we can see the results:-
Example of the alert output below:-
@Name('Learning Phase Variable')
//Change the learningPhaseMinutes variable to the number of minutes for the rule to learn
CREATE VARIABLE INTEGER learningPhaseMinutes = 1440;
@Name('Calculate Learning Phase')
on pattern[Every(timer:at(*, *, *, *, *))] set learningPhaseMinutes = learningPhaseMinutes - 1;
@Name('Create Entropy Window')
CREATE WINDOW aliasHostEntropy.win:length(999999).std:unique(entropy) (entropy double);
@Name('Insert entropy into Window')
INSERT INTO aliasHostEntropy
SELECT calcEntropy(alias_host) as entropy FROM Event(alias_host IS NOT NULL AND learningPhaseMinutes > 1);
SELECT *, (SELECT avg(entropy) FROM aliasHostEntropy as Average), (SELECT calcEntropy(alias_host) FROM Event.win:length(1) as Entropy) FROM Event(learningPhaseMinutes <= 1 AND calcEntropy(alias_host) > 1.3* (SELECT avg(entropy) FROM aliasHostEntropy));
If you are interested in implementing this DGA Detection rule, I wrote up a little guide on how to do so. Everything you need is attached to this post.