Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Script to parse openIOC format files and search Splunk for extracted IP addresses.
#! /usr/bin/perl
#
# Script written to pull IP data from openIOC and search Splunk.
#
use strict;
use warnings;
$|=1;
use LWP::UserAgent;
use XML::LibXML;
my %splunk_id;
my @SPLUNK_SERVERS = ("internal.splunk.com:8089"); # set this to your splunk server(s)
my $ioc_dir = "/tmp/iocfiles"; # Set to your IOC files location
$splunk_id{'username'} = q/USERNAME/; # Splunk Username
$splunk_id{'password'} = q/PASSWORD/; # Splunk Password
$splunk_id{'data_type'} = "csv";
sub splunk_login {
my %BROWSER_PARAMS;
$BROWSER_PARAMS{'username'}= $_[0];
$BROWSER_PARAMS{'password'} = $_[1];
my $base_url = "https://".$_[2];
my $browser = LWP::UserAgent->new;
my $response = $browser->post($base_url."/servicesNS/".$BROWSER_PARAMS{'username'}."/search/auth/login/", \%BROWSER_PARAMS);
my $auth_key = "Login Error (".$response->content.")";
if ($response->content =~ /<sessionKey>(.*?)<\/sessionKey>/){$auth_key = $1;};
return $auth_key;
}
sub splunk_search {
my %BROWSER_PARAMS;
$BROWSER_PARAMS{'search'} = $_[0];
my $browser = LWP::UserAgent->new;
my $base_url = "https://".$_[1];
$browser->default_header('Authorization' => "Splunk ".$_[2]);
my $response = $browser->post($base_url."/servicesNS/".$_[3]."/search/search/jobs", \%BROWSER_PARAMS);
my $search_id = "Error";
if ($response->content =~ /<sid>(.*?)<\/sid>/){$search_id = $1;};
return $search_id;
}
sub splunk_status {
my $base_url = "https://".$_[1];
my $browser = LWP::UserAgent->new;
$browser->default_header('Authorization' => "Splunk ".$_[2]);
my $response = $browser->get($base_url."/servicesNS/".$_[3]."/search/search/jobs/".$_[0]);
if($response->code() != 200){die "Check error (".$response->code().")\n";}
return $response->content;
}
sub splunk_get_data {
my $base_url = "https://".$_[1];
my $browser = LWP::UserAgent->new;
$browser->default_header('Authorization' => "Splunk ".$_[2]);
my $response = $browser->get($base_url."/servicesNS/".$_[3]."/search/search/jobs/".$_[0]."/results?output_mode=".$_[4]);
if($response->content){return $response->content;}else{return "NULL";}
}
sub xml_dispatchState {
my $dom = XML::LibXML->load_xml(string => $_[0]);
my @CONTENT = $dom->getElementsByTagName("s:key");
for(my $j=0;$j<@CONTENT;$j++){
my @TYPE_INFO = $CONTENT[$j]->getAttributeNode("name");
foreach my $type_info (@TYPE_INFO){
if ($type_info->nodeValue eq "dispatchState"){return $CONTENT[$j]->textContent;}
}
}
return;
}
sub ioc_get_ip {
my %BAD_IP_LIST;
my $parser = XML::LibXML->new();
my $dom = $parser->parse_file($_[0]."/".$_[1]);
my @CONTENT = $dom->getElementsByTagName("ns0:Content");
for(my $j=0;$j<@CONTENT;$j++){
my @TYPE_INFO = $CONTENT[$j]->getAttributeNode("type");
foreach my $type_info (@TYPE_INFO){
if ($type_info->nodeValue eq $_[2]){$BAD_IP_LIST{$CONTENT[$j]->textContent} = $_[1];}
}
}
return \%BAD_IP_LIST;
}
sub craft_search{
my $search_string = "sourcetype != \"audittrail\" ";
my $search_suffix = "";
if ($_[0] eq 'IP'){
my $BAD_IP_REF = $_[1];
foreach my $k (keys %$BAD_IP_REF){
# Ignore IP's in private address space
if (!($k =~ /^10\./)){$search_suffix .= $k." OR ";}
}
$search_string = $search_string.$search_suffix;
$search_string =~ s/OR\s+$/\| stats count\(src_ip\) AS \"Hit Count\" by src_ip \, dst_ip | rename src_ip AS \"Source IP\"\, dst_ip AS \"Destination IP\"/;
}
return $search_string;
}
opendir(DIR, $ioc_dir) or die "Can't open $ioc_dir: $!";
my @FILES = readdir DIR;
closedir DIR;
my %BAD_IP; # Hash containing bad IP's
foreach my $file (@FILES){
if ($file =~ m/\.ioc$/){
my $tmp_ip_hash = ioc_get_ip($ioc_dir, $file, "IP");
foreach my $ip_val (keys %$tmp_ip_hash){$BAD_IP{$ip_val}++;}
}
}
my $search_string = craft_search('IP', \%BAD_IP);
my $stop_tm = time(); my $start_tm = $stop_tm-(60*60);
my $search = 'search earliest='.$start_tm.' latest='.$stop_tm.' '.$search_string;
print "\n$search\n\n";
my %SPLUNK_AUTH;
foreach my $splunk_server (@SPLUNK_SERVERS){
my $session_key = splunk_login($splunk_id{'username'}, $splunk_id{'password'}, $splunk_server);
if ($session_key ne "Login Error"){$SPLUNK_AUTH{$splunk_server} = $session_key;}
}
if (!((keys %SPLUNK_AUTH) >= 1)){die "Splunk login error\n";}
my %SEARCH_ID;
while(my ($server, $sessionkey) = each %SPLUNK_AUTH ) {
$SEARCH_ID{$server} = splunk_search( $search, $server, $sessionkey, $splunk_id{'username'});
}
my ($j, $n) = (1, 1);
while ($j){
while ( my ($server, $searchid) = each %SEARCH_ID ) {
my $current_status = xml_dispatchState(splunk_status($searchid, $server, $SPLUNK_AUTH{$server}, $splunk_id{'username'}));
print $current_status."...\t\r";
if ($current_status eq "DONE"){
my $results = splunk_get_data($searchid, $server, $SPLUNK_AUTH{$server}, $splunk_id{'username'}, $splunk_id{'data_type'});
if ($results ne "NULL"){
print "\n".$results."\n\n";
}else{
print "\nSearch complete.... No baddies found!\n\n";
}
delete($SEARCH_ID{$server});
}
}
if (keys %SEARCH_ID){for (my $sleep=0;$sleep<5;$sleep++){$n++;sleep 1;}}else{$j=0;}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment