Learn How To Create a Custom Threat Map in Splunk: Part 1

This tutorial series will be going through the steps of creating a custom Threat Map. During this process we will be using Google Maps, which is fortunately already built into Splunk. We will be using the Splunk Web Framework to build out our app.



One of my recent blog posts/screencasts discusses how to integrate CartoDB into Splunk in order to provide a cool map with some animations. Now, we’re going to move on to the next level. This three-part blog post/screencast combo allows for even more visual control and enables you to drill deeper into the details you’re looking for.

More specifically, we will be going through the steps of creating a custom Threat Map. During this process we will be using Google Maps, which is fortunately already built into Splunk. We will be using the Splunk Web Framework to build out our app.

Part 1 will cover the basics of setting up a Google Map in Splunk with the Splunk Web Framework. Part 2 will show how to customize the map and add our awesome custom threat skull icons. Finally, part 3 portrays how to pass token values from this map to another visualization (in this case, an area chart that will show how many times a source ip hit our firewall over a period of time).

UFW logs indexed by Splunk will be used for these examples, but can replace them with your own logs. Any will do as long as you can pull out the latitude and longitude properties in your search.

The attractive thing about Google Maps is we can use some custom icons. In this specific scenario, we are going to use skulls to represent the amount of times a specific IP hits our firewall. The more times a specific ip hits our firewall the bigger the skull icon will get (as they represent a potentially bigger threat), and they will change color as well.

Here’s what the end result will look like:

Threat Map
Threat Map

If you want to use the skull images, you can download them here.

You can download the full app here.

And just as a friendly reminder, if you'd rather watch a screencast on this, you can go here as well.

A Note about http vs https:

If your Splunk install is using https, you will need to change some core Splunk files in order to get this to work properly. By default, Splunk loads Google Maps through http - I REALLY hope they fix this in the future so it loads whatever you have set by default in order to prevent the necessity to override core files.

The following files need to be updated, so any instance of http, needs to be changed to https:

$SPLUNK_HOME/share/splunk/search_mrsparkle/exposed/js/build/splunkjs.min/mvc/googlemapview.js
$SPLUNK_HOME/share/splunk/search_mrsparkle/exposed/js/build/simplexml.min/mvc/googlemapview.js
$SPLUNK_HOME/share/splunk/search_mrsparkle/exposed/js/build/simplexml/mvc/googlemapview.js
$SPLUNK_HOME/share/splunk/search_mrsparkle/exposed/js/build/splunkjs/mvc/googlemapview.js (2 places)

Once that is done, you’ll need to restart Splunk.

Part 1: Set It Up

First, go to $SPLUNK_HOME/etc/apps/framework and run ./splunkdj createapp , it will then prompt you for your username and password. It will then say something like 

After you restart Splunk go to your template in >span class="theme:twilight font:consolas font-size:16 line-height:25 toolbar:2 lang:default decode:true crayon-inline"><appname>/templates/home.html and open that up. You should now have something like this:

Default App View 1
Default app view

Before moving on, make sure you upload the skulls folder I provided in a zip file above. You want to upload this so the ‘skulls’ folder is in$SPLUNK_HOME/etc/apps/<appname>/django/<appname>/static/<appname>/

Part 2: The Search Obviously, we need a search. Here is the one I will be using:

sourcetype=ufw | iplocation SRC | rename lon as lng | stats count by lat, lng, Country, City, SRC | eval Category=case(count<=50, "low", count<=100, "medium", count<=150, "moderate", count<=249, "critical", count>=250, "severe") | rename SRC as "Source IP" | sort - count

I have a sourcetype of UFW and will be using the iplocation command to to pull out the lon and lat fields from the SRC field, which are source ips hitting my firewall. I then rename the lon field to lng as Google Maps will be expecting lng instead of lon. I perform a stats count by the latitude, longitude, County, City and SRC (source ip). I then want to be able to create a range value depending on the amount of times a specific IP is hitting my firewall. So, I use the eval command with case in order to split up ips into low, medium, moderate, critical and severe categories.

The categories we create are important, as they are how we determine what type of skull icon we will use in the visualization on the map. So, a category of low will load a different skull than a category of critical.

Part 3: Load Our Dependencies

In the home.html file add the following into the <script> tags:

//we first register our dependencies
var deps = [
    "splunkjs/ready!",
    "splunkjs/mvc/searchmanager",
    "splunkjs/mvc/postprocessmanager",
    "splunkjs/mvc/tableview",
    "splunkjs/mvc/chartview",
    "splunkjs/mvc/splunkmapview",
    "splunkjs/mvc/googlemapview"
];

//then we use require to pull in the dependencies and use those
//to require the necessary files
require(deps, function(mvc) {
    // Load individual components
    var SearchManager = require("splunkjs/mvc/searchmanager");
    var GoogleMapView = require("splunkjs/mvc/googlemapview");
});

Here, we are loading our dependencies and then using those dependencies to load the necessary files. All we are loading in right now is our SearchManager and GoogleMapView.

Part 4: Create the Search Manager

Inside the require() method below the SearchManager and the GoogleMapView variable definitions, add the following:

var mapsearch = new SearchManager({
    id: "googlemap-search",
    managerid: "googlemap-search",
    search: 'sourcetype=ufw | iplocation SRC | rename lon as lng | stats count by lat, lng, Country, City, SRC | eval Category=case(count<=50, "low", count<=100, "medium", count<=150, "moderate", count<=249, "critical", count>=250, "severe") | rename SRC as "Source IP" | sort - count',
    earliest_time: "-30d@d",
    latest_time: "now",
    preview: true,
    cache: false
});

Here, I’ve created a new Search Manager with an id and managerid of “googlemap-search”. These don’t have to be named the same, but the managerid is the important one. It’s what we will be referencing this search by in our maps visualization to pull the search results.

Part 5: Create the View

Below the mapsearch SearchManager put the following:

var googleMapView = new GoogleMapView({
    id: "googlemap",
    managerid: "googlemap-search",
    zoom: 2,
    el: $('#googlemap')
}).render();

Then, inside the {% block content %} replace the html in there with:

And finally, up top inside the <style> tags replace the css there with:

.main-area {
    margin: 0px auto;
    margin-top: 30px;
    margin-bottom: 30px;
    padding: 10px;
    width: 95%;
}
#googlemap {
    height: 400px;
    width: 100%;
}

Now, if you go to view your page in the browser at something like: http://localhost:8000/dj/en-us/<appname>/home/ you should see something like this

Map Regular Markers
Regular markers

We can see the our map is successfully showing the location of our source ips (SRC). Now, we want to customize this to replace the icons with our skulls, which we will do in part 2.




Close off Canvas Menu