diff --git a/scripts/base/frameworks/time-machine/notice.bro b/scripts/base/frameworks/time-machine/notice.bro deleted file mode 100644 index f012de8ee9..0000000000 --- a/scripts/base/frameworks/time-machine/notice.bro +++ /dev/null @@ -1,3 +0,0 @@ -# If we asked the Time Machine to capture, the filename prefix. -# TODO: implement this as a timemachine/notice.bro script? -#captured: string &optional; diff --git a/scripts/base/protocols/http/partial-content.bro b/scripts/base/protocols/http/partial-content.bro deleted file mode 100644 index 92bda1b345..0000000000 --- a/scripts/base/protocols/http/partial-content.bro +++ /dev/null @@ -1,98 +0,0 @@ -##! This script makes it possible for the HTTP analysis scripts to analyze -##! the apparent normal case of "206 Partial Content" responses. -##! -##! This script doesn't work yet and isn't loaded by default. - -@load base/frameworks/notice -@load ./main -@load ./utils - -module HTTP; - -export { - redef enum Notice::Type += { - Partial_Content_Out_Of_Order, - }; - - type Range: record { - from: count; - to: count; - } &log; - - redef record Info += { - current_range: count &default=0; - request_ranges: vector of Range &optional; - response_range: Range &optional; - }; - - ## Index is client IP address, server IP address, and URL being requested. The - ## URL is tracked as part of the index in case multiple partial content segmented - ## files are being transferred simultaneously between the server and client. - global partial_content_files: table[addr, addr, string] of Info &read_expire=5mins &redef; -} - -event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=2 - { - local parts: table[count] of string; - if ( is_orig && name == "RANGE" ) - { - # Example --> Range: bytes=1-1,2336-4951 - parts = split(value, /[=]/); - if ( 2 in parts ) - { - local ranges = split(parts[2], /,/); - for ( i in ranges ) - { - if ( ! c$http?$request_ranges ) - c$http$request_ranges = vector(); - parts = split(ranges[i], /-/); - local r: Range = [$from=extract_count(parts[1]), $to=extract_count(parts[2])]; - print r; - c$http$request_ranges[|c$http$request_ranges|] = r; - } - } - } - else if ( ! is_orig && name == "CONTENT-RANGE" ) - { - # Example --> Content-Range: bytes 2336-4951/489528 - parts = split(value, /[0-9]*/); - - c$http$response_range = [$from=extract_count(parts[2]), $to=extract_count(parts[4])]; - - } - } - -event http_reply(c: connection, version: string, code: count, reason: string) &priority=5 - { - if ( code != 206 || ! c$http?$request_ranges ) - return; - - local url = build_url(c$http); - if ( [c$id$orig_h, c$id$resp_h, url] !in partial_content_files ) - { - partial_content_files[c$id$orig_h, c$id$resp_h, url] = copy(c$http); - } - } - -event http_entity_data(c: connection, is_orig: bool, length: count, data: string) - { - if ( is_orig || c$http$status_code != 206 || ! c$http?$request_ranges ) - return; - - local url = build_url(c$http); - local http = partial_content_files[c$id$orig_h, c$id$resp_h, url]; - local range = http$request_ranges[http$current_range]; - - print http$current_range; - if ( http$current_range == 0 && - c$http$response_range$from == 0 ) - { - print "correct file beginning!"; - } - } - -event http_end_entity(c: connection, is_orig: bool) - { - print "end entity"; - ++c$http$current_range; - } diff --git a/scripts/base/protocols/rpc/main.bro b/scripts/base/protocols/rpc/main.bro deleted file mode 100644 index 36a524c880..0000000000 --- a/scripts/base/protocols/rpc/main.bro +++ /dev/null @@ -1,149 +0,0 @@ - -# -# Log RPC request and reply messages. Does not in itself start/activate -# an analyzer. You need to load portmap and/or NFS for that -# -# TODO: maybe automatically load portmap, add a generic RPC analyzer and -# use expect connection, so that we can see RPC request/replies for RPC -# programs for which we don't have an analyzer. -# - -@load base/utils/conn-ids - -module RPC; - -export { - global log_file = open_log_file("rpc") &redef; - # whether to match request to replies on the policy layer. - # (will report on rexmit and missing requests or replies) - global track_requests_replies = T &redef; -} - - -type rpc_call_state: enum { - NONE, - HAVE_CALL, - HAVE_REPLY -}; - -type rpc_call_info: record { - state: rpc_call_state; - calltime: time; - cid: conn_id; -}; - -function new_call(cid: conn_id): rpc_call_info - { - local ci: rpc_call_info; - - ci$state = NONE; - ci$calltime = network_time(); - ci$cid = cid; - return ci; - } - -function rpc_expire_xid(t: table[count] of rpc_call_info, xid: count): interval - { - local ci = t[xid]; - if (ci$state != HAVE_REPLY) - print log_file, fmt("%.6f %s %s note XID %d never recevied a reply", - ci$calltime, id_string(ci$cid), - get_port_transport_proto(ci$cid$orig_p), xid); - return 0 sec; - } - -function new_xid_table(): table[count] of rpc_call_info - { - local inner: table[count] of rpc_call_info &write_expire=rpc_timeout &expire_func=rpc_expire_xid; - return inner; - } - - -# Match requests to replies. -# The analyzer does this indepently and might differ in timeouts and -# handling of xid reuse. -# FIXME: add timeouts. Note, we do clean up on connection_state_remove -global rpc_calls: table[conn_id] of table[count] of rpc_call_info; -# &write_expire = rpc_timeout &expire_func=expire_rpc_call; - - -event rpc_dialogue(c: connection, prog: count, ver: count, proc: count, status: rpc_status, start_time: time, call_len: count, reply_len: count) - { - # TODO: We currently do nothing here. - # using the rpc_call and rpc_reply events, is all we need. - } - -event rpc_call(c: connection, xid: count, prog: count, ver: count, proc: count, call_len: count) - { - if (track_requests_replies) - { - if (c$id !in rpc_calls) - rpc_calls[c$id] = new_xid_table(); - if (xid !in rpc_calls[c$id]) - rpc_calls[c$id][xid] = new_call(c$id); - local curstate = rpc_calls[c$id][xid]$state; - - if (curstate == HAVE_CALL) - print log_file, fmt("%.6f %s %s note XID %d call retransmitted", - network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid); - else if (curstate == HAVE_REPLY) - print log_file, fmt("%.6f %s %s note XID %d call received after reply", - network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid); - rpc_calls[c$id][xid]$state = HAVE_CALL; - } - - print log_file, fmt("%.6f %s %s rpc_call %d %d %d %d %d", - network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid, prog, ver, proc, call_len); - } - -event rpc_reply(c: connection, xid: count, status: rpc_status, reply_len: count) - { - if (track_requests_replies) - { - if (c$id !in rpc_calls) - rpc_calls[c$id] = new_xid_table(); - if (xid !in rpc_calls[c$id]) - { - rpc_calls[c$id][xid] = new_call(c$id); - # XXX: what to do about calltime in rpc_call_info?? - } - if (rpc_calls[c$id][xid]$state == NONE) - print log_file, fmt("%.6f %s %s note XID %d reply but call is missing", - network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid); - else if (rpc_calls[c$id][xid]$state == HAVE_REPLY) - print log_file, fmt("%.6f %s %s note XID %d reply retransmitted", - network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid); - rpc_calls[c$id][xid]$state = HAVE_REPLY; - } - - print log_file, fmt("%.6f %s %s rpc_reply %d %s %d", - network_time(), reverse_id_string(c$id), get_port_transport_proto(c$id$orig_p), - xid, status, reply_len); - } - - - -function finish_calls(cid: conn_id) - { - for (xid in rpc_calls[cid]) - rpc_expire_xid(rpc_calls[cid], xid); - } - -event connection_state_remove(c: connection) - { - if (c$id !in rpc_calls) - return; - finish_calls(c$id); - delete rpc_calls[c$id]; - } - -event bro_done() - { - for (cid in rpc_calls) - finish_calls(cid); - } diff --git a/scripts/policy/hot.conn.bro b/scripts/policy/hot.conn.bro deleted file mode 100644 index 3b29504f73..0000000000 --- a/scripts/policy/hot.conn.bro +++ /dev/null @@ -1,200 +0,0 @@ -@load hot - -redef enum Notice += { - Sensitive_Connection, # connection marked "hot" -}; - -# Whether to translate the local address in Sensitive_Connection notices -# to a hostname. Meant as a demonstration of the "when" construct. -const xlate_hot_local_addr = F &redef; - -# The sets are indexed by the complete hot messages. -global hot_conns_reported: table[conn_id] of set[string]; - -const conn_closed = { TCP_CLOSED, TCP_RESET }; - -const state_graphic = { - ["OTH"] = "?>?", ["REJ"] = "[", - ["RSTO"] = ">]", ["RSTOS0"] = "}]", ["RSTR"] = ">[", ["RSTRH"] = "<[", - ["S0"] = "}", ["S1"] = ">", ["S2"] = "}2", ["S3"] = "}3", - ["SF"] = ">", ["SH"] = ">h", ["SHR"] = " 0 ) - log_hot_conn(c); - } - -event connection_state_remove(c: connection) &priority = -10 - { - local os = c$orig$state; - local rs = c$resp$state; - - if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) - # It was still active, no summary generated. - connection_gone(c, "remove"); - - else if ( (os == TCP_CLOSED || rs == TCP_CLOSED) && - (os == TCP_ESTABLISHED || rs == TCP_ESTABLISHED) ) - # One side has closed, the other hasn't - it's in state S2 - # or S3, hasn't been reported yet. - connection_gone(c, "remove"); - - delete hot_conns_reported[c$id]; - } - - -event partial_connection(c: connection) - { - if ( c$orig$state == TCP_PARTIAL && c$resp$state == TCP_INACTIVE ) - # This appears to be a stealth scan. Don't do hot-checking - # as there wasn't an established connection. - ; - else - { - Hot::check_hot(c, Hot::CONN_ESTABLISHED); - Hot::check_hot(c, Hot::APPL_ESTABLISHED); # assume it's been established - } - - if ( c$hot > 0 ) - log_hot_conn(c); - } - -event connection_attempt(c: connection) - { - Hot::check_spoof(c); - Hot::check_hot(c, Hot::CONN_ATTEMPTED); - } - -event connection_finished(c: connection) - { - if ( c$orig$size == 0 || c$resp$size == 0 ) - # Hard to get excited about this - not worth logging again. - c$hot = 0; - else - Hot::check_hot(c, Hot::CONN_FINISHED); - } - -event connection_partial_close(c: connection) - { - if ( c$orig$size == 0 || c$resp$size == 0 ) - # Hard to get excited about this - not worth logging again. - c$hot = 0; - else - Hot::check_hot(c, Hot::CONN_FINISHED); - } - -event connection_half_finished(c: connection) - { - Hot::check_hot(c, Hot::CONN_ATTEMPTED); - } - -event connection_rejected(c: connection) - { - Hot::check_hot(c, Hot::CONN_REJECTED); - } - -event connection_reset(c: connection) - { - Hot::check_hot(c, Hot::CONN_FINISHED); - } - -event connection_pending(c: connection) - { - if ( c$orig$state in conn_closed && - (c$resp$state == TCP_INACTIVE || c$resp$state == TCP_PARTIAL) ) - # This is a stray FIN or RST - don't bother reporting. - return; - - if ( c$orig$state == TCP_RESET || c$resp$state == TCP_RESET ) - # We already reported this connection when the RST - # occurred. - return; - - Hot::check_hot(c, Hot::CONN_FINISHED); - } - -function connection_gone(c: connection, gone_type: string) - { - if ( c$orig$size == 0 || c$resp$size == 0 ) - { - if ( c$orig$state == TCP_RESET && c$resp$state == TCP_INACTIVE) - # A bare RST, no other context. Ignore it. - return; - - # Hard to get excited about this - not worth logging again, - # per connection_finished(). - c$hot = 0; - } - else - Hot::check_hot(c, Hot::CONN_TIMEOUT); - } diff --git a/scripts/policy/protocols/conn/scan.bro b/scripts/policy/protocols/conn/scan.bro deleted file mode 100644 index a0e2408679..0000000000 --- a/scripts/policy/protocols/conn/scan.bro +++ /dev/null @@ -1,604 +0,0 @@ -@load base/frameworks/notice/main -@load port-name - -module Scan; - -export { - redef enum Notice::Type += { - ## The source has scanned a number of ports. - PortScan, - ## The source has scanned a number of addresses. - AddressScan, - ## Apparent flooding backscatter seen from source. - BackscatterSeen, - - ## Summary of scanning activity. - ScanSummary, - ## Summary of distinct ports per scanner. - PortScanSummary, - ## Summary of distinct low ports per scanner. - LowPortScanSummary, - - ## Source touched a landmine destination. - Landmine, - ## Source reached :bro:id:`Scan::shut_down_threshold` - ShutdownThreshold, - ## Source touched privileged ports. - LowPortTrolling, - }; - - # Whether to consider UDP "connections" for scan detection. - # Can lead to false positives due to UDP fanout from some P2P apps. - const suppress_UDP_scan_checks = F &redef; - - const activate_priv_port_check = T &redef; - const activate_landmine_check = F &redef; - const landmine_thresh_trigger = 5 &redef; - - const landmine_address: set[addr] &redef; - - const scan_summary_trigger = 25 &redef; - const port_summary_trigger = 20 &redef; - const lowport_summary_trigger = 10 &redef; - - # Raise ShutdownThresh after this many failed attempts - const shut_down_threshold = 100 &redef; - - # Which services should be analyzed when detecting scanning - # (not consulted if analyze_all_services is set). - const analyze_services: set[port] &redef; - const analyze_all_services = T &redef; - - # Track address scaners only if at least these many hosts contacted. - const addr_scan_trigger = 0 &redef; - - # Ignore address scanners for further scan detection after - # scanning this many hosts. - # 0 disables. - const ignore_scanners_threshold = 0 &redef; - - # Report a scan of peers at each of these points. - const report_peer_scan: vector of count = { - 20, 100, 1000, 10000, 50000, 100000, 250000, 500000, 1000000, - } &redef; - - const report_outbound_peer_scan: vector of count = { - 100, 1000, 10000, - } &redef; - - # Report a scan of ports at each of these points. - const report_port_scan: vector of count = { - 50, 250, 1000, 5000, 10000, 25000, 65000, - } &redef; - - # Once a source has scanned this many different ports (to however many - # different remote hosts), start tracking its per-destination access. - const possible_port_scan_thresh = 20 &redef; - - # Threshold for scanning privileged ports. - const priv_scan_trigger = 5 &redef; - const troll_skip_service = { - smtp, ftp, ssh, 20/tcp, http, - } &redef; - - const report_accounts_tried: vector of count = { - 20, 100, 1000, 10000, 100000, 1000000, - } &redef; - - const report_remote_accounts_tried: vector of count = { - 100, 500, - } &redef; - - # Report a successful password guessing if the source attempted - # at least this many. - const password_guessing_success_threshhold = 20 &redef; - - const skip_accounts_tried: set[addr] &redef; - - const addl_web = { - 81/tcp, 443/tcp, 8000/tcp, 8001/tcp, 8080/tcp, } - &redef; - - const skip_services = { ident, } &redef; - const skip_outbound_services = { Hot::allow_services, ftp, addl_web, } - &redef; - - const skip_scan_sources = { - 255.255.255.255, # who knows why we see these, but we do - - # AltaVista. Here just as an example of what sort of things - # you might list. - test-scooter.av.pa-x.dec.com, - } &redef; - - const skip_scan_nets: set[subnet] = {} &redef; - - # List of well known local server/ports to exclude for scanning - # purposes. - const skip_dest_server_ports: set[addr, port] = {} &redef; - - # Reverse (SYN-ack) scans seen from these ports are considered - # to reflect possible SYN-flooding backscatter, and not true - # (stealth) scans. - const backscatter_ports = { - http, 53/tcp, 53/udp, bgp, 6666/tcp, 6667/tcp, - } &redef; - - const report_backscatter: vector of count = { - 20, - } &redef; - - global check_scan: - function(c: connection, established: bool, reverse: bool): bool; - - # The following tables are defined here so that we can redef - # the expire timeouts. - # FIXME: should we allow redef of attributes on IDs which - # are not exported? - - # How many different hosts connected to with a possible - # backscatter signature. - global distinct_backscatter_peers: table[addr] of table[addr] of count - &read_expire = 15 min; - - # Expire functions that trigger summaries. - global scan_summary: - function(t: table[addr] of set[addr], orig: addr): interval; - global port_summary: - function(t: table[addr] of set[port], orig: addr): interval; - global lowport_summary: - function(t: table[addr] of set[port], orig: addr): interval; - - # Indexed by scanner address, yields # distinct peers scanned. - # pre_distinct_peers tracks until addr_scan_trigger hosts first. - global pre_distinct_peers: table[addr] of set[addr] - &read_expire = 15 mins &redef; - - global distinct_peers: table[addr] of set[addr] - &read_expire = 15 mins &expire_func=scan_summary &redef; - global distinct_ports: table[addr] of set[port] - &read_expire = 15 mins &expire_func=port_summary &redef; - global distinct_low_ports: table[addr] of set[port] - &read_expire = 15 mins &expire_func=lowport_summary &redef; - - # Indexed by scanner address, yields a table with scanned hosts - # (and ports). - global scan_triples: table[addr] of table[addr] of set[port]; - - global remove_possible_source: - function(s: set[addr], idx: addr): interval; - global possible_scan_sources: set[addr] - &expire_func=remove_possible_source &read_expire = 15 mins; - - # Indexed by source address, yields user name & password tried. - global accounts_tried: table[addr] of set[string, string] - &read_expire = 1 days; - - global ignored_scanners: set[addr] &create_expire = 1 day &redef; - - # These tables track whether a threshold has been reached. - # More precisely, the counter is the next index of threshold vector. - global shut_down_threshold_reached: table[addr] of bool &default=F; - global rb_idx: table[addr] of count - &default=0 &read_expire = 1 days &redef; - global rps_idx: table[addr] of count - &default=0 &read_expire = 1 days &redef; - global rops_idx: table[addr] of count - &default=0 &read_expire = 1 days &redef; - global rpts_idx: table[addr,addr] of count - &default=0 &read_expire = 1 days &redef; - global rat_idx: table[addr] of count - &default=0 &read_expire = 1 days &redef; - global rrat_idx: table[addr] of count - &default=0 &read_expire = 1 days &redef; -} - -global thresh_check: function(v: vector of count, idx: table[addr] of count, - orig: addr, n: count): bool; -global thresh_check_2: function(v: vector of count, - idx: table[addr,addr] of count, orig: addr, - resp: addr, n: count): bool; - -function scan_summary(t: table[addr] of set[addr], orig: addr): interval - { - local num_distinct_peers = orig in t ? |t[orig]| : 0; - - if ( num_distinct_peers >= scan_summary_trigger ) - NOTICE([$note=ScanSummary, $src=orig, $n=num_distinct_peers, - $msg=fmt("%s scanned a total of %d hosts", - orig, num_distinct_peers)]); - - return 0 secs; - } - -function port_summary(t: table[addr] of set[port], orig: addr): interval - { - local num_distinct_ports = orig in t ? |t[orig]| : 0; - - if ( num_distinct_ports >= port_summary_trigger ) - NOTICE([$note=PortScanSummary, $src=orig, $n=num_distinct_ports, - $msg=fmt("%s scanned a total of %d ports", - orig, num_distinct_ports)]); - - return 0 secs; - } - -function lowport_summary(t: table[addr] of set[port], orig: addr): interval - { - local num_distinct_lowports = orig in t ? |t[orig]| : 0; - - if ( num_distinct_lowports >= lowport_summary_trigger ) - NOTICE([$note=LowPortScanSummary, $src=orig, - $n=num_distinct_lowports, - $msg=fmt("%s scanned a total of %d low ports", - orig, num_distinct_lowports)]); - - return 0 secs; - } - -function clear_addr(a: addr) - { - delete distinct_peers[a]; - delete distinct_ports[a]; - delete distinct_low_ports[a]; - delete scan_triples[a]; - delete possible_scan_sources[a]; - delete distinct_backscatter_peers[a]; - delete pre_distinct_peers[a]; - delete rb_idx[a]; - delete rps_idx[a]; - delete rops_idx[a]; - delete rat_idx[a]; - delete rrat_idx[a]; - delete shut_down_threshold_reached[a]; - delete ignored_scanners[a]; - } - -function ignore_addr(a: addr) - { - clear_addr(a); - add ignored_scanners[a]; - } - -function check_scan(c: connection, established: bool, reverse: bool): bool - { - local id = c$id; - - local service = "ftp-data" in c$service ? 20/tcp - : (reverse ? id$orig_p : id$resp_p); - local rev_service = reverse ? id$resp_p : id$orig_p; - local orig = reverse ? id$resp_h : id$orig_h; - local resp = reverse ? id$orig_h : id$resp_h; - local outbound = is_local_addr(orig); - - # The following works better than using get_conn_transport_proto() - # because c might not correspond to an active connection (which - # causes the function to fail). - if ( suppress_UDP_scan_checks && - service >= 0/udp && service <= 65535/udp ) - return F; - - if ( service in skip_services && ! outbound ) - return F; - - if ( outbound && service in skip_outbound_services ) - return F; - - if ( orig in skip_scan_sources ) - return F; - - if ( orig in skip_scan_nets ) - return F; - - # Don't include well known server/ports for scanning purposes. - if ( ! outbound && [resp, service] in skip_dest_server_ports ) - return F; - - if ( orig in ignored_scanners) - return F; - - if ( (! established || service !in Hot::allow_services) && - # not established, service not expressly allowed - - # not known peer set - (orig !in distinct_peers || resp !in distinct_peers[orig]) && - - # want to consider service for scan detection - (analyze_all_services || service in analyze_services) ) - { - if ( reverse && rev_service in backscatter_ports && - # reverse, non-priv backscatter port - service >= 1024/tcp ) - { - if ( orig !in distinct_backscatter_peers ) - { - local empty_bs_table: - table[addr] of count &default=0; - distinct_backscatter_peers[orig] = - empty_bs_table; - } - - if ( ++distinct_backscatter_peers[orig][resp] <= 2 && - # The test is <= 2 because we get two check_scan() - # calls, once on connection attempt and once on - # tear-down. - - distinct_backscatter_peers[orig][resp] == 1 && - - # Looks like backscatter, and it's not scanning - # a privileged port. - - thresh_check(report_backscatter, rb_idx, orig, - |distinct_backscatter_peers[orig]|) - ) - { - local rev_svc = rev_service in port_names ? - port_names[rev_service] : - fmt("%s", rev_service); - - NOTICE([$note=BackscatterSeen, $src=orig, - $p=rev_service, - $msg=fmt("backscatter seen from %s (%d hosts; %s)", - orig, |distinct_backscatter_peers[orig]|, rev_svc)]); - } - - if ( ignore_scanners_threshold > 0 && - |distinct_backscatter_peers[orig]| > - ignore_scanners_threshold ) - ignore_addr(orig); - } - - else - { # done with backscatter check - local ignore = F; - - local svc = service in port_names ? - port_names[service] : fmt("%s", service); - - if ( orig !in distinct_peers && addr_scan_trigger > 0 ) - { - if ( orig !in pre_distinct_peers ) - pre_distinct_peers[orig] = set(); - - add pre_distinct_peers[orig][resp]; - if ( |pre_distinct_peers[orig]| < addr_scan_trigger ) - ignore = T; - } - - if ( ! ignore ) - { # XXXXX - - if ( orig !in distinct_peers ) - distinct_peers[orig] = set() &mergeable; - - if ( resp !in distinct_peers[orig] ) - add distinct_peers[orig][resp]; - - local n = |distinct_peers[orig]|; - - if ( activate_landmine_check && - n >= landmine_thresh_trigger && - mask_addr(resp, 24) in landmine_address ) - { - local msg2 = fmt("landmine address trigger %s%s ", orig, svc); - NOTICE([$note=Landmine, $src=orig, - $p=service, $msg=msg2]); - } - - # Check for threshold if not outbound. - if ( ! shut_down_threshold_reached[orig] && - n >= shut_down_threshold && - ! outbound && orig !in neighbor_nets ) - { - shut_down_threshold_reached[orig] = T; - local msg = fmt("shutdown threshold reached for %s", orig); - NOTICE([$note=ShutdownThreshold, $src=orig, - $p=service, $msg=msg]); - } - - else - { - local address_scan = F; - if ( outbound && - # inside host scanning out? - thresh_check(report_outbound_peer_scan, rops_idx, orig, n) ) - address_scan = T; - - if ( ! outbound && - thresh_check(report_peer_scan, rps_idx, orig, n) ) - address_scan = T; - - if ( address_scan ) - NOTICE([$note=AddressScan, - $src=orig, $p=service, - $n=n, - $msg=fmt("%s has scanned %d hosts (%s)", - orig, n, svc)]); - - if ( address_scan && - ignore_scanners_threshold > 0 && - n > ignore_scanners_threshold ) - ignore_addr(orig); - } - } - } # XXXX - } - - if ( established ) - # Don't consider established connections for port scanning, - # it's too easy to be mislead by FTP-like applications that - # legitimately gobble their way through the port space. - return F; - - # Coarse search for port-scanning candidates: those that have made - # connections (attempts) to possible_port_scan_thresh or more - # distinct ports. - if ( orig !in distinct_ports || service !in distinct_ports[orig] ) - { - if ( orig !in distinct_ports ) - distinct_ports[orig] = set() &mergeable; - - if ( service !in distinct_ports[orig] ) - add distinct_ports[orig][service]; - - if ( |distinct_ports[orig]| >= possible_port_scan_thresh && - orig !in scan_triples ) - { - scan_triples[orig] = table() &mergeable; - add possible_scan_sources[orig]; - } - } - - # For sources that have been identified as possible scan sources, - # keep track of per-host scanning. - if ( orig in possible_scan_sources ) - { - if ( orig !in scan_triples ) - scan_triples[orig] = table() &mergeable; - - if ( resp !in scan_triples[orig] ) - scan_triples[orig][resp] = set() &mergeable; - - if ( service !in scan_triples[orig][resp] ) - { - add scan_triples[orig][resp][service]; - - if ( thresh_check_2(report_port_scan, rpts_idx, - orig, resp, - |scan_triples[orig][resp]|) ) - { - local m = |scan_triples[orig][resp]|; - NOTICE([$note=PortScan, $n=m, $src=orig, - $p=service, - $msg=fmt("%s has scanned %d ports of %s", orig, m, resp)]); - } - } - } - - return T; - } - - -# Hook into the catch&release dropping. When an address gets restored, we reset -# the source to allow dropping it again. -event Drop::address_restored(a: addr) - { - #Drop::debug_log(fmt("received restored for %s (scan.bro)", a)); - clear_addr(a); - } - -event Drop::address_cleared(a: addr) - { - #Drop::debug_log(fmt("received cleared for %s (scan.bro)", a)); - clear_addr(a); - } - -# When removing a possible scan source, we automatically delete its scanned -# hosts and ports. But we do not want the deletion propagated, because every -# peer calls the expire_function on its own (and thus applies the delete -# operation on its own table). -function remove_possible_source(s: set[addr], idx: addr): interval - { - suspend_state_updates(); - delete scan_triples[idx]; - resume_state_updates(); - - return 0 secs; - } - -# To recognize whether a certain threshhold vector (e.g. report_peer_scans) -# has been transgressed, a global variable containing the next vector index -# (idx) must be incremented. This cumbersome mechanism is necessary because -# values naturally don't increment by one (e.g. replayed table merges). -function thresh_check(v: vector of count, idx: table[addr] of count, - orig: addr, n: count): bool - { - if ( ignore_scanners_threshold > 0 && n > ignore_scanners_threshold ) - { - ignore_addr(orig); - return F; - } - - if ( idx[orig] < |v| && n >= v[idx[orig]] ) - { - ++idx[orig]; - return T; - } - else - return F; - } - -# Same as above, except the index has a different type signature. -function thresh_check_2(v: vector of count, idx: table[addr, addr] of count, - orig: addr, resp: addr, n: count): bool - { - if ( ignore_scanners_threshold > 0 && n > ignore_scanners_threshold ) - { - ignore_addr(orig); - return F; - } - - if ( idx[orig,resp] < |v| && n >= v[idx[orig, resp]] ) - { - ++idx[orig,resp]; - return T; - } - else - return F; - } - -event connection_established(c: connection) - { - local is_reverse_scan = (c$orig$state == TCP_INACTIVE); - Scan::check_scan(c, T, is_reverse_scan); - } - -event partial_connection(c: connection) - { - Scan::check_scan(c, T, F); - } - -event connection_attempt(c: connection) - { - Scan::check_scan(c, F, c$orig$state == TCP_INACTIVE); - } - -event connection_half_finished(c: connection) - { - # Half connections never were "established", so do scan-checking here. - Scan::check_scan(c, F, F); - } - -event connection_rejected(c: connection) - { - local is_reverse_scan = c$orig$state == TCP_RESET; - - Scan::check_scan(c, F, is_reverse_scan); - } - -event connection_reset(c: connection) - { - if ( c$orig$state == TCP_INACTIVE || c$resp$state == TCP_INACTIVE ) - # We never heard from one side - that looks like a scan. - Scan::check_scan(c, c$orig$size + c$resp$size > 0, - c$orig$state == TCP_INACTIVE); - } - -event connection_pending(c: connection) - { - if ( c$orig$state == TCP_PARTIAL && c$resp$state == TCP_INACTIVE ) - Scan::check_scan(c, F, F); - } - -# Report the remaining entries in the tables. -event bro_done() - { - for ( orig in distinct_peers ) - scan_summary(distinct_peers, orig); - - for ( orig in distinct_ports ) - port_summary(distinct_ports, orig); - - for ( orig in distinct_low_ports ) - lowport_summary(distinct_low_ports, orig); - }