Python support added

This commit is contained in:
BlackLight 2011-02-03 01:01:36 +01:00
parent 521aeef342
commit 95db8a6486
13 changed files with 1030 additions and 14 deletions

View file

@ -72,7 +72,8 @@ fi
install -m 0644 "${PWD}/htdocs/manual_uncorrelations.xml" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/pcap.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/correlate.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/default.xsl" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/alerts.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0644 "${PWD}/htdocs/default.xsl" "${SHARE_PREFIX}/htdocs"
install -m 0644 "${PWD}/htdocs/js/Curry-1.0.1.js" "${SHARE_PREFIX}/htdocs/js"
install -m 0644 "${PWD}/htdocs/js/dracula_algorithms.js" "${SHARE_PREFIX}/htdocs/js"
install -m 0644 "${PWD}/htdocs/js/dracula_graffle.js" "${SHARE_PREFIX}/htdocs/js"

View file

@ -877,7 +877,8 @@ fi
install -m 0644 "${PWD}/htdocs/manual_uncorrelations.xml" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/pcap.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/correlate.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/default.xsl" "${SHARE_PREFIX}/htdocs"
install -m 0755 "${PWD}/htdocs/alerts.cgi" "${SHARE_PREFIX}/htdocs"
install -m 0644 "${PWD}/htdocs/default.xsl" "${SHARE_PREFIX}/htdocs"
install -m 0644 "${PWD}/htdocs/js/Curry-1.0.1.js" "${SHARE_PREFIX}/htdocs/js"
install -m 0644 "${PWD}/htdocs/js/dracula_algorithms.js" "${SHARE_PREFIX}/htdocs/js"
install -m 0644 "${PWD}/htdocs/js/dracula_graffle.js" "${SHARE_PREFIX}/htdocs/js"

37
README
View file

@ -132,7 +132,14 @@ by tools like tcpdump and Wireshark
- XML::Simple Perl module (RECOMMENDED), used by 'correlate.cgi' CGI script for
reading and writing manual (un)correlations XML files. A quick way for
installing it on a Unix system is by using CPAN:
installing it on a Unix system is by using CPAN.
- Python 2.6 (OPTIONAL), used for interfacing SnortAI module to Python scripts
through snortai module (see README file in pymodule/) and writing new
correlation modules (see example.py in corr_modules/).
Compile the module passing --with-python option to the ./configure script if you
want this feature. You need Python interpreter and libpython2.6 installed on
your system.
# cpan XML::Simple
@ -574,6 +581,34 @@ libsf_ai_preproc.la if you want to use some of the functions from the module,
for example, for reading the alerts stored in the history file, in the
database, the current correlations, and so on.
It is also possible to write your own modules in Python language. See the file
'example.py' in corr_modules/ for a quick overview. All you need to do is to
declare in your module the functions AI_corr_index (taking two arguments, two
alert descriptions) and AI_corr_index_weight (taking no argument),
both returning a real value descibing, respectively, the correlation
value between the two alerts and the weight of that index, both between
0 and 1. You can also access the alert information and all the alerts
acquired so far by the module by importing in your Python code the
'snortai' module. You can compile it and install it by moving to
'pymodule/' directory and running
$ python setup.py build
$ [sudo] python setup.py install
You can acquire the current alerts by writing a code like the following:
import snortai
alerts = snortai.alerts()
for alert in alerts:
# Access the alerts information
The fields in the alert class can be viewed in pymodule/module.py and
corr_modules/example.py examples. Take these files as guides for interfacing
your Python scripts with SnortAI module or writing new correlation modules in
Python.
===========================
9. Additional documentation

28
corr_modules/example.py Normal file
View file

@ -0,0 +1,28 @@
#!/usr/bin/python
# Example correlation index in Python
# XXX You may have an 'undefined reference to PyNone_Struct
# after running Snort with your module, you're facing an
# annoying bug due to the dynamically linked Python library.
# I'm sorry, but I'm still looking for a solution for this,
# and anyway it only happens when you import the module
# 'snortai'
# import snortai
# Function that takes two alerts as arguments (arguments of
# alert object:
# id, gid, sid, rev, description, priority, classification,
# timestamp, from, to, from_port, to_port, latitude,
# longitude, alerts_count) and returns a correlation index
# between 0 and 1 expressing how correlated these two alerts are
def AI_corr_index ( alert1, alert2 ):
return 0.0
# Return the weight of this index, between 0 and 1
def AI_corr_index_weight():
return 0.0

View file

@ -32,6 +32,25 @@
#include <gvc.h>
#endif
#ifdef HAVE_LIBPYTHON2_6
/*******************************************/
/* Avoid conflicts with Snort header files */
#ifdef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#ifdef _XOPEN_C_SOURCE
#undef _XOPEN_C_SOURCE
#endif
#ifdef _XOPEN_SOURCE
#undef _XOPEN_SOURCE
#endif
/*******************************************/
#include <Python.h>
#endif
/** \defgroup correlation Module for the correlation of security alerts
* @{ */
@ -365,8 +384,27 @@ AI_alert_correlation_thread ( void *arg )
double (**corr_functions)( const AI_snort_alert*, const AI_snort_alert* ) = NULL;
double (**corr_weights)() = NULL;
corr_functions = AI_get_corr_functions( &n_corr_functions );
#ifdef HAVE_LIBPYTHON2_6
PyAlert *pyA = NULL,
*pyB = NULL;
PyObject *pArgs = NULL,
*pRet = NULL;
PyObject **py_corr_functions = NULL;
PyObject **py_weight_functions = NULL;
size_t n_py_corr_functions = 0;
size_t n_py_weight_functions = 0;
double py_value = 0.0,
py_weight = 0.0;
#endif
corr_functions = AI_get_corr_functions ( &n_corr_functions );
corr_weights = AI_get_corr_weights ( &n_corr_weights );
py_corr_functions = AI_get_py_functions ( &n_py_corr_functions );
py_weight_functions = AI_get_py_weights ( &n_py_weight_functions );
pthread_mutex_init ( &mutex, NULL );
@ -466,6 +504,64 @@ AI_alert_correlation_thread ( void *arg )
}
}
#ifdef HAVE_LIBPYTHON2_6
if (( py_corr_functions ))
{
pyA = AI_alert_to_pyalert ( corr_key.a );
pyB = AI_alert_to_pyalert ( corr_key.b );
if ( pyA && pyB )
{
for ( i=0; i < n_py_corr_functions; i++ )
{
if ( !( pArgs = Py_BuildValue ( "(OO)", pyA, pyB )))
{
PyErr_Print();
AI_fatal_err ( "Could not initialize the Python arguments for the call", __FILE__, __LINE__ );
}
if ( !( pRet = PyEval_CallObject ( py_corr_functions[i], pArgs )))
{
PyErr_Print();
AI_fatal_err ( "Could not call the correlation function from the Python module", __FILE__, __LINE__ );
}
if ( !( PyArg_Parse ( pRet, "d", &py_value )))
{
PyErr_Print();
AI_fatal_err ( "Could not parse the correlation value out of the Python correlation function", __FILE__, __LINE__ );
}
Py_DECREF ( pRet );
Py_DECREF ( pArgs );
if ( !( pRet = PyEval_CallObject ( py_weight_functions[i], (PyObject*) NULL )))
{
PyErr_Print();
AI_fatal_err ( "Could not call the correlation function from the Python module", __FILE__, __LINE__ );
}
if ( !( PyArg_Parse ( pRet, "d", &py_weight )))
{
PyErr_Print();
AI_fatal_err ( "Could not parse the correlation weight out of the Python correlation function", __FILE__, __LINE__ );
}
Py_DECREF ( pRet );
if ( py_weight != 0.0 )
{
corr->correlation += py_weight * py_value;
n_correlations++;
}
}
free ( pyA ); free ( pyB );
pyA = NULL; pyB = NULL;
}
}
#endif
if ( n_correlations != 0 )
{
corr->correlation /= (double) n_correlations;
@ -548,10 +644,12 @@ AI_alert_correlation_thread ( void *arg )
)
)
) {
if ( !( corr->key.a->derived_alerts = ( AI_snort_alert** ) realloc ( corr->key.a->derived_alerts, (++corr->key.a->n_derived_alerts) * sizeof ( AI_snort_alert* ))))
if ( !( corr->key.a->derived_alerts = ( AI_snort_alert** ) realloc ( corr->key.a->derived_alerts,
(++corr->key.a->n_derived_alerts) * sizeof ( AI_snort_alert* ))))
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
if ( !( corr->key.b->parent_alerts = ( AI_snort_alert** ) realloc ( corr->key.b->parent_alerts, (++corr->key.b->n_parent_alerts) * sizeof ( AI_snort_alert* ))))
if ( !( corr->key.b->parent_alerts = ( AI_snort_alert** ) realloc ( corr->key.b->parent_alerts,
(++corr->key.b->n_parent_alerts) * sizeof ( AI_snort_alert* ))))
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
corr->key.a->derived_alerts[ corr->key.a->n_derived_alerts - 1 ] = corr->key.b;

86
htdocs/alerts.cgi Executable file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env perl
use Env qw(DOCUMENT_ROOT QUERY_STRING);
use strict;
use warnings;
my $method = 'xml';
if ( $QUERY_STRING )
{
if ( $QUERY_STRING =~ /method=([a-z]+)/ )
{
if ( $1 eq 'json' or $1 eq 'xml' )
{
$method = $1;
}
}
}
my %mon2num = qw( jan 1 feb 2 mar 3 apr 4 may 5 jun 6 jul 7 aug 8 sep 9 oct 10 nov 11 dec 12 );
my $json_file = (( $DOCUMENT_ROOT ) ? $DOCUMENT_ROOT : '.' ).'/correlation_graph.json';
my $json_string = '';
open IN, $json_file or die "Alert JSON file not found";
$json_string .= $_ while ( <IN> );
close IN;
if ( $method eq 'json' )
{
print "Content-Type: application/json\n\n";
print $json_string;
} elsif ( $method eq 'xml' ) {
use JSON;
use Time::Local;
my @json = @{JSON->new->utf8->decode ( $json_string )};
print "Content-Type: application/xml\n\n".
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n".
"<alerts>\n";
for ( @json )
{
print "\t<alert";
my %element = %$_;
for my $key ( keys %element )
{
if ( $key !~ /[^a-zA-Z0-9_]/ and !UNIVERSAL::isa ( $element{$key}, "ARRAY" ) and !UNIVERSAL::isa ( $element{$key}, "HASH" ))
{
my $k = $key;
if ( $key eq 'snortSID' or $key eq 'snortGID' or $key eq 'snortREV' )
{
$k =~ s/^snort//;
$k = lc $k;
} elsif ( $key eq 'to' or $key eq 'from' ) {
if ( $element{$key} =~ /:([1-9][0-9]*)$/ )
{
my $port = $1;
$element{$key} =~ s/^(.*):[1-9][0-9]*$/$1/;
print " ${key}_port=\"$port\"";
}
} elsif ( $key eq 'date' ) {
if ( $element{$key} =~ /^\s*[a-z]+\s+([a-z]+)\s+([0-9]+)\s+([0-9]+):([0-9]+):([0-9]+)\s+([0-9]+)\s*$/i )
{
my $mon = $mon2num{ lc substr ( $1, 0, 3 )} - 1;
my $day = $2;
my $hour = $3;
my $min = $4;
my $sec = $5;
my $year = $6;
$element{$key} = timelocal ( $sec, $min, $hour, $day, $mon, $year );
}
}
$element{$key} =~ s/(^|[^\\])"/$1\\"/g;
print " $k=\"".$element{$key}."\"";
}
}
print "></alert>\n";
}
print "</alerts>\n";
}

133
modules.c
View file

@ -33,6 +33,16 @@ PRIVATE size_t n_corr_functions = 0;
PRIVATE double (**weight_functions)() = NULL;
PRIVATE size_t n_weight_functions = 0;
#ifdef HAVE_LIBPYTHON2_6
PRIVATE PyObject **py_corr_functions = NULL;
PRIVATE size_t n_py_corr_functions = 0;
PRIVATE PyObject **py_weight_functions = NULL;
PRIVATE size_t n_py_weight_functions = 0;
#endif
/**
* \brief Get the correlation functions from the extra correlation modules as array of function pointers
* \param n_functions Number of function pointers in the array
@ -59,6 +69,71 @@ double
return weight_functions;
} /* ----- end of function AI_get_corr_weights ----- */
#ifdef HAVE_LIBPYTHON2_6
/**
* \brief Get the correlation functions from the Python modules, if Python support is enabled
* \param n_functions Reference to the number of functions in the array
* \return The array of Python correlation functions as PyObject**
*/
PyObject**
AI_get_py_functions ( size_t *n_functions )
{
*n_functions = n_py_corr_functions;
return py_corr_functions;
} /* ----- end of function AI_get_py_functions ----- */
/**
* \brief Get the correlation index weights from the Python modules, if Python support is enabled
* \param n_functions Reference to the number of correlation weight functions in the array
* \return The array of correlation weight functions as PyObject**
*/
PyObject**
AI_get_py_weights ( size_t *n_functions )
{
*n_functions = n_py_weight_functions;
return py_weight_functions;
} /* ----- end of function AI_get_py_weights ----- */
/**
* \brief Convert an AI_snort_alert object to a PyAlert object that can be managed by a Python module
* \param alert AI_snort_alert object to be converted
* \return A PyAlert object wrapping the original AI_snort_alert object
*/
PyAlert*
AI_alert_to_pyalert ( AI_snort_alert *alert )
{
PyAlert *pyalert = NULL;
char src_addr[INET_ADDRSTRLEN] = { 0 },
dst_addr[INET_ADDRSTRLEN] = { 0 };
if ( !( pyalert = (PyAlert*) malloc ( sizeof ( PyAlert ))))
{
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
}
inet_ntop ( AF_INET, &(alert->ip_src_addr), src_addr, INET_ADDRSTRLEN );
inet_ntop ( AF_INET, &(alert->ip_dst_addr), dst_addr, INET_ADDRSTRLEN );
pyalert->classification = alert->classification ? PyString_FromString ( alert->classification ) : Py_None;
pyalert->clusteredAlertsCount = alert->grouped_alerts_count;
pyalert->desc = alert->desc ? PyString_FromString ( alert->desc ) : Py_None;
pyalert->gid = alert->gid;
pyalert->ip_src_addr = ( strlen ( src_addr ) > 0 ) ? PyString_FromString ( src_addr ) : Py_None;
pyalert->ip_dst_addr = ( strlen ( dst_addr ) > 0 ) ? PyString_FromString ( dst_addr ) : Py_None;
pyalert->priority = alert->priority;
pyalert->rev = alert->rev;
pyalert->sid = alert->sid;
pyalert->tcp_src_port = ntohs ( alert->tcp_src_port );
pyalert->tcp_dst_port = ntohs ( alert->tcp_dst_port );
pyalert->timestamp = alert->timestamp;
return pyalert;
} /* ----- end of function AI_alert_to_pyalert ----- */
#endif
/**
* \brief Initialize the extra modules provided by the user
*/
@ -73,6 +148,12 @@ AI_init_corr_modules ()
size_t n_dl_handles = 0;
struct dirent *dir_info = NULL;
#ifdef HAVE_LIBPYTHON2_6
char *pyPath = NULL;
PyObject *pObj = NULL;
BOOL isPyInit = false;
#endif
if ( !( dir = opendir ( config->corr_modules_dir )))
{
return;
@ -141,6 +222,58 @@ AI_init_corr_modules ()
AI_fatal_err ( "dlsym error", __FILE__, __LINE__ );
}
} else if ( preg_match ( "\\.py$", dir_info->d_name, NULL, NULL )) {
#ifdef HAVE_LIBPYTHON2_6
if ( !( pyPath = (char*) malloc ( strlen ( config->corr_modules_dir ) + strlen ( Py_GetPath() ) + 4 )))
{
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
}
fname = strdup ( dir_info->d_name );
fname[strlen ( fname ) - 3] = 0;
if ( !isPyInit )
{
Py_Initialize();
isPyInit = true;
}
sprintf ( pyPath, "%s:%s", config->corr_modules_dir, Py_GetPath() );
PySys_SetPath ( pyPath );
if ( !( pObj = PyImport_ImportModule ( fname )))
{
PyErr_Print();
AI_fatal_err ( "Could not load a Python correlation module", __FILE__, __LINE__ );
}
free ( fname );
fname = NULL;
if ( !( py_corr_functions = (PyObject**) realloc ( py_corr_functions, (++n_py_corr_functions) * sizeof ( PyObject* ))))
{
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
}
if ( !( py_corr_functions[ n_py_corr_functions - 1 ] = PyObject_GetAttrString ( pObj, "AI_corr_index" )))
{
AI_fatal_err ( "AI_corr_index() method not found in the Python correlation module", __FILE__, __LINE__ );
}
if ( !( py_weight_functions = (PyObject**) realloc ( py_weight_functions, (++n_py_weight_functions) * sizeof ( PyObject* ))))
{
AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ );
}
if ( !( py_weight_functions[ n_py_weight_functions - 1 ] = PyObject_GetAttrString ( pObj, "AI_corr_index_weight" )))
{
AI_fatal_err ( "AI_corr_index_weight() method not found in the Python correlation module", __FILE__, __LINE__ );
}
Py_DECREF ( pObj );
#endif
}
}

