Merge branch 'master' of ssh://git.bro-ids.org/bro

This commit is contained in:
Robin Sommer 2011-10-21 11:38:40 -07:00
commit 4d0d49ecc1
21 changed files with 166 additions and 143 deletions

View file

@ -148,7 +148,7 @@ export {
## from highest value (10) to lowest value (0). ## from highest value (10) to lowest value (0).
priority: count &log &default=5; priority: count &log &default=5;
## An action given to the notice if the predicate return true. ## An action given to the notice if the predicate return true.
result: Notice::Action &log &default=ACTION_NONE; action: Notice::Action &log &default=ACTION_NONE;
## The pred (predicate) field is a function that returns a boolean T ## The pred (predicate) field is a function that returns a boolean T
## or F value. If the predicate function return true, the action in ## or F value. If the predicate function return true, the action in
## this record is applied to the notice that is given as an argument ## this record is applied to the notice that is given as an argument
@ -169,13 +169,13 @@ export {
[$pred(n: Notice::Info) = { return (n$note in Notice::ignored_types); }, [$pred(n: Notice::Info) = { return (n$note in Notice::ignored_types); },
$halt=T, $priority = 9], $halt=T, $priority = 9],
[$pred(n: Notice::Info) = { return (n$note in Notice::not_suppressed_types); }, [$pred(n: Notice::Info) = { return (n$note in Notice::not_suppressed_types); },
$result = ACTION_NO_SUPPRESS, $action = ACTION_NO_SUPPRESS,
$priority = 9], $priority = 9],
[$pred(n: Notice::Info) = { return (n$note in Notice::alarmed_types); }, [$pred(n: Notice::Info) = { return (n$note in Notice::alarmed_types); },
$result = ACTION_ALARM, $action = ACTION_ALARM,
$priority = 8], $priority = 8],
[$pred(n: Notice::Info) = { return (n$note in Notice::emailed_types); }, [$pred(n: Notice::Info) = { return (n$note in Notice::emailed_types); },
$result = ACTION_EMAIL, $action = ACTION_EMAIL,
$priority = 8], $priority = 8],
[$pred(n: Notice::Info) = { [$pred(n: Notice::Info) = {
if (n$note in Notice::type_suppression_intervals) if (n$note in Notice::type_suppression_intervals)
@ -185,9 +185,9 @@ export {
} }
return F; return F;
}, },
$result = ACTION_NONE, $action = ACTION_NONE,
$priority = 8], $priority = 8],
[$result = ACTION_LOG, [$action = ACTION_LOG,
$priority = 0], $priority = 0],
} &redef; } &redef;
@ -357,6 +357,14 @@ function email_notice_to(n: Notice::Info, dest: string, extend: bool)
# The notice emails always start off with the human readable message. # The notice emails always start off with the human readable message.
email_text = string_cat(email_text, "\n", n$msg, "\n"); email_text = string_cat(email_text, "\n", n$msg, "\n");
# Add information about the connection if it exists.
if ( n?$id )
email_text = cat(email_text, "Connection: ",
n$id$orig_h, ":", n$id$orig_p, " -> ",
n$id$resp_h, ":", n$id$resp_p, "\n");
else if ( n?$src )
email_text = cat(email_text, "Address: ", n$src, "\n");
# Add the extended information if it's requested. # Add the extended information if it's requested.
if ( extend ) if ( extend )
{ {
@ -466,7 +474,7 @@ function apply_policy(n: Notice::Info)
# If there's no predicate or the predicate returns F. # If there's no predicate or the predicate returns F.
if ( ! ordered_policy[i]?$pred || ordered_policy[i]$pred(n) ) if ( ! ordered_policy[i]?$pred || ordered_policy[i]$pred(n) )
{ {
add n$actions[ordered_policy[i]$result]; add n$actions[ordered_policy[i]$action];
add n$policy_items[int_to_count(i)]; add n$policy_items[int_to_count(i)];
# If the predicate matched and there was a suppression interval, # If the predicate matched and there was a suppression interval,

View file

@ -54,15 +54,12 @@ event http_entity_data(c: connection, is_orig: bool, length: count, data: string
## incorrect anyway. ## incorrect anyway.
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5 event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
{ {
if ( is_orig || ! c?$http ) return; if ( is_orig || ! c?$http || ! c$http$calculating_md5 ) return;
set_state(c, F, is_orig); set_state(c, F, is_orig);
if ( c$http$calculating_md5 )
{
c$http$calculating_md5 = F; c$http$calculating_md5 = F;
md5_hash_finish(c$id); md5_hash_finish(c$id);
} }
}
## When the file finishes downloading, finish the hash and generate a notice. ## When the file finishes downloading, finish the hash and generate a notice.
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority=-3 event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority=-3

View file

@ -493,40 +493,41 @@ export {
} &default="UNKNOWN"; } &default="UNKNOWN";
const x509_errors: table[count] of string = { const x509_errors: table[count] of string = {
[0] = "X509_V_OK", [0] = "ok",
[1] = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT", [1] = "unable to get issuer cert",
[2] = "X509_V_ERR_UNABLE_TO_GET_CRL", [2] = "unable to get crl",
[3] = "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE", [3] = "unable to decrypt cert signature",
[4] = "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE", [4] = "unable to decrypt crl signature",
[5] = "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY", [5] = "unable to decode issuer public key",
[6] = "X509_V_ERR_CERT_SIGNATURE_FAILURE", [6] = "cert signature failure",
[7] = "X509_V_ERR_CRL_SIGNATURE_FAILURE", [7] = "crl signature failure",
[8] = "X509_V_ERR_CERT_NOT_YET_VALID", [8] = "cert not yet valid",
[9] = "X509_V_ERR_CERT_HAS_EXPIRED", [9] = "cert has expired",
[10] = "X509_V_ERR_CRL_NOT_YET_VALID", [10] = "crl not yet valid",
[11] = "X509_V_ERR_CRL_HAS_EXPIRED", [11] = "crl has expired",
[12] = "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD", [12] = "error in cert not before field",
[13] = "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD", [13] = "error in cert not after field",
[14] = "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD", [14] = "error in crl last update field",
[15] = "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD", [15] = "error in crl next update field",
[16] = "X509_V_ERR_OUT_OF_MEM", [16] = "out of mem",
[17] = "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT", [17] = "depth zero self signed cert",
[18] = "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN", [18] = "self signed cert in chain",
[19] = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY", [19] = "unable to get issuer cert locally",
[20] = "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE", [20] = "unable to verify leaf signature",
[21] = "X509_V_ERR_CERT_CHAIN_TOO_LONG", [21] = "cert chain too long",
[22] = "X509_V_ERR_CERT_REVOKED", [22] = "cert revoked",
[23] = "X509_V_ERR_INVALID_CA", [23] = "invalid ca",
[24] = "X509_V_ERR_PATH_LENGTH_EXCEEDED", [24] = "path length exceeded",
[25] = "X509_V_ERR_INVALID_PURPOSE", [25] = "invalid purpose",
[26] = "X509_V_ERR_CERT_UNTRUSTED", [26] = "cert untrusted",
[27] = "X509_V_ERR_CERT_REJECTED", [27] = "cert rejected",
[28] = "X509_V_ERR_SUBJECT_ISSUER_MISMATCH", [28] = "subject issuer mismatch",
[29] = "X509_V_ERR_AKID_SKID_MISMATCH", [29] = "akid skid mismatch",
[30] = "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH", [30] = "akid issuer serial mismatch",
[31] = "X509_V_ERR_KEYUSAGE_NO_CERTSIGN", [31] = "keyusage no certsign",
[32] = "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER", [32] = "unable to get crl issuer",
[33] = "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION" [33] = "unhandled critical extension"
}; };
} }

View file

@ -1,9 +1,14 @@
##!
module LoadedScripts; module LoadedScripts;
export { export {
redef enum Log::ID += { LOG }; redef enum Log::ID += { LOG };
type Info: record { type Info: record {
## Name of the script loaded potentially with spaces included before
## the file name to indicate load depth. The convention is two spaces
## per level of depth.
name: string &log; name: string &log;
}; };
} }

View file

@ -29,6 +29,8 @@ export {
} }
redef record connection += { redef record connection += {
## This field is to indicate whether or not the processing for detecting
## and logging the service for this connection is complete.
known_services_done: bool &default=F; known_services_done: bool &default=F;
}; };

View file

@ -1,11 +1,12 @@
##! This script handles core generated connection related "weird" events to ##! This script handles core generated connection related "weird" events to
##! push weird information about connections into the weird framework. ##! push weird information about connections into the weird framework.
##! For live operational deployments, this can frequently cause load issues ##! For live operational deployments, this can frequently cause load issues
##! due to large numbers of these events being passed between nodes. ##! due to large numbers of these events and quite possibly shouldn't be
##! loaded.
@load base/frameworks/notice @load base/frameworks/notice
module Weird; module Conn;
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {
@ -19,15 +20,12 @@ export {
} }
event rexmit_inconsistency(c: connection, t1: string, t2: string) event rexmit_inconsistency(c: connection, t1: string, t2: string)
{
if ( c$id !in did_inconsistency_msg )
{ {
NOTICE([$note=Retransmission_Inconsistency, NOTICE([$note=Retransmission_Inconsistency,
$conn=c, $conn=c,
$msg=fmt("%s rexmit inconsistency (%s) (%s)", $msg=fmt("%s rexmit inconsistency (%s) (%s)",
id_string(c$id), t1, t2)]); id_string(c$id), t1, t2),
add did_inconsistency_msg[c$id]; $identifier=fmt("%s", c$id)]);
}
} }
event ack_above_hole(c: connection) event ack_above_hole(c: connection)

View file

@ -1,3 +1,8 @@
##! This script adds authoritative and additional responses for the current
##! query to the DNS log. It can cause severe overhead due to the need
##! for all authoritative and additional responses to have events generated.
##! This script is not recommended for use on heavily loaded links.
@load base/protocols/dns/main @load base/protocols/dns/main
redef dns_skip_all_auth = F; redef dns_skip_all_auth = F;
@ -7,12 +12,14 @@ module DNS;
export { export {
redef record Info += { redef record Info += {
## Authoritative responses for the query.
auth: set[string] &log &optional; auth: set[string] &log &optional;
## Additional responses for the query.
addl: set[string] &log &optional; addl: set[string] &log &optional;
}; };
} }
event do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=4 event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=4
{ {
# The "ready" flag will be set here. This causes the setting from the # The "ready" flag will be set here. This causes the setting from the
# base script to be overridden since the base script will log immediately # base script to be overridden since the base script will log immediately

View file

@ -1,14 +1,9 @@
##! Script for detecting strange activity within DNS. ##! This script detects names which are not within zones considered to be
##! ##! local but resolving to addresses considered local.
##! Notices raised: ##! The :bro:id:`Site::local_zones` variable **must** be set appropriately for
##! ##! this detection.
##! * :bro:enum:`DNS::External_Name`
##!
##! A remote host resolves to a local host, but the name is not considered
##! to be within a local zone. :bro:id:`local_zones` variable **must**
##! be set appropriately for this detection.
@load base/frameworks/notice/main @load base/frameworks/notice
@load base/utils/site @load base/utils/site
module DNS; module DNS;
@ -16,8 +11,8 @@ module DNS;
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {
## Raised when a non-local name is found to be pointing at a local host. ## Raised when a non-local name is found to be pointing at a local host.
## This only works appropriately when all of your authoritative DNS ## :bro:id:`Site::local_zones` variable **must** be set appropriately
## servers are located in your :bro:id:`Site::local_nets`. ## for this detection.
External_Name, External_Name,
}; };
} }
@ -30,11 +25,11 @@ event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priori
# Check for responses from remote hosts that point at local hosts # Check for responses from remote hosts that point at local hosts
# but the name is not considered to be within a "local" zone. # but the name is not considered to be within a "local" zone.
if ( Site::is_local_addr(a) && # referring to a local host if ( Site::is_local_addr(a) && # referring to a local host
!Site::is_local_addr(c$id$resp_h) && # response from an external nameserver
! Site::is_local_name(ans$query) ) # name isn't in a local zone. ! Site::is_local_name(ans$query) ) # name isn't in a local zone.
{ {
NOTICE([$note=External_Name, NOTICE([$note=External_Name,
$msg=fmt("%s is pointing to a local host - %s.", ans$query, a), $msg=fmt("%s is pointing to a local host - %s.", ans$query, a),
$conn=c]); $conn=c,
$identifier=cat(a,ans$query)]);
} }
} }

