Mapping snort alerts
A while back at my day job, my boss was
getting ready to meet with all the executives and explain to them why
security mattered, what the scope of the threat was, and what we planned
to do about it. He knew, from talking to me, that our servers/networks
were routinely under attack, and that those attacks were from all over the
world. But he wanted a way to make it obvious to non-technical folks.
"Say, will snorby display all the snort alerts on a map?" he asked. "Nope.
it's a tool meant for security analysts." But that got me to thinking
(always a dangerous thing - grin). Snort just logs to a mysql database,
and there is both a mysql and geoIP API in php. So it shouldn't be too hard
to make... Lessee, I'd need a map, better make it a mercator projection to
make the lat/long to x/y conversion easy since that's the only projection
I remember (I was a C/asm programmer in a previous life)...
A few hours later I had several map images for his presentation that
were based on real data and suitably eye-opening to the execs, especially
when we pointed out this wasn't all the probes/attacks by any means, just
the stuff we allowed through the firewall that snort believed to be an
attack of some sort. Mission accomplished. :-)
It only took a little extra effort to generalize the PHP scripts a bit and
make them more useful even to a security geek like me. Take for instance
the following image:

This shows only the snort alerts whose TCP destination port is 22 - ssh probes
and brute force attacks. The red dots are source locations scaled to the
number of alerts. The blue dots are locations of SGI networks, and since
these aren't showing up as sources their scaled to a minimum size.
Now look at this one:

This shows snort alerts to/from two DMZ'd VOIP-related systems. Note that
since most VOIP-related snort alerts are from reply traffic coming from
the VOIP servers (things like registration failures), this time it's two of
the SGI locations that are scaled larger. But what I found interesting was
that VOIP attacks have mostly been from US IPs (and a few western european
locations). Whereas ssh attacks are generally from everywhere, especially
a certain country in Asia which shouldn't surprise anyone who's ever
looked at a firewall log. :-)
I even generalized the code so I could feed it different maps and still
have it figure out how to convert lat/lon to x/y correctly. For instance,


This alternate map never really makes sense for SGI but it might if, say,
our datacenter was in Europe. BTW, the big blue dot over Wisconsin
is because of all the failed root logins on an FTP server there.
Since I did most of this for fun in my own time (yeah, I'm a geek, when I'm
working in IT I miss programming and when I'm programming I miss IT/Security),
I'm making it freely available as-is to anyone else who wants it. I just ask
that if you find it useful, please visit
www.sgi.com next time you're looking to
buy server/storage gear. :-)
You can download it here.
So, how does all this stuff work? The first bit to look at is mynets.php.
This is where I tell the code what subnets are at what lat/lon coordinates
and whether or not they're "MINE" (blue dots vs red dots). Yeah, I'm using
GeoIP calls to figure out lat/lon coords but let's be honest... These
databases are a best guess... at best. For instance, they pretty
much always say all of SGI's IP space is in Milpitas which isn't right.
They have no idea how the IP space has been subnetted and split up
amongst different data centers in Japan, California, Wisconsin, the UK, etc.
And what about private IP space like 10.0.0.0/8 or 198.168.0.0/16? This
file lets me specify stuff like "10.101.1.0/24 is in Milpitas, at these
specific coords and it's one of our networks".
Let's assume you want to just use one of the 3 maps I included already,
for now. These are defined in maps.php. This php script defines a
$maps array and at the end, it sets $mapdef (the default map) to be
$maps[0] - the one I like best.
Next you'll want to look at mydb.php. This merely opens a connection to
a mysql server your barnyard daemons are logging to. It sets the $db
variable all the other scripts use.
Ok, now onto the scripts that actually generate a map image. Take a look at
dmz-alerts.php. It creates a map object sets a few colors (not really
needed - these were for debugging statements), and sets a variable used to
store the max number of "hits" a given location was seen. Then it
queries mysql for the IPs (source and dest) logged by snort in the last
day. For each one found, it creates a location object for the
source/dest IPs, and then asks the map object to plot a line between them.
The location objects figure out their lat/lon coords from the IPs given,
and whether or not they are one of "my" networks. If the snort alert
is both from one of my networks and to one of my networks, the line will
be drawn in blue. This is useful for when you're mapping snort alerts
going across your VPN or MPLS/WAN networks. I have two different snort
databases - one for traffic to/from DMZ segments and one for everything
else, and then I also have snort sensors watching WAN traffic, traffic to/from
certain datacenter networks, to/from certain user segments, etc.
Anyway, I note the locations and if it's a source I've seen before I note
how many times and what the max number of times any single location has
been seen (for plotting locations later). Then at the very end, I plot
all the locations I've found. Easy-peasy. :-)
One thing to note is that this script doesn't generate HTML - it just dumps
a jpeg file. This was because initially all I wanted was an image file.
So if you add any debugging print statements, you won't see 'em, and even
if the code does work, any print statements will mess up the resulting
image. So if you're making changes and debugging, run the php script from
the commandline so you can see the raw output and any debugging errors
and/or warnings that your webserver might otherwise hide.
Make your own map - This is a little tricky, but here's what you
need to do. Get an image and run gimp on it. Identify the x/y location of
the "origin" (the upper left corner) as closely as you can. You may need to
zoom in. Next, get the width and height of the map in pixels - this is the
distance between the origin and the lower right corner, not the width/height
of the image itself. For instance on map[0] you'll see the image is 2613
pixels wide, but the width I specify is only 2524 - the horizontal
distance between the origin and the right edge of the usable map within the
image file.
Next, you need to find the x/y location within the image of lat/lon 0/0.
This is the x/y within the image file, not x/y relative to the x/y of the
origin.
Once you have all that data, just add the map to the $maps array in maps.php
such as:
$mapfile = "mc-amr-302270_comp_1_3.jpg";
$maps[$i++] = new MapDefClass ($mapfile, 42, 22, 2524, 1451, 1934, 1000);
Then you can either set $mapdef to point to your new map at whatever index
it's at in the array, or in your individual script, you can ignore the $mapdef
variable and use whatever map you want when creating the map object.