20
pymodule/README Normal file
View file

@ -0,0 +1,20 @@
Python module for interfacing with SnortAI. Compile it and install it through
$ python setup.py build
$ [sudo] python setup.py install
You can then access the alerts information captured by Snort simply by writing a
code like the following (also see module.py):
import snortai
alerts = snortai.alerts()
for alert in alerts:
# Access the information
The alert class has the following members:
# id, gid, sid, rev, description, priority, classification, timestamp
# from, to, from_port, to_port, latitude, longitude, alerts_count

21
pymodule/module.py Normal file
View file

@ -0,0 +1,21 @@
#!/usr/bin/python
# Compile snortai module by typing, in pymodule directory:
# $ python setup.py build
# $ [sudo] python setup.py install
import snortai
# Get the alerts from Snort module as tuple
# (IMPORTANT: Snort and SnortAI module, as well as
# the web server running on top of the module, must
# be running in order to have this call successful)
alerts = snortai.alerts()
# Navigate the tuple of alerts
# Fields:
# id, gid, sid, rev, description, priority, classification,
# timestamp, from, to, from_port, to_port, latitude,
# longitude, alerts_count
for alert in alerts:
print alert.gid, alert.sid, alert.rev, alert.description

34
pymodule/setup.py Normal file
View file

@ -0,0 +1,34 @@
#!/usr/bin/python
from distutils.core import setup, Extension
import commands
import re
xml_include = commands.getoutput ( 'pkg-config --cflags libxml-2.0' )
m = re.match ( '^-I\s*(.+?)\s*$', xml_include )
if m:
xml_include = m.group ( 1 )
xml_libs = commands.getoutput ( 'pkg-config --libs libxml-2.0' )
m = re.match ( '^-l\s*(.+?)\s*$', xml_libs )
if m:
xml_libs = m.group ( 1 )
setup (
name = 'snortai',
version = '0.1',
description = 'Python interface to SnortAI module',
author = 'Fabio "BlackLight" Manganiello',
author_email = 'blacklight@autistici.org',
ext_modules = [
Extension (
'snortai',
sources = ['snortai_module.c'],
include_dirs = ['..', '../include', '../uthash', xml_include],
libraries = [xml_libs]
)
]
)