View file

@ -1,5 +1,7 @@
@load base/frameworks/notice/main ##! Detect various potentially bad FTP activities.
@load base/protocols/ftp/main
@load base/frameworks/notice
@load base/protocols/ftp
module FTP; module FTP;
@ -21,6 +23,7 @@ event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool) &prior
/[Ee][Xx][Ee][Cc]/ in c$ftp$cmdarg$arg ) /[Ee][Xx][Ee][Cc]/ in c$ftp$cmdarg$arg )
{ {
NOTICE([$note=Site_Exec_Success, $conn=c, NOTICE([$note=Site_Exec_Success, $conn=c,
$msg=fmt("%s %s", c$ftp$cmdarg$cmd, c$ftp$cmdarg$arg)]); $msg=fmt("FTP command: %s %s", c$ftp$cmdarg$cmd, c$ftp$cmdarg$arg),
$identifier=cat(c$id$orig_h, c$id$resp_h, "SITE EXEC")]);
} }
} }

View file

@ -1,12 +1,12 @@
##! Software detection with the FTP protocol. ##! Software detection with the FTP protocol.
##!
##! TODO:
##!
##! * Detect server software with initial 220 message
##! * Detect client software with password given for anonymous users
##! (e.g. cyberduck@example.net)
@load base/frameworks/software/main # TODO:
#
# * Detect server software with initial 220 message
# * Detect client software with password given for anonymous users
# (e.g. cyberduck@example.net)
@load base/frameworks/software
module FTP; module FTP;

