diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 5979e6befd..9933508b2d 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -1,18 +1,27 @@ include(InstallPackageConfigFile) install(DIRECTORY ./ DESTINATION ${POLICYDIR} FILES_MATCHING - PATTERN "all.bro" EXCLUDE - PATTERN "site/local.bro" EXCLUDE - PATTERN "bro.init" + PATTERN "site/local*" EXCLUDE PATTERN "*.bro" PATTERN "*.sig" - PATTERN "*.osf" + PATTERN "*.fp" ) -# Install as a config file since the local.bro script is meant to be +# Install all local* scripts as config files since they are meant to be # user modify-able. InstallPackageConfigFile( ${CMAKE_CURRENT_SOURCE_DIR}/site/local.bro ${POLICYDIR}/site local.bro) - +InstallPackageConfigFile( + ${CMAKE_CURRENT_SOURCE_DIR}/site/local-manager.bro + ${POLICYDIR}/site + local-manager.bro) +InstallPackageConfigFile( + ${CMAKE_CURRENT_SOURCE_DIR}/site/local-proxy.bro + ${POLICYDIR}/site + local-proxy.bro) +InstallPackageConfigFile( + ${CMAKE_CURRENT_SOURCE_DIR}/site/local-worker.bro + ${POLICYDIR}/site + local-worker.bro) diff --git a/scripts/base/frameworks/cluster/nodes/manager.bro b/scripts/base/frameworks/cluster/nodes/manager.bro index d52078cc7c..35e3fc2069 100644 --- a/scripts/base/frameworks/cluster/nodes/manager.bro +++ b/scripts/base/frameworks/cluster/nodes/manager.bro @@ -12,6 +12,9 @@ @prefixes += cluster-manager +## Load the script for local site configuration for the manager node. +@load site/local-manager + ## Turn off remote logging since this is the manager and should only log here. redef Log::enable_remote_logging = F; diff --git a/scripts/base/frameworks/cluster/nodes/proxy.bro b/scripts/base/frameworks/cluster/nodes/proxy.bro index 377b087b36..60df27b452 100644 --- a/scripts/base/frameworks/cluster/nodes/proxy.bro +++ b/scripts/base/frameworks/cluster/nodes/proxy.bro @@ -1,6 +1,9 @@ @prefixes += cluster-proxy +## Load the script for local site configuration for proxy nodes. +@load site/local-proxy + ## The proxy only syncs state; does not forward events. redef forward_remote_events = F; redef forward_remote_state_changes = T; diff --git a/scripts/base/frameworks/cluster/nodes/worker.bro b/scripts/base/frameworks/cluster/nodes/worker.bro index eb0c271a17..53d8c5c423 100644 --- a/scripts/base/frameworks/cluster/nodes/worker.bro +++ b/scripts/base/frameworks/cluster/nodes/worker.bro @@ -2,6 +2,9 @@ @prefixes += cluster-worker +## Load the script for local site configuration for the worker nodes. +@load site/local-worker + ## Don't do any local logging. redef Log::enable_local_logging = F; diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 42580e95e7..ec772244a2 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -509,7 +509,7 @@ const tunnel_port = 0/udp &redef; # packets and IP-level bytes transfered by each endpoint. If # true, these values are returned in the connection's endpoint # record val. -const use_conn_size_analyzer = F &redef; +const use_conn_size_analyzer = T &redef; const UDP_INACTIVE = 0; const UDP_ACTIVE = 1; # means we've seen something from this endpoint diff --git a/scripts/base/protocols/smtp/main.bro b/scripts/base/protocols/smtp/main.bro index 63ad8a1953..bc7aaeff10 100644 --- a/scripts/base/protocols/smtp/main.bro +++ b/scripts/base/protocols/smtp/main.bro @@ -7,13 +7,6 @@ module SMTP; export { redef enum Log::ID += { SMTP }; - redef enum Notice::Type += { - ## Indicates that the server sent a reply mentioning an SMTP block list. - BL_Error_Message, - ## Indicates the client's address is seen in the block list error message. - BL_Blocked_Host, - }; - type Info: record { ts: time &log; uid: string &log; @@ -62,26 +55,7 @@ export { ## ALL_HOSTS - always capture the entire path. ## NO_HOSTS - never capture the path. const mail_path_capture = ALL_HOSTS &redef; - - # This matches content in SMTP error messages that indicate some - # block list doesn't like the connection/mail. - const bl_error_messages = - /spamhaus\.org\// - | /sophos\.com\/security\// - | /spamcop\.net\/bl/ - | /cbl\.abuseat\.org\// - | /sorbs\.net\// - | /bsn\.borderware\.com\// - | /mail-abuse\.com\// - | /b\.barracudacentral\.com\// - | /psbl\.surriel\.com\// - | /antispam\.imp\.ch\// - | /dyndns\.com\/.*spam/ - | /rbl\.knology\.net\// - | /intercept\.datapacket\.net\// - | /uceprotect\.net\// - | /hostkarma\.junkemailfilter\.com\// &redef; - + global log_smtp: event(rec: Info); ## Configure the default ports for SMTP analysis. @@ -184,27 +158,6 @@ event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, # This continually overwrites, but we want the last reply, # so this actually works fine. c$smtp$last_reply = fmt("%d %s", code, msg); - - if ( code != 421 && code >= 400 ) - { - # Raise a notice when an SMTP error about a block list is discovered. - if ( bl_error_messages in msg ) - { - local note = BL_Error_Message; - local message = fmt("%s received an error message mentioning an SMTP block list", c$id$orig_h); - - # Determine if the originator's IP address is in the message. - local ips = find_ip_addresses(msg); - local text_ip = ""; - if ( |ips| > 0 && to_addr(ips[0]) == c$id$orig_h ) - { - note = BL_Blocked_Host; - message = fmt("%s is on an SMTP block list", c$id$orig_h); - } - - NOTICE([$note=note, $conn=c, $msg=message, $sub=msg]); - } - } } event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, @@ -259,7 +212,7 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=5 else if ( h$name == "X-ORIGINATING-IP" ) { - local addresses = find_ip_addresses(h$name); + local addresses = find_ip_addresses(h$value); if ( 1 in addresses ) c$smtp$x_originating_ip = to_addr(addresses[1]); } @@ -277,7 +230,7 @@ event mime_one_header(c: connection, h: mime_header_rec) &priority=3 # If we've decided that we're done watching the received headers for # whatever reason, we're done. Could be due to only watching until # local addresses are seen in the received from headers. - if ( ! c?$smtp || h$name != "RECEIVED" || ! c$smtp$process_received_from) + if ( ! c?$smtp || h$name != "RECEIVED" || ! c$smtp$process_received_from ) return; local text_ip = find_address_in_smtp_header(h$value); diff --git a/scripts/base/protocols/ssh/main.bro b/scripts/base/protocols/ssh/main.bro index 1d1747a2fe..4c2af3ee42 100644 --- a/scripts/base/protocols/ssh/main.bro +++ b/scripts/base/protocols/ssh/main.bro @@ -1,3 +1,10 @@ +##! Base SSH analysis script. The heuristic to blindly determine success or +##! failure for SSH connections is implemented here. At this time, it only +##! uses the size of the data being returned from the server to make the +##! heuristic determination about success of the connection. +##! Requires that :bro:id:`use_conn_size_analyzer` is set to T! The heuristic +##! is not attempted if the connection size analyzer isn't enabled. + @load base/frameworks/notice/main @load base/utils/site @load base/utils/thresholds @@ -8,71 +15,49 @@ module SSH; export { redef enum Log::ID += { SSH }; - redef enum Notice::Type += { - Login, - Password_Guessing, - Login_By_Password_Guesser, - Login_From_Interesting_Hostname, - Bytecount_Inconsistency, - }; - type Info: record { ts: time &log; uid: string &log; id: conn_id &log; + ## Indicates if the login was heuristically guessed to be "success" + ## or "failure". status: string &log &optional; - direction: string &log &optional; - remote_location: geo_location &log &optional; + ## Direction of the connection. If the client was a local host + ## logging into an external host, this would be OUTBOUD. INBOUND + ## would be set for the opposite situation. + # TODO: handle local-local and remote-remote better. + direction: Direction &log &optional; + ## The software string given by the client. client: string &log &optional; + ## The software string given by the server. server: string &log &optional; + ## The amount of data returned from the server. This is currently + ## the only measure of the success heuristic and it is logged to + ## assist analysts looking at the logs to make their own determination + ## about the success on a case-by-case basis. resp_size: count &log &default=0; ## Indicate if the SSH session is done being watched. done: bool &default=F; }; - - const password_guesses_limit = 30 &redef; - # The size in bytes at which the SSH connection is presumed to be - # successful. + ## The size in bytes at which the SSH connection is presumed to be + ## successful. const authentication_data_size = 5500 &redef; - # The amount of time to remember presumed non-successful logins to build - # model of a password guesser. - const guessing_timeout = 30 mins &redef; - - # The set of countries for which you'd like to throw notices upon successful login - # requires Bro compiled with libGeoIP support - const watched_countries: set[string] = {"RO"} &redef; - - # Strange/bad host names to originate successful SSH logins - const interesting_hostnames = - /^d?ns[0-9]*\./ | - /^smtp[0-9]*\./ | - /^mail[0-9]*\./ | - /^pop[0-9]*\./ | - /^imap[0-9]*\./ | - /^www[0-9]*\./ | - /^ftp[0-9]*\./ &redef; - - # This is a table with orig subnet as the key, and subnet as the value. - const ignore_guessers: table[subnet] of subnet &redef; - - # If true, we tell the event engine to not look at further data - # packets after the initial SSH handshake. Helps with performance - # (especially with large file transfers) but precludes some - # kinds of analyses (e.g., tracking connection size). + ## If true, we tell the event engine to not look at further data + ## packets after the initial SSH handshake. Helps with performance + ## (especially with large file transfers) but precludes some + ## kinds of analyses (e.g., tracking connection size). const skip_processing_after_detection = F &redef; - # Keeps count of how many rejections a host has had - global password_rejections: table[addr] of TrackCount - &write_expire=guessing_timeout - &synchronized; - - # Keeps track of hosts identified as guessing passwords - # TODO: guessing_timeout doesn't work correctly here. If a user redefs - # the variable, it won't take effect. - global password_guessers: set[addr] &read_expire=guessing_timeout+1hr &synchronized; + ## This event is generated when the heuristic thinks that a login + ## was successful. + global heuristic_successful_login: event(c: connection); + + ## This event is generated when the heuristic thinks that a login + ## failed. + global heuristic_failed_login: event(c: connection); global log_ssh: event(rec: Info); } @@ -110,116 +95,51 @@ function check_ssh_connection(c: connection, done: bool) # If this is still a live connection and the byte count has not # crossed the threshold, just return and let the resheduled check happen later. - if ( !done && c$resp$size < authentication_data_size ) + if ( !done && c$resp$num_bytes_ip < authentication_data_size ) return; # Make sure the server has sent back more than 50 bytes to filter out # hosts that are just port scanning. Nothing is ever logged if the server # doesn't send back at least 50 bytes. - if ( c$resp$size < 50 ) + if ( c$resp$num_bytes_ip < 50 ) return; - local status = "failure"; - local direction = Site::is_local_addr(c$id$orig_h) ? "to" : "from"; - local location: geo_location; - location = (direction == "to") ? lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h); + c$ssh$direction = Site::is_local_addr(c$id$orig_h) ? OUTBOUND : INBOUND; + c$ssh$resp_size = c$resp$num_bytes_ip; - if ( done && c$resp$size < authentication_data_size ) + if ( c$resp$num_bytes_ip < authentication_data_size ) { - # presumed failure - if ( c$id$orig_h !in password_rejections ) - password_rejections[c$id$orig_h] = new_track_count(); - - # Track the number of rejections - if ( !(c$id$orig_h in ignore_guessers && - c$id$resp_h in ignore_guessers[c$id$orig_h]) ) - ++password_rejections[c$id$orig_h]$n; - - if ( default_check_threshold(password_rejections[c$id$orig_h]) ) - { - add password_guessers[c$id$orig_h]; - NOTICE([$note=Password_Guessing, - $conn=c, - $msg=fmt("SSH password guessing by %s", c$id$orig_h), - $sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n), - $n=password_rejections[c$id$orig_h]$n]); - } - } - # TODO: This is to work around a quasi-bug in Bro which occasionally - # causes the byte count to be oversized. - # Watch for Gregors work that adds an actual counter of bytes transferred. - else if ( c$resp$size < 20000000 ) + c$ssh$status = "failure"; + event SSH::heuristic_failed_login(c); + } + else { # presumed successful login - status = "success"; - c$ssh$done = T; - - if ( c$id$orig_h in password_rejections && - password_rejections[c$id$orig_h]$n > password_guesses_limit && - c$id$orig_h !in password_guessers ) - { - add password_guessers[c$id$orig_h]; - NOTICE([$note=Login_By_Password_Guesser, - $conn=c, - $n=password_rejections[c$id$orig_h]$n, - $msg=fmt("Successful SSH login by password guesser %s", c$id$orig_h), - $sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n)]); - } - - local message = fmt("SSH login %s %s \"%s\" \"%s\" %f %f %s (triggered with %d bytes)", - direction, location$country_code, location$region, location$city, - location$latitude, location$longitude, - id_string(c$id), c$resp$size); - NOTICE([$note=Login, - $conn=c, - $msg=message, - $sub=location$country_code]); - - # Check to see if this login came from an interesting hostname - when ( local hostname = lookup_addr(c$id$orig_h) ) - { - if ( interesting_hostnames in hostname ) - { - NOTICE([$note=Login_From_Interesting_Hostname, - $conn=c, - $msg=fmt("Strange login from %s", hostname), - $sub=hostname]); - } - } - - if ( location$country_code in watched_countries ) - { - - } - + c$ssh$status = "success"; + event SSH::heuristic_successful_login(c); } - else if ( c$resp$size >= 200000000 ) - { - NOTICE([$note=Bytecount_Inconsistency, - $conn=c, - $msg="During byte counting in SSH analysis, an overly large value was seen.", - $sub=fmt("%d",c$resp$size)]); - } - - c$ssh$remote_location = location; - c$ssh$status = status; - c$ssh$direction = direction; - c$ssh$resp_size = c$resp$size; - - Log::write(SSH, c$ssh); # Set the "done" flag to prevent the watching event from rescheduling # after detection is done. - c$ssh$done; + c$ssh$done=T; - # Stop watching this connection, we don't care about it anymore. if ( skip_processing_after_detection ) { + # Stop watching this connection, we don't care about it anymore. skip_further_processing(c$id); set_record_packets(c$id, F); } } +event SSH::heuristic_successful_login(c: connection) &priority=-5 + { + Log::write(SSH, c$ssh); + } +event SSH::heuristic_failed_login(c: connection) &priority=-5 + { + Log::write(SSH, c$ssh); + } + event connection_state_remove(c: connection) &priority=-5 { if ( c?$ssh ) @@ -230,7 +150,7 @@ event ssh_watcher(c: connection) { local id = c$id; # don't go any further if this connection is gone already! - if ( !connection_exists(id) ) + if ( ! connection_exists(id) ) return; check_ssh_connection(c, F); @@ -248,5 +168,9 @@ event ssh_client_version(c: connection, version: string) &priority=5 { set_session(c); c$ssh$client = version; - schedule +15secs { ssh_watcher(c) }; + + # The heuristic detection for SSH relies on the ConnSize analyzer. + # Don't do the heuristics if it's disabled. + if ( use_conn_size_analyzer ) + schedule +15secs { ssh_watcher(c) }; } diff --git a/scripts/policy/misc/profiling.bro b/scripts/policy/misc/profiling.bro new file mode 100644 index 0000000000..457675b1d6 --- /dev/null +++ b/scripts/policy/misc/profiling.bro @@ -0,0 +1,19 @@ +##! Turns on profiling of Bro resource consumption. + +module Profiling; + +redef profiling_file = open_log_file("prof"); + +export { + ## Cheap profiling every 15 seconds. + redef profiling_interval = 15 secs &redef; +} + +# Expensive profiling every 5 minutes. +redef expensive_profiling_multiple = 20; + +event bro_init() + { + set_buf(profiling_file, F); + } + diff --git a/scripts/policy/protocols/http/detect-MHR.bro b/scripts/policy/protocols/http/detect-MHR.bro index adf0707205..bc5e5ad2ae 100644 --- a/scripts/policy/protocols/http/detect-MHR.bro +++ b/scripts/policy/protocols/http/detect-MHR.bro @@ -1,7 +1,7 @@ ##! This script takes MD5 sums of files transferred over HTTP and checks them with ##! Team Cymru's Malware Hash Registry (http://www.team-cymru.org/Services/MHR/). ##! By default, not all file transfers will have MD5 sums calculated. Read the -##! documentation for the protocols/http/file-hash.bro script to see how to +##! documentation for the base/protocols/http/file-hash.bro script to see how to ##! configure which transfers will have hashes calculated. @load base/frameworks/notice/main diff --git a/scripts/policy/protocols/smtp/blocklists.bro b/scripts/policy/protocols/smtp/blocklists.bro new file mode 100644 index 0000000000..a3e75318bb --- /dev/null +++ b/scripts/policy/protocols/smtp/blocklists.bro @@ -0,0 +1,58 @@ + +@load base/protocols/smtp + +module SMTP; + +export { + redef enum Notice::Type += { + ## Indicates that the server sent a reply mentioning an SMTP block list. + Blocklist_Error_Message, + ## Indicates the client's address is seen in the block list error message. + Blocklist_Blocked_Host, + }; + + # This matches content in SMTP error messages that indicate some + # block list doesn't like the connection/mail. + const blocklist_error_messages = + /spamhaus\.org\// + | /sophos\.com\/security\// + | /spamcop\.net\/bl/ + | /cbl\.abuseat\.org\// + | /sorbs\.net\// + | /bsn\.borderware\.com\// + | /mail-abuse\.com\// + | /b\.barracudacentral\.com\// + | /psbl\.surriel\.com\// + | /antispam\.imp\.ch\// + | /dyndns\.com\/.*spam/ + | /rbl\.knology\.net\// + | /intercept\.datapacket\.net\// + | /uceprotect\.net\// + | /hostkarma\.junkemailfilter\.com\// &redef; + +} + +event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, + msg: string, cont_resp: bool) &priority=3 + { + if ( code >= 400 && code != 421 ) + { + # Raise a notice when an SMTP error about a block list is discovered. + if ( blocklist_error_messages in msg ) + { + local note = Blocklist_Error_Message; + local message = fmt("%s received an error message mentioning an SMTP block list", c$id$orig_h); + + # Determine if the originator's IP address is in the message. + local ips = find_ip_addresses(msg); + local text_ip = ""; + if ( |ips| > 0 && to_addr(ips[0]) == c$id$orig_h ) + { + note = Blocklist_Blocked_Host; + message = fmt("%s is on an SMTP block list", c$id$orig_h); + } + + NOTICE([$note=note, $conn=c, $msg=message, $sub=msg]); + } + } + } diff --git a/scripts/policy/protocols/ssh/detect-bruteforcing.bro b/scripts/policy/protocols/ssh/detect-bruteforcing.bro new file mode 100644 index 0000000000..36e73bfa59 --- /dev/null +++ b/scripts/policy/protocols/ssh/detect-bruteforcing.bro @@ -0,0 +1,79 @@ + +module SSH; + +export { + redef enum Notice::Type += { + ## Indicates that a host has been identified as crossing the + ## :bro:id:`password_guesses_limit` threshold with heuristically + ## determined failed logins. + Password_Guessing, + ## Indicates that a host previously identified as a "password guesser" + ## has now had a heuristically successful login attempt. + Login_By_Password_Guesser, + }; + + ## The number of failed SSH connections before a host is designated as + ## guessing passwords. + const password_guesses_limit = 30 &redef; + + ## The amount of time to remember presumed non-successful logins to build + ## model of a password guesser. + const guessing_timeout = 30 mins &redef; + + ## This value can be used to exclude hosts or entire networks from being + ## tracked as potential "guessers". There are cases where the success + ## heuristic fails and this acts as the whitelist. The index represents + ## client subnets and the yield value represents server subnets. + const ignore_guessers: table[subnet] of subnet &redef; + + ## Keeps count of how many rejections a host has had. + global password_rejections: table[addr] of TrackCount + &write_expire=guessing_timeout + &synchronized; + + ## Keeps track of hosts identified as guessing passwords. + global password_guessers: set[addr] &read_expire=guessing_timeout+1hr &synchronized; +} + +event SSH::heuristic_successful_login(c: connection) + { + local id = c$id; + + # TODO: this should be migrated to the metrics framework. + if ( id$orig_h in password_rejections && + password_rejections[id$orig_h]$n > password_guesses_limit && + id$orig_h !in password_guessers ) + { + add password_guessers[id$orig_h]; + NOTICE([$note=Login_By_Password_Guesser, + $conn=c, + $n=password_rejections[id$orig_h]$n, + $msg=fmt("Successful SSH login by password guesser %s", id$orig_h), + $sub=fmt("%d failed logins", password_rejections[id$orig_h]$n)]); + } + } + +event SSH::heuristic_failed_login(c: connection) + { + local id = c$id; + + # presumed failure + if ( id$orig_h !in password_rejections ) + password_rejections[id$orig_h] = new_track_count(); + + # Track the number of rejections + # TODO: this should be migrated to the metrics framework. + if ( ! (id$orig_h in ignore_guessers && + id$resp_h in ignore_guessers[id$orig_h]) ) + ++password_rejections[id$orig_h]$n; + + if ( default_check_threshold(password_rejections[id$orig_h]) ) + { + add password_guessers[id$orig_h]; + NOTICE([$note=Password_Guessing, + $conn=c, + $msg=fmt("SSH password guessing by %s", id$orig_h), + $sub=fmt("%d apparently failed logins", password_rejections[id$orig_h]$n), + $n=password_rejections[id$orig_h]$n]); + } + } \ No newline at end of file diff --git a/scripts/policy/protocols/ssh/geo-data.bro b/scripts/policy/protocols/ssh/geo-data.bro new file mode 100644 index 0000000000..97bd0a5803 --- /dev/null +++ b/scripts/policy/protocols/ssh/geo-data.bro @@ -0,0 +1,39 @@ +##! This implements all of the additional information and geodata detections +##! for SSH analysis. + +module SSH; + +export { + redef enum Notice::Type += { + ## If an SSH login is seen to or from a "watched" country based on the + ## :bro:id:`SSH::watched_countries` variable then this notice will + ## be generated. + Login_From_Watched_Country, + }; + + ## The set of countries for which you'd like to throw notices upon + ## successful login + const watched_countries: set[string] = {"RO"} &redef; + + redef record Info += { + ## Add geographic data related to the "remote" host of the connection. + remote_location: geo_location &log &optional; + }; +} + +event SSH::heuristic_successful_login(c: connection) &priority=5 + { + local location: geo_location; + location = (c$ssh$direction == OUTBOUND) ? + lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h); + + # Add the location data to the SSH record. + c$ssh$remote_location = location; + + if ( location$country_code in watched_countries ) + { + NOTICE([$note=Login_From_Watched_Country, + $conn=c, + $msg=fmt("SSH login from watched country: %s", location$country_code)]); + } + } diff --git a/scripts/policy/protocols/ssh/interesting-hostnames.bro b/scripts/policy/protocols/ssh/interesting-hostnames.bro new file mode 100644 index 0000000000..cf6ab7e40a --- /dev/null +++ b/scripts/policy/protocols/ssh/interesting-hostnames.bro @@ -0,0 +1,50 @@ + +module SSH; + +export { + redef enum Notice::Type += { + ## Generated if a login originates from a host matched by the + ## :bro:id:`interesting_hostnames` regular expression. + Login_From_Interesting_Hostname, + ## Generated if a login goes to a host matched by the + ## :bro:id:`interesting_hostnames` regular expression. + Login_To_Interesting_Hostname, + }; + + ## Strange/bad host names to see successful SSH logins from or to. + const interesting_hostnames = + /^d?ns[0-9]*\./ | + /^smtp[0-9]*\./ | + /^mail[0-9]*\./ | + /^pop[0-9]*\./ | + /^imap[0-9]*\./ | + /^www[0-9]*\./ | + /^ftp[0-9]*\./ &redef; +} + +event SSH::heuristic_successful_login(c: connection) + { + # Check to see if this login came from an interesting hostname. + when ( local orig_hostname = lookup_addr(c$id$orig_h) ) + { + if ( interesting_hostnames in orig_hostname ) + { + NOTICE([$note=Login_From_Interesting_Hostname, + $conn=c, + $msg=fmt("Interesting login from hostname: %s", orig_hostname), + $sub=orig_hostname]); + } + } + # Check to see if this login went to an interesting hostname. + when ( local resp_hostname = lookup_addr(c$id$orig_h) ) + { + if ( interesting_hostnames in resp_hostname ) + { + NOTICE([$note=Login_To_Interesting_Hostname, + $conn=c, + $msg=fmt("Interesting login to hostname: %s", resp_hostname), + $sub=resp_hostname]); + } + } + } + diff --git a/scripts/policy/protocols/ssh/software.bro b/scripts/policy/protocols/ssh/software.bro index ea04d44370..d9bfdffb3c 100644 --- a/scripts/policy/protocols/ssh/software.bro +++ b/scripts/policy/protocols/ssh/software.bro @@ -4,8 +4,8 @@ module SSH; export { redef enum Software::Type += { - SSH_SERVER, - SSH_CLIENT, + SERVER, + CLIENT, }; } @@ -13,7 +13,7 @@ event ssh_client_version(c: connection, version: string) &priority=4 { # Get rid of the protocol information when passing to the software framework. local cleaned_version = sub(version, /^SSH[0-9\.\-]+/, ""); - local si = Software::parse(cleaned_version, c$id$orig_h, SSH_CLIENT); + local si = Software::parse(cleaned_version, c$id$orig_h, CLIENT); Software::found(c$id, si); } @@ -21,6 +21,6 @@ event ssh_server_version(c: connection, version: string) &priority=4 { # Get rid of the protocol information when passing to the software framework. local cleaned_version = sub(version, /SSH[0-9\.\-]{2,}/, ""); - local si = Software::parse(cleaned_version, c$id$resp_h, SSH_SERVER); + local si = Software::parse(cleaned_version, c$id$resp_h, SERVER); Software::found(c$id, si); } diff --git a/scripts/site/local-manager.bro b/scripts/site/local-manager.bro new file mode 100644 index 0000000000..aa28bd79da --- /dev/null +++ b/scripts/site/local-manager.bro @@ -0,0 +1,7 @@ +##! Local site policy loaded only by the manager in a cluster. + +# If you are running a cluster you should define your Notice::policy here +# so that notice processing occurs on the manager. +redef Notice::policy += { + +}; diff --git a/scripts/site/local-proxy.bro b/scripts/site/local-proxy.bro new file mode 100644 index 0000000000..1b71cc1870 --- /dev/null +++ b/scripts/site/local-proxy.bro @@ -0,0 +1,2 @@ +##! Local site policy loaded only by the proxies if Bro is running as a cluster. + diff --git a/scripts/site/local-worker.bro b/scripts/site/local-worker.bro new file mode 100644 index 0000000000..b2a100e135 --- /dev/null +++ b/scripts/site/local-worker.bro @@ -0,0 +1 @@ +##! Local site policy loaded only by the workers if Bro is running as a cluster. \ No newline at end of file diff --git a/scripts/site/local.bro b/scripts/site/local.bro index 7165757eab..224ef278a0 100644 --- a/scripts/site/local.bro +++ b/scripts/site/local.bro @@ -22,6 +22,7 @@ redef signature_files += "frameworks/signatures/detect-windows-shells.sig"; # Load all of the scripts that detect software in various protocols. @load protocols/http/software +#@load protocols/http/detect-webapps @load protocols/ftp/software @load protocols/smtp/software @load protocols/ssh/software @@ -45,6 +46,19 @@ redef signature_files += "frameworks/signatures/detect-windows-shells.sig"; # Load the script to enable SSL/TLS certificate validation. @load protocols/ssl/validate-certs +# If you have libGeoIP support built in, do some geographic detections and +# logging for SSH traffic. +@load protocols/ssh/geo-data +# Detect hosts doing SSH bruteforce attacks. +@load protocols/ssh/detect-bruteforcing +# Detect logins using "interesting" hostnames. +@load protocols/ssh/interesting-hostnames + +# Detect MD5 sums in Team Cymru's Malware Hash Registry. +@load protocols/http/detect-MHR +# Detect SQL injection attacks +@load protocols/http/detect-sqli + # Uncomment this redef if you want to extract SMTP MIME entities for # some file types. The numbers given indicate how many bytes to extract for # the various mime types.