496
pymodule/snortai_module.c Normal file
View file

@ -0,0 +1,496 @@
/*
* =====================================================================================
*
* Filename: snortai_module.c
*
* Description: Python module for interfacing to SnortAI
*
* Version: 0.1
* Created: 28/01/2011 21:33:57
* Revision: none
* Compiler: gcc
*
* Author: BlackLight (http://0x00.ath.cx), <blacklight@autistici.org>
* Licence: GNU GPL v.3
* Company: DO WHAT YOU WANT CAUSE A PIRATE IS FREE, YOU ARE A PIRATE!
*
* =====================================================================================
*/
#ifndef HAVE_LIBPYTHON2_6
#define HAVE_LIBPYTHON2_6 1
#endif
#include "spp_ai.h"
#include <libxml/xmlreader.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <structmember.h>
#include <sys/socket.h>
#ifndef LIBXML_READER_ENABLED
#error "libxml2 reader not enabled\n"
#endif
#ifndef PyMODINIT_FUNC
#define PyMODINIT_FUNC void
#endif
#ifndef DEFAULT_WEBSERV
#define DEFAULT_WEBSERV "localhost"
#endif
#ifndef DEFAULT_WEBPORT
#define DEFAULT_WEBPORT 7654
#endif
#ifndef DEFAULT_RESOURCE
#define DEFAULT_RESOURCE "/alerts.cgi"
#endif
/** Enumeration for managing XML response tags */
enum { inAlerts, inAlert, ALERT_TAG_NUM };
static PyObject* Py_alerts ( PyObject *self, PyObject *args );
static PyObject* PyAlert_new ( PyTypeObject *type, PyObject *args, PyObject *kwds );
static int PyAlert_init ( PyAlert *self, PyObject *args, PyObject *kwds );
static void PyAlerts_free ( PyAlert *self );
PyMODINIT_FUNC initsnortai ( void );
char resource[1024] = DEFAULT_RESOURCE;
char webserv[1024] = DEFAULT_WEBSERV;
short int webport = DEFAULT_WEBPORT;
PyObject *exception = NULL;
/** Members of the Python binded alert object */
static PyMemberDef alert_members[] = {
{ "gid", T_INT, offsetof ( PyAlert, gid ), 0, "Snort alert gID" },
{ "sid", T_INT, offsetof ( PyAlert, sid ), 0, "Snort alert sID" },
{ "rev", T_INT, offsetof ( PyAlert, rev ), 0, "Snort alert revision number" },
{ "priority", T_INT, offsetof ( PyAlert, priority ), 0, "Snort alert priority" },
{ "description", T_OBJECT_EX, offsetof ( PyAlert, desc ), 0, "Snort alert description" },
{ "classification", T_OBJECT_EX, offsetof ( PyAlert, classification ), 0, "Snort alert classification" },
{ "timestamp", T_INT, offsetof ( PyAlert, timestamp ), 0, "Snort alert timestamp" },
{ "from", T_OBJECT_EX, offsetof ( PyAlert, ip_src_addr ), 0, "Source IP address" },
{ "to", T_OBJECT_EX, offsetof ( PyAlert, ip_dst_addr ), 0, "Destination IP address" },
{ "from_port", T_INT, offsetof ( PyAlert, tcp_src_port ), 0, "Source port" },
{ "to_port", T_INT, offsetof ( PyAlert, tcp_dst_port ), 0, "Destination port" },
{ "latitude", T_DOUBLE, offsetof ( PyAlert, latitude ), 0, "Geographical latitude, if available" },
{ "longitude", T_DOUBLE, offsetof ( PyAlert, longitude ), 0, "Geographical longitude, if available" },
{ "alert_count", T_INT, offsetof ( PyAlert, clusteredAlertsCount ), 0, "Number of alerts clustered in this object" },
{ NULL }
};
/** Methods inside of the Python binded alert object */
static PyMethodDef alert_methods[] = {{ NULL }};
/** Module methods */
static PyMethodDef module_methods[] = {
{ "alerts", (PyCFunction) Py_alerts, METH_VARARGS, "Return the list of SnortAI alerts" },
{ NULL, NULL, 0, NULL }
};
/** Definition of the Python binded alert object */
static PyTypeObject alert_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size*/
"snortai.__alert", /* tp_name*/
sizeof(PyAlert), /* tp_basicsize*/
0, /* tp_itemsize*/
(destructor)PyAlerts_free, /* tp_dealloc*/
0, /* tp_print*/
0, /* tp_getattr*/
0, /* tp_setattr*/
0, /* tp_compare*/
0, /* tp_repr*/
0, /* tp_as_number*/
0, /* tp_as_sequence*/
0, /* tp_as_mapping*/
0, /* tp_hash */
0, /* tp_call*/
0, /* tp_str*/
0, /* tp_getattro*/
0, /* tp_setattro*/
0, /* tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"SnortAI alert type", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
alert_methods, /* tp_methods */
alert_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)PyAlert_init, /* tp_init */
0, /* tp_alloc */
PyAlert_new, /* tp_new */
};
static PyObject*
Py_alerts ( PyObject *module, PyObject *args )
{
int i, sd, len = 0, xml_offset = -1;
char addr[INET_ADDRSTRLEN] = { 0 };
char *response = NULL;
unsigned int response_len = 1;
unsigned int n_alerts = 0;
FILE *fsock = NULL;
struct sockaddr_in server;
struct hostent *host;
PyAlert *alerts = NULL, *cur_alert = NULL, *prev_alert = NULL;
PyObject *self = NULL;
BOOL xmlFlags[ALERT_TAG_NUM] = { false };
xmlTextReaderPtr xml;
const xmlChar *tagname;
/* Connect to the web server and get the XML containing the alerts */
if ( !( host = gethostbyname ( webserv )))
{
PyErr_SetString ( exception, "Could not resolve web server name" );
return NULL;
}
if ( !( host->h_addr ))
{
PyErr_SetString ( exception, "Could not resolve web server name" );
return NULL;
}
snprintf ( addr, sizeof ( addr ), "%u.%u.%u.%u",
(unsigned char) host->h_addr[0],
(unsigned char) host->h_addr[1],
(unsigned char) host->h_addr[2],
(unsigned char) host->h_addr[3] );
if (( sd = socket ( AF_INET, SOCK_STREAM, IPPROTO_IP )) < 0 )
{
PyErr_SetString ( exception, "Could not initialize the socket" );
return NULL;
}
server.sin_family = AF_INET;
server.sin_port = htons ( webport );
server.sin_addr.s_addr = inet_addr ( addr );
if ( connect ( sd, ( struct sockaddr* ) &server, sizeof ( struct sockaddr )) < 0 )
{
PyErr_SetString ( exception, "Could not connect to the web server" );
close ( sd );
return NULL;
}
if ( !( fsock = fdopen ( sd, "r+" )))
{
PyErr_SetString ( exception, "Could not open the socket" );
close ( sd );
return NULL;
}
fprintf ( fsock,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: Python SnortAI request\r\n"
"Connection: close\r\n\r\n",
resource, webserv );
while ( !feof ( fsock ))
{
if ( !( response = (char*) realloc ( response, ++response_len )))
{
PyErr_SetString ( exception, "Dynamic memory allocation error" );
fclose ( fsock );
close ( sd );
return NULL;
}
response[ response_len - 2 ] = fgetc ( fsock );
}
response[ (--response_len) - 1 ] = 0;
fclose ( fsock );
close ( sd );
/*****************************/
/* Remove the HTTP headers from the response */
if (( xml_offset = (int) strstr ( response, "\n\n" ) - (int) response ) >= 0 ) {}
else if (( xml_offset = (int) strstr ( response, "\r\n\r\n" ) - (int) response ) >= 0 ) {}
else {
PyErr_SetString ( exception, "The HTTP response provided by the server has no valid HTTP header" );
free ( response );
return NULL;
}
len = strlen ( response );
for ( i=0; i < len - xml_offset; i++ )
{
response[i] = response [i + xml_offset];
}
response [len - xml_offset] = 0;
for ( xml_offset=0; response[xml_offset] != '<'; xml_offset++ );
for ( i=0; i < len - xml_offset; i++ )
{
response[i] = response[i + xml_offset];
}
response [len - xml_offset] = 0;
/*****************************/
/* Parse the XML document */
LIBXML_TEST_VERSION
if ( !( xml = xmlReaderForMemory ( response, strlen ( response ), NULL, NULL, 0 )))
{
PyErr_SetString ( exception, "Could not initialize the XML reader object" );
free ( response );
return NULL;
}
while ( xmlTextReaderRead ( xml ))
{
if ( !( tagname = xmlTextReaderConstName ( xml )))
continue;
if ( xmlTextReaderNodeType ( xml ) == XML_READER_TYPE_ELEMENT )
{
if ( !strcasecmp ((const char*) tagname, "alerts" ))
{
if ( xmlFlags[inAlerts] )
{
PyErr_SetString ( exception, "Parse error in received XML: 'alerts' tag opened twice" );
free ( response );
return NULL;
} else {
xmlFlags[inAlerts] = true;
}
} else if ( !strcasecmp ((const char*) tagname, "alert" ))
{
if ( xmlFlags[inAlert] )
{
PyErr_SetString ( exception, "Parse error in received XML: 'alert' tag opened inside of another 'alert' tag" );
free ( response );
return NULL;
} else {
xmlFlags[inAlert] = true;
/* Fill the PyObject */
if ( !( cur_alert = (PyAlert*) alert_type.tp_alloc ( &alert_type, 0 )))
{
PyErr_SetString ( exception, "Could not initialize the PyAlert object" );
free ( response );
return NULL;
}
if ( !alerts )
{
alerts = cur_alert;
}
if ( prev_alert )
{
prev_alert->next = cur_alert;
}
n_alerts++;
cur_alert->next = NULL;
cur_alert->id = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "id" )) ?
(unsigned int) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "id" ), NULL, 10 ) : 0;
cur_alert->gid = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "gid" )) ?
(unsigned int) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "gid" ), NULL, 10 ) : 0;
cur_alert->sid = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "sid" )) ?
(unsigned int) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "sid" ), NULL, 10 ) : 0;
cur_alert->rev = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "rev" )) ?
(unsigned int) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "rev" ), NULL, 10 ) : 0;
cur_alert->priority = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "priority" )) ?
(unsigned int) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "priority" ), NULL, 10 ) : 0;
cur_alert->timestamp = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "timestamp" )) ?
(time_t) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "timestamp" ), NULL, 10 ) : (time_t) 0;
cur_alert->tcp_src_port = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "from_port" )) ?
(unsigned short) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "from_port" ), NULL, 10 ) : 0;
cur_alert->tcp_dst_port = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "to_port" )) ?
(unsigned short) strtol ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "to_port" ), NULL, 10 ) : 0;
cur_alert->latitude = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "latitude" )) ?
strtod ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "latitude" ), NULL ) : 0.0;
cur_alert->longitude = ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "longitude" )) ?
strtod ((const char*) xmlTextReaderGetAttribute ( xml, (const xmlChar*) "longitude" ), NULL ) : 0.0;
if ( !( cur_alert->desc =
xmlTextReaderGetAttribute ( xml, (const xmlChar*) "label" ) ?
PyString_FromString ((char*) ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "label" ))) : Py_None ))
{
PyErr_SetString ( exception, "Could not initialize a field in PyAlert object" );
free ( response );
return NULL;
}
if ( !( cur_alert->classification =
xmlTextReaderGetAttribute ( xml, (const xmlChar*) "classification" ) ?
PyString_FromString ((char*) ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "classification" ))) : Py_None ))
{
PyErr_SetString ( exception, "Could not initialize a field in PyAlert object" );
free ( response );
return NULL;
}
if ( !( cur_alert->ip_src_addr =
xmlTextReaderGetAttribute ( xml, (const xmlChar*) "from" ) ?
PyString_FromString ((char*) ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "from" ))) : Py_None ))
{
PyErr_SetString ( exception, "Could not initialize a field in PyAlert object" );
free ( response );
return NULL;
}
if ( !( cur_alert->ip_dst_addr =
xmlTextReaderGetAttribute ( xml, (const xmlChar*) "to" ) ?
PyString_FromString ((char*) ( xmlTextReaderGetAttribute ( xml, (const xmlChar*) "to" ))) : Py_None ))
{
PyErr_SetString ( exception, "Could not initialize a field in PyAlert object" );
free ( response );
return NULL;
}
prev_alert = cur_alert;
}
} else {
PyErr_SetString ( exception, "Unrecognized XML tag in received response" );
free ( response );
return NULL;
}
} else if ( xmlTextReaderNodeType ( xml ) == XML_READER_TYPE_END_ELEMENT ) {
if ( !strcasecmp ((const char*) tagname, "alerts" ))
{
if ( !xmlFlags[inAlerts] )
{
PyErr_SetString ( exception, "'alerts' tag closed, but never opened" );
free ( response );
return NULL;
} else {
xmlFlags[inAlerts] = false;
}
} else if ( !strcasecmp ((const char*) tagname, "alert" )) {
if ( !xmlFlags[inAlert] )
{
PyErr_SetString ( exception, "'alert' tag closed, but never opened" );
free ( response );
return NULL;
} else {
xmlFlags[inAlert] = false;
}
}
}
}
xmlFreeTextReader ( xml );
xmlCleanupParser();
free ( response );
/*****************************/
/* Build the alerts tuple */
if ( n_alerts > 0 )
{
if ( !( self = PyTuple_New ( n_alerts )))
{
PyErr_SetString ( exception, "Could not initialize the vector of alerts" );
return NULL;
}
for ( i=0, cur_alert = alerts; cur_alert; cur_alert = cur_alert->next, i++ )
{
PyTuple_SetItem ( self, i, Py_BuildValue ( "O", cur_alert ));
Py_INCREF ((PyObject*) cur_alert );
}
} else {
Py_RETURN_NONE;
}
return self;
}
static int
PyAlert_init ( PyAlert *self, PyObject *args, PyObject *kwds )
{
static char *kwlist[] = {
"gid", "sid", "rev",
"priority", "description", "classifcation",
"timestamp", "from", "to", "from_port", "to_port",
"latitude", "longitude", "alert_count", NULL
};
if ( !( PyArg_ParseTupleAndKeywords ( args, kwds, "|iiiiOOiOOiiddi", kwlist,
&self->gid, &self->sid, &self->rev, &self->priority, &self->desc,
&self->classification, &self->timestamp,
&self->ip_src_addr, &self->ip_dst_addr, &self->tcp_src_port, &self->tcp_dst_port,
&self->latitude, &self->longitude, &self->clusteredAlertsCount )))
{
PyErr_SetString ( exception, "Could not initialize a PyAlert object" );
return -1;
}
return 0;
}
static PyObject*
PyAlert_new ( PyTypeObject *type, PyObject *args, PyObject *kwds )
{
PyAlert *self = NULL;
if ( !( self = (PyAlert*) type->tp_alloc ( type, 0 )))
{
PyErr_SetString ( exception, "Could not allocate a PyAlert object" );
return NULL;
}
return (PyObject*) self;
}
static void
PyAlerts_free ( PyAlert *self )
{
Py_XDECREF ( self->classification );
Py_XDECREF ( self->desc );
self->ob_type->tp_free (( PyObject* ) self );
}
PyMODINIT_FUNC
initsnortai ( void )
{
PyObject *m = NULL;
if ( PyType_Ready ( &alert_type ) < 0 )
{
return;
}
if ( !( m = Py_InitModule ( "snortai", module_methods )))
{
return;
}
if ( !exception )
{
exception = PyErr_NewException ( "snortai.error", NULL, NULL );
Py_INCREF ( exception );
}
Py_INCREF ( &alert_type );
PyModule_AddObject ( m, "resource", PyString_FromString ( resource ));
PyModule_AddObject ( m, "webserv", PyString_FromString ( webserv ));
PyModule_AddObject ( m, "webport", PyInt_FromLong ((long int) webport ));
/* PyModule_AddObject ( m, "alerts", (PyObject*) &alert_type ); */
}