View file

@ -4,10 +4,8 @@
##! documentation for the :doc:base/protocols/http/file-hash.bro script to see how to ##! documentation for the :doc:base/protocols/http/file-hash.bro script to see how to
##! configure which transfers will have hashes calculated. ##! configure which transfers will have hashes calculated.
@load base/frameworks/notice/main @load base/frameworks/notice
@load base/protocols/http/main @load base/protocols/http
@load base/protocols/http/utils
@load base/protocols/http/file-hash
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {

View file

@ -1,4 +1,4 @@
##! SQL injection detection in HTTP. ##! SQL injection attack detection in HTTP.
@load base/frameworks/notice @load base/frameworks/notice
@load base/frameworks/metrics @load base/frameworks/metrics
@ -8,7 +8,10 @@ module HTTP;
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {
## Indicates that a host performing SQL injection attacks was detected.
SQL_Injection_Attacker, SQL_Injection_Attacker,
## Indicates that a host was seen to have SQL injection attacks against
## it. This is tracked by IP address as opposed to hostname.
SQL_Injection_Attack_Against, SQL_Injection_Attack_Against,
}; };
@ -49,6 +52,10 @@ export {
event bro_init() &priority=3 event bro_init() &priority=3
{ {
# Add filters to the metrics so that the metrics framework knows how to
# determine when it looks like an actual attack and how to respond when
# thresholds are crossed.
Metrics::add_filter(SQL_ATTACKER, [$log=F, Metrics::add_filter(SQL_ATTACKER, [$log=F,
$notice_threshold=sqli_requests_threshold, $notice_threshold=sqli_requests_threshold,
$break_interval=sqli_requests_interval, $break_interval=sqli_requests_interval,

View file

@ -1,7 +1,6 @@
@load base/frameworks/signatures/main @load base/frameworks/signatures
@load base/frameworks/software/main @load base/frameworks/software
@load base/protocols/http/main @load base/protocols/http
@load base/protocols/http/utils
module HTTP; module HTTP;

View file

@ -1,5 +1,6 @@
##! This script take advantage of a few ways that installed plugin information ##! This script take advantage of a few ways that installed plugin information
##! leaks from web browsers ##! leaks from web browsers.
@load base/protocols/http @load base/protocols/http
@load base/frameworks/software @load base/frameworks/software

View file

@ -1,6 +1,6 @@
##! Software identification and extraction for HTTP traffic. ##! Software identification and extraction for HTTP traffic.
@load base/frameworks/software/main @load base/frameworks/software
module HTTP; module HTTP;

View file

@ -1,7 +1,6 @@
##! This script extracts and logs variables from the requested URI ##! This script extracts and logs variables from the requested URI
@load base/protocols/http/main @load base/protocols/http
@load base/protocols/http/utils
module HTTP; module HTTP;

View file

@ -38,7 +38,8 @@ export {
const ignore_guessers: table[subnet] of subnet &redef; const ignore_guessers: table[subnet] of subnet &redef;
## Keeps track of hosts identified as guessing passwords. ## Keeps track of hosts identified as guessing passwords.
global password_guessers: set[addr] &read_expire=guessing_timeout+1hr &synchronized; global password_guessers: set[addr]
&read_expire=guessing_timeout+1hr &synchronized &redef;
} }
event bro_init() event bro_init()

View file

@ -1,8 +1,8 @@
##! This implements all of the additional information and geodata detections ##! This implements all of the additional information and geodata detections
##! for SSH analysis. ##! for SSH analysis.
@load base/frameworks/notice/main @load base/frameworks/notice
@load base/protocols/ssh/main @load base/protocols/ssh
module SSH; module SSH;
@ -11,17 +11,17 @@ export {
## If an SSH login is seen to or from a "watched" country based on the ## 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 ## :bro:id:`SSH::watched_countries` variable then this notice will
## be generated. ## be generated.
Login_From_Watched_Country, Watched_Country_Login,
}; };
## 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 += { redef record Info += {
## Add geographic data related to the "remote" host of the connection. ## Add geographic data related to the "remote" host of the connection.
remote_location: geo_location &log &optional; remote_location: geo_location &log &optional;
}; };
## The set of countries for which you'd like to throw notices upon
## successful login
const watched_countries: set[string] = {"RO"} &redef;
} }
event SSH::heuristic_successful_login(c: connection) &priority=5 event SSH::heuristic_successful_login(c: connection) &priority=5
@ -35,8 +35,10 @@ event SSH::heuristic_successful_login(c: connection) &priority=5
if ( location?$country_code && location$country_code in watched_countries ) if ( location?$country_code && location$country_code in watched_countries )
{ {
NOTICE([$note=Login_From_Watched_Country, NOTICE([$note=Watched_Country_Login,
$conn=c, $conn=c,
$msg=fmt("SSH login from watched country: %s", location$country_code)]); $msg=fmt("SSH login %s watched country: %s",
(c$ssh$direction == OUTBOUND) ? "to" : "from",
location$country_code)]);
} }
} }

