From 9145cd380baed90f35a97989187888d97212ebc4 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Thu, 4 Jul 2013 15:49:02 +0000 Subject: [PATCH 1/7] Add support for querying Bro conn.log via ELSA's cli.pl --- .inc/callback.php | 78 ++++++++++++++++++++++++++++++++++++++++------- .js/capme.js | 2 +- index.php | 10 +++++- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/.inc/callback.php b/.inc/callback.php index 1261f61..1074925 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -14,27 +14,80 @@ $spt = h2s($d[1]); $dip = h2s($d[2]); $dpt = h2s($d[3]); -$st = $d[4]; -$et = $d[5]; +$st_unix= $d[4]; +$et_unix= $d[5]; $usr = h2s($d[6]); $pwd = h2s($d[7]); $sidsrc = h2s($d[8]); // Format timestamps -$st = date("Y-m-d H:i:s", $st); -$et = date("Y-m-d H:i:s", $et); +$st = date("Y-m-d H:i:s", $st_unix); +$et = date("Y-m-d H:i:s", $et_unix); // Defaults $err = 0; $fmtd = $debug = $errMsg = ''; -// Find appropriate sensor +/* +We need to determine 3 pieces of data: +sensor - sensor name (for Security Onion this is HOSTNAME-INTERFACE) +st - time of the event from the sensor's perspective (may be more accurate than what we were given), in Y-m-d H:i:s format +sid - sensor id +*/ + +if ($sidsrc == "elsa") { + /* + If ELSA is enabled, then we need to: + - construct the ELSA query and submit it via cli.pl + - receive the response and parse out the sensor name (HOSTNAME-INTERFACE) and timestamp + - convert the timestamp to the proper format + NOTE: This requires that ELSA has access to Bro conn.log AND that the conn.log + has been extended to include the sensor name (HOSTNAME-INTERFACE). + */ + + $elsa_query = "class=bro_conn start:'$st_unix' end:'$et_unix' +$sip +$spt +$dip +$dpt limit:1"; + $elsa_command = "perl /opt/elsa/web/cli.pl -q '$elsa_query' "; + $elsa_response = shell_exec($elsa_command); + + // A successful query response looks like this: + // timestamp class host program msg fields + // 1372897204 BRO_CONN 127.0.0.1 bro_conn original_timestamp|Many|Pipe|Delimited|Fields|etc|sensorIsOffset22 + + // Explode the output into separate lines and pull out the data line + $pieces = explode("\n", $elsa_response); + $elsa_response_data = $pieces[1]; + + // Explode the tab-delimited data line and pull out the pipe-delimited raw log + $pieces = explode("\t", $elsa_response_data); + $elsa_response_data_raw_log = $pieces[4]; + + // Explode the pipe-delimited raw log and pull out the original timestamp and sensor name + $pieces = explode("|", $elsa_response_data_raw_log); + $elsa_response_data_raw_log_timestamp = $pieces[0]; + $elsa_response_data_raw_log_sensor = $pieces[22]; + + // Convert timestamp to proper format + $st = date("Y-m-d H:i:s", $elsa_response_data_raw_log_timestamp); + + // Clean up $sensor + $sensor = rtrim($elsa_response_data_raw_log_sensor); + + // We now have 2 of the 3 pieces of data that we need. + // Next, we'll use $sensor to look up the $sid in Sguil's sensor table. +} +/* +Query the Sguil database +If the user selected sancp or event, query those tables and get +the 3 pieces of data that we need. +*/ $queries = array( + "elsa" => "SELECT sid FROM sensor WHERE hostname='$sensor' AND agent_type='pcap' LIMIT 1", + "sancp" => "SELECT sancp.start_time, s2.sid, s2.hostname FROM sancp LEFT JOIN sensor ON sancp.sid = sensor.sid - LEFT JOIN sensor AS s2 ON sensor.net_name = s2.hostname + LEFT JOIN sensor AS s2 ON sensor.net_name = s2.net_name WHERE sancp.start_time >= '$st' AND sancp.end_time <= '$et' AND ((src_ip = INET_ATON('$sip') AND src_port = $spt AND dst_ip = INET_ATON('$dip') AND dst_port = $dpt) OR (src_ip = INET_ATON('$dip') AND src_port = $dpt AND dst_ip = INET_ATON('$sip') AND dst_port = $spt)) AND s2.agent_type = 'pcap' LIMIT 1", @@ -42,7 +95,7 @@ "event" => "SELECT event.timestamp AS start_time, s2.sid, s2.hostname FROM event LEFT JOIN sensor ON event.sid = sensor.sid - LEFT JOIN sensor AS s2 ON sensor.net_name = s2.hostname + LEFT JOIN sensor AS s2 ON sensor.net_name = s2.net_name WHERE timestamp BETWEEN '$st' AND '$et' AND ((src_ip = INET_ATON('$sip') AND src_port = $spt AND dst_ip = INET_ATON('$dip') AND dst_port = $dpt) OR (src_ip = INET_ATON('$dip') AND src_port = $dpt AND dst_ip = INET_ATON('$sip') AND dst_port = $spt)) AND s2.agent_type = 'pcap' LIMIT 1"); @@ -55,12 +108,15 @@ $debug = $queries[$sidsrc]; } else if (mysql_num_rows($response) == 0) { $err = 1; - $errMsg = "Failed to find a matching sid, please try again in a few seconds"; $debug = $queries[$sidsrc]; + $errMsg = "Failed to find a matching sid, please try again in a few seconds"; } else { $row = mysql_fetch_assoc($response); - $st = $row["start_time"]; - $sensor = $row["hostname"]; + // If using ELSA, we already set $st and $sensor above so don't overwrite that here + if ($sidsrc != "elsa") { + $st = $row["start_time"]; + $sensor = $row["hostname"]; + } $sid = $row["sid"]; } @@ -71,7 +127,7 @@ } else { - // CLIscript command + // We have all the data we need, so pass the parameters to cliscript $cmd = "cliscript.tcl -sid $sid -sensor '$sensor' -timestamp '$st' -u '$usr' -pw '$pwd' -sip $sip -spt $spt -dip $dip -dpt $dpt"; exec("../.scripts/$cmd",$raw); diff --git a/.js/capme.js b/.js/capme.js index 99c3605..a886628 100644 --- a/.js/capme.js +++ b/.js/capme.js @@ -36,7 +36,7 @@ $(document).ready(function(){ $(".capme_submit").click(function() { frmArgs = $('input[value!=""]').length; - if (frmArgs == 11) { + if (frmArgs == 12) { reqCap("usefrm"); } else { theMsg("Please complete all form fields"); diff --git a/index.php b/index.php index ce0eac4..63730da 100644 --- a/index.php +++ b/index.php @@ -4,7 +4,7 @@ $s = 0; // Argument defaults -$sip = $spt = $dip = $dpt = $stime = $etime = $usr = $pwd = $sancp = $event = ''; +$sip = $spt = $dip = $dpt = $stime = $etime = $usr = $pwd = $sancp = $event = $elsa = ''; // Grab any arguments provided in URI if (isset($_REQUEST['sip'])) { $sip = $_REQUEST['sip']; $s++; } if (isset($_REQUEST['spt'])) { $spt = $_REQUEST['spt']; $s++; } @@ -14,8 +14,15 @@ if (isset($_REQUEST['etime'])) { $etime = $_REQUEST['etime']; $s++; } if (isset($_REQUEST['user'])) { $usr = $_REQUEST['user']; $s++; } if (isset($_REQUEST['password'])) { $pwd = $_REQUEST['password']; $s++; } +// If we see a filename parameter, we know the request came from Snorby +// and if so we can just query the event table since Snorby just has NIDS alerts +// If the referer contains ":3154", then it's most likely a Security Onion user +// pivoting from ELSA, so we should query using ELSA. +// If all else fails, query sancp. if (isset($_REQUEST['filename'])) { $event = " checked"; +} elseif (strpos($_SERVER['HTTP_REFERER'],":3154") !== false) { + $elsa = " checked"; } else { $sancp = " checked"; } @@ -87,6 +94,7 @@ >sancp >event +>elsa From e2a30a442192f90155cc6add9860ff94082201f8 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Thu, 4 Jul 2013 18:20:57 +0000 Subject: [PATCH 2/7] initialize $sensor --- .inc/callback.php | 1 + 1 file changed, 1 insertion(+) diff --git a/.inc/callback.php b/.inc/callback.php index 1074925..b8c605c 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -35,6 +35,7 @@ sid - sensor id */ +$sensor = ""; if ($sidsrc == "elsa") { /* If ELSA is enabled, then we need to: From c9a00c8dd5fdfd7a7038eab90b1bbed96c162a57 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Thu, 4 Jul 2013 21:29:27 -0400 Subject: [PATCH 3/7] swap blue/red coloring to match Sguil --- .css/capme.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.css/capme.css b/.css/capme.css index 21374f7..f886298 100644 --- a/.css/capme.css +++ b/.css/capme.css @@ -185,11 +185,11 @@ span.capme_close:hover { } .txtext_src { - color: #ff0000; + color: #0000ff; } .txtext_dst { - color: #0000ff; + color: #ff0000; } .txtext_dbg { From 4b99862719bcc009afe40ff4133ac777128dba15 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Mon, 30 Dec 2013 16:27:01 -0500 Subject: [PATCH 4/7] Download pcap and fix Snorby timezone issue --- .inc/callback.php | 89 ++++++++++- .inc/config.php.sample | 6 +- .inc/timezone.php.sample | 11 ++ .js/capme.js | 15 +- .scripts/cliscript.tcl | 2 +- .scripts/cliscriptbro.tcl | 322 ++++++++++++++++++++++++++++++++++++++ index.php | 14 +- pcap/index.php | 5 + 8 files changed, 449 insertions(+), 15 deletions(-) create mode 100644 .inc/timezone.php.sample create mode 100755 .scripts/cliscriptbro.tcl create mode 100644 pcap/index.php diff --git a/.inc/callback.php b/.inc/callback.php index b8c605c..66a5e12 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -19,11 +19,29 @@ $usr = h2s($d[6]); $pwd = h2s($d[7]); $sidsrc = h2s($d[8]); +$xscript = h2s($d[9]); // Format timestamps $st = date("Y-m-d H:i:s", $st_unix); $et = date("Y-m-d H:i:s", $et_unix); +// Fix Snorby timezone +if ($sidsrc == "event") { + + // load the user's timezone setting + include 'timezone.php'; + + // convert the start time from the user's timezone to UTC/GMT + $st = date_create($st, timezone_open($timezone)); + date_timezone_set($st, timezone_open('Etc/GMT')); + $st = date_format($st, 'Y-m-d H:i:s'); + + // convert the end time from the user's timezone to UTC/GMT + $et = date_create($et, timezone_open($timezone)); + date_timezone_set($et, timezone_open('Etc/GMT')); + $et = date_format($et, 'Y-m-d H:i:s'); +} + // Defaults $err = 0; $fmtd = $debug = $errMsg = ''; @@ -46,7 +64,7 @@ has been extended to include the sensor name (HOSTNAME-INTERFACE). */ - $elsa_query = "class=bro_conn start:'$st_unix' end:'$et_unix' +$sip +$spt +$dip +$dpt limit:1"; + $elsa_query = "class=bro_conn start:'$st_unix' end:'$et_unix' +$sip +$spt +$dip +$dpt limit:1 timeout:0"; $elsa_command = "perl /opt/elsa/web/cli.pl -q '$elsa_query' "; $elsa_response = shell_exec($elsa_command); @@ -128,13 +146,61 @@ } else { - // We have all the data we need, so pass the parameters to cliscript - $cmd = "cliscript.tcl -sid $sid -sensor '$sensor' -timestamp '$st' -u '$usr' -pw '$pwd' -sip $sip -spt $spt -dip $dip -dpt $dpt"; + // We have all the data we need, so pass the parameters to the correct cliscript + $script = "cliscript.tcl"; + if ($xscript == "bro") { + $script = "cliscriptbro.tcl"; + } + $cmd = "$script -sid $sid -sensor '$sensor' -timestamp '$st' -u '$usr' -pw '$pwd' -sip $sip -spt $spt -dip $dip -dpt $dpt"; exec("../.scripts/$cmd",$raw); +$found_pcap = 0; + foreach ($raw as $line) { + /* + $DEBUG either looks like this: + + DEBUG: Using archived data: /nsm/server_data/securityonion/archive/2013-11-08/doug-virtual-machine-eth1/10.0.2.15:1066_192.168.56.50:80-6.raw + + OR it looks like this: + + DEBUG: Raw data request sent to doug-virtual-machine-eth1. + DEBUG: Making a list of local log files. + DEBUG: Looking in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. + DEBUG: Making a list of local log files in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. + DEBUG: Available log files: + DEBUG: 1383910121 + DEBUG: Creating unique data file: /usr/sbin/tcpdump -r /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08/snort.log.1383910121 -w /tmp/10.0.2.15:1066_192.168.56.50:80-6.raw (ip and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) or (vlan and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) + DEBUG: Receiving raw file from sensor. + */ + + $archive = 'Using archived data:'; + $pos = strpos($line, $archive); + if ($pos !== false) { + $found_pcap = 1; + $pieces = explode(" ", $line); + $full_filename = $pieces[4]; + $pieces = explode("/", $full_filename); + $filename = $pieces[7]; + } + + $unique = 'Creating unique data file:'; + $pos = strpos($line, $unique); + if ($pos !== false) { + $found_pcap = 1; + $pieces = explode(" ", $line); + $sensor_filename = $pieces[7]; + $server_filename = $pieces[9]; + $pieces = explode("/", $sensor_filename); + $sensorname = $pieces[3]; + $dailylog = $pieces[5]; + $pieces = explode("/", $server_filename); + $filename = $pieces[2]; + $full_filename = "/nsm/server_data/securityonion/archive/$dailylog/$sensorname/$filename"; + } + $line = htmlspecialchars($line); $type = substr($line, 0,3); @@ -142,7 +208,7 @@ case "DEB": $debug .= preg_replace('/^DEBUG:.*$/', "$0", $line) . "
"; $line = ''; break; case "HDR": $line = preg_replace('/(^HDR:)(.*$)/', "$2", $line); break; case "DST": $line = preg_replace('/^DST:.*$/', "$0", $line); break; - case "SRC": $line = preg_replace('/^SRC:.*$/', "$0", $line); break; + case "SRC": $line = preg_replace('/^SRC:.*$/', "$0", $line); break; } if (strlen($line) > 0) { @@ -150,11 +216,22 @@ } } + $tmpstring = rand(); + $filename_random = str_replace(".raw", "", "$filename-$tmpstring"); + $filename_download = "$filename_random.pcap"; + $link = "/var/www/capme/pcap/$filename_download"; + symlink($full_filename, $link); + // Add query to debug $debug .= "QUERY: " . $queries[$sidsrc] . ""; - $result = array("tx" => "$fmtd", - "dbg" => "$debug", + $mytx = $fmtd; + if ($xscript == "pcap") { + $mytx = $filename_download; + } + + $result = array("tx" => "$mytx", + "dbg" => "$debug
$filename_download", "err" => "$errMsg"); } diff --git a/.inc/config.php.sample b/.inc/config.php.sample index 61feb0d..856ecfc 100644 --- a/.inc/config.php.sample +++ b/.inc/config.php.sample @@ -1,7 +1,7 @@ diff --git a/.inc/timezone.php.sample b/.inc/timezone.php.sample new file mode 100644 index 0000000..e8fcca5 --- /dev/null +++ b/.inc/timezone.php.sample @@ -0,0 +1,11 @@ + diff --git a/.js/capme.js b/.js/capme.js index a886628..aae00b8 100644 --- a/.js/capme.js +++ b/.js/capme.js @@ -36,7 +36,7 @@ $(document).ready(function(){ $(".capme_submit").click(function() { frmArgs = $('input[value!=""]').length; - if (frmArgs == 12) { + if (frmArgs == 15) { reqCap("usefrm"); } else { theMsg("Please complete all form fields"); @@ -51,6 +51,9 @@ $(document).ready(function(){ bOFF('.capme_submit'); theMsg("Sending request.."); + // Transcript + var xscript = s2h($('input:radio[name=xscript]:checked').val()); + // SID Source var sidsrc = s2h($('input:radio[name=sidsrc]:checked').val()); @@ -88,7 +91,7 @@ $(document).ready(function(){ // Continue if no errors if (err == 0) { - var urArgs = "d=" + sip + "-" + spt + "-" + dip + "-" + dpt + "-" + st + "-" + et + "-" + usr + "-" + pwd + "-" + sidsrc; + var urArgs = "d=" + sip + "-" + spt + "-" + dip + "-" + dpt + "-" + st + "-" + et + "-" + usr + "-" + pwd + "-" + sidsrc + "-" + xscript; $(function(){ $.get(".inc/callback.php?" + urArgs, function(data){cbtx(data)}); @@ -110,7 +113,9 @@ $(document).ready(function(){ txt += ""; txt += ""; txt += ""; - txt += txResult; + if (txResult.indexOf("OS Fingerprint:") >= 0) { + txt += txResult; + } txt += txDebug; txt += txError; txt += ""; @@ -119,6 +124,10 @@ $(document).ready(function(){ $(".capme_div").hide(); $(".capme_result").show(); $(".capme_msg").fadeOut('slow'); + if (txResult.indexOf("OS Fingerprint:") == -1) { + url = "/capme/pcap/" + txResult; + window.open(url, "_self"); + } } else { theMsg(txError); } diff --git a/.scripts/cliscript.tcl b/.scripts/cliscript.tcl index 2ecd8a5..aa39421 100755 --- a/.scripts/cliscript.tcl +++ b/.scripts/cliscript.tcl @@ -227,7 +227,7 @@ if { [catch {tls::import $socketID} tlsError] } { } # Give SSL a sec -after 1000 +# after 1000 # Send sguild a ping to confirm comms SendToSguild $socketID "PING" diff --git a/.scripts/cliscriptbro.tcl b/.scripts/cliscriptbro.tcl new file mode 100755 index 0000000..89b3469 --- /dev/null +++ b/.scripts/cliscriptbro.tcl @@ -0,0 +1,322 @@ +#!/usr/bin/tclsh + +# cliscript.tcl - Based on "quickscript.tcl" +# Portions Copyright (C) 2012 Paul Halliday + +# Copyright (C) 2002-2006 Robert (Bamm) Visscher +# +# This program is distributed under the terms of version 1.0 of the +# Q Public License. See LICENSE.QPL for further details. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +########################## GLOBALS ################################## + +set VERSION "SGUIL-0.8.0 OPENSSL ENABLED" +set SERVER 127.0.0.1 +set PORT 7734 + +# Comment out the following 2 lines if +# you wish to be prompted for a user/pass + +#set USERNAME "beta" +#set PASSWD "band" + +######################################################################### +# Get cmd line args +######################################################################### + +proc DisplayUsage { cmdName } { + + puts "Usage: $cmdName \[-s \] \[-p \] \[-u \]" + puts " \[-o \] \[-sensor \] \[-timestamp \]" + puts " \[-sid \] \[-sip \] \[-dip \]" + puts " \[-spt \] \[-dpt \]\n" + puts " -s : Hostname of sguild server." + puts " -p : Port of sguild server." + puts " -u : Username to connect as." + puts " -pw : Password to connect with." + puts " -o : PATH to tls libraries if needed." + puts " -sensor : The sensor name." + puts " -timestamp <\"timestamp\">: Event timestamp. e.g.: \"2012-08-18 16:28:00\"" + puts " -sid : The sensor ID." + puts " -sip : Source IP." + puts " -dip : Destination IP." + puts " -spt : Source port." + puts " -dpt : Destination port.\n" + exit 1 + +} + +set state flag + +foreach arg $argv { + + switch -- $state { + + flag { + switch -glob -- $arg { + -s { set state server } + -p { set state port } + -u { set state username } + -pw { set state password } + -o { set state openssl } + -sensor { set state sensorname } + -timestamp { set state timestamp } + -sid { set state sensorid } + -sip { set state sip } + -dip { set state dip } + -spt { set state spt } + -dpt { set state dpt } + default { DisplayUsage $argv0 } + } + } + + server { set SERVER $arg; set state flag } + port { set PORT $arg; set state flag } + username { set USERNAME $arg; set state flag } + password { set PASSWD $arg; set state flag } + openssl { set TLS_PATH $arg; set state flag } + sensorname { set SENSORNAME $arg; set state flag } + timestamp { set TIMESTAMP $arg; set state flag } + sensorid { set SENSORID $arg; set state flag } + sip { set SRCIP $arg; set state flag } + dip { set DSTIP $arg; set state flag } + spt { set SRCPORT $arg; set state flag } + dpt { set DSTPORT $arg; set state flag } + default { DisplayUsage $argv0 } + + } + +} + +# Check if we got all of our arguments + +if { [catch {set eventInfo "$SENSORNAME \"$TIMESTAMP\" $SENSORID $SRCIP $DSTIP $SRCPORT $DSTPORT"}] } { + DisplayUsage $argv0 +} + +# Now verify + +if { [regexp -expanded { + + ^.+\s + \"\d\d\d\d-\d\d-\d\d\s\d\d:\d\d:\d\d\"\s + \d+\s + \d+\.\d+\.\d+\.\d+\s + \d+\.\d+\.\d+\.\d+\s + \d+\s + \d+$ } $eventInfo match] } { + +} else { + + DisplayUsage $argv0 + +} + +######################################################################### +# Package/Extension Requirements +######################################################################### + +# Check to see if a path to the tls libs was provided +if { [info exists TLS_PATH] } { + + if [catch {load $TLS_PATH} tlsError] { + + puts "ERROR: Unable to load tls libs ($TLS_PATH): $tlsError" + DisplayUsage $argv0 + + } + +} + +if { [catch {package require tls} tlsError] } { + + puts "ERROR: The tcl tls package does NOT appear to be installed on this sysem." + puts "Please see http://tls.sourceforge.net/ for more info." + exit 1 + +} + + +######################################################################### +# Procs +######################################################################### + +# A simple proc to send commands to sguild and catch errors +proc SendToSguild { socketID message } { + + if { [catch {puts $socketID $message} sendError] } { + + # Send failed. Close the socket and exit. + catch {close $socketID} closeError + + if { [info exists sendError] } { + + puts "ERROR: Caught exception while sending data: $sendError" + + } else { + + puts "ERROR: Caught unknown exception" + + } + + exit 1 + + } + +} + +######################################################################### +# Main +######################################################################### + +flush stdout + +# Try to connect to sguild +if [catch {socket $SERVER $PORT} socketID ] { + + # Exit on fail. + puts "ERROR: Connection failed" + exit 1 + +} + +# Successfully connected +fconfigure $socketID -buffering line + +# Check version compatibality +if [catch {gets $socketID} serverVersion] { + + # Caught an unknown error + puts "ERROR: $serverVersion" + catch {close $socketID} + exit 1 + +} + +if { $serverVersion == "Connection Refused." } { + + # Connection refused error + puts "ERROR: $serverVersion" + catch {close $socketID} + exit 1 + +} + +if { $serverVersion != $VERSION } { + + # Mismatched versions + catch {close $socketID} + puts "ERROR: Mismatched versions.\nSERVER= ($serverVersion)\nCLIENT= ($VERSION)" + exit 1 + +} + +# Send the server our version info +SendToSguild $socketID [list VersionInfo $VERSION] + +# SSL-ify the socket +if { [catch {tls::import $socketID} tlsError] } { + + puts "ERROR: $tlsError" + exit 1 + +} + +# Give SSL a sec +# after 1000 + +# Send sguild a ping to confirm comms +SendToSguild $socketID "PING" +# Get the PONG +set INIT [gets $socketID] + +# +# Auth starts here +# + +# Get username if not provided at cmd line +if { ![info exists USERNAME] } { + + puts -nonewline "Enter username: " + flush stdout + set USERNAME [gets stdin] + +} + +# Get users password +if { ![info exists PASSWD] } { + puts -nonewline "Enter password: " + flush stdout + exec stty -echo + set PASSWD [gets stdin] + exec stty echo + flush stdout + puts "" +} + +# Authenticate with sguild +SendToSguild $socketID [list ValidateUser $USERNAME $PASSWD] + +# Get the response. Success will return the users ID and failure will send INVALID. +if { [catch {gets $socketID} authMsg] } { + + puts "ERROR: $authMsg" + exit 1 + +} + +set authResults [lindex $authMsg 1] +if { $authResults == "INVALID" } { + + puts "ERROR: Authentication failed." + exit 1 + +} + +# Send info to Sguild +SendToSguild $socketID [list CliScriptBro $eventInfo] + +set SESSION_STATE DEBUG + +# Xscript data comes in the format XscriptMainMsg window message +# Tags are HDR, SRC, and DST. They are sent when state changes. + +while { 1 } { + + if { [eof $socketID] } { puts "ERROR: Lost connection to server."; exit 1 } + + if { [catch {gets $socketID} msg] } { + + puts "ERROR: $msg" + exit 1 + + } + + # Strip the command and faux winname from the msg + set data [lindex $msg 2] + + + switch -exact -- $data { + + HDR { set SESSION_STATE HDR } + SRC { set SESSION_STATE SRC } + DST { set SESSION_STATE DST } + DEBUG { set SESSION_STATE DEBUG } + DONE { break } + ERROR { set SESSION_STATE ERROR } + default { puts "${SESSION_STATE}: [lindex $msg 2]" } + + } + + # Exit if agent returns no data after debug + if { $SESSION_STATE == "DEBUG" && $data == "" } { + break + } + +} + +catch {close $socketID} diff --git a/index.php b/index.php index 63730da..11f7bcb 100644 --- a/index.php +++ b/index.php @@ -4,7 +4,7 @@ $s = 0; // Argument defaults -$sip = $spt = $dip = $dpt = $stime = $etime = $usr = $pwd = $sancp = $event = $elsa = ''; +$sip = $spt = $dip = $dpt = $stime = $etime = $usr = $pwd = $sancp = $event = $elsa = $bro = $tcpflow = $pcap = ''; // Grab any arguments provided in URI if (isset($_REQUEST['sip'])) { $sip = $_REQUEST['sip']; $s++; } if (isset($_REQUEST['spt'])) { $spt = $_REQUEST['spt']; $s++; } @@ -21,11 +21,12 @@ // If all else fails, query sancp. if (isset($_REQUEST['filename'])) { $event = " checked"; -} elseif (strpos($_SERVER['HTTP_REFERER'],":3154") !== false) { +} elseif ( isset($_SERVER['HTTP_REFERER']) && (strpos($_SERVER['HTTP_REFERER'],":3154") !== false)) { $elsa = " checked"; } else { $sancp = " checked"; } +$tcpflow = " checked"; ?> + +Output: + +>tcpflow +>bro +>pcap + + + diff --git a/pcap/index.php b/pcap/index.php new file mode 100644 index 0000000..fd72ca4 --- /dev/null +++ b/pcap/index.php @@ -0,0 +1,5 @@ + From 34bfb53b8456413e7dbbcfec788f331465586f28 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Tue, 31 Dec 2013 11:00:13 -0500 Subject: [PATCH 5/7] code cleanup --- .inc/callback.php | 116 +++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/.inc/callback.php b/.inc/callback.php index 66a5e12..e996eaa 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -155,52 +155,8 @@ exec("../.scripts/$cmd",$raw); -$found_pcap = 0; - foreach ($raw as $line) { - /* - $DEBUG either looks like this: - - DEBUG: Using archived data: /nsm/server_data/securityonion/archive/2013-11-08/doug-virtual-machine-eth1/10.0.2.15:1066_192.168.56.50:80-6.raw - - OR it looks like this: - - DEBUG: Raw data request sent to doug-virtual-machine-eth1. - DEBUG: Making a list of local log files. - DEBUG: Looking in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. - DEBUG: Making a list of local log files in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. - DEBUG: Available log files: - DEBUG: 1383910121 - DEBUG: Creating unique data file: /usr/sbin/tcpdump -r /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08/snort.log.1383910121 -w /tmp/10.0.2.15:1066_192.168.56.50:80-6.raw (ip and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) or (vlan and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) - DEBUG: Receiving raw file from sensor. - */ - - $archive = 'Using archived data:'; - $pos = strpos($line, $archive); - if ($pos !== false) { - $found_pcap = 1; - $pieces = explode(" ", $line); - $full_filename = $pieces[4]; - $pieces = explode("/", $full_filename); - $filename = $pieces[7]; - } - - $unique = 'Creating unique data file:'; - $pos = strpos($line, $unique); - if ($pos !== false) { - $found_pcap = 1; - $pieces = explode(" ", $line); - $sensor_filename = $pieces[7]; - $server_filename = $pieces[9]; - $pieces = explode("/", $sensor_filename); - $sensorname = $pieces[3]; - $dailylog = $pieces[5]; - $pieces = explode("/", $server_filename); - $filename = $pieces[2]; - $full_filename = "/nsm/server_data/securityonion/archive/$dailylog/$sensorname/$filename"; - } - $line = htmlspecialchars($line); $type = substr($line, 0,3); @@ -216,22 +172,74 @@ } } - $tmpstring = rand(); - $filename_random = str_replace(".raw", "", "$filename-$tmpstring"); - $filename_download = "$filename_random.pcap"; - $link = "/var/www/capme/pcap/$filename_download"; - symlink($full_filename, $link); - + // default to sending transcript + $mytx = $fmtd; + + /* + $debug EITHER looks like this: + + DEBUG: Using archived data: /nsm/server_data/securityonion/archive/2013-11-08/doug-virtual-machine-eth1/10.0.2.15:1066_192.168.56.50:80-6.raw + + OR it looks like this: + + DEBUG: Raw data request sent to doug-virtual-machine-eth1. + DEBUG: Making a list of local log files. + DEBUG: Looking in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. + DEBUG: Making a list of local log files in /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08. + DEBUG: Available log files: + DEBUG: 1383910121 + DEBUG: Creating unique data file: /usr/sbin/tcpdump -r /nsm/sensor_data/doug-virtual-machine-eth1/dailylogs/2013-11-08/snort.log.1383910121 -w /tmp/10.0.2.15:1066_192.168.56.50:80-6.raw (ip and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) or (vlan and host 10.0.2.15 and host 192.168.56.50 and port 1066 and port 80 and proto 6) + DEBUG: Receiving raw file from sensor. + */ + + // Find pcap + $archive = '/DEBUG: Using archived data.*/'; + $unique = '/DEBUG: Creating unique data file.*/'; + $found_pcap = 0; + if (preg_match($archive, $debug, $matches)) { + $found_pcap = 1; + $match = str_replace("
", "", $matches[0]); + $pieces = explode(" ", $match); + $full_filename = $pieces[4]; + $pieces = explode("/", $full_filename); + $filename = $pieces[7]; + } else if (preg_match($unique, $debug, $matches)) { + $found_pcap = 1; + $match = str_replace("
", "", $matches[0]); + $pieces = explode(" ", $match); + $sensor_filename = $pieces[7]; + $server_filename = $pieces[9]; + $pieces = explode("/", $sensor_filename); + $sensorname = $pieces[3]; + $dailylog = $pieces[5]; + $pieces = explode("/", $server_filename); + $filename = $pieces[2]; + $full_filename = "/nsm/server_data/securityonion/archive/$dailylog/$sensorname/$filename"; + } + // Add query to debug $debug .= "QUERY: " . $queries[$sidsrc] . ""; - $mytx = $fmtd; - if ($xscript == "pcap") { - $mytx = $filename_download; + // if we found the pcap, create a symlink in /var/www/capme/pcap/ + // and then create a hyperlink to that symlink + if ($found_pcap == 1) { + $tmpstring = rand(); + $filename_random = str_replace(".raw", "", "$filename-$tmpstring"); + $filename_download = "$filename_random.pcap"; + $link = "/var/www/capme/pcap/$filename_download"; + symlink($full_filename, $link); + $debug .= "
$filename_download"; + $mytx = "$filename_download

