We can use the find_anomaly_periods function to extract this range.
This takes the output data frame from time series anomaly's STL
function and outputs the time ranges where one or more
anomalies occurred. In this case, the middle period is the one
that's most interesting to us.
We want to use that time range to query for the full logon details
of all the failures happening in that time range.
Recall that the original query we did was just hourly summaries.
We can pick out the middle period to index 1 and load it into
MSTICPy's QueryTime widget. This let us edit the time range if
needed. And in this case, we want to extend the time a little at
the start in the beginning.
Once we have the QueryTime range in our widget, we can pass that to the query.
This will tell the query to use these adjusted start and end times. To using
this, we can query for full logon details for that time range.
Note that this query may return a very large result set and take
a while to complete. To avoid timeouts, we can tell MSTICPy to
split the query into 30 minute chunks, which will be assembled
into a single data frame.
We're also going to run a similar query to collect a baseline of normal
activity that we can use to compare against our anomalous period. We picked
the same time of day, but from 14 days earlier.
Now that we have both data sets, we can start to analyze the
period that we saw with logon anomalies.
What we want to do here is see if there are any properties of the logons in
our suspect periods that don't match those of our baseline.
Usually passwords sprays are automated, and at least the less sophisticated
attacks tend to repeat values like source IPAddress, UserAgent, request
AppId, and others. In normal logon failures, we wouldn't expect a higher
rate of repetition for these values.
There is a pandas function called value counts that's
going to be particularly helpful to us.
It counts the number of occurrences of unique values for a column.
The output is ordered with the most frequently occurring values at the top.
It's also possible to use this on an entire data frame.
We're showing a couple of simple examples here, showing the top repeating
values for the ClientAppUsed column and the UserAgent column.
I've written a short function in the notebook that combines data
from both our baseline and suspected attack data and compares
them. Running this for three properties,
Location, AppId, and UserAgent, we can see the distribution of
values for these two datasets is very different. In the notebook,
I've also included code that helps automate this kind of comparison
for two datasets and plots a graph that reflects the amount of
repetition for each column.
In practice, we often don't need this kind of analysis, and we can
just pick a few properties that are likely to be repeated in an
automated attack, such as AppId and UserAgent.
To help us visualize the comparison between the two datasets, we
can use MSTICPy's matrix plot. This plots the frequency of
interaction between two data points.
In the first example, we're looking at our baseline data, and at the
intersection of IP address and location country code.
What we're seeing here is how many times the same combination of IP
address and country code occurred in the same logon failure.
The IP addresses are plotted on the Y or vertical axis.
We can see individual addresses if we zoom in.
However,
we're not really interested in the individual values here, just the
pattern. In most cases, the logon failures are broadly distributed across
multiple IPs with a few cases of repeated failures from the same IP
address. Contrast this with our anomaly data, where we can see that a small
number of IP addresses account for a large number of repeated failures
across multiple geographies.
Putting these two side by side shows this more clearly. Also
notice that we're seeing logon failures from countries that
didn't occur at all in the baseline data.
It is also interesting and quite powerful to see the logon
failures plotted on a map. MSTICPy uses the folium package to do
this. Here we're showing failures from a random sample of 500
events from our baseline data.
Then we add a sample from the anomaly period and color those in red.