View file

@ -1,15 +1,19 @@
@load base/frameworks/notice/main ##! This script will generate a notice if an apparent SSH login originates
##! or heads to a host with a reverse hostname that looks suspicious. By
##! default, the regular expression to match "interesting" hostnames includes
##! names that are typically used for infrastructure hosts like nameservers,
##! mail servers, web servers and ftp servers.
@load base/frameworks/notice
module SSH; module SSH;
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {
## Generated if a login originates from a host matched by the ## Generated if a login originates or responds with a host and the
## reverse hostname lookup resolves to a name matched by the
## :bro:id:`interesting_hostnames` regular expression. ## :bro:id:`interesting_hostnames` regular expression.
Login_From_Interesting_Hostname, Interesting_Hostname_Login,
## 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. ## Strange/bad host names to see successful SSH logins from or to.
@ -25,27 +29,17 @@ export {
event SSH::heuristic_successful_login(c: connection) event SSH::heuristic_successful_login(c: connection)
{ {
# Check to see if this login came from an interesting hostname. for ( host in set(c$id$orig_h, c$id$resp_h) )
when ( local orig_hostname = lookup_addr(c$id$orig_h) )
{ {
if ( interesting_hostnames in orig_hostname ) when ( local hostname = lookup_addr(host) )
{ {
NOTICE([$note=Login_From_Interesting_Hostname, if ( interesting_hostnames in hostname )
$conn=c, {
$msg=fmt("Interesting login from hostname: %s", orig_hostname), NOTICE([$note=Interesting_Hostname_Login,
$sub=orig_hostname]); $msg=fmt("Interesting login from hostname: %s", hostname),
$sub=hostname, $conn=c]);
} }
} }
# Check to see if this login went to an interesting hostname.
when ( local resp_hostname = lookup_addr(c$id$resp_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]);
}
} }
} }

