diff --git a/Makefile.am b/Makefile.am index cd0dcb2..d12de53 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,15 +47,14 @@ doc: doxygen; \ fi -doc-install: - if test ! -z "${DOC_PREFIX}"; then \ - mkdir -p "${DOC_PREFIX}"; \ - cp -r doc/* "${DOC_PREFIX}"; \ -fi - install-data-hook: + if test ! -z "${PWD}/doc"; then \ + mkdir -p "${SHARE_PREFIX}/doc"; \ + cp -r "${PWD}/doc/"* "${SHARE_PREFIX}/doc"; \ +fi mkdir -p "${SHARE_PREFIX}/htdocs" mkdir -p "${SHARE_PREFIX}/htdocs/js" + mkdir -p "${SHARE_PREFIX}/schemas" install -m 0644 "${PWD}/htdocs/index.html" "${SHARE_PREFIX}/htdocs" install -m 0755 "${PWD}/htdocs/pcap.cgi" "${SHARE_PREFIX}/htdocs" install -m 0644 "${PWD}/htdocs/js/Curry-1.0.1.js" "${SHARE_PREFIX}/htdocs/js" @@ -64,3 +63,6 @@ install-data-hook: install -m 0644 "${PWD}/htdocs/js/dracula_graph.js" "${SHARE_PREFIX}/htdocs/js" install -m 0644 "${PWD}/htdocs/js/raphael.js" "${SHARE_PREFIX}/htdocs/js" install -m 0644 "${PWD}/htdocs/js/seedrandom.js" "${SHARE_PREFIX}/htdocs/js" + install -m 0644 "${PWD}/schemas/mysql.sql" "${SHARE_PREFIX}/schemas" + install -m 0644 "${PWD}/schemas/postgresql.sql" "${SHARE_PREFIX}/schemas" + install -m 0644 "${PWD}/schemas/database_ER.png" "${SHARE_PREFIX}/schemas" diff --git a/Makefile.in b/Makefile.in index df02346..03d1867 100644 --- a/Makefile.in +++ b/Makefile.in @@ -823,15 +823,14 @@ doc: doxygen; \ fi -doc-install: - if test ! -z "${DOC_PREFIX}"; then \ - mkdir -p "${DOC_PREFIX}"; \ - cp -r doc/* "${DOC_PREFIX}"; \ -fi - install-data-hook: + if test ! -z "${PWD}/doc"; then \ + mkdir -p "${SHARE_PREFIX}/doc"; \ + cp -r "${PWD}/doc/"* "${SHARE_PREFIX}/doc"; \ +fi mkdir -p "${SHARE_PREFIX}/htdocs" mkdir -p "${SHARE_PREFIX}/htdocs/js" + mkdir -p "${SHARE_PREFIX}/schemas" install -m 0644 "${PWD}/htdocs/index.html" "${SHARE_PREFIX}/htdocs" install -m 0755 "${PWD}/htdocs/pcap.cgi" "${SHARE_PREFIX}/htdocs" install -m 0644 "${PWD}/htdocs/js/Curry-1.0.1.js" "${SHARE_PREFIX}/htdocs/js" @@ -840,6 +839,9 @@ install-data-hook: install -m 0644 "${PWD}/htdocs/js/dracula_graph.js" "${SHARE_PREFIX}/htdocs/js" install -m 0644 "${PWD}/htdocs/js/raphael.js" "${SHARE_PREFIX}/htdocs/js" install -m 0644 "${PWD}/htdocs/js/seedrandom.js" "${SHARE_PREFIX}/htdocs/js" + install -m 0644 "${PWD}/schemas/mysql.sql" "${SHARE_PREFIX}/schemas" + install -m 0644 "${PWD}/schemas/postgresql.sql" "${SHARE_PREFIX}/schemas" + install -m 0644 "${PWD}/schemas/database_ER.png" "${SHARE_PREFIX}/schemas" # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/README b/README index 702993f..6a541f3 100644 --- a/README +++ b/README @@ -28,7 +28,9 @@ Table of contents: 3.2 Configure options 4. Basic configuration 5. Correlation rules - 6. Additional documentation + 6. Output database + 7. Web interface + 8. Additional documentation =============================== @@ -119,6 +121,14 @@ disabled by the default, and can be enabled by specifying the option --with-postgresql to ./configure. On a Debian-based system you may need to install libpq-dev. +- A DBMS (RECOMMANDED), MySQL and PostgreSQL are supported for now, for writing +clusters, correlations and packet streams information on a DBMS, making the +analysis easier. + +- Perl (OPTIONAL), used for the CGI script in the web interface that saves a +packet stream associated to an alert in .pcap format, to be analyzed by tools +like tcpdump and Wireshark. + ===================== 3.2 Configure options @@ -145,8 +155,6 @@ following: preprocessor ai: \ - hashtable_cleanup_interval 300 \ - tcp_stream_expire_interval 300 \ alertfile "/your/snort/dir/log/alert" \ alert_history_file "/your/snort/dir/log/alert_history" \ alert_serialization_interval 3600 \ @@ -158,9 +166,6 @@ preprocessor ai: \ correlation_rules_dir "/your/snort/dir/etc/corr_rules" \ correlated_alerts_dir "/your/snort/dir/log/correlated_alerts" \ correlation_threshold_coefficient 0.5 \ - database ( type="dbtype", name="snort", user="snortusr", password="snortpass", host="dbhost" ) \ - output_database ( type="dbtype", name="snort", user="snortusr", password="snortpass", host="dbhost" ) \ - database_parsing_interval 30 \ cluster_max_alert_interval 14400 \ clusterfile "/your/snort/dir/log/clustered_alerts" \ cluster ( class="dst_port", name="privileged_ports", range="1-1023" ) \ @@ -170,7 +175,16 @@ preprocessor ai: \ cluster ( class="src_addr", name="vpn_net", range="10.8.0.0/24" ) \ cluster ( class="dst_addr", name="local_net", range="192.168.1.0/24" ) \ cluster ( class="dst_addr", name="dmz_net", range="155.185.0.0/16" ) \ - cluster ( class="dst_addr", name="vpn_net", range="10.8.0.0/24" ) + cluster ( class="dst_addr", name="vpn_net", range="10.8.0.0/24" ) \ + database ( type="dbtype", name="snort", user="snortusr", password="snortpass", host="dbhost" ) \ + database_parsing_interval 30 \ + hashtable_cleanup_interval 300 \ + output_database ( type="dbtype", name="snort", user="snortusr", password="snortpass", host="dbhost" ) \ + tcp_stream_expire_interval 300 \ + webserv_banner "Snort AIPreprocessor module" \ + webserv_dir "/prefix/share/htdocs" \ + webserv_port 7654 + The options are the following: @@ -298,6 +312,19 @@ stream as "expired", if no more packets are received inside of that and it's not "marked" as suspicious (default if not specified: 300 seconds) +- webserv_banner: Banner of the web server, to be placed on the error pages and +in the "Server" HTTP reply header + +- webserver_dir: Directory containing the contents of the web server running +over the module (default if none is specified: +$PREFIX/share/snort_ai_preprocessor/htdocs) + +- webserver_port: Port where the web server will listen (default if none is +specified: 7654). Set this value to 0 if you don't want to run the web server +over the module for having the web interface (in this case, if you want to see +the web graphical visualization of the alerts, you should manually copy the +files contained in htdocs/ in a web server directory) + ==================== 5. Correlation rules ==================== @@ -365,11 +392,73 @@ matching would fail. Any new predicates can be defined as well in hyperalerts, provided that it respects this constraint. +================== +6. Output database +================== + +If the output_database option is specified in the documentation, the alerts, and +the relative clusters, correlations and packet streams information, will be +saved on a database as well. This is strongly suggested, first for making the +management of the module's information easier (a SELECT query needs to be done + for doing also complex searches instead of grep-ing or manually + searching inside of a text file), second because the web interface of +the module can work ONLY if the output_database option is specified (the web + interface strongly depends on the unique IDs assigned to the alerts by + the database interface). Note that for using this option you should +explicitly tell to the ./configure script which DBMS you're going to use, so +that it knows which APIs to use for interfacing with the database, e.g. via +--with-mysql or --with-postgresql. + +After you compile the module, you should pick up the right .sql file from +schemas/ directory (for example mysql.sql or postgresql.sql), or from +$PREFIX/share/snort_ai_preprocessor/schemas after the installation of the +module, and import it in your database, + +$ mysql -uusername -ppassword dbname < schemas/mysql.sql (for MySQL) +$ psql -U username -W dbname < schemas/postgresql.sql (for PostgreSQL) + +You can check the structure of the database from the SQL file for your DBMS, or +from the E/R schema saved in schemas/database_ER.png. + + +================ +7. Web interface +================ + +The module provides an optional (but strongly recommanded) web interface for +browsing the triggered (and already clustered) security alerts, their +correlations and their packet streams information from your browser. This +feature can be switched off by setting the configuration option "webserv_port" +of the module to 0. Otherwise, if none between webserv_dir and webserv_port are +specified, the web server thread starts with the module picking by default the +directory $PREFIX/share/snort_ai_preproc/htdocs as document root and listening +for incoming connections on the port 7654. + +You should use a browser supporting JavaScript, AJAX and SVG technologies in +order to view correctly the alert web interface on your browser (successfully + tested with Firefox 3.5, Chrome and Opera 10), for example, connecting +to the address http://localhost:7654. You can drag and drop the nodes in the +graph, modifying the layout of the graph on the fly or using the "redraw" +function. Each node represents a clustered alert. For viewing the information +over that cluster and the alerts group inside, just click on the node. You can +optionally save the stream of packets associated to a certain alert in .pcap +format (analyzable by tools like tcpdump and Wireshark) from this same +interface. This feature, anyway, is based on the CGI script pcap.cgi inside of +the document root, and it requires the Perl interpreter to be installed on the +machine. + +The web server running over the module is a true web server with its own +document path, so you can use it as stand-alone web server as well and place +your documents and files inside. You can moreover place some CGI scripts or +applications made in the language you prefer, as long as they are files +executable by any users and they have the extension ".cgi". + + =========================== -6. Additional documentation +8. Additional documentation =========================== The additional documentation over the code, functions and data structures can be automatically generated by Doxygen by typing `make doc', and installed in -SNORT_DIR/doc then typing `make doc-install'. +$PREFIX/share/snort_ai_preproc/doc then after `make install'. diff --git a/TODO b/TODO index 567cbe1..6260f11 100644 --- a/TODO +++ b/TODO @@ -6,9 +6,9 @@ AVERAGE/HIGH PRIORITY: - Modules for correlation coefficients - Code profiling - Comment all the code!!! -- Saving packet flows as .pcap - Neural network for computing k - Testing more scenarios, making more hyperalert models +- Manual alert correlation from the web interface ============= LOW PRIORITY: @@ -36,4 +36,5 @@ DONE: + Full PostgreSQL support for output db + Web interface + Function names (private functions with _ or __ ?) ++ Saving packet flows as .pcap diff --git a/htdocs/index.html b/htdocs/index.html index 5802146..5984ca6 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -211,19 +211,19 @@ window.onload = function() { switch ( month ) { - case 'Jan': month = 1; break; - case 'Feb': month = 2; break; - case 'Mar': month = 3; break; - case 'Apr': month = 4; break; - case 'May': month = 5; break; - case 'Jun': month = 6; break; - case 'Jul': month = 7; break; - case 'Ago': month = 8; break; - case 'Sep': month = 9; break; - case 'Oct': month = 10; break; - case 'Nov': month = 11; break; - case 'Dec': month = 12; break; - default : month = 1; break; + case 'Jan': month = 0; break; + case 'Feb': month = 1; break; + case 'Mar': month = 2; break; + case 'Apr': month = 3; break; + case 'May': month = 4; break; + case 'Jun': month = 5; break; + case 'Jul': month = 6; break; + case 'Ago': month = 7; break; + case 'Sep': month = 8; break; + case 'Oct': month = 9; break; + case 'Nov': month = 10; break; + case 'Dec': month = 11; break; + default : month = 0; break; } var timestamp = new Date ( year, month, day, hour, min, sec).getTime().toString(); diff --git a/htdocs/js/base64.js b/htdocs/js/base64.js deleted file mode 100644 index 7504cdd..0000000 --- a/htdocs/js/base64.js +++ /dev/null @@ -1,143 +0,0 @@ -/** -* -* Base64 encode / decode -* http://www.webtoolkit.info/ -* -**/ -  -var Base64 = { -  - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", -  - // public method for encoding - encode : function (input) { - var output = ""; - var chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 0; -  - input = Base64._utf8_encode(input); -  - while (i < input.length) { -  - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); -  - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; -  - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } -  - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); -  - } -  - return output; - }, -  - // public method for decoding - decode : function (input) { - var output = ""; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; -  - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); -  - while (i < input.length) { -  - enc1 = this._keyStr.indexOf(input.charAt(i++)); - enc2 = this._keyStr.indexOf(input.charAt(i++)); - enc3 = this._keyStr.indexOf(input.charAt(i++)); - enc4 = this._keyStr.indexOf(input.charAt(i++)); -  - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; -  - output = output + String.fromCharCode(chr1); -  - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } -  - } -  - output = Base64._utf8_decode(output); -  - return output; -  - }, -  - // private method for UTF-8 encoding - _utf8_encode : function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; -  - for (var n = 0; n < string.length; n++) { -  - var c = string.charCodeAt(n); -  - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } -  - } -  - return utftext; - }, -  - // private method for UTF-8 decoding - _utf8_decode : function (utftext) { - var string = ""; - var i = 0; - var c = c1 = c2 = 0; -  - while ( i < utftext.length ) { -  - c = utftext.charCodeAt(i); -  - if (c < 128) { - string += String.fromCharCode(c); - i++; - } - else if((c > 191) && (c < 224)) { - c2 = utftext.charCodeAt(i+1); - string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); - i += 2; - } - else { - c2 = utftext.charCodeAt(i+1); - c3 = utftext.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } -  - } -  - return string; - } -  -} - diff --git a/htdocs/js/popup.js b/htdocs/js/popup.js deleted file mode 100644 index c491170..0000000 --- a/htdocs/js/popup.js +++ /dev/null @@ -1,129 +0,0 @@ -var dom = (document.getElementById) ? true : false; -var ns5 = ((navigator.userAgent.indexOf("Gecko")>-1) && dom) ? true: false; -var ie5 = ((navigator.userAgent.indexOf("MSIE")>-1) && dom) ? true : false; -var ns4 = (document.layers && !dom) ? true : false; -var ie4 = (document.all && !dom) ? true : false; -var nodyn = (!ns5 && !ns4 && !ie4 && !ie5) ? true : false; - -var origWidth, origHeight; -if (ns4) { - origWidth = window.innerWidth; origHeight = window.innerHeight; - window.onresize = function() { if (window.innerWidth != origWidth || window.innerHeight != origHeight) history.go(0); } -} - -if (nodyn) { event = "nope" } -var tipFollowMouse = true; -var tipWidth = 160; -var offX = 12; // how far from mouse to show tip -var offY = 12; -var tipFontFamily = "Verdana, arial, helvetica, sans-serif"; -var tipFontSize = "8pt"; -var tipFontColor = "#000000"; -var tipBgColor = "#DDECFF"; -var origBgColor = tipBgColor; // in case no bgColor set in array -var tipBorderColor = "#000080"; -var tipBorderWidth = 2; -var tipBorderStyle = "ridge"; -var tipPadding = 4; - -var messages = new Array(); -messages[0] = new Array('http://planmagic.com/images/plmbiz.jpg','Professional business planning software.',"#FFFFFF"); -messages[1] = new Array('http://planmagic.com/images/plmmar.jpg','Professional marketing planning software.',"#DDECFF"); -messages[2] = new Array('http://planmagic.com/images/plmfinance.jpg','Financial calculations and projections made easy.',"#E9E9E9"); - -if (document.images) { - var theImgs = new Array(); - for (var i=0; i'; -var endStr = ''; - -var tooltip, tipcss; -function initTip() { - if (nodyn) return; - tooltip = (ns4)? document.tipDiv.document: (ie4)? document.all['tipDiv']: (ie5||ns5)? document.getElementById('tipDiv'): null; - tipcss = (ns4)? document.tipDiv: tooltip.style; - if (ie4||ie5||ns5) { // ns4 would lose all this on rewrites - tipcss.width = tipWidth+"px"; - tipcss.fontFamily = tipFontFamily; - tipcss.fontSize = tipFontSize; - tipcss.color = tipFontColor; - tipcss.backgroundColor = tipBgColor; - tipcss.borderColor = tipBorderColor; - tipcss.borderWidth = tipBorderWidth+"px"; - tipcss.padding = tipPadding+"px"; - tipcss.borderStyle = tipBorderStyle; - } - if (tooltip&&tipFollowMouse) { - if (ns4) document.captureEvents(Event.MOUSEMOVE); - document.onmousemove = trackMouse; - } -} - -window.onload = initTip; - -var t1,t2; // for setTimeouts -var tipOn = false; // check if over tooltip link -function doTooltip(evt,num) { - - if (!tooltip) return; - if (t1) clearTimeout(t1); if (t2) clearTimeout(t2); - tipOn = true; - // set colors if included in messages array - if (messages[num][2]) var curBgColor = messages[num][2]; - else curBgColor = tipBgColor; - if (messages[num][3]) var curFontColor = messages[num][3]; - else curFontColor = tipFontColor; - if (ns4) { - var tip = '
'+ startStr + messages[num][0] + midStr + '' + messages[num][1] + '' + endStr + '
'; - tooltip.write(tip); - tooltip.close(); - } else if (ie4||ie5||ns5) { - var tip = startStr + messages[num][0] + midStr + '' + messages[num][1] + '' + endStr; - tipcss.backgroundColor = curBgColor; - tooltip.innerHTML = tip; - } - if (!tipFollowMouse) positionTip(evt); - else t1=setTimeout("tipcss.visibility='visible'",100); -} - -var mouseX, mouseY; -function trackMouse(evt) { - mouseX = (ns4||ns5)? evt.pageX: window.event.clientX + document.body.scrollLeft; - mouseY = (ns4||ns5)? evt.pageY: window.event.clientY + document.body.scrollTop; - if (tipOn) positionTip(evt); -} - -function positionTip(evt) { - if (!tipFollowMouse) { - mouseX = (ns4||ns5)? evt.pageX: window.event.clientX + document.body.scrollLeft; - mouseY = (ns4||ns5)? evt.pageY: window.event.clientY + document.body.scrollTop; - } - // tooltip width and height - var tpWd = (ns4)? tooltip.width: (ie4||ie5)? tooltip.clientWidth: tooltip.offsetWidth; - var tpHt = (ns4)? tooltip.height: (ie4||ie5)? tooltip.clientHeight: tooltip.offsetHeight; - // document area in view (subtract scrollbar width for ns) - var winWd = (ns4||ns5)? window.innerWidth-20+window.pageXOffset: document.body.clientWidth+document.body.scrollLeft; - var winHt = (ns4||ns5)? window.innerHeight-20+window.pageYOffset: document.body.clientHeight+document.body.scrollTop; - // check mouse position against tip and window dimensions - // and position the tooltip - if ((mouseX+offX+tpWd)>winWd) - tipcss.left = (ns4)? mouseX-(tpWd+offX): mouseX-(tpWd+offX)+"px"; - else tipcss.left = (ns4)? mouseX+offX: mouseX+offX+"px"; - if ((mouseY+offY+tpHt)>winHt) - tipcss.top = (ns4)? winHt-(tpHt+offY): winHt-(tpHt+offY)+"px"; - else tipcss.top = (ns4)? mouseY+offY: mouseY+offY+"px"; - if (!tipFollowMouse) t1=setTimeout("tipcss.visibility='visible'",100); -} - -function hideTip() { - if (!tooltip) return; - t2=setTimeout("tipcss.visibility='hidden'",100); - tipOn = false; -} - diff --git a/schemas/database_ER.png b/schemas/database_ER.png new file mode 100644 index 0000000..b4513a2 Binary files /dev/null and b/schemas/database_ER.png differ