View file

@ -543,7 +543,60 @@ typedef struct {
UT_hash_handle hh;
} AI_alert_type_pair;
/*****************************************************************/
#ifdef HAVE_LIBPYTHON2_6
/*******************************************/
/* Avoid conflicts with Snort header files */
#ifdef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#ifdef _XOPEN_C_SOURCE
#undef _XOPEN_C_SOURCE
#endif
#ifdef _XOPEN_SOURCE
#undef _XOPEN_SOURCE
#endif
/*******************************************/
#include <Python.h>
/** Definition of the alert type for Python binding */
typedef struct _PyAlert
{
PyObject_HEAD
unsigned int id;
/* Identifiers of the alert */
unsigned int gid;
unsigned int sid;
unsigned int rev;
/* Snort priority, description,
* classification and timestamp
* of the alert */
unsigned short priority;
PyObject* desc;
PyObject* classification;
time_t timestamp;
PyObject* ip_src_addr;
PyObject* ip_dst_addr;
unsigned short tcp_src_port;
unsigned short tcp_dst_port;
double latitude;
double longitude;
unsigned int clusteredAlertsCount;
struct _PyAlert *next;
} PyAlert;
#endif
/*****************************************************************/
/** Enumeration for describing the table in the output database */
@ -585,7 +638,7 @@ void AI_pkt_enqueue ( SFSnortPacket* );
void AI_set_stream_observed ( struct pkt_key key );
void AI_hierarchies_build ( hierarchy_node**, int );
void AI_free_alerts ( AI_snort_alert *node );
void AI_init_corr_modules ();
void AI_init_corr_modules ( void );
struct pkt_info* AI_get_stream_by_key ( struct pkt_key );
AI_snort_alert* AI_get_alerts ( void );
@ -594,32 +647,40 @@ AI_snort_alert* AI_get_clustered_alerts ( void );
void AI_serialize_alerts ( AI_snort_alert**, unsigned int );
void AI_serializer ( AI_snort_alert* );
void* AI_deserialize_alerts ();
void* AI_deserialize_alerts ( void );
void* AI_alerts_pool_thread ( void* );
void* AI_neural_thread ( void* );
void* AI_manual_correlations_parsing_thread ( void* );
void* AI_neural_clustering_thread ( void* );
const AI_alert_event* AI_get_alert_events_by_key ( AI_alert_event_key );
unsigned int AI_get_history_alert_number ();
unsigned int AI_get_history_alert_number ( void );
double AI_alert_bayesian_correlation ( const AI_snort_alert*, const AI_snort_alert* );
double AI_alert_neural_som_correlation ( const AI_snort_alert*, const AI_snort_alert* );
double AI_kb_correlation_coefficient ( const AI_snort_alert*, const AI_snort_alert* );
double AI_neural_correlation_weight ();
double AI_bayesian_correlation_weight ();
double AI_neural_correlation_weight ( void );
double AI_bayesian_correlation_weight ( void );
int AI_geoinfobyaddr ( const char*, double** );
void AI_outdb_mutex_initialize ();
void AI_outdb_mutex_initialize ( void );
void AI_store_alert_to_db ( AI_snort_alert* );
void AI_store_cluster_to_db ( AI_alerts_couple* );
void AI_store_correlation_to_db ( AI_alert_correlation* );
void AI_kb_index_init ( AI_snort_alert* );
AI_alerts_per_neuron* AI_get_alerts_per_neuron ();
AI_alerts_per_neuron* AI_get_alerts_per_neuron ( void );
double(**AI_get_corr_functions ( size_t* ))(const AI_snort_alert*, const AI_snort_alert*);
double(**AI_get_corr_weights ( size_t* ))();
double(**AI_get_corr_weights ( size_t* ))( void );
#ifdef HAVE_LIBPYTHON2_6
PyObject** AI_get_py_functions ( size_t* );
PyObject** AI_get_py_weights ( size_t* );
PyAlert* AI_alert_to_pyalert ( AI_snort_alert* );
#endif
/** Function pointer to the function used for getting the alert list (from log file, db, ...) */
extern AI_snort_alert* (*get_alerts)(void);

View file

@ -385,6 +385,8 @@ __AI_webservlet_thread ( void *arg )
strncpy ( content_type, "application/x-javascript", sizeof ( content_type ));
} else if ( !strcasecmp ( extension, "json" )) {
strncpy ( content_type, "application/json", sizeof ( content_type ));
} else if ( !strcasecmp ( extension, "xml" )) {
strncpy ( content_type, "application/xml", sizeof ( content_type ));
} else if ( !strcasecmp ( extension, "jpg" ) || !strcasecmp ( extension, "jpeg" )) {
strncpy ( content_type, "image/jpeg", sizeof ( content_type ));
} else if ( !strcasecmp ( extension, "cgi" )) {