View file

@ -1,4 +1,7 @@
@load base/frameworks/software/main ##! This script extracts SSH client and server information from SSH
##! connections and forwards it to the software framework.
@load base/frameworks/software
module SSH; module SSH;

View file

@ -1,26 +1,29 @@
##! Perform full certificate chain validation for SSL certificates. ##! Perform full certificate chain validation for SSL certificates.
@load base/frameworks/notice/main @load base/frameworks/notice
@load base/protocols/ssl/main @load base/protocols/ssl
@load protocols/ssl/cert-hash @load protocols/ssl/cert-hash
module SSL; module SSL;
export { export {
redef enum Notice::Type += { redef enum Notice::Type += {
## This notice indicates that the result of validating the certificate
## along with it's full certificate chain was invalid.
Invalid_Server_Cert Invalid_Server_Cert
}; };
redef record Info += { redef record Info += {
## This stores and logs the result of certificate validation for
## this connection.
validation_status: string &log &optional; validation_status: string &log &optional;
}; };
## MD5 hash values for recently validated certs along with the validation ## MD5 hash values for recently validated certs along with the validation
## status message are kept in this table so avoid constant validation ## status message are kept in this table to avoid constant validation
## everytime the same certificate is seen. ## everytime the same certificate is seen.
global recently_validated_certs: table[string] of string = table() global recently_validated_certs: table[string] of string = table()
&read_expire=5mins &synchronized; &read_expire=5mins &synchronized &redef;
} }
event ssl_established(c: connection) &priority=3 event ssl_established(c: connection) &priority=3