$mytx"; + // if the user requested pcap, send the pcap instead of the transcript + if ($xscript == "pcap") { + $mytx = $filename_download; + } + } else { + $debug .= "
WARNING: Unable to find pcap."; } $result = array("tx" => "$mytx", - "dbg" => "$debug
$filename_download", + "dbg" => "$debug", "err" => "$errMsg"); } From c290189e94c1772c62ec3002714de618d7fc7d32 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Fri, 1 Aug 2014 10:53:57 -0400 Subject: [PATCH 6/7] check pcap_agent --- .inc/callback.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.inc/callback.php b/.inc/callback.php index e996eaa..7f0d993 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -129,6 +129,10 @@ $err = 1; $debug = $queries[$sidsrc]; $errMsg = "Failed to find a matching sid, please try again in a few seconds"; + $response = mysql_query("select * from sensor where agent_type='pcap' and active='Y';"); + if (mysql_num_rows($response) == 0) { + $errMsg = "Error: No pcap_agent found"; + } } else { $row = mysql_fetch_assoc($response); // If using ELSA, we already set $st and $sensor above so don't overwrite that here From 52de120fb9698a9e12bc62eafd188913fd6109f7 Mon Sep 17 00:00:00 2001 From: Doug Burks Date: Fri, 1 Aug 2014 11:01:20 -0400 Subject: [PATCH 7/7] Patch from Ryan Peck to fix failure when ELSA's cli.pl returns a warning --- .inc/callback.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.inc/callback.php b/.inc/callback.php index 7f0d993..88ac9fd 100644 --- a/.inc/callback.php +++ b/.inc/callback.php @@ -74,7 +74,21 @@ // Explode the output into separate lines and pull out the data line $pieces = explode("\n", $elsa_response); - $elsa_response_data = $pieces[1]; + + // Sometimes the response contains a warning - this means that the + // expected query response data is not located on the second line. + // Iterate through until we find the header line - the next + // line is the data line we want. See line 35 of /opt/elsa/web/cli.pl + $data_line_n = 1; + + for ($n=0; $n<=count($pieces); $n++) { + if ($pieces[$n] === "timestamp\tclass\thost\tprogram\tmsg\tfields") { + $data_line_n = $n + 1; + break; + } + } + + $elsa_response_data = $pieces[$data_line_n]; // Explode the tab-delimited data line and pull out the pipe-delimited raw log $pieces = explode("\t", $elsa_response_data);