From e084f75b73aff7f12a263a3b5695faa6d185f3c8 Mon Sep 17 00:00:00 2001 From: BlackLight Date: Wed, 1 Dec 2010 23:25:41 +0100 Subject: [PATCH] Integration with GeoIP and GMaps in web interface --- Makefile.am | 1 + Makefile.in | 8 ++- TODO | 5 +- alert_parser.c | 41 ++++++++++- corr_rules/1-1147-10.xml | 14 ++++ corr_rules/1-1292-9.xml | 14 ++++ corr_rules/1-1390-8.xml | 2 +- corr_rules/1-15384-3.xml | 15 ++++ correlation.c | 14 +++- db.c | 38 +++++++++++ geo.c | 144 +++++++++++++++++++++++++++++++++++++++ htdocs/index.html | 25 ++++++- spp_ai.h | 13 ++++ 13 files changed, 322 insertions(+), 12 deletions(-) create mode 100644 corr_rules/1-1147-10.xml create mode 100644 corr_rules/1-1292-9.xml create mode 100644 corr_rules/1-15384-3.xml create mode 100644 geo.c diff --git a/Makefile.am b/Makefile.am index a79e0f3..2b0ca10 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,7 @@ correlation.c \ db.c \ fkmeans/kmeans.c \ fsom/fsom.c \ +geo.c \ modules.c \ mysql.c \ neural.c \ diff --git a/Makefile.in b/Makefile.in index 1a02617..93b3ffb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -85,8 +85,8 @@ am_libsf_ai_preproc_la_OBJECTS = libsf_ai_preproc_la-alert_history.lo \ libsf_ai_preproc_la-cluster.lo \ libsf_ai_preproc_la-correlation.lo libsf_ai_preproc_la-db.lo \ libsf_ai_preproc_la-kmeans.lo libsf_ai_preproc_la-fsom.lo \ - libsf_ai_preproc_la-modules.lo libsf_ai_preproc_la-mysql.lo \ - libsf_ai_preproc_la-neural.lo \ + libsf_ai_preproc_la-geo.lo libsf_ai_preproc_la-modules.lo \ + libsf_ai_preproc_la-mysql.lo libsf_ai_preproc_la-neural.lo \ libsf_ai_preproc_la-neural_cluster.lo \ libsf_ai_preproc_la-outdb.lo libsf_ai_preproc_la-postgresql.lo \ libsf_ai_preproc_la-regex.lo libsf_ai_preproc_la-spp_ai.lo \ @@ -271,6 +271,7 @@ correlation.c \ db.c \ fkmeans/kmeans.c \ fsom/fsom.c \ +geo.c \ modules.c \ mysql.c \ neural.c \ @@ -426,6 +427,9 @@ libsf_ai_preproc_la-kmeans.lo: fkmeans/kmeans.c libsf_ai_preproc_la-fsom.lo: fsom/fsom.c $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-fsom.lo `test -f 'fsom/fsom.c' || echo '$(srcdir)/'`fsom/fsom.c +libsf_ai_preproc_la-geo.lo: geo.c + $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-geo.lo `test -f 'geo.c' || echo '$(srcdir)/'`geo.c + libsf_ai_preproc_la-modules.lo: modules.c $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_ai_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_ai_preproc_la-modules.lo `test -f 'modules.c' || echo '$(srcdir)/'`modules.c diff --git a/TODO b/TODO index 4935c88..8a79ea7 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,9 @@ AVERAGE/HIGH PRIORITY: - Code profiling - Comment all the code!!! -- Testing more scenarios, making more hyperalert models +- Support for more logs +- Geographical IP localization and visualization +- True bayesian temporal correlation ============= LOW PRIORITY: @@ -35,4 +37,5 @@ DONE: + Manual alert correlation from the web interface + Neural network for alert correlation + Supporting extra modules for alert correlation ++ Testing more scenarios, making more hyperalert models diff --git a/alert_parser.c b/alert_parser.c index 9abb40b..cfed5d5 100644 --- a/alert_parser.c +++ b/alert_parser.c @@ -31,6 +31,7 @@ /** \defgroup alert_parser Parse the alert log into binary structures * @{ */ +PRIVATE AI_geoip_cache *geoip = NULL; PRIVATE AI_snort_alert *alerts = NULL; PRIVATE FILE *alert_fp = NULL; PRIVATE pthread_mutex_t alert_mutex; @@ -124,16 +125,19 @@ AI_file_alertparser_thread ( void* arg ) int nmatches = 0; char line[8192]; char strtime[256]; + char ip[INET_ADDRSTRLEN] = { 0 }; char **matches = NULL; + double *geocoord = NULL; time_t stamp; struct tm *_tm; struct logtime ltime; struct pkt_key key; struct pkt_info *info; - AI_snort_alert *alert = NULL; - AI_snort_alert *tmp = NULL; - BOOL in_alert = false; + AI_geoip_cache *found = NULL; + AI_snort_alert *alert = NULL; + AI_snort_alert *tmp = NULL; + BOOL in_alert = false; pthread_t alerts_pool_thread; @@ -264,6 +268,37 @@ AI_file_alertparser_thread ( void* arg ) alert->stream = info; } } + + /* GeoIP stuff */ + memset ( ip, 0, sizeof ( ip )); + inet_ntop ( AF_INET, &(alert->ip_src_addr), ip, sizeof ( ip )); + HASH_FIND_STR ( geoip, ip, found ); + + if ( !found ) + { + if ( !( found = (AI_geoip_cache*) malloc ( sizeof ( AI_geoip_cache )))) + { + AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ ); + } + + geocoord = NULL; + strcpy ( found->ip, ip ); + + if ( AI_geoinfobyaddr ( ip, &geocoord ) > 0 ) + { + found->geocoord[0] = geocoord[0]; + found->geocoord[1] = geocoord[1]; + } else { + found->geocoord[0] = 0.0; + found->geocoord[1] = 0.0; + } + + HASH_ADD_STR ( geoip, ip, found ); + free ( geocoord ); + } + + alert->geocoord[0] = found->geocoord[0]; + alert->geocoord[1] = found->geocoord[1]; } if ( alerts == NULL ) diff --git a/corr_rules/1-1147-10.xml b/corr_rules/1-1147-10.xml new file mode 100644 index 0000000..63d77cf --- /dev/null +++ b/corr_rules/1-1147-10.xml @@ -0,0 +1,14 @@ + + + + + 1.1147.10 + WEB-MISC cat%20 access + +
HostExists(+DST_ADDR+)
+
HasService(+DST_ADDR+, +DST_PORT+)
+
HasHttpInfo(+SRC_ADDR+, +DST_ADDR+)
+ + HasFileInfo(+SRC_ADDR+, +DST_ADDR+) +
+ diff --git a/corr_rules/1-1292-9.xml b/corr_rules/1-1292-9.xml new file mode 100644 index 0000000..1a557b2 --- /dev/null +++ b/corr_rules/1-1292-9.xml @@ -0,0 +1,14 @@ + + + + + 1.1292.9 + ATTACK-RESPONSES directory listing + +
HostExists(+DST_ADDR+)
+
HasService(+DST_ADDR+, +DST_PORT+)
+
HasHttpInfo(+SRC_ADDR+, +DST_ADDR+)
+ + HasFileInfo(+SRC_ADDR+, +DST_ADDR+) +
+ diff --git a/corr_rules/1-1390-8.xml b/corr_rules/1-1390-8.xml index 4ab130b..0058d4e 100644 --- a/corr_rules/1-1390-8.xml +++ b/corr_rules/1-1390-8.xml @@ -2,7 +2,7 @@ - 1.1380.8 + 1.1390.8 Shellcode x86 inc ebx NOOP
HostExists(+DST_ADDR+)
diff --git a/corr_rules/1-15384-3.xml b/corr_rules/1-15384-3.xml new file mode 100644 index 0000000..a054ba9 --- /dev/null +++ b/corr_rules/1-15384-3.xml @@ -0,0 +1,15 @@ + + + + + 1.15384.3 + WEB-CLIENT Apple QuickTime pict image poly structure memory corruption attempt + +
HostExists(+DST_ADDR+)
+
HasService(+DST_ADDR+, +DST_PORT+)
+
HasHttpInfo(+SRC_ADDR+, +DST_ADDR+)
+
HasFileInfo(+SRC_ADDR+, +DST_ADDR+)
+ + HasRemoteAccess(+SRC_ADDR+, +DST_ADDR+) +
+ diff --git a/correlation.c b/correlation.c index fb94896..d1e09d0 100644 --- a/correlation.c +++ b/correlation.c @@ -190,7 +190,9 @@ __AI_correlated_alerts_to_json () "\t\"date\": \"%s\",\n" "\t\"clusteredAlertsCount\": %u,\n" "\t\"from\": \"%s:%s\",\n" - "\t\"to\": \"%s:%s\"", + "\t\"to\": \"%s:%s\",\n" + "\t\"latitude\": \"%f\",\n" + "\t\"longitude\": \"%f\"", alert_iterator->alert_id, alert_iterator->sid, alert_iterator->gid, @@ -198,7 +200,9 @@ __AI_correlated_alerts_to_json () alert_iterator->desc, strtime, alert_iterator->grouped_alerts_count, - srcip, srcport, dstip, dstport + srcip, srcport, dstip, dstport, + alert_iterator->geocoord[0], + alert_iterator->geocoord[1] ); if ( alert_iterator->stream ) @@ -255,11 +259,15 @@ __AI_correlated_alerts_to_json () "\t\t\t\"label\": \"%s\",\n" "\t\t\t\"date\": \"%s\",\n" "\t\t\t\"from\": \"%s:%s\",\n" - "\t\t\t\"to\": \"%s:%s\"%s", + "\t\t\t\"to\": \"%s:%s\",\n" + "\t\t\t\"latitude\": \"%f\",\n" + "\t\t\t\"longitude\": \"%f\"%s", alert_iterator->grouped_alerts[i]->alert_id, alert_iterator->grouped_alerts[i]->desc, strtime, srcip, srcport, dstip, dstport, + alert_iterator->grouped_alerts[i]->geocoord[0], + alert_iterator->grouped_alerts[i]->geocoord[1], (( alert_iterator->grouped_alerts[i]->stream ) ? ",\n" : "\n" ) ); diff --git a/db.c b/db.c index 00baba0..ede3790 100644 --- a/db.c +++ b/db.c @@ -30,6 +30,7 @@ PRIVATE AI_snort_alert *alerts = NULL; +PRIVATE AI_geoip_cache *geoip = NULL; PRIVATE pthread_mutex_t mutex; /** @@ -40,9 +41,11 @@ void* AI_db_alertparser_thread ( void *arg ) { unsigned int i = 0; + char ip[INET_ADDRSTRLEN] = { 0 }; char query[1024] = { 0 }; int rows = 0; int latest_cid = 0; + double *geocoord = NULL; time_t latest_time = time ( NULL ); pthread_t alerts_pool_thread; @@ -53,6 +56,7 @@ AI_db_alertparser_thread ( void *arg ) struct pkt_info *info = NULL; AI_snort_alert *alert = NULL; AI_snort_alert *tmp = NULL; + AI_geoip_cache *found = NULL; pthread_mutex_init ( &mutex, NULL ); @@ -235,6 +239,40 @@ AI_db_alertparser_thread ( void *arg ) } } + /* Get, if available, the geographical coordinates of the attacking IP address */ + if ( alert->ip_src_addr ) + { + memset ( ip, 0, sizeof ( ip )); + inet_ntop ( AF_INET, &(alert->ip_src_addr), ip, sizeof ( ip )); + HASH_FIND_STR ( geoip, ip, found ); + + if ( !found ) + { + if ( !( found = (AI_geoip_cache*) malloc ( sizeof ( AI_geoip_cache )))) + { + AI_fatal_err ( "Fatal dynamic memory allocation error", __FILE__, __LINE__ ); + } + + geocoord = NULL; + strcpy ( found->ip, ip ); + + if ( AI_geoinfobyaddr ( ip, &geocoord ) > 0 ) + { + found->geocoord[0] = geocoord[0]; + found->geocoord[1] = geocoord[1]; + } else { + found->geocoord[0] = 0.0; + found->geocoord[1] = 0.0; + } + + HASH_ADD_STR ( geoip, ip, found ); + free ( geocoord ); + } + + alert->geocoord[0] = found->geocoord[0]; + alert->geocoord[1] = found->geocoord[1]; + } + DB_free_result ( res ); latest_time = time ( NULL ); diff --git a/geo.c b/geo.c new file mode 100644 index 0000000..3c52cad --- /dev/null +++ b/geo.c @@ -0,0 +1,144 @@ +/* + * ===================================================================================== + * + * Filename: geo.c + * + * Description: Get the coordinates of an IP using www.hostip.info + * + * Version: 0.1 + * Created: 01/12/2010 17:18:21 + * Revision: none + * Compiler: gcc + * + * Author: BlackLight (http://0x00.ath.cx), + * Licence: GNU GPL v.3 + * Company: DO WHAT YOU WANT CAUSE A PIRATE IS FREE, YOU ARE A PIRATE! + * + * ===================================================================================== + */ + +#include "spp_ai.h" + +#include +#include +#include +#include +#include +#include +#include + +/** \defgroup geoinfo Geographic info management given an IP address using geoinfo.c + * @{ */ + +/** + * \brief Get latitude and longitude + * \param ip IP address + * \param coord double[2] object (NULL or not) that will contain latitude and longitude + * \return 1 if the coordinates were found, -1 otherwise + */ + +int +AI_geoinfobyaddr ( const char *ip, double **coord ) +{ + int i, sd, n_read, n_matches; + char buf[1024] = { 0 }, + hostip[INET_ADDRSTRLEN] = { 0 }, + query[100] = { 0 }; + + char **matches = NULL; + FILE *fp = NULL; + struct hostent *host = NULL; + struct sockaddr_in addr; + + if ( *coord == NULL ) + { + if ( !( *coord = (double*) calloc ( 2, sizeof ( double )))) + { + return -1; + } + } + + if (( sd = socket ( AF_INET, SOCK_STREAM, IPPROTO_IP )) < 0 ) + { + return -1; + } + + if ( !( host = gethostbyname ( "www.hostip.info" ))) + { + return -1; + } + + inet_ntop ( AF_INET, host->h_addr_list[0], hostip, sizeof ( hostip )); + memset ( &addr, 0, sizeof ( addr )); + + addr.sin_family = AF_INET; + addr.sin_port = htons ( 80 ); + addr.sin_addr.s_addr = inet_addr ( hostip ); + + if ( connect ( sd, (struct sockaddr*) &addr, sizeof ( struct sockaddr )) < 0 ) + { + return -1; + } + + if ( !( fp = fdopen ( sd, "r+" ))) + { + close ( sd ); + return -1; + } + + snprintf ( query, sizeof ( query ), "spip=%s&submit=Go", ip ); + fprintf ( fp, + "POST /index.html HTTP/1.1\r\n" + "Host: www.hostip.info\r\n" + "Content-Length: %u\r\n" + "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n" + "Connection: close\r\n\r\n" + "%s\r\n", + strlen ( query ), query + ); + + do + { + memset ( buf, 0, sizeof ( buf )); + n_read = fread ( buf, sizeof ( buf ), 1, fp ); + + if ( preg_match ( "new GLatLng.([^,]+), ([^\\)]+)", buf, &matches, &n_matches ) > 0 ) + { + (*coord)[0] = strtod ( matches[0], NULL ); + (*coord)[1] = strtod ( matches[1], NULL ); + + for ( i=0; i < n_matches; i++ ) + { + free ( matches[i] ); + } + + free ( matches ); + matches = NULL; + + fclose ( fp ); + close ( sd ); + + if ( (*coord)[0] == 0.0 && (*coord)[1] == 0.0 ) + { + return -1; + } else { + return 1; + } + } + + for ( i=0; i < n_matches; i++ ) + { + free ( matches[i] ); + } + + free ( matches ); + matches = NULL; + } while ( n_read > 0 ); + + fclose ( fp ); + close ( sd ); + return -1; +} /* ----- end of function AI_geoinfobyaddr ----- */ + +/** @} */ + diff --git a/htdocs/index.html b/htdocs/index.html index 2840347..e16b498 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -375,7 +375,17 @@ window.onload = function() { } content += '' + - json_element.from + '' + json_element.to + '' + + json_element.from; + + if ( parseFloat(json_element.latitude) != 0.0 || + parseFloat(json_element.longitude) != 0.0 ) + { + content += ' (locate)'; + } + + content += '' + json_element.to + '' + '' + json_element.date + ''; if ( json_element.clusteredAlerts ) @@ -433,7 +443,18 @@ window.onload = function() { } content += '' + - '' + json_element.clusteredAlerts[j].from + '' + + '' + json_element.clusteredAlerts[j].from; + + if ( parseFloat(json_element.clusteredAlerts[j].latitude) != 0.0 || + parseFloat(json_element.clusteredAlerts[j].longitude) != 0.0 ) + { + content += ' (locate)'; + } + + content += '' + '' + json_element.clusteredAlerts[j].to + '' + '' + json_element.clusteredAlerts[j].date + ''; } diff --git a/spp_ai.h b/spp_ai.h index f0cbe24..ffdc47e 100644 --- a/spp_ai.h +++ b/spp_ai.h @@ -28,6 +28,7 @@ #include "sf_dynamic_preprocessor.h" #include "uthash.h" +#include #include #define PRIVATE static @@ -420,6 +421,10 @@ typedef struct _AI_snort_alert { * and post-conditions*/ AI_hyperalert_info *hyperalert; + /** Latitude and longitude of the attacker IP, + * if available */ + double geocoord[2]; + /* Parent alerts in the chain, if any */ struct _AI_snort_alert **parent_alerts; @@ -509,6 +514,13 @@ typedef struct { UT_hash_handle hh; } AI_alerts_per_neuron; /*****************************************************************/ +/** Hash table holding analyzed geographical IP info */ +typedef struct { + char ip[INET_ADDRSTRLEN]; + double geocoord[2]; + UT_hash_handle hh; +} AI_geoip_cache; +/*****************************************************************/ /** Enumeration for describing the table in the output database */ @@ -567,6 +579,7 @@ double AI_alert_bayesian_correlation ( const AI_snort_alert*, co double AI_alert_neural_som_correlation ( const AI_snort_alert*, const AI_snort_alert* ); double AI_neural_correlation_weight (); double AI_bayesian_correlation_weight (); +int AI_geoinfobyaddr ( const char*, double** ); void AI_outdb_mutex_initialize (); void AI_store_alert_to_db ( AI_snort_alert* );