Merge remote-tracking branch 'origin/topic/dist-cleanup'

* origin/topic/dist-cleanup:
  Updating INSTALL
  Updating README
  Remove $Id$ tags
  Remove policy.old directory, adresses #511
This commit is contained in:
Robin Sommer 2011-09-18 16:17:42 -07:00
commit bd2e30f521
479 changed files with 23 additions and 22780 deletions

13
INSTALL
View file

@ -62,7 +62,6 @@ Installation
To build and install into /usr/local/bro:
> ./configure
> cd build
> make
> make install
@ -89,17 +88,19 @@ Running Bro
===========
Bro is a complex program and it takes a bit of time to get familiar
with it. In the following we give a few simple examples. See the
quickstart guide at http://www.bro-ids.org for more information; you
can the source that in doc/quick-start.
with it. A good place for newcomers to start is the quick start guide
available here:
http://www.bro-ids.org/documentation/quickstart.html
For developers that wish to run Bro from the the build/ directory
after performing "make", but without performing "make install", they
will have to first set BROPATH to look for scripts inside the build
directory. Sourcing either build/bro-path-dev.sh or build/bro-path-dev.csh
as appropriate for the current shell accomplishes this. e.g.:
as appropriate for the current shell accomplishes this and also augments your
PATH so you can use Bro without qualifying the path to it. e.g.:
> ./configure
> make
> source build/bro-path-dev.sh
> ./build/src/bro
> bro <options>

38
README
View file

@ -1,31 +1,23 @@
This is release 1.6 of Bro, a system for detecting network intruders in
real-time using passive network monitoring.
============================
Bro Network Security Monitor
============================
Please see the file INSTALL for installation instructions and
pointers for getting started. For more documentation, see the
documentation on Bro's home page:
Bro is a powerful framework for network analysis and security
monitoring.
http://www.bro-ids.org/docs
Please see the INSTALL file for installation instructions and pointers
for getting started. For more documentation, research publications, or
community contact information see Bro's home page:
The main parts of Bro's documentation are also available in the doc/
directory of the distribution. (Please note that the documentation
is still a work in progress; there will be more in future releases.)
http://www.bro-ids.org
Numerous other Bro-related publications, including a paper describing the
system, can be found at
Please see COPYING for licensing information.
http://www.bro-ids.org/publications.html
Send comments, etc., to the Bro mailing list, bro@bro-ids.org.
However, please note that you must first subscribe to the list in
order to be able to post to it.
- Vern Paxson & Robin Sommer, on behalf of the Bro development team
On behalf of the Bro Development Team,
Vern Paxson & Robin Sommer,
International Computer Science Institute &
Lawrence Berkeley National Laboratory
University of California, Berkeley USA
ICSI Center for Internet Research (ICIR)
International Computer Science Institute
Berkeley, CA USA
vern@icir.org / robin@icir.org

View file

@ -1,18 +0,0 @@
# $Id: OS-fingerprint.bro 1071 2005-03-08 14:09:31Z vern $
#
# Tracks operating system versioning using the "software" framework.
@load software
event OS_version_found(c: connection, host: addr, OS: OS_version)
{
local version: software_version;
version$major = version$minor = version$minor2 = -1;
version$addl = OS$detail;
local sw: software;
sw$name = OS$genre;
sw$version = version;
event software_version_found(c, host, sw, "OS");
}

View file

@ -1,278 +0,0 @@
# $Id: adu.bro 5152 2007-12-04 21:48:56Z vern $
@load conn-id
module adu;
# This script parses application-layer data (ADU) units, or "messages",
# out of the packet streams. Since the analysis is generic, we define
# an ADU simply as all application-layer data in a 5-tuple flow going
# in one direction without any data going the other way. Once we see
# data in the other direction, we finish the current ADU and start
# a new one (going the other way). While this approach is only
# approximate, it can work well for both UDP and TCP.
#
# The script reports ADUs as strings, up to a configurable maximum size, and
# up to a configurable depth into the flow.
#
# Generated events:
#
# - adu_tx(c: connection, a: adu_state) reports an ADU seen from
# c's originator to its responder.
#
# - adu_rx(c: connection, a: adu_state) reports an ADU seen from
# c's responder to the originator.
#
# - adu_done(c: connection) indicates that no more ADUs will be seen
# on connection c. This is useful to know in case your statekeeping
# relies on event connection_state_remove(), which is also used by
# adu.bro.
#
# --- Input configuration -- which ports to look at --------------------
# Right now: everything!
#
redef tcp_content_deliver_all_orig = T;
redef tcp_content_deliver_all_resp = T;
redef udp_content_deliver_all_orig = T;
redef udp_content_deliver_all_resp = T;
# --- Debugging -- should really be a separate policy ------------------
# Comment out to disable debugging output:
#global adu_debug = T;
# Uncomment to enable tests:
#global adu_test = T;
@ifdef (adu_debug)
function DBG(msg: string) { print fmt("DBG[adu.bro]: %s", msg); }
@else
function DBG(msg: string) { }
@endif
export {
# --- Constants --------------------------------------------------------
# The maximum depth in bytes up to which we follow a flow.
# This is counting bytes seen in both directions.
const adu_conn_max_depth = 100000 &redef;
# The maximum message depth that we report.
const adu_max_depth = 3 &redef;
# The maximum message size in bytes that we report.
const adu_max_size = 1000 &redef;
# Whether ADUs are reported beyond content gaps.
const adu_gaps_ok = F &redef;
# --- Types ------------------------------------------------------------
# adu_state records contain the latest ADU and aditional flags to help
# the user identify the direction of the message, its depth in the flow,
# etc.
type adu_state: record {
adu: string &default = ""; # the current ADU
# Message counter (>= 1), orig->resp and resp->orig.
depth_tx: count &default = 1;
depth_rx: count &default = 1;
# TCP: seqno tracking to recognize gaps.
seen_tx: count &default = 0;
seen_rx: count &default = 0;
size: count &default = 0; # total connection size in bytes
is_orig: bool &default = F; # whether ADU is orig->resp
ignore: bool &default = F; # ignore future activity on conn
};
# Tell the ADU policy that you do not wish to receive further
# adu_tx/adu_rx events for a given connection. Other policies
# may continue to process the connection.
#
global adu_skip_further_processing: function(cid: conn_id);
}
# --- Globals ----------------------------------------------------------
# A global table that tracks each flow's messages.
global adu_conns: table[conn_id] of adu_state;
# Testing invokes the following events.
global adu_tx: event(c: connection, astate: adu_state);
global adu_rx: event(c: connection, astate: adu_state);
global adu_done: event(c: connection);
# --- Functions --------------------------------------------------------
function adu_skip_further_processing(cid: conn_id)
{
if ( cid !in adu_conns )
return;
adu_conns[cid]$ignore = T;
}
function flow_contents(c: connection, is_orig: bool, seq: count, contents: string)
{
local astate: adu_state;
DBG(fmt("contents %s, %s: %s", id_string(c$id), is_orig, contents));
# Ensure we track the given connection.
if ( c$id !in adu_conns )
adu_conns[c$id] = astate;
else
astate = adu_conns[c$id];
# Forget it if we've been asked to ignore.
#
if ( astate$ignore == T )
return;
# Don't report if flow is too big.
#
if ( astate$size >= adu_conn_max_depth )
return;
# If we have an assembled message, we may now have something
# to report.
if ( |astate$adu| > 0 )
{
# If application-layer data flow is switching
# from resp->orig to orig->resp, report the assembled
# message as a received ADU.
if ( is_orig && ! astate$is_orig )
{
event adu_rx(c, copy(astate));
astate$adu = "";
if ( ++astate$depth_rx > adu_max_depth )
adu_skip_further_processing(c$id);
}
# If application-layer data flow is switching
# from orig->resp to resp->orig, report the assembled
# message as a transmitted ADU.
#
if ( !is_orig && astate$is_orig )
{
event adu_tx(c, copy(astate));
astate$adu = "";
if ( ++astate$depth_tx > adu_max_depth )
adu_skip_further_processing(c$id);
}
}
# Check for content gaps. If we identify one, only continue
# if user allowed it.
#
if ( !adu_gaps_ok && seq > 0 )
{
if ( is_orig )
{
if ( seq > astate$seen_tx + 1 )
return;
else
astate$seen_tx += |contents|;
}
else
{
if ( seq > astate$seen_rx + 1 )
return;
else
astate$seen_rx += |contents|;
}
}
# Append the contents to the end of the currently
# assembled message, if the message hasn't already
# reached the maximum size.
#
if ( |astate$adu| < adu_max_size )
{
astate$adu += contents;
# As a precaution, clip the string to the maximum
# size. A long content string with astate$adu just
# below its maximum allowed size could exceed that
# limit by a lot.
### str_clip(astate$adu, adu_max_size);
}
# Note that this counter is bumped up even if we have
# exceeded the maximum size of an individual message.
#
astate$size += |contents|;
astate$is_orig = is_orig;
}
# --- Event Handlers ---------------------------------------------------
event tcp_contents(c: connection, is_orig: bool, seq: count, contents: string)
{
flow_contents(c, is_orig, seq, contents);
}
event udp_contents(u: connection, is_orig: bool, contents: string)
{
flow_contents(u, is_orig, 0, contents);
}
event connection_state_remove(c: connection)
{
if ( c$id !in adu_conns )
return;
local astate = adu_conns[c$id];
# Forget it if we've been asked to ignore.
#
if ( astate$ignore == T )
return;
# Report the remaining data now, if any.
#
if ( |astate$adu| > 0 ) {
if ( astate$is_orig )
{
if ( astate$depth_tx <= adu_max_depth )
event adu_tx(c, copy(astate));
}
else
{
if ( astate$depth_rx <= adu_max_depth )
event adu_rx(c, copy(astate));
}
}
delete adu_conns[c$id];
event adu_done(c);
}
# --- Tests ------------------------------------------------------------
@ifdef (adu_test)
event adu_tx(c: connection, astate: adu_state)
{
print fmt("%s ---- %s, %d -> ----", network_time(), id_string(c$id), astate$depth_tx);
# print astate$adu;
}
event adu_rx(c: connection, astate: adu_state)
{
print fmt("%s ---- %s, %d <- ----", network_time(), id_string(c$id), astate$depth_rx);
# print astate$adu;
}
@endif

View file

@ -1,3 +0,0 @@
# $Id: alarm.bro 340 2004-09-09 06:38:27Z vern $
redef bro_alarm_file = open_log_file("alarm");

View file

@ -1,141 +0,0 @@
@load heavy-analysis
@load OS-fingerprint
@load adu
@load alarm
@load analy
@load anon
@load arp
@load backdoor
@load bittorrent
@load blaster
@load bt-tracker
@load brolite-backdoor
@load capture-events
@load capture-loss
@load capture-state-updates
@load checkpoint
@load clear-passwords
@load conn-flood
@load conn-id
@load conn
@load contents
@load cpu-adapt
@load dce
@load demux
@load detect-protocols-http
@load detect-protocols
@load dhcp
@load dns-info
@load dns-lookup
@load dns
@load dpd
@load drop-adapt
@load dyn-disable
@load file-flush
@load finger
@load firewall
@load flag-irc
@load flag-warez
@load frag
@load ftp
@load gnutella
@load hot-ids
@load hot
@load http-abstract
@load http-anon-server
@load http-anon-useragent
@load http-anon-utils
@load http-body
@load http-detect-passwd
@load http-entity
@load http-event
@load http-header
@load http-identified-files.bro
@load http-reply
@load http-request
@load http-rewriter
@load http
@load icmp
@load ident-rewriter
@load ident
@load inactivity
@load interconn
@load irc-bot-syslog
@load irc-bot
@load irc
@load large-conns
@load listen-clear
@load listen-ssl
@load load-level
@load load-sample
@load log-append
@load login
@load mime-pop
@load mime
@load mt
@load ncp
@load netflow
@load netstats
@load nfs
@load notice-action-filters
@load notice
@load ntp
@load passwords
@load pcap
@load pkt-profile
@load pop3
@load port-name
@load portmapper
@load print-filter
@load print-globals
@load print-resources
@load print-sig-states
@load profiling
@load proxy
@load remote-pcap
@load remote-ping
@load remote-print-id-reply
@load remote-print-id
@load remote-print
@load remote-report-notices
@load remote-send-id
@load remote
@load rotate-logs
@load rsh
@load scan
@load secondary-filter
@load sensor-sshd
@load server-ports
@load service-probe
@load signatures
@load site
@load smb
@load smtp-relay
@load smtp-rewriter
@load smtp
@load snort
@load software
@load ssh
@load ssh-stepping
@load ssl-alerts
@load ssl-ciphers
@load ssl-errors
@load ssl-worm
@load ssl
@load stats
@load stepping
@load synflood
@load targeted-scan
@load tcp
@load tftp
@load trw-impl
@load trw
@load udp-common
@load udp
@load vlan
@load weird
@load worm
@load notice-policy
# The following keeps us running after the bro_init event.
redef PrintFilter::terminate_bro = F;

View file

@ -1,16 +0,0 @@
# Statistical analysis of TCP connection in terms of the packet streams
# in each direction.
@load dns-lookup
@load udp
event conn_stats(c: connection, os: endpoint_stats, rs: endpoint_stats)
{
local id = c$id;
print fmt("%.6f %s %s %s %s %s %s %s %s %s",
c$start_time, c$duration, id$orig_p, id$resp_p,
conn_size(c$orig, tcp), conn_size(c$resp, tcp),
id$orig_h, id$resp_h, os, rs);
}

View file

@ -1,193 +0,0 @@
# $Id: anon.bro 6889 2009-08-21 16:45:17Z vern $
redef anonymize_ip_addr = T;
const orig_addr_anonymization = RANDOM_MD5 &redef;
const resp_addr_anonymization = RANDOM_MD5 &redef;
const other_addr_anonymization = SEQUENTIALLY_NUMBERED &redef;
const preserve_orig_addr: set[addr] = {} &redef;
const preserve_resp_addr: set[addr] = {} &redef;
const preserve_other_addr: set[addr] = {
0.0.0.0,
} &redef;
const preserved_subnet: set[subnet] = {
# 192.150.186/23,
} &redef;
const preserved_net: set[net] = {
# 192.150.186, 192.150.187,
} &redef;
global anon_log = open_log_file("anon") &redef;
global anonymized_args: table[string] of string;
global ip_anon_mapping: set[addr, addr];
event bro_init()
{
for ( n in preserved_net )
preserve_net(n);
}
function anonymize_address(a: addr, id: conn_id): addr
{
if ( a == id$orig_h )
return anonymize_addr(a, ORIG_ADDR);
else if ( a == id$resp_h )
return anonymize_addr(a, RESP_ADDR);
else
return anonymize_addr(a, OTHER_ADDR);
}
event anonymization_mapping(orig: addr, mapped: addr)
{
if ( [orig, mapped] !in ip_anon_mapping )
{
add ip_anon_mapping[orig, mapped];
print anon_log, fmt("%s -> %s", orig, mapped);
}
}
function string_anonymized(from: string, to: string, seed: count)
{
print anon_log, fmt("\"%s\" %d=> \"%s\"", from, seed, to);
}
global num_string_id: count = 0 &redef;
global anonymized_strings: table[string] of record {
s: string;
c: count;
} &redef;
# Hopefully, the total number of strings to anonymize is much less than
# 36^unique_string_length.
const unique_string_length = 8 &redef;
# const anonymized_string_pattern = /U[0-9a-f]+U/;
global unique_string_set: set[string];
event bro_init()
{
for ( s in anonymized_strings )
add unique_string_set[anonymized_strings[s]$s];
}
function unique_string(s: string, seed: count): string
{
local t = cat("U", sub_bytes(md5_hmac(seed, s),
1, unique_string_length), "U");
if ( t in unique_string_set )
return unique_string(s, seed+1);
anonymized_strings[s] = [$s = t, $c = 1];
add unique_string_set[t];
string_anonymized(s, t, seed);
return t;
}
function anonymize_string(from: string): string
{
if ( from in anonymized_strings )
{
++anonymized_strings[from]$c;
return anonymized_strings[from]$s;
}
local t = unique_string(from, 0);
return t;
}
function anonymize_arg(typ: string, arg: string): string
{
if ( arg == "" )
return ""; # an empty argument is safe
local arg_seed = string_cat(typ, arg);
if ( arg_seed in anonymized_args )
return anonymized_args[arg_seed];
local a = anonymize_string(arg_seed);
anonymized_args[arg_seed] = a;
print anon_log, fmt("anonymize_arg: (%s) {%s} -> %s ",
typ, to_string_literal(arg), to_string_literal(a));
return a;
}
# Does not contain ? and ends with an allowed suffix.
const path_to_file_pat =
/\/[^?]+\.(html|ico|icon|pdf|ps|doc|ppt|htm|js|crl|swf|shtml|h|old|c|cc|java|class|src|cfm|gif|jpg|php|rdf|rss|asp|bmp|owl|phtml|jpeg|jsp|cgi|png|txt|xml|css|avi|tex|dvi)/
;
# Acceptable domain names.
const kosher_dom_pat =
/ar|au|biz|br|ca|cc|cl|cn|co|com|cx|cz|de|ec|es|edu|fi|fm|fr|gov|hn|il|is|it|jp|lv|mx|net|no|nz|org|pe|pl|ru|sk|tv|tw|uk|us|arpa/
;
# Simple filename pattern.
const simple_filename =
/[0-9\-A-Za-z]+\.(html|ico|icon|pdf|ps|doc|ppt|htm|js|crl|swf|shtml|h|old|c|cc|java|class|src|cfm|gif|jpg|php|rdf|rss|asp|bmp|owl|phtml|jpeg|jsp|cgi|png|txt|xml|css|avi|tex|dvi)/
;
function anonymize_path(path: string): string
{
local hashed_path = "";
if ( to_lower(path) != path_to_file_pat )
{
hashed_path = anonymize_arg("path", path);
return hashed_path;
}
local file_parts = split(path, /\./);
local i = 1;
for ( part in file_parts )
{
# This looks broken to me - VP.
hashed_path = fmt("%s.%s", hashed_path, file_parts[i]);
if ( ++i == length(file_parts) )
break;
}
return fmt("%s.%s", anonymize_arg("path", hashed_path), file_parts[i]);
}
function anonymize_host(host: string): string
{
local hashed_host = "";
local host_parts = split(host, /\./);
local i = 1;
for ( hosty in host_parts )
{
if ( i == length(host_parts) )
break;
# Check against "kosher" tld list.
hashed_host = fmt("%s%s.", hashed_host,
anonymize_arg("host", host_parts[i]));
++i;
}
if ( host_parts[i] == kosher_dom_pat )
return string_cat(hashed_host, host_parts[i]);
print anon_log, fmt("anonymize_host: non-kosher domain %s", host);
return string_cat(hashed_host, anonymize_arg("host", host_parts[i]));
}
event bro_done()
{
for ( s in anonymized_strings )
{
print anon_log, fmt("appearance: %d: \"%s\" => \"%s\"",
anonymized_strings[s]$c, s, anonymized_strings[s]$s);
}
}

View file

@ -1,160 +0,0 @@
# $Id: arp.bro 4909 2007-09-24 02:26:36Z vern $
@load notice
module ARP;
export {
redef enum Notice += {
ARPSourceMAC_Mismatch, # source MAC doesn't match mappings
ARPAddlMAC_Mapping, # another MAC->addr seen beyond just one
ARPUnsolicitedReply, # could be poisoning; or just gratuitous
# ARPRequestProvidesTargetAddr, # request includes non-triv addr
# MAC/addr pair seen in request/reply different from
# that in the cache.
ARPCacheInconsistency,
# ARP reply gives different value than previously seen.
ARPMappingChanged,
};
const arp_log = open_log_file("arp") &redef;
}
redef capture_filters += { ["arp"] = "arp" };
# Abbreviations taken from RFC 826:
#
# SHA: source hardware address
# SPA: source protocol address (i.e., IP address)
# THA: target hardware address
# TPA: target protocol address
# ARP requests indexed on SHA/SPA/TPA (no THA, as it's what it's being
# queried).
global arp_requests: set[string, addr, addr] &create_expire = 1 min;
# ARP responses we've seen: indexed by IP address, yielding MAC address.
global ARP_cache: table[addr] of string;
# Bad ARPs can occur when:
# - type/size pairs are not OK for HW and L3 addresses (Ethernet=6, IP=4)
# - opcode is neither request (1) nor reply (2)
# - MAC src address != ARP sender MAC address
event bad_arp(SPA: addr, SHA: string, TPA: addr, THA: string,
explanation: string)
{
print arp_log, fmt("%.06f bad-arp %s(%s) ? %s(%s): %s",
network_time(), SPA, SHA, TPA, THA, explanation);
}
# The first of these maps a MAC address to the last protocol address seen
# for it. The second tracks every protocol address seen.
global mac_addr_map: table[string] of addr;
global mac_addr_associations: table[string] of set[addr];
# A somewhat general notion of broadcast MAC/IP addresses.
const broadcast_mac_addrs = { "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff", };
const broadcast_addrs = { 0.0.0.0, 255.255.255.255, };
# Called to note that we've seen an association between a MAC address
# and an IP address. Note that this is *not* an association advertised
# in an ARP reply (those are tracked in ARP_cache), but instead the
# pairing of hardware address + protocol address as expressed in
# an ARP request or reply header.
function mac_addr_association(mac_addr: string, a: addr)
{
# Ignore placeholders.
if ( mac_addr in broadcast_mac_addrs || a in broadcast_addrs )
return;
local is_addl = F;
if ( mac_addr in mac_addr_associations )
is_addl = a !in mac_addr_associations[mac_addr];
else
mac_addr_associations[mac_addr] = set();
print arp_log, fmt("%.06f association %s -> %s%s", network_time(),
mac_addr, a, is_addl ? " <addl>" : "");
mac_addr_map[mac_addr] = a;
add mac_addr_associations[mac_addr][a];
if ( a in ARP_cache && ARP_cache[a] != mac_addr )
NOTICE([$note=ARPCacheInconsistency, $src=a,
$msg=fmt("mapping for %s to %s doesn't match cache of %s",
mac_addr, a, ARP_cache[a])]);
}
# Returns the IP address associated with a MAC address, if we've seen one.
# Otherwise just returns the MAC address.
function addr_from_mac(mac_addr: string): string
{
return mac_addr in mac_addr_map ?
fmt("%s", mac_addr_map[mac_addr]) : mac_addr;
}
event arp_request(mac_src: string, mac_dst: string, SPA: addr, SHA: string,
TPA: addr, THA: string)
{
mac_addr_association(SHA, SPA);
local msg = fmt("%s -> %s who-has %s",
addr_from_mac(mac_src), addr_from_mac(mac_dst), TPA);
local mismatch = SHA != mac_src;
if ( mismatch )
NOTICE([$note=ARPSourceMAC_Mismatch, $src=SPA, $msg=msg]);
# It turns out that some hosts fill in the THA field even though
# that doesn't make sense. (The RFC specifically allows this,
# however.) Perhaps there's an attack that can be launched
# doing so, but it's hard to see what it might be, so for now
# we don't bother notice'ing these.
# if ( THA !in broadcast_addrs )
# NOTICE([$note=ARPRequestProvidesTargetAddr, $src=SPA,
# $msg=fmt("%s: %s", msg, THA)]);
print arp_log, fmt("%.06f %s%s", network_time(), msg,
mismatch ? " <source-mismatch>" : "");
add arp_requests[SHA, SPA, TPA];
}
event arp_reply(mac_src: string, mac_dst: string, SPA: addr, SHA: string,
TPA: addr, THA: string)
{
mac_addr_association(SHA, SPA);
mac_addr_association(THA, TPA);
local msg = fmt("%s -> %s: %s is-at %s",
addr_from_mac(mac_src), addr_from_mac(mac_dst),
SPA, SHA);
local unsolicited = [THA, TPA, SPA] !in arp_requests;
delete arp_requests[THA, TPA, SPA];
if ( unsolicited )
NOTICE([$note=ARPUnsolicitedReply, $src=SPA,
$msg=fmt("%s: request[%s, %s, %s]", msg, THA, TPA, SPA)]);
local mismatch = SHA != mac_src;
if ( mismatch )
NOTICE([$note=ARPSourceMAC_Mismatch, $src=SPA, $msg=msg]);
local mapping_changed = SPA in ARP_cache && ARP_cache[SPA] != SHA;
if ( mapping_changed )
NOTICE([$note=ARPMappingChanged, $src=SPA,
$msg=fmt("%s: was %s", msg, ARP_cache[SPA])]);
print arp_log, fmt("%.06f %s%s%s%s", network_time(), msg,
unsolicited ? " <unsolicited>" : "",
mismatch ? " <source-mismatch>" : "",
mapping_changed ?
fmt(" <changed from %s>", ARP_cache[SPA]) : "");
ARP_cache[SPA] = SHA;
}

View file

@ -1,559 +0,0 @@
# $Id: backdoor.bro 4909 2007-09-24 02:26:36Z vern $
# Looks for a variety of applications running on ports other than
# their usual ports.
#
# Note that this script by itself does *not* change capture_filters
# to add in the extra ports to look at. You need to specify that
# separately.
# Some tcpdump filters can be used to replace or work together with
# some detection algorithms. They could be used with the "secondary
# filter" for more efficient (but in some cases potentially less reliable)
# matching:
#
# - looking for "SSH-1." or "SSH-2." at the beginning of the packet;
# somewhat weaker than ssh-sig in that ssh-sig only looks for such
# pattern in the first packet of a connection:
#
# tcp[(tcp[12]>>2):4] = 0x5353482D and
# (tcp[((tcp[12]>>2)+4):2] = 0x312e or tcp[((tcp[12]>>2)+4):2] = 0x322e)
#
# - looking for pkts with 8k+4 (<=128) bytes of data (combined with ssh-len);
# only effective for ssh 1.x:
#
# (ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12]>>2)) & 0xFF87 = 4
#
# - looking for packets with <= 512 bytes of data that ends with a NUL
# (can be potentially combined with rlogin-sig or rlogin-sig-1byte):
#
# (tcp[(ip[2:2] - ((ip[0]&0x0f)<<2))-1] == 0) and
# ((ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12]>>2)) != 0) and
# ((ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12]>>2)) <= 512)
#
# - looking for telnet negotiation (can be combined with telnet-sig(-3byte)):
#
# (tcp[(tcp[12]>>2):2] > 0xfffa) and
# (tcp[(tcp[12]>>2):2] < 0xffff) and
# ((ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12] >> 2)) >= 3)
#
# - looking for packets with <= 20 bytes of data (combined with small-pkt):
#
# (ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12]>>2)) <= 20
#
# - looking for FTP servers by the initial "220-" or "220 " sent by the server:
#
# tcp[(tcp[12]>>2):4] = 0x3232302d or tcp[(tcp[12]>>2):4] = 0x32323020
#
# - looking for root backdoors by seeing a server payload of exactly "# ":
#
# tcp[(tcp[12]>>2):2] = 0x2320 and
# (ip[2:2] - ((ip[0]&0x0f)<<2) - (tcp[12]>>2)) == 2
#
# - looking for Napster by the initial "GET" or "SEND" sent by the originator:
#
# ((ip[2:2]-((ip[0]&0x0f)<<2)-(tcp[12]>>2))=4 and
# tcp[(tcp[12]>>2):4]=0x53454e44) or
# ((ip[2:2]-((ip[0]&0x0f)<<2)-(tcp[12]>>2))=3 and
# tcp[(tcp[12]>>2):2]=0x4745 and tcp[(tcp[12]>>2)+2]=0x54)
#
# - looking for Gnutella handshaking "GNUTELLA "
#
# tcp[(tcp[12]>>2):4] = 0x474e5554 and
# tcp[(4+(tcp[12]>>2)):4] = 0x454c4c41 and
# tcp[8+(tcp[12]>>2)] = 0x20
#
# - looking for KaZaA via "GIVE " (not present in all connections)
#
# tcp[(tcp[12]>>2):4] = 0x47495645 and
# tcp[(4+(tcp[12]>>2)):1] = 0x20
#
@load site
@load port-name
@load demux
@load notice
redef enum Notice += { BackdoorFound, };
# Set to dump the packets that trigger the backdoor detector to a file.
const dump_backdoor_packets = F &redef;
redef backdoor_stat_period = 60 sec;
redef backdoor_stat_backoff = 2.0;
const ssh_min_num_pkts = 8 &redef;
const ssh_min_ssh_pkts_ratio = 0.6 &redef;
const backdoor_min_num_lines = 2 &redef;
const backdoor_min_normal_line_ratio = 0.5 &redef;
const backdoor_min_bytes = 10 &redef;
const backdoor_min_7bit_ascii_ratio = 0.75 &redef;
type rlogin_conn_info : record {
o_num_null: count;
o_len: count;
r_num_null: count;
r_len: count;
};
const backdoor_demux_disabled = T &redef;
const backdoor_demux_skip_tags: set[string] &redef;
const ftp_backdoor_sigs = "ftp-sig";
const ssh_backdoor_sigs = { "ssh-sig", "ssh-len-v1.x", "ssh-len-v2.x" };
const rlogin_backdoor_sigs = { "rlogin-sig", "rlogin-sig-1byte" };
const root_backdoor_sigs = "root-bd-sig";
const telnet_backdoor_sigs = { "telnet-sig", "telnet-sig-3byte" };
const napster_backdoor_sigs = "napster-sig";
const gnutella_backdoor_sigs = "gnutella-sig";
const kazaa_backdoor_sigs = "kazaa-sig";
const http_backdoor_sigs = "http-sig";
const http_proxy_backdoor_sigs = "http-proxy-sig";
const smtp_backdoor_sigs = "smtp-sig";
const irc_backdoor_sigs = "irc-sig";
const gaobot_backdoor_sigs = "gaobot-sig";
# List of backdoors, so you can use it when defining sets and tables
# with values over all of them.
const backdoor_sigs = {
ftp_backdoor_sigs, ssh_backdoor_sigs, rlogin_backdoor_sigs,
root_backdoor_sigs, telnet_backdoor_sigs,
napster_backdoor_sigs, gnutella_backdoor_sigs, kazaa_backdoor_sigs,
http_backdoor_sigs, http_proxy_backdoor_sigs,
smtp_backdoor_sigs, irc_backdoor_sigs, gaobot_backdoor_sigs,
};
# List of address-port pairs that if present in a backdoor are ignored.
# Note that these can be either the client and its source port (unusual)
# or the server and its service port (the common case).
const backdoor_ignore_host_port_pairs: set[addr, port] &redef;
const backdoor_ignore_ports: table[string, port] of bool = {
# The following ignore backdoors that are detected on their
# usual ports. The definitions for ftp-sig, telnet-sig and
# telnet-sig-3byte are somehwat broad since those backdoors
# are also frequently triggered for other similar protocols.
[ftp_backdoor_sigs, [ftp, smtp, 587/tcp ]] = T,
[ssh_backdoor_sigs, ssh] = T,
[rlogin_backdoor_sigs , [512/tcp, rlogin, 514/tcp]] = T,
[root_backdoor_sigs, [telnet, 512/tcp, rlogin, 514/tcp]] = T,
[telnet_backdoor_sigs, [telnet, ftp, smtp, 143/tcp, 110/tcp]] = T,
# The following don't have well-known ports (well, Napster does
# somewhat, as shown below), hence the definitions are F rather
# than T.
[napster_backdoor_sigs, [6688/tcp, 6699/tcp]] = F,
[gnutella_backdoor_sigs, 6346/tcp] = F,
[kazaa_backdoor_sigs, 1214/tcp] = F,
[http_backdoor_sigs, [http, 8000/tcp, 8080/tcp]] = T,
[smtp_backdoor_sigs, [smtp, 587/tcp]] = T,
# Skip FTP, as "USER foo" generates false positives. There's
# also a lot of IRC on 7000/tcp.
[irc_backdoor_sigs, [ftp, 6666/tcp, 6667/tcp, 7000/tcp]] = T,
# The following are examples of wildcards, and since they're defined
# to be F, they don't affect the policy unless redefined.
["*", http] = F, # entry for "any backdoor, service http"
["ssh-sig", 0/tcp] = F, # entry for "ssh-sig, any port"
} &redef &default = F;
# Indexed by the backdoor, indicates which backdoors residing on
# a local (remote) host should be ignored.
const backdoor_ignore_local: set[string] &redef;
const backdoor_ignore_remote: set[string] &redef;
# Indexed by the source (destination) address and the backdoor.
# Also indexed by the /24 and /16 versions of the source address.
# backdoor "*" means "all backdoors".
const backdoor_ignore_src_addrs: table[string, addr] of bool &redef &default=F;
const backdoor_ignore_dst_addrs: table[string, addr] of bool &redef &default=F;
const backdoor_standard_ports = {
telnet, rlogin, 512/tcp, 514/tcp, ftp, ssh, smtp, 143/tcp,
110/tcp, 6667/tcp,
} &redef;
const backdoor_annotate_standard_ports = T &redef;
const backdoor_ignore_hosts: set[addr] &redef;
const backdoor_ignore_src_nets: set[subnet] &redef;
const backdoor_ignore_dst_nets: set[subnet] &redef;
# Most backdoors are enabled by default, but a few are disabled by
# default (T below) because they generated too many false positives
# (or, for HTTP, too many uninteresting true positives).
const ftp_sig_disabled = F &redef;
const gaobot_sig_disabled = F &redef;
const gnutella_sig_disabled = F &redef;
const http_proxy_sig_disabled = T &redef;
const http_sig_disabled = T &redef;
const irc_sig_disabled = F &redef;
const kazaa_sig_disabled = F &redef;
const napster_sig_disabled = F &redef;
const rlogin_sig_1byte_disabled = T &redef;
const rlogin_sig_disabled = T &redef;
const root_backdoor_sig_disabled = T &redef;
const smtp_sig_disabled = F &redef;
# Note, for the following there's a corresponding variable
# interconn_ssh_len_disabled in interconn.bro.
const ssh_len_disabled = T &redef;
const ssh_sig_disabled = F &redef;
const telnet_sig_3byte_disabled = T &redef;
const telnet_sig_disabled = T &redef;
global ssh_len_conns: set[conn_id];
global rlogin_conns: table[conn_id] of rlogin_conn_info;
global root_backdoor_sig_conns: set[conn_id];
global did_sig_conns: table[conn_id] of set[string];
const BACKDOOR_UNKNOWN = 0;
const BACKDOOR_YES = 1;
const BACKDOOR_NO = 2;
const BACKDOOR_SIG_FOUND = 3;
global telnet_sig_conns: table[conn_id] of count;
global telnet_sig_3byte_conns: table[conn_id] of count;
global smtp_sig_conns: table[conn_id] of count;
global irc_sig_conns: table[conn_id] of count;
global gaobot_sig_conns: table[conn_id] of count;
const backdoor_log = open_log_file("backdoor") &redef;
function ignore_backdoor_conn(c: connection, bd: string): bool
{
local oa = c$id$orig_h;
local ra = c$id$resp_h;
local op = c$id$orig_p;
local rp = c$id$resp_p;
if ( backdoor_ignore_ports[bd, op] ||
backdoor_ignore_ports[bd, rp] ||
# Check port wildcards.
backdoor_ignore_ports[bd, 0/tcp] ||
(ra in local_nets && bd in backdoor_ignore_local) ||
(ra !in local_nets && bd in backdoor_ignore_remote) ||
backdoor_ignore_src_addrs[bd, oa] ||
backdoor_ignore_src_addrs[bd, mask_addr(oa, 16)] ||
backdoor_ignore_src_addrs[bd, mask_addr(oa, 24)] ||
backdoor_ignore_dst_addrs[bd, ra] ||
backdoor_ignore_dst_addrs[bd, mask_addr(ra, 16)] ||
backdoor_ignore_dst_addrs[bd, mask_addr(ra, 24)] )
return T;
if ( [oa, op] in backdoor_ignore_host_port_pairs ||
[ra, rp] in backdoor_ignore_host_port_pairs )
return T;
if ( bd != "*" )
# Evaluate again, but for wildcarding the backdoor.
return ignore_backdoor_conn(c, "*");
else
return F;
}
function log_backdoor(c: connection, tag: string): bool
{
if ( ignore_backdoor_conn(c, tag) )
return F;
local id = c$id;
if ( backdoor_annotate_standard_ports &&
(id$orig_p in backdoor_standard_ports ||
id$resp_p in backdoor_standard_ports) )
append_addl(c, fmt("[%s]", tag));
else if ( id$orig_h in backdoor_ignore_hosts ||
id$resp_h in backdoor_ignore_hosts ||
id$orig_h in backdoor_ignore_src_nets ||
id$resp_h in backdoor_ignore_dst_nets )
return F;
else
{
print backdoor_log, fmt("%.6f %s > %s %s",
c$start_time,
endpoint_id(id$orig_h, id$orig_p),
endpoint_id(id$resp_h, id$resp_p),
tag);
NOTICE([$note=BackdoorFound, $msg=tag, $conn=c]);
if ( dump_backdoor_packets )
{
mkdir("backdoor-packets");
local fname = fmt("backdoor-packets/%s:%.2f",
tag, current_time());
dump_current_packet(fname);
}
if ( backdoor_demux_disabled ||
tag in backdoor_demux_skip_tags )
{
if ( active_connection(c$id) )
skip_further_processing(c$id);
}
else
demux_conn(id, tag, "orig", "resp");
}
return T;
}
event new_connection(c: connection)
{
local id = c$id;
if ( ! rlogin_sig_disabled || ! rlogin_sig_1byte_disabled )
{
local i: rlogin_conn_info;
i$o_num_null = i$o_len = i$r_num_null = i$r_len = 0;
rlogin_conns[id] = i;
}
}
event backdoor_remove_conn(c: connection)
{
local id = c$id;
delete ssh_len_conns[id];
delete telnet_sig_conns[id];
delete telnet_sig_3byte_conns[id];
delete rlogin_conns[id];
delete root_backdoor_sig_conns[id];
delete smtp_sig_conns[id];
delete irc_sig_conns[id];
delete gaobot_sig_conns[id];
delete did_sig_conns[id];
}
event root_backdoor_signature_found(c: connection)
{
if ( root_backdoor_sig_disabled ||
ignore_backdoor_conn(c, "root-bd-sig") )
return;
local id = c$id;
# For root backdoors, don't ignore standard ports. This is because
# we shouldn't see such a backdoor even 23/tcp or 513/tcp!
if ( id !in root_backdoor_sig_conns )
{
add root_backdoor_sig_conns[id];
log_backdoor(c, "root-bd-sig");
}
}
function signature_found(c: connection, sig_disabled: bool, sig_name: string)
{
if ( sig_disabled )
return;
if ( ignore_backdoor_conn(c, sig_name) )
return;
if ( c$id !in did_sig_conns )
did_sig_conns[c$id] = set();
if ( sig_name !in did_sig_conns[c$id] )
{
add did_sig_conns[c$id][sig_name];
log_backdoor(c, sig_name);
}
}
event ftp_signature_found(c: connection)
{
signature_found(c, ftp_sig_disabled, "ftp-sig");
}
event napster_signature_found(c: connection)
{
signature_found(c, napster_sig_disabled, "napster-sig");
}
event gnutella_signature_found(c: connection)
{
signature_found(c, gnutella_sig_disabled, "gnutella-sig");
}
event kazaa_signature_found(c: connection)
{
signature_found(c, kazaa_sig_disabled, "kazaa-sig");
}
event http_signature_found(c: connection)
{
signature_found(c, http_sig_disabled, "http-sig");
}
event http_proxy_signature_found(c: connection)
{
signature_found(c, http_proxy_sig_disabled, "http-proxy-sig");
}
event ssh_signature_found(c: connection, is_orig: bool)
{
signature_found(c, ssh_sig_disabled, "ssh-sig");
}
event smtp_signature_found(c: connection)
{
signature_found(c, smtp_sig_disabled, "smtp-sig");
}
event irc_signature_found(c: connection)
{
signature_found(c, irc_sig_disabled, "irc-sig");
}
event gaobot_signature_found(c: connection)
{
signature_found(c, gaobot_sig_disabled, "gaobot-sig");
}
event telnet_signature_found(c: connection, is_orig: bool, len: count)
{
local id = c$id;
if ( ignore_backdoor_conn(c, "telnet-sig") )
return;
if ( ! telnet_sig_disabled && id !in telnet_sig_conns )
telnet_sig_conns[id] = BACKDOOR_SIG_FOUND;
if ( ! telnet_sig_3byte_disabled && len == 3 &&
id !in telnet_sig_3byte_conns )
telnet_sig_3byte_conns[id] = BACKDOOR_SIG_FOUND;
}
event rlogin_signature_found(c: connection, is_orig: bool,
num_null: count, len: count)
{
local id = c$id;
if ( (rlogin_sig_disabled && rlogin_sig_1byte_disabled) ||
ignore_backdoor_conn(c, "rlogin-sig") )
return;
local ri = rlogin_conns[id];
if ( is_orig && ri$o_num_null == 0 )
ri$o_num_null = num_null;
else if ( ! is_orig && ri$r_num_null == 0 )
{
ri$r_num_null = num_null;
ri$r_len = len;
}
else
return;
if ( ri$o_num_null == 0 || ri$r_num_null == 0 )
return;
if ( ! rlogin_sig_1byte_disabled && ri$r_len == 1 )
log_backdoor(c, "rlogin-sig-1byte");
if ( ! rlogin_sig_disabled )
log_backdoor(c, "rlogin-sig");
}
function ssh_len_stats(c: connection, os: backdoor_endp_stats,
rs: backdoor_endp_stats) : bool
{
if ( ssh_len_disabled || c$id in ssh_len_conns )
return F;
if ( os$num_pkts == 0 || rs$num_pkts == 0 )
return F;
# xxx: only use ssh-len for partial connection
local is_partial = os$is_partial || rs$is_partial;
if ( ! is_partial )
return F;
local num_pkts = os$num_pkts + rs$num_pkts;
if ( num_pkts < ssh_min_num_pkts )
return F;
local num_8k0_pkts = os$num_8k0_pkts + rs$num_8k0_pkts;
local num_8k4_pkts = os$num_8k4_pkts + rs$num_8k4_pkts;
local id = c$id;
if ( num_8k0_pkts >= num_pkts * ssh_min_ssh_pkts_ratio )
{
add ssh_len_conns[id];
log_backdoor(c, "ssh-len-v2.x");
}
else if ( num_8k4_pkts >= num_pkts * ssh_min_ssh_pkts_ratio )
{
add ssh_len_conns[id];
log_backdoor(c, "ssh-len-v1.x");
}
return T;
}
function telnet_stats(c: connection, os: backdoor_endp_stats,
rs: backdoor_endp_stats) : bool
{
local num_lines = os$num_lines + rs$num_lines;
local num_normal_lines = os$num_normal_lines + rs$num_normal_lines;
if ( num_lines < backdoor_min_num_lines ||
num_normal_lines < num_lines * backdoor_min_normal_line_ratio )
return F;
local num_bytes = os$num_bytes + rs$num_bytes;
local num_7bit_ascii = os$num_7bit_ascii + rs$num_7bit_ascii;
if ( num_bytes < backdoor_min_bytes ||
num_7bit_ascii < num_bytes * backdoor_min_7bit_ascii_ratio )
return F;
local id = c$id;
if ( id in telnet_sig_conns &&
telnet_sig_conns[id] != BACKDOOR_YES )
{
telnet_sig_conns[id] = BACKDOOR_YES;
log_backdoor(c, "telnet-sig");
}
if ( id in telnet_sig_3byte_conns &&
telnet_sig_3byte_conns[id] != BACKDOOR_YES )
{
telnet_sig_3byte_conns[id] = BACKDOOR_YES;
log_backdoor(c, "telnet-sig-3byte");
}
return T;
}
event backdoor_stats(c: connection,
os: backdoor_endp_stats, rs: backdoor_endp_stats)
{
telnet_stats(c, os, rs);
ssh_len_stats(c, os, rs);
}

View file

@ -1,277 +0,0 @@
# $Id:$
#
# bittorrent.bro - policy script for analyzing BitTorrent traffic
# ---------------------------------------------------------------
# This code contributed by Nadi Sarrar.
@load dpd
@load weird
module BitTorrent;
export {
# Whether to log the length of PDUs.
global log_pdu_length = T &redef;
}
redef capture_filters += { ["bittorrent"] = "tcp" };
type bt_peer_state: enum {
choked, # peer won't receive any responses to requests (initial state)
unchoked # peer may do requests
};
type bt_peer_info: record {
# Total of pure peer wire protocol overhead data (w/o pieces).
protocol_total: count &default = 0;
# State of the peer - choked or unchoked.
state: bt_peer_state &default = choked;
# Total number of seconds the peer was unchoked.
unchoked: interval &default = 0 secs;
# Time of the last received unchoke message.
time_last_unchoked: time;
};
type bt_peer_conn: record {
id: count;
orig: bt_peer_info;
resp: bt_peer_info;
weird: bool &default = F;
};
global bittorrent_log = open_log_file("bittorrent") &redef;
global bt_peer_conns : table[conn_id] of bt_peer_conn;
global peer_conn_count = 0;
function record_peer_protocol_traffic(c: connection, is_orig: bool,
protocol_len: count): count
{
if ( c$id in bt_peer_conns )
{
local pc = bt_peer_conns[c$id];
if ( is_orig )
pc$orig$protocol_total += protocol_len;
else
pc$resp$protocol_total += protocol_len;
return pc$id;
}
return 0;
}
function record_choke(pi: bt_peer_info, now: time)
{
if ( pi$state == unchoked )
{
pi$state = choked;
pi$unchoked += now - pi$time_last_unchoked;
}
}
function record_unchoke(pi: bt_peer_info, now: time)
{
if ( pi$state == choked )
{
pi$state = unchoked;
pi$time_last_unchoked = now;
}
}
function lookup_bt_peer(id: conn_id): bt_peer_conn
{
if ( id in bt_peer_conns )
return bt_peer_conns[id];
local orig: bt_peer_info;
local resp: bt_peer_info;
local pc: bt_peer_conn;
pc$orig = orig;
pc$resp = resp;
pc$id = ++peer_conn_count;
bt_peer_conns[id] = pc;
return pc;
}
function bt_log_id(id: conn_id, cid: count, tag: string, is_orig: bool): string
{
return fmt("%.6f P%d %s %s:%d %s %s:%d",
network_time(), cid, tag, id$orig_h, id$orig_p,
is_orig ? ">" : "<", id$resp_h, id$resp_p);
}
function pdu_log_len(len: count): string
{
return log_pdu_length ? fmt("[PDU-len:%d]", len) : "";
}
function log_pdu(c: connection, is_orig: bool, tag: string, len: count): count
{
local cid = record_peer_protocol_traffic(c, is_orig, len);
print bittorrent_log,
fmt("%s %s", bt_log_id(c$id, cid, tag, is_orig),
pdu_log_len(len));
return cid;
}
function log_pdu_str(c: connection, is_orig: bool, tag: string, len: count,
str: string)
{
local cid = record_peer_protocol_traffic(c, is_orig, len);
print bittorrent_log,
fmt("%s %s %s", bt_log_id(c$id, cid, tag, is_orig),
pdu_log_len(len), str);
}
function log_pdu_str_n(c: connection, is_orig: bool, tag: string, len: count,
n: count, str: string)
{
local cid = record_peer_protocol_traffic(c, is_orig, len);
print bittorrent_log,
fmt("%s %s %s", bt_log_id(c$id, cid, tag, is_orig),
pdu_log_len(n), str);
}
event bittorrent_peer_handshake(c: connection, is_orig: bool, reserved: string,
info_hash: string, peer_id: string)
{
local pc = lookup_bt_peer(c$id);
log_pdu_str(c, is_orig, "handshake", 68,
fmt("[peer_id:%s info_hash:%s reserved:%s]",
bytestring_to_hexstr(peer_id),
bytestring_to_hexstr(info_hash),
bytestring_to_hexstr(reserved)));
}
event bittorrent_peer_keep_alive(c: connection, is_orig: bool)
{
log_pdu(c, is_orig, "keep-alive", 4);
}
event bittorrent_peer_choke(c: connection, is_orig: bool)
{
local cid = log_pdu(c, is_orig, "choke", 5);
if ( cid > 0 )
{
local pc = bt_peer_conns[c$id];
record_choke(is_orig ? pc$resp : pc$orig, network_time());
}
}
event bittorrent_peer_unchoke(c: connection, is_orig: bool)
{
local cid = log_pdu(c, is_orig, "unchoke", 5);
if ( cid > 0 )
{
local pc = bt_peer_conns[c$id];
record_unchoke(is_orig ? pc$resp : pc$orig, network_time());
}
}
event bittorrent_peer_interested(c: connection, is_orig: bool)
{
log_pdu(c, is_orig, "interested", 5);
}
event bittorrent_peer_not_interested(c: connection, is_orig: bool)
{
log_pdu(c, is_orig, "not-interested", 5);
}
event bittorrent_peer_have(c: connection, is_orig: bool, piece_index: count)
{
log_pdu(c, is_orig, "have", 9);
}
event bittorrent_peer_bitfield(c: connection, is_orig: bool, bitfield: string)
{
log_pdu_str(c, is_orig, "bitfield", 5 + byte_len(bitfield),
fmt("[bitfield:%s]",
bytestring_to_hexstr(bitfield)));
}
event bittorrent_peer_request(c: connection, is_orig: bool, index: count,
begin: count, length: count)
{
log_pdu_str(c, is_orig, "request", 17,
fmt("[index:%d begin:%d length:%d]", index, begin, length));
}
event bittorrent_peer_piece(c: connection, is_orig: bool, index: count,
begin: count, piece_length: count)
{
log_pdu_str_n(c, is_orig, "piece", 13, 13 + piece_length,
fmt("[index:%d begin:%d piece_length:%d]",
index, begin, piece_length));
}
event bittorrent_peer_cancel(c: connection, is_orig: bool, index: count,
begin: count, length: count)
{
log_pdu_str(c, is_orig, "cancel", 7,
fmt("[index:%d begin:%d length:%d]",
index, begin, length));
}
event bittorrent_peer_port(c: connection, is_orig: bool, listen_port: port)
{
log_pdu_str(c, is_orig, "port", 5,
fmt("[listen_port:%s]", listen_port));
}
event bittorrent_peer_unknown(c: connection, is_orig: bool, message_id: count,
data: string)
{
log_pdu_str(c, is_orig, "<unknown>", 5 + byte_len(data),
fmt("[message_id:%d]", message_id));
}
event bittorrent_peer_weird(c: connection, is_orig: bool, msg: string)
{
local pc = lookup_bt_peer(c$id);
pc$weird = T;
print bittorrent_log,
fmt("%s [%s]", bt_log_id(c$id, pc$id, "<weird>", is_orig), msg);
event conn_weird(msg, c);
}
function log_close(c: connection, pc: bt_peer_conn, is_orig: bool)
{
local endp = is_orig ? c$orig : c$resp;
local peer_i = is_orig ? pc$orig : pc$resp;
local status =
pc$weird ?
fmt("size:%d", endp$size) :
fmt("unchoked:%.06f size_protocol:%d size_pieces:%d",
peer_i$unchoked, peer_i$protocol_total,
endp$size - peer_i$protocol_total);
print bittorrent_log,
fmt("%s [duration:%.06f %s]",
bt_log_id(c$id, pc$id, "<closed>", is_orig),
c$duration, status);
}
event connection_state_remove(c: connection)
{
if ( c$id !in bt_peer_conns )
return;
local pc = bt_peer_conns[c$id];
delete bt_peer_conns[c$id];
record_choke(pc$orig, c$start_time + c$duration);
record_choke(pc$resp, c$start_time + c$duration);
log_close(c, pc, T);
log_close(c, pc, F);
}

View file

@ -1,52 +0,0 @@
# $Id: blaster.bro 5952 2008-07-13 19:45:15Z vern $
#
# Identifies W32.Blaster-infected hosts by observing their scanning
# activity.
@load notice
@load site
# Which hosts have scanned which addresses via 135/tcp.
global w32b_scanned: table[addr] of set[addr] &write_expire = 5min;
global w32b_reported: set[addr] &persistent;
const W32B_port = 135/tcp;
const W32B_MIN_ATTEMPTS = 50 &redef;
redef enum Notice += {
W32B_SourceLocal,
W32B_SourceRemote,
};
event connection_attempt(c: connection)
{
if ( c$id$resp_p != W32B_port )
return;
local ip = c$id$orig_h;
if ( ip in w32b_reported )
return;
if ( ip in w32b_scanned )
{
add (w32b_scanned[ip])[c$id$resp_h];
if ( length(w32b_scanned[ip]) >= W32B_MIN_ATTEMPTS )
{
if ( is_local_addr(ip) )
NOTICE([$note=W32B_SourceLocal, $conn=c,
$msg=fmt("W32.Blaster local source: %s",
ip)]);
else
NOTICE([$note=W32B_SourceRemote, $conn=c,
$msg=fmt("W32.Blaster remote source: %s",
ip)]);
add w32b_reported[ip];
}
}
else
w32b_scanned[ip] = set(ip) &mergeable;
}

View file

@ -1,55 +0,0 @@
# $Id: brolite-backdoor.bro 2956 2006-05-14 01:08:34Z vern $
# Sample file for running backdoor detector
#
# Note, this can consume significant processing resources when running
# on live traffic.
#
# To run bro with this script using a Bro Lite setup:
#
# rename this script to hostname.bro
# run: $BROHOME/etc/bro.rc start
# or bro -i interface brolite-backdoor.bro
@load site
@load backdoor
@load weird
# By default, do backdoor detection on everything except standard HTTP
# and SMTP ports.
redef capture_filters += [ ["tcp"] = "tcp" ];
redef restrict_filters +=
[ ["not-http"] = "not (port 80 or port 8000 or port 8080)" ];
redef restrict_filters += [ ["not-smtp"] = "not (port 25 or port 587)" ];
redef use_tagging = T;
# Set if you want to dump packets that trigger the detections.
redef dump_backdoor_packets = T;
# Disable (set to T) if you don't care about this traffic.
# redef gnutella_sig_disabled = T;
# redef kazaa_sig_disabled = T;
redef napster_sig_disabled = T; # too many false positives
# Ignore outgoing, only report incoming backdoors.
redef backdoor_ignore_remote += {
ftp_backdoor_sigs, ssh_backdoor_sigs, rlogin_backdoor_sigs,
http_backdoor_sigs, http_proxy_backdoor_sigs, smtp_backdoor_sigs,
};
# Set these to send mail on backdoor alarms.
# redef mail_dest = "youremail@yourhost.dom";
# redef notice_action_filters += {
# [BackdoorFound] = send_email_notice,
#};
# Tuning: use more aggressive timeouts to reduce CPU and memory, as these
# have little effect on backdoor analysis.
redef tcp_SYN_timeout = 1 sec;
redef tcp_attempt_delay = 1 sec;
redef tcp_inactivity_timeout = 1 min;
redef udp_inactivity_timeout = 5 secs;
redef icmp_inactivity_timeout = 5 secs;

View file

@ -1,82 +0,0 @@
# $Id: brolite-sigs.bro 3856 2006-12-02 00:18:57Z vern $
# Bro Lite signature configuration file
# General policy - these scripts are more infrastructural than service
# oriented, so in general avoid changing anything here.
# Set global constant. This can be used in ifdef statements to determine
# if signatures are enabled.
const use_signatures = T;
@load snort # basic definitions for signatures
@load signatures # the signature policy engine
@load sig-functions # addl. functions added for signature accuracy
@load sig-action # actions related to particular signatures
# Flag HTTP worm sources such as Code Red.
@load worm
# Do worm processing
redef notice_action_filters += { [RemoteWorm] = file_notice };
# Ports that need to be captured for signatures to see a useful
# cross section of traffic.
redef capture_filters += {
["sig-http"] =
"tcp port 80 or tcp port 8080 or tcp port 8000 or tcp port 8001",
["sig-ftp"] = "port ftp",
["sig-telnet"] = "port telnet",
["sig-portmapper"] = "port 111",
["sig-smtp"] = "port smtp",
["sig-imap"] = "port 143",
["sig-snmp"] = "port 161 or port 162",
["sig-dns"] = "port 53",
# rsh/rlogin/rexec
["sig-rfoo"] = "port 512 or port 513 or port 515",
# Range of TCP ports for general RPC traffic. This can also
# occur on other ports, but these should catch a lot without
# a major performance hit. We skip ports assosciated with
# HTTP, SSH and M$.
["sig-rpc"] = "tcp[2:2] > 32770 and tcp[2:2] < 32901 and tcp[0:2] != 80 and tcp[0:2] != 22 and tcp[0:2] != 139",
};
### Why is this called "tcp3"?
# Catch outbound M$ scanning. Returns filter listing local addresses
# along with the interesting ports.
function create_tcp3_filter(): string
{
local local_addrs = "";
local firsttime = T;
for ( l in local_nets )
{
if ( firsttime )
{
local_addrs = fmt("src net %s", l);
firsttime = F;
}
else
local_addrs = fmt("%s or src net %s", local_addrs, l);
}
local MS_scan_ports =
"dst port 135 or dst port 137 or dst port 139 or dst port 445";
if ( local_addrs == "" )
return MS_scan_ports;
else
return fmt("(%s) and (%s)", local_addrs, MS_scan_ports);
}
# Create and apply the filter.
redef capture_filters += { ["tcp3"] = create_tcp3_filter()};
# Turn on ICMP analysis.
redef capture_filters += { ["icmp"] = "icmp"};
# Load the addendum signatures. These are utility signatures that do not
# produce event messages.
redef signature_files += "sig-addendum";

View file

@ -1,195 +0,0 @@
# Bro Lite base configuration file.
# General policy - these scripts are more infrastructural than service
# oriented, so in general avoid changing anything here.
@load site # defines local and neighbor networks from static config
@load tcp # initialize BPF filter for SYN/FIN/RST TCP packets
@load weird # initialize generic mechanism for unusual events
@load conn # access and record connection events
@load hot # defines certain forms of sensitive access
@load frag # process TCP fragments
@load print-resources # on exit, print resource usage information
# Scan detection policy.
@load scan # generic scan detection mechanism
@load trw # additional, more sensitive scan detection
#@load drop # include if installation has ability to drop hostile remotes
# Application level policy - these scripts operate on the specific service.
@load http # general http analyzer, low level of detail
@load http-request # detailed analysis of http requests
@load http-reply # detailed analysis of http reply's
# Track software versions; required for some signature matching. Also
# can be used by http and ftp policies.
@load software
@load ftp # FTP analysis
@load portmapper # record and analyze RPC portmapper requests
@load tftp # identify and log TFTP sessions
@load login # rlogin/telnet analyzer
@load irc # IRC analyzer
@load blaster # blaster worm detection
@load stepping # "stepping stone" detection
@load synflood # synflood attacks detection
@load smtp # record and analyze email traffic - somewhat expensive
@load notice-policy # tuning of notices to downgrade some alarms
# off by default
#@load icmp # icmp analysis
# Tuning of memory consumption.
@load inactivity # time out connections for certain services more quickly
# @load print-globals # on exit, print the size of global script variables
# Record system statistics to the notice file
@load stats
# udp analysis - potentially expensive, depending on a site's traffic profile
#@load udp.all
#@load remove-multicast
# Prints the pcap filter and immediately exits. Not used during
# normal operation.
#@load print-filter
## End policy script loading.
## General configuration.
@load rotate-logs
redef log_rotate_base_time = "0:00";
redef log_rotate_interval = 24 hr;
# Set additional policy prefixes.
@prefixes += lite
## End basic configuration.
## Scan configuration.
@ifdef ( Scan::analyze_all_services )
redef Scan::analyze_all_services = T;
# The following turns off scan detection.
#redef Scan::suppress_scan_checks = T;
# Be a bit more aggressive than default (though the defaults
# themselves should be fixed).
redef Scan::report_outbound_peer_scan = { 100, 1000, };
# These services are skipped for scan detection due to excessive
# background noise.
redef Scan::skip_services += {
http, # Avoid Code Red etc. overload
27374/tcp, # Massive scanning in Jan 2002
1214/tcp, # KaZaa scans
12345/tcp, # Massive scanning in Apr 2002
445/tcp, # Massive distributed scanning Oct 2002
135/tcp, # These days, NetBIOS scanning is endemic
137/udp, # NetBIOS
139/tcp, # NetBIOS
1025/tcp,
6129/tcp, # Dameware
3127/tcp, # MyDoom worms worms worms!
2745/tcp, # Bagel worm
1433/tcp, # Distributed scanning, April 2004
5000/tcp, # Distributed scanning, May 2004
5554/tcp, # More worm food, May 2004
9898/tcp, # Worms attacking worms. ugh - May 2004
3410/tcp, # More worm food, June 2004
3140/tcp, # Dyslexic worm food, June 2004
27347/tcp, # Can't kids type anymore?
1023/tcp, # Massive scanning, July 2004
17300/tcp, # Massive scanning, July 2004
};
@endif
@ifdef ( ICMP::detect_scans )
# Whether to detect ICMP scans.
redef ICMP::detect_scans = F;
redef ICMP::scan_threshold = 100;
@endif
@ifdef ( TRW::TRWAddressScan )
# remove logging TRW scan events
redef notice_action_filters += {
[TRW::TRWAddressScan] = ignore_notice,
};
@endif
# Note: default scan configuration is conservative in terms of memory use and
# might miss slow scans. Consider uncommenting these based on your sites scan
# traffic.
#redef distinct_peers &create_expire = 30 mins;
#redef distinct_ports &create_expire = 30 mins;
#redef distinct_low_ports &create_expire= 30 mins;
## End scan configuration.
## additional IRC checks
redef IRC::hot_words += /.*exe/ ;
## Dynamic Protocol Detection configuration
#
# This is off by default, as it requires a more powerful Bro host.
# Uncomment next line to activate.
# const use_dpd = T;
@ifdef ( use_dpd )
@load dpd
@load irc-bot
@load dyn-disable
@load detect-protocols
@load detect-protocols-http
@load proxy
@load ssh
# By default, DPD looks at all traffic except port 80.
# For lightly loaded networks, comment out the restrict_filters line.
# For heavily loaded networks, try adding addition ports (e.g., 25) to
# the restrict filters.
redef capture_filters += [ ["tcp"] = "tcp" ];
redef restrict_filters += [ ["not-http"] = "not (port 80)" ];
@endif
@ifdef ( ProtocolDetector::ServerFound )
# Report servers on non-standard ports only for local addresses.
redef notice_policy += {
[$pred(a: notice_info) =
{ return a$note == ProtocolDetector::ServerFound &&
! is_local_addr(a$src); },
$result = NOTICE_FILE,
$priority = 1],
# Report protocols on non-standard ports only for local addresses
# (unless it's IRC).
[$pred(a: notice_info) =
{ return a$note == ProtocolDetector::ProtocolFound &&
! is_local_addr(a$dst) &&
a$sub != "IRC"; },
$result = NOTICE_FILE,
$priority = 1],
};
@endif
# The following is used to transfer state between Bro's when one
# takes over from another.
#
# NOTE: not implemented in the production version, so ignored for now.
@ifdef ( remote_peers_clear )
redef remote_peers_clear += {
[127.0.0.1, 55555/tcp] = [$hand_over = T],
[127.0.0.1, 0/tcp] = [$hand_over = T]
};
@endif
# Use tagged log files for notices.
redef use_tagging = T;

View file

@ -1,190 +0,0 @@
# $Id:$
#
# bt-tracker.bro - analysis of BitTorrent tracker traffic
# ------------------------------------------------------------------------------
# This code contributed by Nadi Sarrar.
@load dpd
@load weird
module BitTorrent;
export {
# Whether to log tracker URIs.
global log_tracker_request_uri = F &redef;
}
redef capture_filters += { ["bittorrent"] = "tcp", };
global bt_tracker_log = open_log_file("bt-tracker") &redef;
global bt_tracker_conns: table[conn_id] of count;
global tracker_conn_count: count = 0;
function bt_log_tag(id: conn_id, cid: count, tag: string, is_orig: bool): string
{
return fmt("%.6f T%d %s %s:%d %s %s:%d",
network_time(), cid, tag, id$orig_h, id$orig_p,
is_orig ? ">" : "<", id$resp_h, id$resp_p);
}
event bt_tracker_request(c: connection, uri: string,
headers: bt_tracker_headers)
{
# Parse and validate URI.
local pair = split1(uri, /\?/);
local keys = split(pair[2], /&/);
local info_hash = "";
local peer_ide = "";
local peer_port = 0/udp;
local uploaded = -1;
local downloaded = -1;
local left = -1;
local compact = T;
local peer_event = "empty";
for ( idx in keys )
{
local keyval = split1(keys[idx], /=/);
if ( length(keyval) != 2 )
next;
local key = to_lower(keyval[1]);
local val = keyval[2];
if ( key == "info_hash" )
info_hash = unescape_URI(val);
else if ( key == "peer_id" )
peer_ide = unescape_URI(val);
else if ( key == "port" )
peer_port = to_port(to_count(val), tcp);
else if ( key == "uploaded" )
uploaded = to_int(val);
else if ( key == "downloaded" )
downloaded = to_int(val);
else if ( key == "left" )
left = to_int(val);
else if ( key == "compact" )
compact = (to_int(val) == 1);
else if ( key == "event" )
{
val = to_lower(val);
if ( val == /started|stopped|completed/ )
peer_event = val;
}
}
if ( info_hash == "" || peer_ide == "" || peer_port == 0/udp )
{ # Does not look like BitTorrent.
disable_analyzer(c$id, current_analyzer());
delete bt_tracker_conns[c$id];
return;
}
if ( peer_port != 0/tcp )
expect_connection(to_addr("0.0.0.0"), c$id$orig_h,
peer_port, ANALYZER_BITTORRENT, 1 min);
local id: count;
if ( c$id in bt_tracker_conns )
id = bt_tracker_conns[c$id];
else
{
id = ++tracker_conn_count;
bt_tracker_conns[c$id] = id;
}
print bt_tracker_log,
fmt("%s [peer_id:%s info_hash:%s port:%s event:%s up:%d down:%d left:%d compact:%s]%s",
bt_log_tag(c$id, id, "request", T),
bytestring_to_hexstr(peer_ide),
bytestring_to_hexstr(info_hash),
peer_port, peer_event,
uploaded, downloaded, left,
compact ? "yes" : "no",
log_tracker_request_uri ? fmt(" GET %s", uri) : "");
}
function benc_status(benc: bittorrent_benc_dir, tag: string): string
{
if ( tag !in benc || ! benc[tag]?$i )
return "";
local fmt_tag = sub(tag, / /, "_");
return fmt("%s:%d", fmt_tag, benc[tag]$i);
}
event bt_tracker_response(c: connection, status: count,
headers: bt_tracker_headers,
peers: bittorrent_peer_set,
benc: bittorrent_benc_dir)
{
if ( c$id !in bt_tracker_conns )
return;
local id = bt_tracker_conns[c$id];
for ( peer in peers )
expect_connection(c$id$orig_h, peer$h, peer$p,
ANALYZER_BITTORRENT, 1 min);
if ( "failure reason" in benc )
{
print bt_tracker_log,
fmt("%s [failure_reason:\"%s\"]",
bt_log_tag(c$id, id, "response", F),
benc["failure reason"]?$s ?
benc["failure reason"]$s : "");
return;
}
print bt_tracker_log,
fmt("%s [%s%s%s%s%speers:%d]",
bt_log_tag(c$id, id, "response", F),
benc_status(benc, "warning message"),
benc_status(benc, "complete"),
benc_status(benc, "incomplete"),
benc_status(benc, "interval"),
benc_status(benc, "min interval"),
length(peers));
}
event bt_tracker_response_not_ok(c: connection, status: count,
headers: bt_tracker_headers)
{
if ( c$id in bt_tracker_conns )
{
local id = bt_tracker_conns[c$id];
print bt_tracker_log,
fmt("%s [status:%d]",
bt_log_tag(c$id, id, "response", F), status);
}
}
event bt_tracker_weird(c: connection, is_orig: bool, msg: string)
{
local id = (c$id in bt_tracker_conns) ? bt_tracker_conns[c$id] : 0;
print bt_tracker_log,
fmt("%s [%s]", bt_log_tag(c$id, id, "<weird>", is_orig), msg);
event conn_weird(msg, c);
}
event connection_state_remove(c: connection)
{
if ( c$id !in bt_tracker_conns )
return;
local id = bt_tracker_conns[c$id];
delete bt_tracker_conns[c$id];
print bt_tracker_log,
fmt("%s [duration:%.06f total:%d]",
# Ideally the direction here wouldn't be T or F
# but both, displayed as "<>".
bt_log_tag(c$id, id, "<closed>", T), c$duration,
c$orig$size + c$resp$size);
}

View file

@ -1,9 +0,0 @@
#! $Id: capture-events.bro 4674 2007-07-30 22:00:43Z vern $
#
# Captures all events to events.bst.
#
event bro_init()
{
capture_events("events.bst");
}

View file

@ -1,74 +0,0 @@
# $Id:$
# Logs evidence regarding the degree to which the packet capture process
# suffers from measurment loss.
#
# By default, only reports loss computed in terms of number of "gap events"
# (ACKs for a sequence number that's above a gap). You can also get an
# estimate in terms of number of bytes missing; this however is sometimes
# heavily affected by miscomputations due to broken packets with incorrect
# sequence numbers. (These packets also affect the first estimator, but
# only to a quite minor degree.)
@load notice
module CaptureLoss;
export {
redef enum Notice += {
CaptureLossReport, # interval report
CaptureLossSummary, # end-of-run summary
};
# Whether to also report byte-weighted estimates.
global report_byte_based_estimates = F &redef;
# Whether to generate per-interval reports even if there
# was no evidence of loss.
global report_if_none = F &redef;
# Whether to generate a summary even if there was no
# evidence of loss.
global summary_if_none = F &redef;
}
# Redefine this to be non-zero to get per-interval reports.
redef gap_report_freq = 0 sec;
event gap_report(dt: interval, info: gap_info)
{
if ( info$gap_events > 0 || report_if_none )
{
local msg = report_byte_based_estimates ?
fmt("gap-dt=%.6f acks=%d bytes=%d gaps=%d gap-bytes=%d",
dt, info$ack_events, info$ack_bytes,
info$gap_events, info$gap_bytes) :
fmt("gap-dt=%.6f acks=%d gaps=%d",
dt, info$ack_events, info$gap_events);
NOTICE([$note=CaptureLossReport, $msg=msg]);
}
}
event bro_done()
{
local g = get_gap_summary();
local gap_rate =
g$ack_events == 0 ? 0.0 :
(1.0 * g$gap_events) / (1.0 * g$ack_events);
local gap_bytes =
g$ack_bytes == 0 ? 0.0 :
(1.0 * g$gap_bytes) / (1.0 * g$ack_bytes);
if ( gap_rate == 0.0 && gap_bytes == 0.0 && ! summary_if_none )
return;
local msg = report_byte_based_estimates ?
fmt("estimated rate = %g / %g (events/bytes)",
gap_rate, gap_bytes) :
fmt("estimated rate = %g", gap_rate);
NOTICE([$note=CaptureLossSummary, $msg=msg]);
}

View file

@ -1,9 +0,0 @@
#! $Id: capture-events.bro 6 2004-04-30 00:31:26Z jason $
#
# Captures all operations on &synchronized variables to state-updates.bst.
#
event bro_init()
{
capture_state_updates("state-updates.bst");
}

View file

@ -1,54 +0,0 @@
# $Id: checkpoint.bro 6724 2009-06-07 09:23:03Z vern $
#
# Checkpoints Bro's persistent state at regular intervals and scans
# the state directory for external updates.
const state_rescan_interval = 15 secs &redef;
const state_checkpoint_interval = 15 min &redef;
# Services for which the internal connection state is stored.
const persistent_services = {
21/tcp, # ftp
22/tcp, # ssh
23/tcp, # telnet
513/tcp, # rlogin
} &redef;
# The first timer fires immediately. This flags lets us ignore it.
global state_ignore_first = T;
event state_checkpoint()
{
if ( state_ignore_first )
state_ignore_first = F;
else if ( ! bro_is_terminating() )
checkpoint_state();
if ( state_checkpoint_interval > 0 secs )
schedule state_checkpoint_interval { state_checkpoint() };
}
event state_rescan()
{
rescan_state();
if ( state_rescan_interval > 0 secs )
schedule state_rescan_interval { state_rescan() };
}
event bro_init()
{
if ( state_checkpoint_interval > 0 secs )
schedule state_checkpoint_interval { state_checkpoint() };
if ( state_rescan_interval > 0 secs )
schedule state_rescan_interval { state_rescan() };
}
event connection_established(c: connection)
{
# Buggy?
# if ( c$id$resp_p in persistent_services )
# make_connection_persistent(c);
}

View file

@ -1,36 +0,0 @@
# $Id: clear-passwords.bro 4758 2007-08-10 06:49:23Z vern $
# Monitoring for use of cleartext passwords.
@load ftp
@load login
@load pop3
@load irc
const passwd_file = open_log_file("passwords") &redef;
# ftp, login and pop3 call login_{success,failure}, which in turn
# calls account_tried(), so we can snarf all at once here:
event account_tried(c: connection, user: string, passwd: string)
{
print passwd_file, fmt("%s account name '%s', password '%s': %s",
is_local_addr(c$id$orig_h) ? "local" : "remote",
user, passwd, id_string(c$id));
}
# IRC raises a different event on login, so we hook into it here:
event irc_join_message(c: connection, info_list: irc_join_list)
{
for ( l in info_list)
{
print passwd_file, fmt("IRC JOIN name '%s', password '%s'",
l$nick, l$password);
}
}
# Raised if IRC user tries to become operator:
event irc_oper_message(c: connection, user: string, password: string)
{
print passwd_file, fmt("IRC OPER name '%s', password '%s'",
user, password);
}

View file

@ -1,71 +0,0 @@
# $Id$
#
# Script which alarms if the number of connections per time interval
# exceeds a threshold.
#
# This script is mainly meant as a demonstration; it hasn't been hardened
# with/for operational use.
@load notice
module ConnFlood;
export {
redef enum Notice += {
ConnectionFloodStart, ConnectionFloodEnd,
};
# Thresholds to reports (conns/sec).
const thresholds: set[count] =
{ 1000, 2000, 4000, 6000, 8000, 10000, 20000, 50000 }
&redef;
# Average over this time interval.
const avg_interval = 10 sec &redef;
}
global conn_counter = 0;
global last_thresh = 0;
# Note: replace with connection_attempt if too expensive.
event new_connection(c: connection)
{
++conn_counter;
}
event check_flood()
{
local thresh = 0;
local rate = double_to_count(interval_to_double((conn_counter / avg_interval)));
# Find the largest threshold reached this interval.
for ( i in thresholds )
{
if ( rate >= i && rate > thresh )
thresh = i;
}
# Report if larger than last reported threshold.
if ( thresh > last_thresh )
{
NOTICE([$note=ConnectionFloodStart, $n=thresh,
$msg=fmt("flood begins at rate %d conns/sec", rate)]);
last_thresh = thresh;
}
# If no threshold was reached, the flood is over.
else if ( thresh == 0 && last_thresh > 0 )
{
NOTICE([$note=ConnectionFloodEnd, $n=thresh,
$msg=fmt("flood ends at rate %d conns/sec", rate)]);
last_thresh = 0;
}
conn_counter = 0;
schedule avg_interval { check_flood() };
}
event bro_init()
{
schedule avg_interval { check_flood() };
}

View file

@ -1,24 +0,0 @@
# $Id: conn-id.bro 45 2004-06-09 14:29:49Z vern $
# Simple functions for generating ASCII connection identifiers.
@load port-name
function id_string(id: conn_id): string
{
return fmt("%s > %s",
endpoint_id(id$orig_h, id$orig_p),
endpoint_id(id$resp_h, id$resp_p));
}
function reverse_id_string(id: conn_id): string
{
return fmt("%s < %s",
endpoint_id(id$orig_h, id$orig_p),
endpoint_id(id$resp_h, id$resp_p));
}
function directed_id_string(id: conn_id, is_orig: bool): string
{
return is_orig ? id_string(id) : reverse_id_string(id);
}

View file

@ -1,425 +0,0 @@
# $Id: conn.bro 6782 2009-06-28 02:19:03Z vern $
@load notice
@load hot
@load port-name
@load netstats
@load conn-id
redef enum Notice += {
SensitiveConnection, # connection marked "hot"
};
const conn_closed = { TCP_CLOSED, TCP_RESET };
global have_FTP = F; # if true, we've loaded ftp.bro
global have_SMTP = F; # if true, we've loaded smtp.bro
# TODO: Do we have a nicer way of doing this?
export { global FTP::is_ftp_data_conn: function(c: connection): bool; }
# Whether to include connection state history in the logs generated
# by record_connection.
const record_state_history = F &redef;
# Whether to translate the local address in SensitiveConnection notices
# to a hostname. Meant as a demonstration of the "when" construct.
const xlate_hot_local_addr = F &redef;
# Whether to use DPD for generating the service field in the summaries.
# Default off, because it changes the format of conn.log in a way
# potentially incompatible with existing scripts.
const dpd_conn_logs = F &redef;
# Maps a given port on a given server's address to an RPC service.
# If we haven't loaded portmapper.bro, then it will be empty
# (and, ideally, queries to it would be optimized away ...).
global RPC_server_map: table[addr, port] of string;
const conn_file = open_log_file("conn") &redef;
function conn_state(c: connection, trans: transport_proto): string
{
local os = c$orig$state;
local rs = c$resp$state;
local o_inactive = os == TCP_INACTIVE || os == TCP_PARTIAL;
local r_inactive = rs == TCP_INACTIVE || rs == TCP_PARTIAL;
if ( trans == tcp )
{
if ( rs == TCP_RESET )
{
if ( os == TCP_SYN_SENT || os == TCP_SYN_ACK_SENT ||
(os == TCP_RESET &&
c$orig$size == 0 && c$resp$size == 0) )
return "REJ";
else if ( o_inactive )
return "RSTRH";
else
return "RSTR";
}
else if ( os == TCP_RESET )
return r_inactive ? "RSTOS0" : "RSTO";
else if ( rs == TCP_CLOSED && os == TCP_CLOSED )
return "SF";
else if ( os == TCP_CLOSED )
return r_inactive ? "SH" : "S2";
else if ( rs == TCP_CLOSED )
return o_inactive ? "SHR" : "S3";
else if ( os == TCP_SYN_SENT && rs == TCP_INACTIVE )
return "S0";
else if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED )
return "S1";
else
return "OTH";
}
else if ( trans == udp )
{
if ( os == UDP_ACTIVE )
return rs == UDP_ACTIVE ? "SF" : "S0";
else
return rs == UDP_ACTIVE ? "SHR" : "OTH";
}
else
return "OTH";
}
function conn_size(e: endpoint, trans: transport_proto): string
{
if ( e$size > 0 || (trans == tcp && e$state == TCP_CLOSED) )
return fmt("%d", e$size);
else
### should return 0 for TCP_RESET that went through TCP_CLOSED
return "?";
}
function service_name(c: connection): string
{
local p = c$id$resp_p;
if ( p in port_names )
return port_names[p];
else
return "other";
}
const state_graphic = {
["OTH"] = "?>?", ["REJ"] = "[",
["RSTO"] = ">]", ["RSTOS0"] = "}]", ["RSTR"] = ">[", ["RSTRH"] = "<[",
["S0"] = "}", ["S1"] = ">", ["S2"] = "}2", ["S3"] = "}3",
["SF"] = ">", ["SH"] = ">h", ["SHR"] = "<h",
};
function full_id_string(c: connection): string
{
local id = c$id;
local trans = get_port_transport_proto(id$orig_p);
local state = conn_state(c, trans);
local state_gr = state_graphic[state];
local service = service_name(c);
if ( state == "S0" || state == "S1" || state == "REJ" )
return fmt("%s %s %s/%s %s", id$orig_h, state_gr,
id$resp_h, service, c$addl);
else
return fmt("%s %sb %s %s/%s %sb %.1fs %s",
id$orig_h, conn_size(c$orig, trans),
state_gr, id$resp_h, service,
conn_size(c$resp, trans), c$duration, c$addl);
}
# The sets are indexed by the complete hot messages.
global hot_conns_reported: table[conn_id] of set[string];
# Low-level routine that generates the actual SensitiveConnection
# notice associated with a "hot" connection.
function do_hot_notice(c: connection, dir: string, host: string)
{
NOTICE([$note=SensitiveConnection, $conn=c,
$msg=fmt("hot: %s %s local host: %s",
full_id_string(c), dir, host)]);
}
# Generate a SensitiveConnection notice with the local hostname
# translated. Mostly intended as a demonstration of using "when".
function gen_hot_notice_with_hostnames(c: connection)
{
local id = c$id;
local inbound = is_local_addr(id$resp_h);
local dir = inbound ? "to" : "from";
local local_addr = inbound ? id$orig_h : id$resp_h;
add_notice_tag(c);
when ( local hostname = lookup_addr(local_addr) )
do_hot_notice(c, dir, hostname);
timeout 5 sec
{ do_hot_notice(c, dir, fmt("%s", local_addr)); }
}
function log_hot_conn(c: connection)
{
if ( c$id !in hot_conns_reported )
hot_conns_reported[c$id] = set() &mergeable;
local msg = full_id_string(c);
if ( msg !in hot_conns_reported[c$id] )
{
if ( xlate_hot_local_addr )
gen_hot_notice_with_hostnames(c);
else
NOTICE([$note=SensitiveConnection, $conn=c,
$msg=fmt("hot: %s", full_id_string(c))]);
add hot_conns_reported[c$id][msg];
}
}
function determine_service_non_DPD(c: connection) : string
{
if ( length(c$service) != 0 )
{
for ( i in c$service )
return i; # return first;
}
else if ( have_FTP && FTP::is_ftp_data_conn(c) )
return port_names[20/tcp];
else if ( [c$id$resp_h, c$id$resp_p] in RPC_server_map )
# Alternatively, perhaps this should be stored in $addl
# rather than $service, so the port number remains
# visible .... ?
return RPC_server_map[c$id$resp_h, c$id$resp_p];
else if ( c$orig$state == TCP_INACTIVE )
{
# We're seeing a half-established connection. Use the
# service of the originator if it's well-known and the
# responder isn't.
if ( c$id$resp_p !in port_names && c$id$orig_p in port_names )
return port_names[c$id$orig_p];
}
return service_name(c);
}
function determine_service(c: connection) : string
{
if ( ! dpd_conn_logs )
return determine_service_non_DPD(c);
if ( [c$id$resp_h, c$id$resp_p] in RPC_server_map )
add c$service[RPC_server_map[c$id$resp_h, c$id$resp_p]];
if ( length(c$service) == 0 )
{
# Empty service set. Use port as a hint.
if ( c$orig$state == TCP_INACTIVE )
{
# We're seeing a half-established connection. Use the
# service of the originator if it's well-known and the
# responder isn't.
if ( c$id$resp_p !in port_names &&
c$id$orig_p in port_names )
return fmt("%s?", port_names[c$id$orig_p]);
}
if ( c$id$resp_p in port_names )
return fmt("%s?", port_names[c$id$resp_p]);
return "other";
}
local service = "";
for ( s in c$service )
{
if ( sub_bytes(s, 0, 1) != "-" )
service = service == "" ? s : cat(service, ",", s);
}
return service != "" ? to_lower(service) : "other";
}
function record_connection(f: file, c: connection)
{
local id = c$id;
local local_init = is_local_addr(id$orig_h);
local local_addr = local_init ? id$orig_h : id$resp_h;
local remote_addr = local_init ? id$resp_h : id$orig_h;
local flags = local_init ? "L" : "X";
local trans = get_port_transport_proto(id$orig_p);
local duration: string;
# Do this first so we see the tag in addl.
if ( c$hot > 0 )
log_hot_conn(c);
if ( trans == tcp )
{
if ( c$orig$state in conn_closed || c$resp$state in conn_closed )
duration = fmt("%.06f", c$duration);
else
duration = "?";
}
else
duration = fmt("%.06f", c$duration);
local addl = c$addl;
@ifdef ( estimate_flow_size_and_remove )
# Annotate connection with separately-estimated size, if present.
local orig_est = estimate_flow_size_and_remove(id, T);
local resp_est = estimate_flow_size_and_remove(id, F);
if ( orig_est$have_est )
addl = fmt("%s olower=%.0fMB oupper=%.0fMB oincon=%s", addl,
orig_est$lower / 1e6, orig_est$upper / 1e6,
orig_est$num_inconsistent);
if ( resp_est$have_est )
addl = fmt("%s rlower=%.0fMB rupper=%.0fMB rincon=%s", addl,
resp_est$lower / 1e6, resp_est$upper / 1e6,
resp_est$num_inconsistent);
@endif
local service = determine_service(c);
local log_msg =
fmt("%.6f %s %s %s %s %d %d %s %s %s %s %s",
c$start_time, duration, id$orig_h, id$resp_h, service,
id$orig_p, id$resp_p, trans,
conn_size(c$orig, trans), conn_size(c$resp, trans),
conn_state(c, trans), flags);
if ( record_state_history )
log_msg = fmt("%s %s", log_msg,
c$history == "" ? "X" : c$history);
if ( addl != "" )
log_msg = fmt("%s %s", log_msg, addl);
print f, log_msg;
}
event connection_established(c: connection)
{
Hot::check_hot(c, Hot::CONN_ESTABLISHED);
if ( c$hot > 0 )
log_hot_conn(c);
}
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);
}
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");
record_connection(conn_file, c);
delete hot_conns_reported[c$id];
}

View file

@ -1,40 +0,0 @@
# $Id: contents.bro 47 2004-06-11 07:26:32Z vern $
redef capture_filters += { ["contents"] = "tcp" };
# Keeps track of to which given contents files we've written.
global contents_files: set[string];
event new_connection_contents(c: connection)
{
local id = c$id;
local orig_file =
fmt("contents.%s.%d-%s.%d",
id$orig_h, id$orig_p, id$resp_h, id$resp_p);
local resp_file =
fmt("contents.%s.%d-%s.%d",
id$resp_h, id$resp_p, id$orig_h, id$orig_p);
local orig_f: file;
local resp_f: file;
if ( orig_file !in contents_files )
{
add contents_files[orig_file];
orig_f = open(orig_file);
}
else
orig_f = open_for_append(orig_file);
if ( resp_file !in contents_files )
{
add contents_files[resp_file];
resp_f = open(resp_file);
}
else
resp_f = open_for_append(resp_file);
set_contents_file(id, CONTENTS_ORIG, orig_f);
set_contents_file(id, CONTENTS_RESP, resp_f);
}

View file

@ -1,62 +0,0 @@
# $Id: cpu-adapt.bro 1904 2005-12-14 03:27:15Z vern $
#
# Adjust load level based on cpu load.
@load load-level
# We increase the load-level if the average CPU load (percentage) is
# above this limit.
global cpu_upper_limit = 70.0 &redef;
# We derease the load-level if the average CPU load is below this limit.
global cpu_lower_limit = 30.0 &redef;
# Time interval over which we average the CPU load.
global cpu_interval = 1 min &redef;
global cpu_last_proc_time = 0 secs;
global cpu_last_wall_time: time = 0;
event cpu_measure_load()
{
local res = resource_usage();
local proc_time = res$user_time + res$system_time;
local wall_time = current_time();
if ( cpu_last_proc_time > 0 secs )
{
local dproc = proc_time - cpu_last_proc_time;
local dwall = wall_time - cpu_last_wall_time;
local load = dproc / dwall * 100.0;
print ll_file, fmt("%.6f CPU load %.02f", network_time(), load);
# Second test is for whether we have any room to change
# things. It shouldn't be hardwired to "xxx10" ....
if ( load > cpu_upper_limit &&
current_load_level != LoadLevel10 )
{
print ll_file, fmt("%.6f CPU load above limit: %.02f",
network_time(), load);
increase_load_level();
}
else if ( load < cpu_lower_limit &&
current_load_level != LoadLevel1 )
{
print ll_file, fmt("%.6f CPU load below limit: %.02f",
network_time(), load);
decrease_load_level();
}
}
cpu_last_proc_time = proc_time;
cpu_last_wall_time = wall_time;
schedule cpu_interval { cpu_measure_load() };
}
event bro_init()
{
schedule cpu_interval { cpu_measure_load() };
}

View file

@ -1,8 +0,0 @@
# $Id:$
redef capture_filters += { ["dce"] = "port 135" };
global dce_ports = { 135/tcp } &redef;
redef dpd_config += { [ANALYZER_DCE_RPC] = [$ports = dce_ports] };
# No default implementation for events.

View file

@ -1,41 +0,0 @@
# $Id: demux.bro 4758 2007-08-10 06:49:23Z vern $
const demux_dir = log_file_name("xscript") &redef;
global created_demux_dir = F;
# Table of which connections we're demuxing.
global demuxed_conn: set[conn_id];
# tag: identifier to use for the reason for demuxing
# otag: identifier to use for originator side of the connection
# rtag: identifier to use for responder side of the connection
function demux_conn(id: conn_id, tag: string, otag: string, rtag: string): bool
{
if ( id in demuxed_conn || ! active_connection(id) )
return F;
if ( ! created_demux_dir )
{
mkdir(demux_dir);
created_demux_dir = T;
}
local orig_file =
fmt("%s/%s.%s.%s.%d-%s.%d", demux_dir, otag, tag,
id$orig_h, id$orig_p, id$resp_h, id$resp_p);
local resp_file =
fmt("%s/%s.%s.%s.%d-%s.%d", demux_dir, rtag, tag,
id$resp_h, id$resp_p, id$orig_h, id$orig_p);
set_contents_file(id, CONTENTS_ORIG, open(orig_file));
set_contents_file(id, CONTENTS_RESP, open(resp_file));
add demuxed_conn[id];
return T;
}
event connection_finished(c: connection)
{
delete demuxed_conn[c$id];
}

View file

@ -1,156 +0,0 @@
# $Id: detect-protocols-http.bro,v 1.1.4.2 2006/05/31 00:16:21 sommer Exp $
#
# Identifies protocols that use HTTP.
@load detect-protocols
module DetectProtocolHTTP;
export {
# Defines characteristics of a protocol. All attributes must match
# to trigger the detection. We match patterns against lower-case
# versions of the data.
type protocol : record {
url: pattern &optional;
client_header: pattern &optional;
client_header_content: pattern &optional;
server_header: pattern &optional;
server_header_content: pattern &optional;
};
const protocols: table[string] of protocol = {
["Kazaa"] = [$url=/^\/\.hash=.*/, $server_header=/^x-kazaa.*/],
["Gnutella"] = [$url=/^\/(uri-res|gnutella).*/,
$server_header=/^x-gnutella-.*/],
["Gnutella_"] = [$url=/^\/(uri-res|gnutella).*/,
$server_header=/^x-(content-urn|features).*/],
["Gnutella__"] = [$url=/^\/(uri-res|gnutella).*/,
$server_header=/^content-type/,
$server_header_content=/.*x-gnutella.*/],
["BitTorrent"] = [$url=/^.*\/(scrape|announce)\?.*info_hash.*/],
["SOAP"] = [$client_header=/^([:print:]+-)?(soapaction|methodname|messagetype).*/],
["Squid"] = [$server_header=/^x-squid.*/],
} &redef;
}
# Bit masks.
const url_found = 1;
const client_header_found = 2;
const server_header_found = 2;
type index : record {
id: conn_id;
pid: string;
};
# Maps to characteristics found so far.
# FIXME: An integer would suffice for the bit-field
# if we had bit-operations ...
global conns: table[index] of set[count] &read_expire = 1hrs;
function check_match(c: connection, pid: string, mask: set[count])
{
conns[[$id=c$id, $pid=pid]] = mask;
local p = protocols[pid];
if ( p?$url && url_found !in mask )
return;
if ( p?$client_header && client_header_found !in mask )
return;
if ( p?$server_header && server_header_found !in mask )
return;
# All found.
ProtocolDetector::found_protocol(c, ANALYZER_HTTP, pid);
}
event http_request(c: connection, method: string, original_URI: string,
unescaped_URI: string, version: string)
{
for ( pid in protocols )
{
local p = protocols[pid];
if ( ! p?$url )
next;
local mask: set[count];
local idx = [$id=c$id, $pid=pid];
if ( idx in conns )
mask = conns[idx];
if ( url_found in mask )
# Already found a match.
next;
# FIXME: There are people putting NULs into the URLs
# (BitTorrent), which to_lower() does not like. Not sure
# what the right fix is, though.
unescaped_URI = subst_string(unescaped_URI, "\x00", "");
if ( to_lower(unescaped_URI) == p$url )
{
add mask[url_found];
check_match(c, pid, mask);
}
}
}
event http_header(c: connection, is_orig: bool, name: string, value: string)
{
if ( name == /[sS][eE][rR][vV][eE][rR]/ )
{
# Try to extract the server software.
local s = split1(strip(value), /[[:space:]\/]/);
if ( s[1] == /[-a-zA-Z0-9_]+/ )
ProtocolDetector::found_protocol(c, ANALYZER_HTTP, s[1]);
}
for ( pid in protocols )
{
local p = protocols[pid];
local mask: set[count];
local idx = [$id=c$id, $pid=pid];
if ( idx in conns )
mask = conns[idx];
if ( p?$client_header && is_orig )
{
if ( client_header_found in mask )
return;
if ( to_lower(name) == p$client_header )
{
if ( p?$client_header_content )
if ( to_lower(value) !=
p$client_header_content )
return;
add mask[client_header_found];
check_match(c, pid, mask);
}
}
if ( p?$server_header && ! is_orig )
{
if ( server_header_found in mask )
return;
if ( to_lower(name) == p$server_header )
{
if ( p?$server_header_content )
if ( to_lower(value) !=
p$server_header_content )
return;
add mask[server_header_found];
check_match(c, pid, mask);
}
}
}
}

View file

@ -1,258 +0,0 @@
# $Id: detect-protocols.bro,v 1.1.4.4 2006/05/31 18:07:27 sommer Exp $
#
# Finds connections with protocols on non-standard ports using the DPM
# framework.
@load site
@load conn-id
@load notice
module ProtocolDetector;
export {
redef enum Notice += {
ProtocolFound, # raised for each connection found
ServerFound, # raised once per dst host/port/protocol tuple
};
# Table of (protocol, resp_h, resp_p) tuples known to be uninteresting
# in the given direction. For all other protocols detected on
# non-standard ports, we raise a ProtocolFound notice. (More specific
# filtering can then be done via notice_filters.)
#
# Use 0.0.0.0 for to wildcard-match any resp_h.
type dir: enum { NONE, INCOMING, OUTGOING, BOTH };
const valids: table[count, addr, port] of dir = {
# A couple of ports commonly used for benign HTTP servers.
# For now we want to see everything.
# [ANALYZER_HTTP, 0.0.0.0, 81/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 82/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 83/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 88/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 8001/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 8090/tcp] = OUTGOING,
# [ANALYZER_HTTP, 0.0.0.0, 8081/tcp] = OUTGOING,
#
# [ANALYZER_HTTP, 0.0.0.0, 6346/tcp] = BOTH, # Gnutella
# [ANALYZER_HTTP, 0.0.0.0, 6347/tcp] = BOTH, # Gnutella
# [ANALYZER_HTTP, 0.0.0.0, 6348/tcp] = BOTH, # Gnutella
} &redef;
# Set of analyzers for which we suppress ServerFound notices
# (but not ProtocolFound). Along with avoiding clutter in the
# log files, this also saves memory because for these we don't
# need to remember which servers we already have reported, which
# for some can be a lot.
const suppress_servers: set [count] = {
# ANALYZER_HTTP
} &redef;
# We consider a connection to use a protocol X if the analyzer for X
# is still active (i) after an interval of minimum_duration, or (ii)
# after a payload volume of minimum_volume, or (iii) at the end of the
# connection.
const minimum_duration = 30 secs &redef;
const minimum_volume = 4e3 &redef; # bytes
# How often to check the size of the connection.
const check_interval = 5 secs;
# Entry point for other analyzers to report that they recognized
# a certain (sub-)protocol.
global found_protocol: function(c: connection, analyzer: count,
protocol: string);
# Table keeping reported (server, port, analyzer) tuples (and their
# reported sub-protocols).
global servers: table[addr, port, string] of set[string]
&read_expire = 14 days;
}
# Table that tracks currently active dynamic analyzers per connection.
global conns: table[conn_id] of set[count];
# Table of reports by other analyzers about the protocol used in a connection.
global protocols: table[conn_id] of set[string];
type protocol : record {
a: string; # analyzer name
sub: string; # "sub-protocols" reported by other sources
};
function get_protocol(c: connection, a: count) : protocol
{
local str = "";
if ( c$id in protocols )
{
for ( p in protocols[c$id] )
str = |str| > 0 ? fmt("%s/%s", str, p) : p;
}
return [$a=analyzer_name(a), $sub=str];
}
function fmt_protocol(p: protocol) : string
{
return p$sub != "" ? fmt("%s (via %s)", p$sub, p$a) : p$a;
}
function do_notice(c: connection, a: count, d: dir)
{
if ( d == BOTH )
return;
if ( d == INCOMING && is_local_addr(c$id$resp_h) )
return;
if ( d == OUTGOING && ! is_local_addr(c$id$resp_h) )
return;
local p = get_protocol(c, a);
local s = fmt_protocol(p);
NOTICE([$note=ProtocolFound,
$msg=fmt("%s %s on port %s", id_string(c$id), s, c$id$resp_p),
$sub=s, $conn=c, $n=a]);
# We report multiple ServerFound's per host if we find a new
# sub-protocol.
local known = [c$id$resp_h, c$id$resp_p, p$a] in servers;
local newsub = F;
if ( known )
newsub = (p$sub != "" &&
p$sub !in servers[c$id$resp_h, c$id$resp_p, p$a]);
if ( (! known || newsub) && a !in suppress_servers )
{
NOTICE([$note=ServerFound,
$msg=fmt("%s: %s server on port %s%s", c$id$resp_h, s,
c$id$resp_p, (known ? " (update)" : "")),
$p=c$id$resp_p, $sub=s, $conn=c, $src=c$id$resp_h, $n=a]);
if ( ! known )
servers[c$id$resp_h, c$id$resp_p, p$a] = set();
add servers[c$id$resp_h, c$id$resp_p, p$a][p$sub];
}
}
function report_protocols(c: connection)
{
# We only report the connection if both sides have transferred data.
if ( c$resp$size == 0 || c$orig$size == 0 )
{
delete conns[c$id];
delete protocols[c$id];
return;
}
local analyzers = conns[c$id];
for ( a in analyzers )
{
if ( [a, c$id$resp_h, c$id$resp_p] in valids )
do_notice(c, a, valids[a, c$id$resp_h, c$id$resp_p]);
else if ( [a, 0.0.0.0, c$id$resp_p] in valids )
do_notice(c, a, valids[a, 0.0.0.0, c$id$resp_p]);
else
do_notice(c, a, NONE);
append_addl(c, analyzer_name(a));
}
delete conns[c$id];
delete protocols[c$id];
}
event ProtocolDetector::check_connection(c: connection)
{
if ( c$id !in conns )
return;
local duration = network_time() - c$start_time;
local size = c$resp$size + c$orig$size;
if ( duration >= minimum_duration || size >= minimum_volume )
report_protocols(c);
else
{
local delay = min_interval(minimum_duration - duration,
check_interval);
schedule delay { ProtocolDetector::check_connection(c) };
}
}
event connection_state_remove(c: connection)
{
if ( c$id !in conns )
{
delete protocols[c$id];
return;
}
# Reports all analyzers that have remained to the end.
report_protocols(c);
}
event protocol_confirmation(c: connection, atype: count, aid: count)
{
# Don't report anything running on a well-known port.
if ( atype in dpd_config && c$id$resp_p in dpd_config[atype]$ports )
return;
if ( c$id in conns )
{
local analyzers = conns[c$id];
add analyzers[atype];
}
else
{
conns[c$id] = set(atype);
local delay = min_interval(minimum_duration, check_interval);
schedule delay { ProtocolDetector::check_connection(c) };
}
}
# event connection_analyzer_disabled(c: connection, analyzer: count)
# {
# if ( c$id !in conns )
# return;
#
# delete conns[c$id][analyzer];
# }
function append_proto_addl(c: connection)
{
for ( a in conns[c$id] )
append_addl(c, fmt_protocol(get_protocol(c, a)));
}
function found_protocol(c: connection, analyzer: count, protocol: string)
{
# Don't report anything running on a well-known port.
if ( analyzer in dpd_config &&
c$id$resp_p in dpd_config[analyzer]$ports )
return;
if ( c$id !in protocols )
protocols[c$id] = set();
add protocols[c$id][protocol];
}
event connection_state_remove(c: connection)
{
if ( c$id !in conns )
return;
append_proto_addl(c);
}

View file

@ -1,525 +0,0 @@
# $Id: dhcp.bro 4054 2007-08-14 21:45:58Z pclin $
@load dpd
@load weird
module DHCP;
export {
# Set to false to disable printing to dhcp.log.
const logging = T &redef;
}
# Type of states in DHCP client. See Figure 5 in RFC 2131.
# Each state name is prefixed with DHCP_ to avoid name conflicts.
type dhcp_state: enum {
DHCP_INIT_REBOOT,
DHCP_INIT,
DHCP_SELECTING,
DHCP_REQUESTING,
DHCP_REBINDING,
DHCP_BOUND,
DHCP_RENEWING,
DHCP_REBOOTING,
# This state is not in Figure 5. Client has been externally configured.
DHCP_INFORM,
};
global dhcp_log: file;
# Source port 68: client -> server; source port 67: server -> client.
global dhcp_ports: set[port] = { 67/udp, 68/udp } &redef;
redef dpd_config += { [ANALYZER_DHCP_BINPAC] = [$ports = dhcp_ports] };
# Default handling for peculiarities in DHCP analysis.
redef Weird::weird_action += {
["DHCP_no_type_option"] = Weird::WEIRD_FILE,
["DHCP_wrong_op_type"] = Weird::WEIRD_FILE,
["DHCP_wrong_msg_type"] = Weird::WEIRD_FILE,
};
# Types of DHCP messages, identified from the 'options' field. See RFC 1533.
global dhcp_msgtype_name: table[count] of string = {
[1] = "DHCP_DISCOVER",
[2] = "DHCP_OFFER",
[3] = "DHCP_REQUEST",
[4] = "DHCP_DECLINE",
[5] = "DHCP_ACK",
[6] = "DHCP_NAK",
[7] = "DHCP_RELEASE",
[8] = "DHCP_INFORM",
};
# Type of DHCP client state, inferred from the messages. See RFC 2131, fig 5.
global dhcp_state_name: table[dhcp_state] of string = {
[DHCP_INIT_REBOOT] = "INIT-REBOOT",
[DHCP_INIT] = "INIT",
[DHCP_SELECTING] = "SELECTING",
[DHCP_REQUESTING] = "REQUESTING",
[DHCP_REBINDING] = "REBINDING",
[DHCP_BOUND] = "BOUND",
[DHCP_RENEWING] = "RENEWING",
[DHCP_REBOOTING] = "REBOOTING",
[DHCP_INFORM] = "INFORM",
};
type dhcp_session_info: record {
state: dhcp_state; # the state of a DHCP client
seq: count; # sequence of session in the trace
lease: interval; # lease time of an IP address
h_addr: string; # hardware/MAC address of the client
};
# Track the DHCP session info of each client, indexed by the transaction ID.
global dhcp_session: table[count] of dhcp_session_info
&default = record($state = DHCP_INIT_REBOOT, $seq = 0, $lease = 0 sec,
$h_addr = "")
&write_expire = 5 min
;
# We need the following table to track some DHCPINFORM messages since they
# use xid = 0 (I do not know why), starting from the second pair of INFORM
# and ACK. Since the client address is ready before DHCPINFORM, we can use
# it as the index to find its corresponding xid.
global session_xid: table[addr] of count &read_expire = 30 sec;
# Count how many DHCP sessions have been detected, for use in dhcp_session_seq.
global pkt_cnt: count = 0;
global session_cnt: count = 0;
# Record the address of client that sends a DHCPINFORM message with xid = 0.
global recent_client: addr;
global BROADCAST_ADDR = 255.255.255.255;
global NULL_ADDR = 0.0.0.0;
# Used to detect if an ACK is duplicated. They are used only in dhcp_ack().
# We put them here since Bro scripts lacks the equivalent of "static" variables.
global ack_from: addr;
global duplicated_ack: bool;
function warning_wrong_state(msg_type: count): string
{
return fmt("%s not sent in a correct state.",
dhcp_msgtype_name[msg_type]);
}
function dhcp_message(c: connection, seq: count, show_conn: bool): string
{
local conn_info = fmt("%.06f #%d", network_time(), seq);
if ( show_conn )
return fmt("%s %s > %s", conn_info,
endpoint_id(c$id$orig_h, c$id$orig_p),
endpoint_id(c$id$resp_h, c$id$resp_p));
return conn_info;
}
function new_dhcp_session(xid: count, state: dhcp_state, h_addr: string)
: dhcp_session_info
{
local session: dhcp_session_info;
session$state = state;
session$seq = ++session_cnt;
session$lease = 0 sec;
session$h_addr = h_addr;
dhcp_session[xid] = session;
return session;
}
event bro_init()
{
if ( logging )
dhcp_log = open_log_file("dhcp");
}
event dhcp_discover(c: connection, msg: dhcp_msg, req_addr: addr)
{
local old_session = T;
if ( msg$xid !in dhcp_session )
{
local session =
new_dhcp_session(msg$xid, DHCP_SELECTING, msg$h_addr);
old_session = F;
}
if ( logging )
{
if ( old_session &&
dhcp_session[msg$xid]$state == DHCP_SELECTING )
print dhcp_log, fmt("%s DISCOVER (duplicated)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F));
else
print dhcp_log,
fmt("%s DISCOVER (xid = %x, client state = %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, T),
msg$xid, dhcp_state_name[dhcp_session[msg$xid]$state]);
}
}
event dhcp_offer(c: connection, msg: dhcp_msg, mask: addr,
router: dhcp_router_list, lease: interval, serv_addr: addr)
{
local standalone = msg$xid !in dhcp_session;
local err_state =
standalone && dhcp_session[msg$xid]$state != DHCP_SELECTING;
if ( logging )
{
# Note that no OFFER messages are considered duplicated,
# since they may come from multiple DHCP servers in a session.
if ( standalone )
print dhcp_log, fmt("%s OFFER (standalone)",
dhcp_message(c, ++session_cnt, T));
else if ( err_state )
print dhcp_log, fmt("%s OFFER (in error state %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, T),
dhcp_state_name[dhcp_session[msg$xid]$state]);
else
print dhcp_log, fmt("%s OFFER (client state = %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, T),
dhcp_state_name[DHCP_SELECTING]);
}
}
event dhcp_request(c: connection, msg: dhcp_msg,
req_addr: addr, serv_addr: addr)
{
local log_info: string;
if ( msg$xid in dhcp_session )
{
if ( ! logging )
return;
local state = dhcp_session[msg$xid]$state;
if ( state == DHCP_REBOOTING )
recent_client = req_addr;
else
recent_client = c$id$orig_h;
session_xid[recent_client] = msg$xid;
if ( state == DHCP_RENEWING || state == DHCP_REBINDING ||
state == DHCP_REQUESTING || state == DHCP_REBOOTING )
print dhcp_log, fmt("%s REQUEST (duplicated)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F));
else
{
log_info = dhcp_message(c, dhcp_session[msg$xid]$seq, T);
print dhcp_log, fmt("%s REQUEST (in error state %s)",
log_info,
dhcp_state_name[dhcp_session[msg$xid]$state]);
}
}
else
{
local d_state = DHCP_REBOOTING;
if ( c$id$resp_h != BROADCAST_ADDR )
d_state = DHCP_RENEWING;
else if ( msg$ciaddr != NULL_ADDR )
d_state = DHCP_REBINDING;
else if ( serv_addr != NULL_ADDR )
d_state = DHCP_REQUESTING;
local session = new_dhcp_session(msg$xid, d_state, msg$h_addr);
if ( session$state == DHCP_REBOOTING )
recent_client = req_addr;
else
recent_client = c$id$orig_h;
session_xid[recent_client] = msg$xid;
if ( logging )
{
log_info = dhcp_message(c, session$seq, T);
if ( req_addr != NULL_ADDR )
log_info = fmt("%s REQUEST %As",
log_info, req_addr);
else
log_info = fmt("%s REQUEST", log_info);
print dhcp_log, fmt("%s (xid = %x, client state = %s)",
log_info, msg$xid,
dhcp_state_name[session$state]);
}
}
}
event dhcp_decline(c: connection, msg: dhcp_msg)
{
local old_session = msg$xid in dhcp_session;
local err_state = F;
if ( old_session )
{
if ( dhcp_session[msg$xid]$state == DHCP_REQUESTING )
dhcp_session[msg$xid]$state = DHCP_INIT;
else
err_state = T;
}
else
new_dhcp_session(msg$xid, DHCP_INIT, "");
if ( ! logging )
return;
if ( old_session )
{
if ( err_state )
print dhcp_log, fmt("%s DECLINE (in error state %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, T),
dhcp_state_name[dhcp_session[msg$xid]$state]);
else
print dhcp_log, fmt("%s DECLINE (duplicated)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F));
}
else
print dhcp_log, fmt("%s DECLINE (xid = %x)",
dhcp_message(c, ++session_cnt, T), msg$xid);
}
event dhcp_ack(c: connection, msg: dhcp_msg, mask: addr,
router: dhcp_router_list, lease: interval, serv_addr: addr)
{
local log_info: string;
if ( msg$xid == 0 )
{ # An ACK for a DHCPINFORM message with xid = 0.
local xid =
c$id$orig_h in session_xid ?
# An ACK to the client.
session_xid[c$id$orig_h]
:
# Assume ACK from a relay agent to the server.
session_xid[recent_client];
local seq: count;
if ( xid > 0 )
{
duplicated_ack = dhcp_session[xid]$state != DHCP_INFORM;
dhcp_session[xid]$state = DHCP_BOUND;
seq = dhcp_session[xid]$seq;
}
else
{
# This is a weird situation. We arbitrarily set
# duplicated_ack to false to have more information
# shown.
duplicated_ack = F;
seq = session_cnt;
}
if ( ! logging )
return;
log_info = dhcp_message(c, seq, F);
if ( c$id$orig_h in session_xid )
{
if ( duplicated_ack )
print dhcp_log, fmt("%s ACK (duplicated)",
log_info);
else
print dhcp_log,
fmt("%s ACK (client state = %s)",
log_info,
dhcp_state_name[DHCP_BOUND]);
}
else
print dhcp_log,
fmt("%s ACK (relay agent at = %As)",
log_info, c$id$orig_h);
return;
}
if ( msg$xid in dhcp_session )
{
local last_state = dhcp_session[msg$xid]$state;
local from_reboot_state = last_state == DHCP_REBOOTING;
if ( last_state == DHCP_REQUESTING ||
last_state == DHCP_REBOOTING ||
last_state == DHCP_RENEWING ||
last_state == DHCP_REBINDING ||
last_state == DHCP_INFORM )
{
dhcp_session[msg$xid]$state = DHCP_BOUND;
dhcp_session[msg$xid]$lease = lease;
}
if ( ! logging )
return;
if ( last_state == DHCP_BOUND )
{
log_info = dhcp_message(c, dhcp_session[msg$xid]$seq, F);
if ( c$id$orig_h == ack_from )
log_info = fmt("%s ACK (duplicated)",
log_info);
else
# Not a duplicated ACK.
log_info = fmt("%s ACK (relay agent at = %As)",
log_info, c$id$orig_h);
}
else
{
ack_from = c$id$orig_h;
# If in a reboot state, we had better
# explicitly show the original address
# and the destination address of ACK,
# because the client initally has a
# zero address.
if ( from_reboot_state )
log_info = dhcp_message(c, dhcp_session[msg$xid]$seq, T);
else
log_info = dhcp_message(c, dhcp_session[msg$xid]$seq, F);
if ( last_state != DHCP_INFORM &&
lease > 0 sec )
log_info = fmt("%s ACK (lease time = %s, ",
log_info, lease);
else
log_info = fmt("%s ACK (", log_info);
log_info = fmt("%sclient state = %s)",
log_info,
dhcp_state_name[dhcp_session[msg$xid]$state]);
}
print dhcp_log, log_info;
}
else if ( logging )
print dhcp_log, fmt("%s ACK (standalone)",
dhcp_message(c, ++session_cnt, T));
}
event dhcp_nak(c: connection, msg: dhcp_msg)
{
if ( msg$xid in dhcp_session )
{
local last_state = dhcp_session[msg$xid]$state;
if ( last_state == DHCP_REQUESTING ||
last_state == DHCP_REBOOTING ||
last_state == DHCP_RENEWING ||
last_state == DHCP_REBINDING )
dhcp_session[msg$xid]$state = DHCP_INIT;
if ( logging )
print dhcp_log, fmt("%s NAK (client state = %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F),
dhcp_state_name[dhcp_session[msg$xid]$state]);
}
else if ( logging )
print dhcp_log, fmt("%s NAK (standalone)",
dhcp_message(c, ++session_cnt, T));
}
event dhcp_release(c: connection, msg: dhcp_msg)
{
local old_session = msg$xid in dhcp_session;
if ( ! old_session )
# We assume the client goes back to DHCP_INIT
# because the RFC does not specify which state to go to.
new_dhcp_session(msg$xid, DHCP_INIT, "");
if ( ! logging )
return;
if ( old_session )
{
if ( dhcp_session[msg$xid]$state == DHCP_INIT )
print dhcp_log, fmt("%s RELEASE (duplicated)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F));
else
print dhcp_log, fmt("%s RELEASE, (client state = %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F),
dhcp_state_name[dhcp_session[msg$xid]$state]);
}
else
print dhcp_log, fmt("%s RELEASE (xid = %x, IP addr = %As)",
dhcp_message(c, session_cnt, T), msg$xid, c$id$orig_h);
}
event dhcp_inform(c: connection, msg: dhcp_msg)
{
recent_client = c$id$orig_h;
if ( msg$xid == 0 )
{
# Oops! Try to associate message with transaction ID 0 with
# a previous session.
local xid: count;
local seq: count;
if ( c$id$orig_h in session_xid )
{
xid = session_xid[c$id$orig_h];
dhcp_session[xid]$state = DHCP_INFORM;
seq = dhcp_session[xid]$seq;
}
else
{
# Weird: xid = 0 and no previous INFORM-ACK dialog.
xid = 0;
seq = ++session_cnt;
# Just record that a INFORM message has appeared,
# although the xid is not useful.
session_xid[c$id$orig_h] = 0;
}
if ( logging )
print dhcp_log,
fmt("%s INFORM (xid = %x, client state = %s)",
dhcp_message(c, seq, T),
xid, dhcp_state_name[DHCP_INFORM]);
return;
}
if ( msg$xid in dhcp_session )
{
if ( logging )
if ( dhcp_session[msg$xid]$state == DHCP_INFORM )
print dhcp_log, fmt("%s INFORM (duplicated)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F));
else {
print dhcp_log,
fmt("%s INFORM (duplicated, client state = %s)",
dhcp_message(c, dhcp_session[msg$xid]$seq, F),
dhcp_state_name[dhcp_session[msg$xid]$state]);
}
return;
}
local session = new_dhcp_session(msg$xid, DHCP_INFORM, msg$h_addr);
# Associate this transaction ID with the host so we can identify
# subsequent pairs of INFORM/ACK if client uses xid=0.
session_xid[c$id$orig_h] = msg$xid;
if ( logging )
print dhcp_log, fmt("%s INFORM (xid = %x, client state = %s)",
dhcp_message(c, session$seq, T),
msg$xid, dhcp_state_name[session$state]);
}

View file

@ -1,81 +0,0 @@
# $Id: dns-info.bro 3919 2007-01-14 00:27:09Z vern $
# Types, errors, and fields for analyzing DNS data. A helper file
# for dns.bro.
const PTR = 12;
const EDNS = 41;
const ANY = 255;
const query_types = {
[1] = "A", [2] = "NS", [3] = "MD", [4] = "MF",
[5] = "CNAME", [6] = "SOA", [7] = "MB", [8] = "MG",
[9] = "MR", [10] = "NULL", [11] = "WKS", [PTR] = "PTR",
[13] = "HINFO", [14] = "MINFO", [15] = "MX", [16] = "TXT",
[17] = "RP", [18] = "AFSDB", [19] = "X25", [20] = "ISDN",
[21] = "RT", [22] = "NSAP", [23] = "NSAP-PTR", [24] = "SIG",
[25] = "KEY", [26] = "PX" , [27] = "GPOS", [28] = "AAAA",
[29] = "LOC", [30] = "EID", [31] = "NIMLOC", [32] = "NB",
[33] = "SRV", [34] = "ATMA", [35] = "NAPTR", [36] = "KX",
[37] = "CERT", [38] = "A6", [39] = "DNAME", [40] = "SINK",
[EDNS] = "EDNS", [42] = "APL", [43] = "DS", [44] = "SINK",
[45] = "SSHFP", [46] = "RRSIG", [47] = "NSEC", [48] = "DNSKEY",
[49] = "DHCID", [99] = "SPF", [100] = "DINFO", [101] = "UID",
[102] = "GID", [103] = "UNSPEC", [249] = "TKEY", [250] = "TSIG",
[251] = "IXFR", [252] = "AXFR", [253] = "MAILB", [254] = "MAILA",
[32768] = "TA", [32769] = "DLV",
[ANY] = "*",
} &default = function(n: count): string { return fmt("query-%d", n); };
const DNS_code_types = {
[0] = "X0",
[1] = "Xfmt",
[2] = "Xsrv",
[3] = "Xnam",
[4] = "Ximp",
[5] = "X[",
} &default = function(n: count): string { return "?"; };
# Used for non-TSIG/EDNS types.
const base_error = {
[0] = "NOERROR", # No Error
[1] = "FORMERR", # Format Error
[2] = "SERVFAIL", # Server Failure
[3] = "NXDOMAIN", # Non-Existent Domain
[4] = "NOTIMP", # Not Implemented
[5] = "REFUSED", # Query Refused
[6] = "YXDOMAIN", # Name Exists when it should not
[7] = "YXRRSET", # RR Set Exists when it should not
[8] = "NXRRSet", # RR Set that should exist does not
[9] = "NOTAUTH", # Server Not Authoritative for zone
[10] = "NOTZONE", # Name not contained in zone
[11] = "unassigned-11", # available for assignment
[12] = "unassigned-12", # available for assignment
[13] = "unassigned-13", # available for assignment
[14] = "unassigned-14", # available for assignment
[15] = "unassigned-15", # available for assignment
[16] = "BADVERS", # for EDNS, collision w/ TSIG
[17] = "BADKEY", # Key not recognized
[18] = "BADTIME", # Signature out of time window
[19] = "BADMODE", # Bad TKEY Mode
[20] = "BADNAME", # Duplicate key name
[21] = "BADALG", # Algorithm not supported
[22] = "BADTRUNC", # draft-ietf-dnsext-tsig-sha-05.txt
[3842] = "BADSIG", # 16 <= number collision with EDNS(16);
# this is a translation from TSIG(16)
} &default = function(n: count): string { return "?"; };
# This deciphers EDNS Z field values.
const edns_zfield = {
[0] = "NOVALUE", # regular entry
[32768] = "DNS_SEC_OK", # accepts DNS Sec RRs
} &default = function(n: count): string { return "?"; };
const dns_class = {
[1] = "C_INTERNET",
[2] = "C_CSNET",
[3] = "C_CHAOS",
[4] = "C_HESOD",
[254] = "C_NONE",
[255] = "C_ANY",
} &default = function(n: count): string { return "?"; };

View file

@ -1,65 +0,0 @@
# $Id: dns-lookup.bro 340 2004-09-09 06:38:27Z vern $
@load notice
redef enum Notice += {
DNS_MappingChanged, # some sort of change WRT previous Bro lookup
};
const dns_interesting_changes = {
"unverified", "old name", "new name", "mapping",
} &redef;
function dump_dns_mapping(msg: string, dm: dns_mapping): bool
{
if ( msg in dns_interesting_changes ||
127.0.0.1 in dm$addrs )
{
local req = dm$req_host == "" ?
fmt("%As", dm$req_addr) : dm$req_host;
NOTICE([$note=DNS_MappingChanged,
$msg=fmt("DNS %s: %s/%s %s-> %As", msg, req,
dm$hostname, dm$valid ?
"" : "(invalid) ", dm$addrs),
$sub=msg]);
return T;
}
else
return F;
}
event dns_mapping_valid(dm: dns_mapping)
{
dump_dns_mapping("valid", dm);
}
event dns_mapping_unverified(dm: dns_mapping)
{
dump_dns_mapping("unverified", dm);
}
event dns_mapping_new_name(dm: dns_mapping)
{
dump_dns_mapping("new name", dm);
}
event dns_mapping_lost_name(dm: dns_mapping)
{
dump_dns_mapping("lost name", dm);
}
event dns_mapping_name_changed(old_dm: dns_mapping, new_dm: dns_mapping)
{
if ( dump_dns_mapping("old name", old_dm) )
dump_dns_mapping("new name", new_dm);
}
event dns_mapping_altered(dm: dns_mapping,
old_addrs: set[addr], new_addrs: set[addr])
{
if ( dump_dns_mapping("mapping", dm) )
NOTICE([$note=DNS_MappingChanged,
$msg=fmt("changed addresses: %As -> %As", old_addrs, new_addrs),
$sub="changed addresses"]);
}

View file

@ -1,675 +0,0 @@
# $Id: dns.bro 6724 2009-06-07 09:23:03Z vern $
@load notice
@load weird
@load udp-common
@load dns-info
module DNS;
export {
# Lookups of hosts in here are flagged ...
const sensitive_lookup_hosts: set[addr] &redef;
# ... unless the lookup comes from one of these hosts.
const okay_to_lookup_sensitive_hosts: set[addr] &redef;
# Start considering whether we're seeing PTR scanning if we've seen
# at least this many rejected PTR queries.
const report_rejected_PTR_thresh = 100 &redef;
# Generate a PTR_scan event if at any point (once we're above
# report_rejected_PTR_thresh) we see this many more distinct
# rejected PTR requests than distinct answered PTR requests.
const report_rejected_PTR_factor = 2.0 &redef;
# The following sources are allowed to do PTR scanning.
const allow_PTR_scans: set[addr] &redef;
# Annotations that if returned for a PTR lookup actually indicate
# a rejected query; for example, "illegal-address.lbl.gov".
const actually_rejected_PTR_anno: set[string] &redef;
# Hosts allowed to do zone transfers.
const zone_transfers_okay: set[addr] &redef;
# Set to false to disable printing to dns.log.
const logging = T &redef;
redef enum Notice += {
SensitiveDNS_Lookup, # DNS lookup of sensitive hostname/addr
DNS_PTR_Scan, # A set of PTR lookups
DNS_PTR_Scan_Summary, # Summary of a set of PTR lookups
ResolverInconsistency, # DNS answer changed
ZoneTransfer, # a DNS zone transfer request was seen
};
# This is a list of domains that have a history of providing
# more RR's in response than they are supposed to. There is
# some danger here in that record inconsistancies will not be
# identified for these domains...
const bad_domain_resp: set[string] &redef;
# Same idea, except that it applies to a list of host names.
const bad_host_resp: set[string] &redef;
# Turn resolver consistancy checking on/off.
const resolver_consist_check = F &redef;
# Should queries be checked against 'bad' domains?
const check_domain_list = T;
# List of 'bad' domains.
const hostile_domain_list: set[string] &redef;
# Used for PTR scan detection. Exported so their timeouts can be
# adjusted.
global distinct_PTR_requests:
table[addr, string] of count &default = 0 &write_expire = 5 min;
global distinct_rejected_PTR_requests:
table[addr] of count &default = 0 &write_expire = 5 min;
global distinct_answered_PTR_requests:
table[addr] of count &default = 0 &write_expire = 5 min;
}
redef capture_filters += {
["dns"] = "port 53",
["netbios-ns"] = "udp port 137",
};
# DPM configuration.
global dns_ports = { 53/udp, 53/tcp, 137/udp } &redef;
redef dpd_config += { [ANALYZER_DNS] = [$ports = dns_ports] };
global dns_udp_ports = { 53/udp, 137/udp } &redef;
global dns_tcp_ports = { 53/tcp } &redef;
redef dpd_config += { [ANALYZER_DNS_UDP_BINPAC] = [$ports = dns_udp_ports] };
redef dpd_config += { [ANALYZER_DNS_TCP_BINPAC] = [$ports = dns_tcp_ports] };
# Default handling for peculiarities in DNS analysis. You can redef these
# again in your site-specific script if you want different behavior.
redef Weird::weird_action += {
["DNS_AAAA_neg_length"] = Weird::WEIRD_FILE,
["DNS_Conn_count_too_large"] = Weird::WEIRD_FILE,
["DNS_NAME_too_long"] = Weird::WEIRD_FILE,
["DNS_RR_bad_length"] = Weird::WEIRD_FILE,
["DNS_RR_length_mismatch"] = Weird::WEIRD_FILE,
["DNS_RR_unknown_type"] = Weird::WEIRD_FILE,
["DNS_label_forward_compress_offset"] = Weird::WEIRD_FILE,
["DNS_label_len_gt_name_len"] = Weird::WEIRD_FILE,
["DNS_label_len_gt_pkt"] = Weird::WEIRD_FILE,
["DNS_label_too_long"] = Weird::WEIRD_FILE,
["DNS_name_too_long"] = Weird::WEIRD_FILE,
["DNS_truncated_RR_rdlength_lt_len"] = Weird::WEIRD_FILE,
["DNS_truncated_ans_too_short"] = Weird::WEIRD_FILE,
["DNS_truncated_len_lt_hdr_len"] = Weird::WEIRD_FILE,
["DNS_truncated_quest_too_short"] = Weird::WEIRD_FILE,
};
type dns_session_info: record {
id: count;
is_zone_transfer: bool;
last_active: time; # when we last saw activity
# Indexed by query id, returns string annotation corresponding to
# queries for which no answer seen yet.
pending_queries: table[count] of string;
};
# Indexed by client and server.
global dns_sessions: table[addr, addr, count] of dns_session_info;
global num_dns_sessions = 0;
const PTR_pattern = /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.in-addr\.arpa/;
# Keeps track of for which addresses we processed a PTR_scan event.
global did_PTR_scan_event: table[addr] of count &default = 0;
# The following definitions relate to tracking when DNS records
# change and whether they do so in a consistent fashion.
type dns_response_record: record {
dns_name: string; # domain name in question
dns_type: count; # type of query
num_resp: count; # number of responses
resp_count: count; # how many responses have been registered
addrs: set[addr]; # addresses in response
};
global dns_history: table[string, count, count] of dns_response_record;
global did_zone_transfer_notice: table[addr] of count &default = 0;
# Sample known irregular domains.
redef bad_domain_resp += { "instacontent.net", "mirror-image.net", };
# Sample hostile domains.
redef hostile_domain_list += { "undernet.org", "afraid.org", };
global dns_log : file;
event bro_init()
{
if ( logging )
dns_log = open_log_file("dns");
}
event remove_name(name: string, qtype: count, id: count)
{
if ( [name, qtype, id] in dns_history )
{
# We need to remove the dns_history record and the assosciated
# dns_consistency_info records.
local drr = dns_history[name, qtype, id];
local a: addr;
for ( a in drr$addrs )
delete drr$addrs[a];
delete dns_history[name, qtype, id];
}
else if ( logging )
print dns_log, fmt("ERROR in history session removal: %s/%d doesn't exist", name, qtype);
}
# Returns the second-level domain, so for example an argument of "a.b.c.d"
# returns "c.d".
function second_level_domain(name: string): string
{
local split_on_dots = split(name, /\./);
local num_dots = length(split_on_dots);
if ( num_dots <= 1 )
return name;
return fmt("%s.%s", split_on_dots[num_dots-1], split_on_dots[num_dots]);
}
function insert_name(c: connection, msg: dns_msg, ans: dns_answer, a: addr)
{
local drr: dns_response_record;
if ( [ans$query, ans$qtype, msg$id] !in dns_history )
{ # add record
drr$dns_name = ans$query;
drr$dns_type = ans$qtype;
# Here we modified the expected number of addresses to allow
# for the number of answer RR's along with the provided
# additional RR's.
drr$num_resp = msg$num_answers+msg$num_addl;
drr$resp_count = 0;
add drr$addrs[a];
dns_history[ans$query, ans$qtype, msg$id] = drr;
if ( ans$TTL < 0 sec )
# Strangely enough, the spec allows this,
# though it's hard to see why! But because
# of that, we don't generate a Weird, we
# just change the TTL to 0.
ans$TTL = 0 sec;
# Check the TTL, but allow a smidgen of skew to avoid
# possible race conditions.
schedule ans$TTL + 1 sec
{ remove_name(ans$query, ans$qtype, msg$id) };
}
else
{ # extract record and do some counting
drr = dns_history[ans$query, ans$qtype, msg$id];
# In some broken records, the number of reported records is 0.
# This makes the test below fail, to 'fix' set to 1 ...
if ( drr$num_resp == 0 )
drr$num_resp = 1;
# Check if we have filled in the expected number of responses
# already - it should be > current responder count to allow
# for resolver timeouts. Addresses are only added if they
# are not already prsent. This comes at a slight performance
# cost.
if ( a !in drr$addrs )
{
add drr$addrs[a];
++drr$resp_count;
dns_history[ans$query, ans$qtype, msg$id]=drr;
}
if ( drr$num_resp >= drr$resp_count )
return;
if ( second_level_domain(ans$query) in bad_domain_resp )
return;
if ( ans$query in bad_host_resp )
return;
# Too many responses to the request, or some other
# inconsistency has been introduced.
NOTICE([$note=ResolverInconsistency, $conn=c,
$msg=fmt("address inconsistency for %s, %s", ans$query, a),
$dst=a]);
}
}
event expire_DNS_session(orig: addr, resp: addr, trans_id: count)
{
if ( [orig, resp, trans_id] in dns_sessions )
{
local session = dns_sessions[orig, resp, trans_id];
local last_active = session$last_active;
if ( network_time() > last_active + dns_session_timeout ||
done_with_network )
{
# Flush out any pending requests.
if ( logging )
{
for ( query in session$pending_queries )
print dns_log, fmt("%0.6f #%d %s",
network_time(), session$id,
session$pending_queries[query]);
print dns_log, fmt("%.06f #%d finish",
network_time(), session$id);
}
delete dns_sessions[orig, resp, trans_id];
}
else
schedule dns_session_timeout {
expire_DNS_session(orig, resp, trans_id)
};
}
}
function lookup_DNS_session(c: connection, trans_id: count): dns_session_info
{
local id = c$id;
local orig = id$orig_h;
local resp = id$resp_h;
if ( [orig, resp, trans_id] !in dns_sessions )
{
local session: dns_session_info;
session$id = ++num_dns_sessions;
session$last_active = network_time();
session$is_zone_transfer = F;
if ( logging )
print dns_log, fmt("%.06f #%d %s start",
c$start_time, session$id, id_string(id));
dns_sessions[orig, resp, trans_id] = session;
schedule 15 sec { expire_DNS_session(orig, resp, trans_id) };
append_addl(c, fmt("#%d", session$id));
return session;
}
else
return dns_sessions[orig, resp, trans_id];
}
event sensitive_addr_lookup(c: connection, a: addr, is_query: bool)
{
local orig = c$id$orig_h;
local resp = c$id$resp_h;
local holding = 0;
if ( orig in okay_to_lookup_sensitive_hosts )
return;
local session_id: string;
if ( [orig, resp, holding] in dns_sessions )
session_id = fmt("#%d", dns_sessions[orig, resp, holding]$id);
else
session_id = "#?";
local id = fmt("%s > %s (%s)", orig, resp, session_id);
if ( is_query )
NOTICE([$note=SensitiveDNS_Lookup, $conn=c,
$msg=fmt("%s PTR lookup of %s", id, a),
$sub="PTR lookup"]);
else
NOTICE([$note=SensitiveDNS_Lookup, $conn=c,
$msg=fmt("%s name lookup of %s", id, a),
$sub="name lookup"]);
}
function DNS_query_annotation(c: connection, msg: dns_msg, query: string,
qtype: count, is_zone_xfer: bool): string
{
local anno: string;
if ( (qtype == PTR || qtype == ANY) && query == PTR_pattern )
{
# convert PTR text to more readable form.
local a = ptr_name_to_addr(query);
if ( a in sensitive_lookup_hosts && ! is_zone_xfer )
event sensitive_addr_lookup(c, a, T);
anno = fmt("?%s %As", query_types[qtype], a);
}
else
anno = fmt("%s %s", query_types[qtype], query);
if ( ! is_zone_xfer &&
(msg$num_answers > 0 || msg$num_auth > 0 || msg$num_addl > 0) )
anno = fmt("%s <query addl = %d/%d/%d>", anno,
msg$num_answers, msg$num_auth, msg$num_addl);
return anno;
}
event dns_zone_transfer_request(c: connection, session: dns_session_info,
msg: dns_msg, query: string)
{
session$is_zone_transfer = T;
if ( ! is_tcp_port(c$id$orig_p) )
event conn_weird("UDP_zone_transfer", c);
local src = c$id$orig_h;
if ( src !in zone_transfers_okay &&
++did_zone_transfer_notice[src] == 1 )
{
NOTICE([$note=ZoneTransfer, $src=src, $conn=c,
$msg=fmt("transfer of %s requested by %s", query, src)]);
}
}
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
{
local id = c$id;
local orig = id$orig_h;
local resp = id$resp_h;
local session = lookup_DNS_session(c, msg$id);
local anno = DNS_query_annotation(c, msg, query, qtype, F);
local report = fmt("%.06f #%d %s", network_time(), session$id, c$id$orig_h);
local q: string;
if ( query_types[qtype] == "AXFR" )
{
event dns_zone_transfer_request(c, session, msg, query);
q = DNS_query_annotation(c, msg, query, qtype, T);
report = fmt("%s ?%s", report, q);
}
else
report = fmt("%s <query ?%s> %s Trunc:%s Recurs:%s",
report, query_types[qtype], query, msg$TC, msg$RD);
if ( logging )
print dns_log, fmt("%s", report);
# Check to see if this is a host or MX lookup for a designated
# hostile domain.
if ( check_domain_list &&
(query_types[qtype] == "A" || query_types[qtype] == "MX") &&
second_level_domain(query) in hostile_domain_list )
{
NOTICE([$note=SensitiveDNS_Lookup, $conn=c,
$msg=fmt("%s suspicious domain lookup: %s", id, query)]);
}
session$pending_queries[msg$id] = anno;
session$last_active = network_time();
}
event dns_rejected(c: connection, msg: dns_msg,
query: string, qtype: count, qclass: count)
{
local session = lookup_DNS_session(c, msg$id);
local code = DNS_code_types[msg$rcode];
local id = msg$id;
if ( id in session$pending_queries )
{
if ( logging )
print dns_log, fmt("%.06f #%d %s %s", network_time(),
session$id,
session$pending_queries[id],
code);
delete session$pending_queries[id];
}
else if ( logging )
{
if ( c$start_time == network_time() )
print dns_log, fmt("%.06f #%d [?%s] %s", network_time(),
session$id, query, code);
else
print dns_log, fmt("%.06f #%d %s", network_time(),
session$id, code);
}
}
event PTR_scan_summary(src: addr)
{
NOTICE([$note=DNS_PTR_Scan_Summary, $src=src,
$msg=fmt("%s totaled %d/%d un/successful PTR lookups", src,
distinct_rejected_PTR_requests[src],
distinct_answered_PTR_requests[src]),
$sub="final summary"]);
}
event PTR_scan(src: addr)
{
++did_PTR_scan_event[src];
if ( src !in allow_PTR_scans && src !in okay_to_lookup_sensitive_hosts )
{
NOTICE([$note=DNS_PTR_Scan, $src=src,
$msg=fmt("%s has made %d/%d un/successful PTR lookups",
src, distinct_rejected_PTR_requests[src],
distinct_answered_PTR_requests[src]),
$sub="scan detected"]);
schedule 1 day { PTR_scan_summary(src) };
}
}
function check_PTR_scan(src: addr)
{
if ( src !in did_PTR_scan_event &&
distinct_rejected_PTR_requests[src] >=
distinct_answered_PTR_requests[src] * report_rejected_PTR_factor )
event PTR_scan(src);
}
function DNS_answer(c: connection, msg: dns_msg,
ans: dns_answer, annotation: string)
{
local is_answer = ans$answer_type == DNS_ANS;
local session = lookup_DNS_session(c, msg$id);
local report =
fmt("%.06f #%d %s", network_time(), session$id, c$id$orig_h);
local id = msg$id;
local query: string;
if ( id in session$pending_queries )
{
query = fmt("%s = <ans %s>", session$pending_queries[id],
query_types[ans$qtype]);
delete session$pending_queries[id];
report = fmt("%s %s", report, query);
}
else if ( session$is_zone_transfer )
{ # need to provide the query directly.
query = fmt("<ans %s>", query_types[ans$qtype]);
report = fmt("%s ?%s", report, query);
}
else
{
# No corresponding query. This can happen if it's
# already been deleted because we've already processed
# an answer to it; or if the session itself was timed
# out prior to this answer being generated. In the
# first case, we don't want to provide the query again;
# in the second, we do. We can determine that we're
# likely in the second case if either (1) this session
# was just now created, or (2) we're now processing the
# sole answer to the original query.
#
# However, for now we punt.
#
# if ( c$start_time == network_time() ||
# (is_answer && msg$num_answers == 1) )
# {
# query = DNS_query_annotation(c, msg, ans$query, ans$qtype, F);
# report = fmt("%s [?%s]", report, query);
# }
# else
# query = "";
query = fmt("<ans %s>", query_types[ans$qtype]);
report = fmt("%s %s", report, query);
}
# Append a bunch of additional annotation.
report = fmt("%s %s RCode:%s AA=%s TR=%s %s/%s/%s/%s",
report, annotation, base_error[msg$rcode], msg$AA, msg$TC,
msg$num_queries, msg$num_answers, msg$num_auth, msg$num_addl );
local src = c$id$orig_h;
if ( msg$rcode != 0 )
{
if ( /\?(PTR|\*.*in-addr).*/ in query )
##### should check for private address
{
if ( ++distinct_PTR_requests[src, query] == 1 &&
++distinct_rejected_PTR_requests[src] >=
report_rejected_PTR_thresh )
check_PTR_scan(src);
}
report = fmt("%s %s", report, DNS_code_types[msg$rcode]);
}
else if ( is_answer )
{
if ( /\?(PTR|\*.*in-addr).*/ in query )
{
if ( annotation in actually_rejected_PTR_anno )
{
if ( ++distinct_PTR_requests[src, query] == 1 &&
++distinct_rejected_PTR_requests[src] >=
report_rejected_PTR_thresh )
check_PTR_scan(src);
}
else
{
if ( ++distinct_PTR_requests[src, query] == 1 )
++distinct_answered_PTR_requests[src];
}
}
}
if ( logging )
print dns_log, fmt("%s TTL=%g", report, ans$TTL);
### Note, DNS_AUTH and DNS_ADDL not processed.
session$last_active = network_time();
}
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr)
{
if ( a in sensitive_lookup_hosts )
event sensitive_addr_lookup(c, a, F);
DNS_answer(c, msg, ans, fmt("%As", a));
if ( resolver_consist_check )
insert_name(c, msg, ans, a );
}
event dns_NS_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string)
{
DNS_answer(c, msg, ans, fmt("%s", name));
}
event dns_CNAME_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string)
{
DNS_answer(c, msg, ans, fmt("%s %s", query_types[ans$qtype], name));
}
event dns_PTR_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string)
{
DNS_answer(c, msg, ans, fmt("%s", name));
}
event dns_SOA_reply(c: connection, msg: dns_msg, ans: dns_answer, soa: dns_soa)
{
DNS_answer(c, msg, ans, fmt("%s", soa$mname));
}
event dns_MX_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string,
preference: count)
{
DNS_answer(c, msg, ans, fmt("%s/%d", name, preference));
}
event dns_EDNS(c: connection, msg: dns_msg, ans: dns_answer)
{
DNS_answer(c, msg, ans, "<---?--->");
}
# From here on down we need to modify the way that data is recorded. The
# standard resource record format is no longer universally applicable in
# that we may see modified structs or some number of value pairs that may take
# more flexability in reporting.
event dns_EDNS_addl(c: connection, msg: dns_msg, ans: dns_edns_additional)
{
local session = lookup_DNS_session(c, msg$id);
local report =
fmt("%.06f #%d %s", network_time(), session$id, c$id$orig_h);
if ( ans$is_query == 1 )
report = fmt("%s <addl_edns ?>", report);
else
report = fmt("%s <addl_edns> ", report);
if ( logging )
print dns_log, fmt("%s pldsize:%s RCode:%s VER:%s Z:%s",
report, ans$payload_size,
base_error[ans$extended_rcode],
ans$version, edns_zfield[ans$z_field]);
}
event dns_TSIG_addl(c: connection, msg: dns_msg, ans: dns_tsig_additional)
{
local session = lookup_DNS_session(c, msg$id);
local report =
fmt("%.06f #%d %s", network_time(), session$id, c$id$orig_h);
# Error handling with this is a little odd: number collision with EDNS.
# We set the collided value to the first private space number. gross.
local trans_error_num = (ans$rr_error == 16) ? 3842 : ans$rr_error;
if ( ans$is_query == 1 )
report = fmt("%s <addl_tsig ?> ", report);
else
report = fmt("%s <addl_tsig> ", report);
if ( logging )
print dns_log, fmt("%s name:%s alg:%s origID:%s RCode:%s",
report, ans$query, ans$alg_name,
ans$orig_id, base_error[trans_error_num]);
}

View file

@ -1,74 +0,0 @@
# $Id: drop-adapt.bro 6940 2009-11-14 00:38:53Z robin $
#
# Adjust load level based on packet drops.
#
@load load-level
# Increase load-level if packet drops are successively 'count' times
# above 'threshold' percent.
const drop_increase_count = 5 &redef;
const drop_increase_threshold = 5.0 &redef;
# Same for decreasing load-level.
const drop_decrease_count = 15 &redef;
const drop_decrease_threshold = 0.0 &redef;
# Minimum time to wait after a load-level increase before new decrease.
const drop_decrease_wait = 20 mins &redef;
global drop_last_stat: net_stats;
global drop_have_stats = F;
global drop_above = 0;
global drop_below = 0;
global drop_last_increase: time = 0;
event net_stats_update(t: time, ns: net_stats)
{
if ( drop_have_stats )
{
local new_recvd = ns$pkts_recvd - drop_last_stat$pkts_recvd;
local new_dropped =
ns$pkts_dropped - drop_last_stat$pkts_dropped;
local p = new_dropped * 100.0 / new_recvd;
drop_last_stat = ns;
if ( p >= 0 )
{
if ( p >= drop_increase_threshold )
{
if ( ++drop_above >= drop_increase_count )
{
increase_load_level();
drop_above = 0;
drop_last_increase = t;
}
}
else
drop_above = 0;
if ( t - drop_last_increase < drop_decrease_wait )
return;
if ( p <= drop_decrease_threshold )
{
if ( ++drop_below >= drop_decrease_count )
{
decrease_load_level();
drop_below = 0;
}
}
else
drop_below = 0;
}
}
else
{
drop_have_stats = T;
drop_last_stat = ns;
}
}

View file

@ -1,340 +0,0 @@
# $Id:$
#
# drop.bro implements a drop/restore policy termed "catch-and-release"
# whereby the first time an address is dropped, it is restored a while after
# the last connection attempt seen. If a connection attempt is subsequently
# seen, however, then the system is blocked again, and for a longer time.
#
# This policy has significant benefits when using Bro to update router
# ACLs for which:
# - The router has a limited number of ACLs slots.
# - You care about possible reuse of IP addresses by now-benign hosts,
# so don't want blocks to last forever.
#
# Original code by Jim Mellander, LBNL.
# Updated by Brian Tierney, LBNL and by Robin Sommer, ICSI.
@load site
module Drop;
export {
redef enum Notice += {
# Connectivity with given address has been dropped.
AddressDropped,
# A request to drop connectivity has been ignored.
AddressDropIgnored,
# Connectivity with given address has been restored.
AddressRestored,
AddressAlreadyDropped, # host is already dropped
# Previously dropped host connects again.
AddressSeenAgain,
# Previous offenders re-dropped or re-restored.
RepeatAddressDropped,
RepeatAddressRestored,
};
# True if we have the capability to drop hosts at all.
const can_drop_connectivity = F &redef;
# True if we never want to drop local addresses.
const dont_drop_locals = T &redef;
# True if we should use the catch-and-release scheme. If not then
# we simply drop addresses via the drop_connectivity_script and
# never restore them (they must be restored out-of-band).
const use_catch_release = F &redef;
# Catch-and-release parameters.
# Interval to wait for release following inactivity after
# first offense.
global drop_time = 5 min &redef;
# For repeat offenders: if the total time a host has already been
# dropped reaches persistent_offender_time, we drop the host for
# long_drop_time. Setting persistent_offender_time to zero disables
# this functionality.
const persistent_offender_time = 2 hr &redef;
global long_drop_time = 12 hr &redef;
# Scripts to perform the actual dropping/restore. They get the
# IP address as their first argument.
const drop_connectivity_script = "drop-connectivity" &redef;
const restore_connectivity_script = "restore-connectivity" &redef;
const root_servers = {
a.root-servers.net, b.root-servers.net, c.root-servers.net,
d.root-servers.net, e.root-servers.net, f.root-servers.net,
g.root-servers.net, h.root-servers.net, i.root-servers.net,
j.root-servers.net, k.root-servers.net, l.root-servers.net,
m.root-servers.net,
} &redef;
const gtld_servers = {
a.gtld-servers.net, b.gtld-servers.net, c.gtld-servers.net,
d.gtld-servers.net, e.gtld-servers.net, f.gtld-servers.net,
g.gtld-servers.net, h.gtld-servers.net, i.gtld-servers.net,
j.gtld-servers.net, k.gtld-servers.net, l.gtld-servers.net,
m.gtld-servers.net,
} &redef;
const never_shut_down = {
root_servers, gtld_servers,
} &redef;
const never_drop_nets: set[subnet] &redef;
# Drop the connectivity for the address. "msg" gives a reason.
# It returns a copy of the NOTICE generated for the drop, which
# gives more information about the kind of dropping performed.
# If the notice type is NoticeNone, the drop was not successful
# (e.g., because this Bro instance is not configured to do drops.)
global drop_address: function(a: addr, msg: string) : notice_info;
# The following events are used to communicate information about the
# drops, in particular for C&R in the cluster setting.
# Address has been dropped.
global address_dropped: event(a: addr);
# Raised when an IP is restored.
global address_restored: event(a: addr);
# Raised when an that was dropped in the past is no
# longer monitored specifically for new connections.
global address_cleared: event(a: addr);
const debugging = F &redef;
global debug_log: function(msg: string);
}
type drop_rec: record {
tot_drop_count: count &default=0;
tot_restore_count: count &default=0;
actual_restore_count: count &default=0;
tot_drop_time: interval &default=0secs;
last_timeout: interval &default=0secs;
};
global clear_host: function(t: table[addr] of drop_rec, a: addr): interval;
global drop_info: table[addr] of drop_rec
&read_expire = 1 days &expire_func=clear_host &persistent;
global last_notice: notice_info;
function do_notice(n: notice_info)
{
last_notice = n;
NOTICE(n);
}
function dont_drop(a: addr) : bool
{
return ! can_drop_connectivity || a in never_shut_down ||
a in never_drop_nets || (dont_drop_locals && is_local_addr(a));
}
function is_dropped(a: addr) : bool
{
if ( a !in drop_info )
return F;
local di = drop_info[a];
if ( di$tot_drop_count < di$tot_restore_count )
{ # This shouldn't happen.
# FIXME: We need an assert().
print "run-time error: more restores than drops!";
return F;
}
return di$tot_drop_count > di$tot_restore_count;
}
global debug_log_file: file;
function debug_log(msg: string)
{
if ( ! debugging )
return;
print debug_log_file,
fmt("%.6f [%s] %s", network_time(), peer_description, msg);
}
event bro_init()
{
if ( debugging )
{
debug_log_file =
open_log_file(fmt("drop-debug.%s", peer_description));
set_buf(debug_log_file, F);
}
}
function do_direct_drop(a: addr, msg: string)
{
if ( msg != "" )
msg = fmt(" (%s)", msg);
if ( a !in drop_info )
{
local tmp: drop_rec;
drop_info[a] = tmp;
}
local di = drop_info[a];
if ( is_dropped(a) )
# Already dropped. Nothing to do.
do_notice([$note=Drop::AddressAlreadyDropped, $src=a,
$msg=fmt("%s%s", a, msg)]);
else
{
system(fmt("%s %s", Drop::drop_connectivity_script, a));
debug_log(fmt("sending drop for %s", a));
event Drop::address_dropped(a);
if ( di$tot_drop_count == 0 )
do_notice([$note=Drop::AddressDropped, $src=a,
$msg=fmt("%s%s", a, msg)]);
else
{
local s = fmt("(%d times)", di$tot_drop_count + 1);
do_notice([$note=Drop::RepeatAddressDropped,
$src=a, $n=di$tot_drop_count+1,
$msg=fmt("%s%s %s", a, msg, s), $sub=s]);
}
}
++di$tot_drop_count;
debug_log(fmt("dropped %s: tot_drop_count=%d tot_restore_count=%d",
a, di$tot_drop_count, di$tot_restore_count));
}
# Restore a previously dropped address.
global do_restore: function(a: addr, force: bool);
event restore_dropped_address(a: addr)
{
do_restore(a, F);
}
function do_catch_release_drop(a: addr, msg: string)
{
do_direct_drop(a, msg);
local di = drop_info[a];
local t = (persistent_offender_time != 0 sec &&
di$tot_drop_time >= persistent_offender_time) ?
long_drop_time : drop_time;
di$tot_drop_time += t;
di$last_timeout = t;
schedule t { restore_dropped_address(a) };
}
function do_restore(a: addr, force: bool)
{
if ( a !in drop_info )
return;
local di = drop_info[a];
++drop_info[a]$tot_restore_count;
debug_log(fmt("restored %s: tot_drop_count=%d tot_restore_count=%d force=%s", a, drop_info[a]$tot_drop_count, drop_info[a]$tot_restore_count, force));
if ( di$tot_drop_count == di$tot_restore_count || force )
{
++di$actual_restore_count;
system(fmt("%s %s", Drop::restore_connectivity_script, a));
debug_log(fmt("sending restored for %s", a));
event Drop::address_restored(a);
local t = di$last_timeout;
if ( di$actual_restore_count == 1 )
{
local s1 = fmt("(timeout %.1f)", t);
do_notice([$note=Drop::AddressRestored, $src=a,
$msg=fmt("%s %s", a, s1), $sub=s1]);
}
else
{
local s2 = fmt("(%d times, timeout %.1f)",
di$actual_restore_count, t);
do_notice([$note=Drop::RepeatAddressRestored, $src=a,
$n=di$tot_restore_count,
$msg=fmt("%s %s", a, s2), $sub=s2]);
}
}
}
function clear_host(t: table[addr] of drop_rec, a: addr): interval
{
if ( is_dropped(a) )
# Restore address.
do_restore(a, T);
debug_log(fmt("sending cleared for %s", a));
event Drop::address_cleared(a);
return 0 secs;
}
# Returns true if drop was successful (or IP was already dropped).
function drop_address(a: addr, msg: string) : notice_info
{
debug_log(fmt("drop_address(%s, %s)", a, msg));
last_notice = [$note=NoticeNone];
if ( dont_drop(a) )
do_notice([$note=AddressDropIgnored, $src=a,
$msg=fmt("ignoring request to drop %s (%s)", a, msg)]);
else if ( use_catch_release )
do_catch_release_drop(a, msg);
else
do_direct_drop(a, msg);
if ( last_notice$note == NoticeNone )
print "run-time error: drop_address did not raise a NOTICE";
return last_notice;
}
event new_connection(c: connection)
{
if ( ! can_drop_connectivity )
return;
# With Catch & Release, 1 connection from a previously dropped system
# triggers an immediate redrop.
if ( ! use_catch_release )
return;
local a = c$id$orig_h;
if ( a !in drop_info )
# Never dropped.
return;
local di = drop_info[a];
if ( is_dropped(a) )
# Still dropped.
return;
NOTICE([$note=AddressSeenAgain, $src=a,
$msg=fmt("%s seen again after release", a)]);
}

View file

@ -1,53 +0,0 @@
# $Id: dyn-disable.bro,v 1.1.4.3 2006/05/31 01:52:02 sommer Exp $
#
# When this script is loaded, analyzers that raise protocol_violation events
# are disabled for the affected connection.
# Note that this a first-shot solution. Eventually, we should make the
# disable-decision more fine-grained/sophisticated.
@load conn
@load notice
module DynDisable;
export {
redef enum Notice += {
ProtocolViolation
};
# Ignore violations which go this many bytes into the connection.
const max_volume = 10 * 1024 &redef;
}
global conns: table[conn_id] of set[count];
event protocol_violation(c: connection, atype: count, aid: count,
reason: string)
{
if ( c$id in conns && aid in conns[c$id] )
return;
local size = c$orig$size + c$resp$size;
if ( max_volume > 0 && size > max_volume )
return;
# Disable the analyzer that raised the last core-generated event.
disable_analyzer(c$id, aid);
NOTICE([$note=ProtocolViolation, $conn=c,
$msg=fmt("%s analyzer %s disabled due to protocol violation",
id_string(c$id), analyzer_name(atype)),
$sub=reason, $n=atype]);
if ( c$id !in conns )
conns[c$id] = set();
add conns[c$id][aid];
}
event connection_state_remove(c: connection)
{
delete conns[$id=c$id];
}

View file

@ -1,18 +0,0 @@
# $Id: file-flush.bro 786 2004-11-24 08:25:16Z vern $
# Causes all files to be flushed every file_flush_interval seconds.
# Useful if you want to poke through the log files in real time,
# particularly if network traffic is light.
global file_flush_interval = 10 sec &redef;
event file_flush_event()
{
flush_all();
schedule file_flush_interval { file_flush_event() };
}
event bro_init()
{
schedule file_flush_interval { file_flush_event() };
}

View file

@ -1,69 +0,0 @@
# $Id: finger.bro 4758 2007-08-10 06:49:23Z vern $
module Finger;
export {
const hot_names = {
"root", "lp", "uucp", "nuucp", "demos", "operator", "sync",
"r00t", "tutor", "tour", "admin", "system", "guest", "visitor",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
} &redef;
const max_finger_request_len = 80 &redef;
}
redef capture_filters += { ["finger"] = "port finger" };
# DPM configuration.
global finger_ports = { 79/tcp } &redef;
redef dpd_config += { [ANALYZER_FINGER] = [$ports = finger_ports] };
function public_user(user: string): bool
{
return T;
}
function authorized_client(host: addr): bool
{
return T;
}
event finger_request(c: connection, full: bool, username: string, hostname: string)
{
local id = c$id;
local request: string;
if ( hostname != "" )
request = cat(username, "@", hostname);
else
request = username;
if ( byte_len(request) > max_finger_request_len )
{
request = fmt("%s...", sub_bytes(request, 1, max_finger_request_len));
++c$hot;
}
if ( hostname != "" )
++c$hot;
if ( username in hot_names )
++c$hot;
local req = request == "" ? "ALL" : fmt("\"%s\"", request);
if ( full )
req = fmt("%s (/W)", req);
if ( c$addl != "" )
# This is an additional request.
req = fmt("(%s)", req);
append_addl_marker(c, req, " *");
}
function is_finger_conn(c: connection): bool
{
return c$id$resp_p == finger;
}

View file

@ -1,195 +0,0 @@
# $Id: firewall.bro 4758 2007-08-10 06:49:23Z vern $
#
# Firewall-like rules.
@load notice
@load conn
@load ftp
module Firewall;
export {
type action: enum { ALLOW, DENY };
type cmp: enum { EQ, NE };
type rule: record {
label: string &default = "<no-label>";
orig: subnet &default = 0.0.0.0/0;
orig_set: set[addr] &optional;
orig_cmp: cmp &default = EQ;
orig_p: port &default = 0/tcp;
orig_p_cmp: cmp &default = EQ;
resp: subnet &default = 0.0.0.0/0;
resp_set: set[addr] &optional;
resp_cmp: cmp &default = EQ;
resp_p: port &default = 0/tcp;
resp_p_cmp: cmp &default = EQ;
prot: transport_proto &default = unknown_transport;
prot_cmp: cmp &default = EQ;
state: string &default = "";
state_cmp: cmp &default = EQ;
is_ftp: bool &default = F;
action: action &default = ALLOW;
};
redef enum Notice += {
DenyRuleMatched
};
global begin: function(c: connection);
global match_rule: function(c: connection, r: rule);
}
const log_file = open_log_file("firewall") &redef;
global stop_matching = F;
function do_match(c: connection, r: rule): bool
{
if ( r$orig_cmp == EQ )
{
if ( r?$orig_set )
{
if ( c$id$orig_h !in r$orig_set && c$id$orig_h !in r$orig )
return F;
}
else
{
if ( c$id$orig_h !in r$orig )
return F;
}
}
else
{
if ( r?$orig_set )
{
if ( c$id$orig_h in r$orig_set || c$id$orig_h in r$orig )
return F;
}
else
{
if ( c$id$orig_h in r$orig )
return F;
}
}
if ( r$resp_cmp == EQ )
{
if ( r?$resp_set )
{
if ( c$id$resp_h !in r$resp_set && c$id$resp_h !in r$resp )
return F;
}
else
{
if ( c$id$resp_h !in r$resp )
return F;
}
}
else
{
if ( r?$resp_set )
{
if ( c$id$resp_h in r$resp_set || c$id$resp_h in r$resp )
return F;
}
else
{
if ( c$id$resp_h in r$resp )
return F;
}
}
if ( r$orig_p != 0/tcp )
{
if ( r$orig_p_cmp == EQ )
{
if ( c$id$orig_p != r$orig_p )
return F;
}
else
if ( c$id$orig_p == r$orig_p )
return F;
}
if ( r$resp_p != 0/tcp )
{
if ( r$resp_p_cmp == EQ )
{
if ( c$id$resp_p != r$resp_p )
return F;
}
else
if ( c$id$resp_p == r$resp_p )
return F;
}
if ( r$state != "" )
{
local state = conn_state(c, get_port_transport_proto(c$id$orig_p));
if ( r$state_cmp == EQ )
{
if ( state != r$state )
return F;
}
else
if ( state == r$state )
return F;
}
if ( r$prot != unknown_transport )
{
local proto = get_port_transport_proto(c$id$orig_p);
if ( r$prot_cmp == EQ )
{
if ( proto != r$prot )
return F;
}
else
if ( proto == r$prot )
return F;
}
if ( r$is_ftp && ! FTP::is_ftp_data_conn(c) )
return F;
return T;
}
function report_violation(c: connection, r:rule)
{
local trans = get_port_transport_proto(c$id$orig_p);
local state = conn_state(c, trans);
NOTICE([$note=DenyRuleMatched,
$msg=fmt("%s %s",
id_string(c$id), trans), $conn=c, $sub=r$label]);
append_addl(c, fmt("<%s>", r$label));
record_connection(log_file, c);
}
function begin(c: connection)
{
stop_matching = F;
}
function match_rule(c: connection, r: rule)
{
if ( stop_matching )
return;
if ( do_match(c, r) )
{
stop_matching = T;
if ( r$action == DENY )
report_violation(c, r);
}
}
event bro_init()
{
set_buf(log_file, F);
}

View file

@ -1,18 +0,0 @@
# $Id: flag-irc.bro 4758 2007-08-10 06:49:23Z vern $
#
# include this module to flag various forms of IRC access.
@load ftp
redef FTP::hot_files +=
/.*eggdrop.*/
| /.*eggsun.*/
;
redef Hot::flag_successful_inbound_service: table[port] of string += {
[[6666/tcp, 6667/tcp]] = "inbound IRC",
};
redef Hot::hot_dsts: table[addr] of string += {
[bitchx.com] = "IRC source sites",
};

View file

@ -1,11 +0,0 @@
# $Id: flag-warez.bro 416 2004-09-17 03:52:28Z vern $
#
# include this module to flag various forms of Warez access.
@load hot-ids
@load ftp
redef FTP::hot_files += /.*[wW][aA][rR][eE][zZ].*/ ;
redef always_hot_ids += { "warez", "hanzwarez", "zeraw", };
redef hot_ids += { "warez", "hanzwarez", "zeraw", };

View file

@ -1,6 +0,0 @@
# Capture TCP fragments, but not UDP (or ICMP), since those are a lot more
# common due to high-volume, fragmenting protocols such as NFS :-(.
redef capture_filters += { ["frag"] = "(ip[6:2] & 0x3fff != 0) and tcp" };
redef frag_timeout = 5 min;

File diff suppressed because it is too large Load diff

View file

@ -1,61 +0,0 @@
# $Id: gnutella.bro 4017 2007-02-28 07:11:54Z vern $
redef capture_filters += { ["gnutella"] = "port 6346 or port 8436" };
global gnutella_ports = { 6346/tcp, 8436/tcp } &redef;
redef dpd_config += { [ANALYZER_GNUTELLA] = [$ports = gnutella_ports] };
event gnutella_text_msg(c: connection, orig: bool, headers: string)
{
if ( orig )
print fmt("gnu txt %s -> %s %s", c$id$orig_h, c$id$resp_h, headers);
else
print fmt("gnu txt %s -> %s %s", c$id$resp_h, c$id$orig_h, headers);
}
event gnutella_binary_msg(c: connection, orig: bool, msg_type: count,
ttl: count, hops: count, msg_len: count,
payload: string, payload_len: count,
trunc: bool, complete: bool)
{
local s = "";
if ( orig )
s = fmt("gnu bin %s -> %s", c$id$orig_h, c$id$resp_h);
else
s = fmt("gnu bin %s -> %s", c$id$resp_h, c$id$orig_h);
print fmt("%s %d %d %d %d %d %d %d %s",
s, msg_type, ttl, hops, msg_len,
trunc, complete, payload_len, payload);
}
event gnutella_partial_binary_msg(c: connection, orig: bool,
msg: string, len: count)
{
if ( orig )
print fmt("gnu pbin %s -> %s", c$id$orig_h, c$id$resp_h);
else
print fmt("gnu pbin %s -> %s", c$id$resp_h, c$id$orig_h);
}
event gnutella_establish(c: connection)
{
print fmt("gnu est %s <-> %s", c$id$orig_h, c$id$resp_h);
}
event gnutella_not_establish(c: connection)
{
print fmt("gnu !est %s <-> %s", c$id$orig_h, c$id$resp_h);
}
event gnutella_http_notify(c: connection)
{
print fmt("gnu http %s/%s <-> %s/%s", c$id$orig_h, c$id$orig_p,
c$id$resp_h, c$id$resp_p);
}

View file

@ -1,144 +0,0 @@
# $Id: hand-over.bro 617 2004-11-02 00:54:31Z scottc $
#
# Hand-over between two instances of Bro.
@load remote
# The host from which we want to take over the state has to be
# added to remote_peers_{clear,ssl}, setting hand_over to T.
#
# The host which we want to allow to perform a hand-over with us
# has to be added to remote_peers with a port of 0/tcp and
# hand_over = T.
function is_it_us(host: addr, p: port): bool
{
@ifdef ( listen_if_clear )
if ( is_local_interface(host) && p == listen_port_clear )
return T;
@endif
@ifdef ( listen_if_ssl )
if ( is_local_interface(host) && p == listen_port_ssl )
return T;
@endif
return F;
}
function is_handover_peer(p: event_peer): bool
{
local peer: Remote::Destination;
if ( p$id in Remote::pending_peers )
peer = Remote::pending_peers[p$id];
else
return F;
return peer$hand_over;
}
function handover_start_processing()
{
uninstall_src_net_filter(0.0.0.0/0);
}
event bro_init()
{
# Disable packet processing.
install_src_net_filter(0.0.0.0/0, 0, 100);
# Reporter::message("waiting for hand-over - packet processing disabled.");
}
event remote_connection_error(p: event_peer, reason: string)
{
if ( is_remote_event() || ! ( p$id in Remote::connected_peers) )
return;
# Seems that the other side in not running.
# Reporter::error("can't connect for hand-over - starting processing ...");
handover_start_processing();
}
event remote_connection_established(p: event_peer)
{
if ( is_remote_event() )
return;
# If [p$id] is defined in Remote::connected_peers and p != 0, we have connected
# to the host.
if ( p$p != 0/tcp &&
([p$id] in Remote::connected_peers ) )
{
if ( ! is_handover_peer(p) )
return;
# Reporter::message(fmt("requesting hand-over from %s:%d", p$host, p$p));
request_remote_events(p, /handover_.*|finished_send_state/);
# Give the remote side some time to register its handlers.
schedule 3 secs { handover_request(p$host, p$p) };
return;
}
# If the other side connected to us, we will allow the hand-over
# if the remote host is defined as a hand-over host in remote_peers.
if ( is_handover_peer(p) )
{
# Reporter::message(fmt("allowing hand-over from %s:%d", p$host, p$p));
request_remote_events(p, /handover_.*|finished_send_state/);
}
}
event handover_send_state(p: event_peer)
{
if ( is_remote_event() )
return;
# There may be a serialization in progress in which case
# we will have to try again.
if ( ! send_state(p) )
{
# Reporter::message("can't send state; serialization in progress");
schedule 5 secs { handover_send_state(p$host, p$p) };
}
}
event handover_request(p: event_peer)
{
# Make sure the event is for us.
if ( ! (is_remote_event() && is_it_us(p$host, p$p)) )
return;
# Send state to other side.
schedule 1 sec { handover_send_state(p) };
}
event finished_send_state(p: event_peer)
{
# We will get this event from the remote side.
# Make sure it's indeed for us.
if ( ! is_remote_event() )
return;
if ( ! is_handover_peer(p) )
return;
#Reporter::message(fmt("full state received from %s:%d - starting processing ...",
# p$host, p$p));
event handover_got_state(p);
# Start processing.
handover_start_processing();
}
event handover_got_state(p: event_peer)
{
# Make sure the event is for us.
if ( ! (is_remote_event() && is_it_us(p$host, p$p)) )
return;
# Reporter::message(fmt("%s:%d received our state - terminating", p$host, p$p));
terminate();
}

View file

@ -1,26 +0,0 @@
# $Id: heavy-analysis.bro 2771 2006-04-18 23:53:09Z vern $
#
# Loading this files enables somewhat more accurate, yet also significantly
# more expensive, analysis (in terms of memory as well as CPU time).
#
# This script only sets core-level options. Script-level timeouts are
# adjusted in heavy.*.bro, loaded via Bro's prefix mechanism. To make this
# work, the prefix has to be set *before* reading other scripts, either by
# loading this script first of all, or by manually putting a @prefix
# at the start of Bro's configuration.
@prefixes += heavy
redef tcp_SYN_timeout = 120 secs;
redef tcp_session_timer = 30 secs;
redef tcp_connection_linger = 30 secs;
redef tcp_attempt_delay = 300 secs;
redef tcp_close_delay = 15 secs;
redef tcp_reset_delay = 15 secs;
redef tcp_partial_close_delay = 10 secs;
redef max_timer_expires = 32;
redef tcp_inactivity_timeout = 2 hrs;
redef udp_inactivity_timeout = 1 hrs;
redef icmp_inactivity_timeout = 1 hrs;

View file

@ -1,4 +0,0 @@
# $Id: heavy.irc.bro 4723 2007-08-07 18:14:35Z vern $
redef active_users &persistent &read_expire = 1 days;
redef active_channels &persistent &read_expire = 1 days;

View file

@ -1,6 +0,0 @@
# $Id: heavy.scan.bro 4758 2007-08-10 06:49:23Z vern $
redef distinct_peers &create_expire = 10 hrs;
redef distinct_ports &create_expire = 10 hrs;
redef distinct_low_ports &create_expire = 10 hrs;
redef possible_scan_sources &create_expire = 10 hrs;

View file

@ -1,3 +0,0 @@
# $Id: heavy.software.bro 2771 2006-04-18 23:53:09Z vern $
redef only_report_local = F;

View file

@ -1,8 +0,0 @@
# $Id: heavy.trw.bro 4723 2007-08-07 18:14:35Z vern $
redef TRW::scan_sources &write_expire = 1 day;
redef TRW::benign_sources &write_expire = 1 day;
redef TRW::failed_locals &write_expire = 12 hrs;
redef TRW::successful_locals &write_expire = 12 hrs;
redef TRW::lambda &write_expire = 12 hrs;
redef TRW::num_scanned_locals &write_expire = 12 hrs;

View file

@ -1,29 +0,0 @@
# @(#) $Id: hot-ids.bro 785 2004-11-24 05:56:06Z rwinslow $ (LBL)
# If these ids are seen, the corresponding connection is terminated.
const forbidden_ids = {
"uucp", "daemon", "rewt", "nuucp",
"EZsetup", "OutOfBox", "4Dgifts",
"ezsetup", "outofbox", "4dgifts", "sgiweb",
"r00t", "ruut", "bomb", "backdoor",
"bionic", "warhead", "check_mate", "checkmate", "check_made",
"themage", "darkmage", "y0uar3ownd", "netfrack", "netphrack",
} &redef;
const forbidden_ids_if_no_password = { "lp" } &redef;
const forbidden_id_patterns = /(y[o0]u)(r|ar[e3])([o0]wn.*)/ &redef;
const always_hot_ids = {
"sync", "tutor", "tour",
"retro", "milk", "moof", "own", "gdm", "anacnd",
"lp", "demos", forbidden_ids,
} &redef;
# The ones here that aren't in always_hot_ids are only hot upon
# success.
const hot_ids = {
"root", "system", "smtp", "sysadm", "diag", "sysdiag", "sundiag",
"operator", "sys", "toor", "issadmin", "msql", "sysop", "sysoper",
"wank", always_hot_ids,
} &redef;

View file

@ -1,160 +0,0 @@
# $Id: hot.bro 7057 2010-07-19 23:22:19Z vern $
@load site
@load port-name
@load notice
@load terminate-connection
module Hot;
export {
# True if it should be considered a spoofing attack if a connection has
# the same local net for source and destination.
const same_local_net_is_spoof = F &redef;
const allow_spoof_services = {
110/tcp, # pop-3
139/tcp, # netbios-ssn
} &redef;
# Indexed by source address and destination address.
const allow_pairs: set[addr, addr] &redef;
const hot_srcs: table[addr] of string = {
# [ph33r.the.eleet.com] = "kidz",
} &redef;
const hot_dsts: table[addr] of string = {
[206.101.197.226] = "ILOVEYOU worm destination",
} &redef;
const allow_services = {
ssh, http, gopher, ident, smtp, 20/tcp,
53/udp, # DNS queries
123/udp, # NTP
} &redef;
const allow_services_to: set[addr, port] &redef;
const allow_services_from: set[addr, port] &redef;
const allow_service_pairs: set[addr, addr, port] &redef;
const flag_successful_service: table[port] of string = {
[[31337/tcp]] = "popular backdoors",
} &redef;
const flag_successful_inbound_service: table[port] of string = {
[1524/tcp] = "popular backdoor, but with false hits outbound",
} &redef;
const terminate_successful_inbound_service: table[port] of string &redef;
const flag_rejected_service: table[port] of string &redef;
# Different values to hand to check_hot() at different stages in
# a connection's lifetime.
const CONN_ATTEMPTED = 1;
const CONN_ESTABLISHED = 2;
const APPL_ESTABLISHED = 3;
const CONN_FINISHED = 4;
const CONN_REJECTED = 5;
const CONN_TIMEOUT = 6;
const CONN_REUSED = 7;
global check_hot: function(c: connection, state: count): bool;
global check_spoof: function(c: connection): bool;
}
# An internal function used by check_hot.
function do_hot_check(c: connection, a: addr, t: table[addr] of string)
{
if ( a in t )
{
++c$hot;
local hot_msg = fmt("<%s>", t[a]);
append_addl(c, hot_msg);
}
}
function check_spoof(c: connection): bool
{
local orig = c$id$orig_h;
local resp = c$id$resp_h;
local service = c$id$resp_p;
if ( is_local_addr(orig) && is_local_addr(resp) &&
service !in allow_spoof_services )
{
if ( c$id$orig_p == service && orig == resp )
event conn_weird("Land_attack", c, "");
if ( same_local_net_is_spoof )
++c$hot;
}
return c$hot != 0;
}
function check_hot(c: connection, state: count): bool
{
local id = c$id;
local service = id$resp_p;
if ( service in allow_services || "ftp-data" in c$service )
return F;
if ( state == CONN_ATTEMPTED )
check_spoof(c);
else if ( state == CONN_REJECTED )
{
check_spoof(c);
if ( service in flag_rejected_service )
++c$hot;
}
else if ( state == CONN_ESTABLISHED )
{
check_spoof(c);
local inbound = is_local_addr(id$resp_h);
if ( (service in flag_successful_service ||
(inbound &&
service in flag_successful_inbound_service)) &&
([id$resp_h, id$resp_p] !in allow_services_to ||
[id$orig_h, id$resp_p] !in allow_services_from) )
{
if ( inbound &&
service in terminate_successful_inbound_service )
TerminateConnection::terminate_connection(c);
++c$hot;
if ( service in flag_successful_service )
append_addl(c, flag_successful_service[service]);
else
append_addl(c, flag_successful_inbound_service[service]);
}
}
else if ( state == APPL_ESTABLISHED ||
((state == CONN_FINISHED || state == CONN_TIMEOUT ||
state == CONN_REUSED) &&
service != telnet && c$orig$size > 0 && c$resp$size > 0) )
{
# Connection established and has a non-trivial size.
local orig = c$id$orig_h;
local resp = c$id$resp_h;
if ( [resp, service] in allow_services_to ||
[orig, service] in allow_services_from ||
[orig, resp, service] in allow_service_pairs ||
[orig, resp] in allow_pairs )
return F;
do_hot_check(c, resp, hot_srcs);
do_hot_check(c, resp, hot_dsts);
}
return c$hot != 0;
}

View file

@ -1,54 +0,0 @@
# $Id: http-abstract.bro 47 2004-06-11 07:26:32Z vern $
@load http
@load http-entity
module HTTP;
export {
const abstract_max_length = 512 &redef;
}
redef http_entity_data_delivery_size = 4096;
redef include_HTTP_abstract = T;
function skip_abstract(c: connection, is_orig: bool, msg: http_message)
{
msg$skip_abstract = T;
if ( ! process_HTTP_data )
skip_http_entity_data(c, is_orig);
}
event http_content_type(c: connection, is_orig: bool, ty: string, subty: string)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
if ( msg$entity_level == 1 && ty == "TEXT" )
# Do not skip the body in this case.
return;
skip_abstract(c, is_orig, msg);
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
if ( msg$skip_abstract )
return;
local len = byte_len(data);
if ( len > abstract_max_length )
msg$abstract = sub_bytes(data, 1, abstract_max_length);
else
msg$abstract = data;
skip_abstract(c, is_orig, msg);
# print http_log, fmt("%.6f %s %s %d bytes: \"%s\"",
# network_time(), s$id,
# is_orig ? "=>" : "<=", byte_len(msg$abstract),
# msg$abstract);
}

View file

@ -1,209 +0,0 @@
# $Id:$
# Anonymize values in Server: headers.
#
# TODO:
#
# - Zedo and IBM web servers can have Apache mods -- the parsing should
# be extended to support them
#
@load anon
@load http-anon-utils
# ---------------------------------------------------------------------
# Apache (and friends)
# - abandon all hope ye who enter here .....
# ---------------------------------------------------------------------
const apache_server =
/apache(-ish)?(\/([0-9]+\.)*[0-9]+)? *(\(?(red hat( linux)?|cobalt|suse\/linux|linux\/suse|darwin|gentoo\/linux|debian gnu\/linux|win32|fedora|freebsd|red-hat\/linux|unix)\)? *)*/;
const apache_mod_pat =
/mod_fastcgi\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /openssl\/([0-9]+\.)*[0-9a-z]{1,4}(-beta[0-9]{0,2})?/
| /dav\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /php-cgi\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /ben-ssl\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /embperl\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_ruby\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /nexadesic\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /postgresql\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_tsunami\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_auth_svn\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_auth_mda\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /rus\/pl(([0-9]+\.)*[0-9]{1,4})/
| /authmysql\/(([0-9]+\.)*[0-9]{1,4})/
| /mod_auth_pgsql\/(([0-9]+\.)*[0-9]{1,4})/
| /mod_ssl\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /php\/(([0-9]+\.)*[0-9a-z]{1,4})(-[0-9]+)?/
| /mod_perl\/(([0-9]+\.)*[0-9a-z]{1,4})(\_[0-9]+)?/
| /mod_macro\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_auth_pam\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_oas\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_cap\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /powweb\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_gzip\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /resin\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_jk\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /python\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /perl\/(v)?(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_python\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_log_bytes\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_auth_passthrough\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_bwlimited\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_throttle\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /mod_webapp\/(([0-9]+\.)*[0-9a-z]{1,4})(-dev)?/
| /frontpage\/(([0-9]+\.)*[0-9a-z]{1,5})/
| /mod_pubcookie\/[0-9a-z]{2}\/[0-9]+\.[0-9]+\-[0-9]+/
| /(-)?coyote\/(([0-9]+\.)*[0-9a-z]{1,4})/
| /svn\/(([0-9]+\.)*[0-9a-z]{1,4})/
;
# Various Apache variants (e.g., stronghold).
const apache_misc =
/stronghold\/(([0-9]+\.)*[0-9]+) apache(\/([0-9]+\.)*[0-9]+)? (c2neteu\/[0-9])? *(\(?(red hat( linux)?|cobalt|suse\/linux|linux\/suse|darwin|gentoo\/linux|debian gnu\/linux|win32|fedora|freebsd|red-hat\/linux|unix)\)? *)*/;
const apache_basic = /apache?(\/([0-9]+\.)*[0-9]+)?/;
const apache_platforms =
/(\(?(red hat( linux)?|cobalt|suse\/linux|linux\/suse|darwin|gentoo\/linux|debian gnu\/linux|win32|fedora|freebsd|red-hat\/linux|unix)\)? *)*/;
# ibm_http_server/1.3.26.2, apache/1.3.26 (unix).
const IBM_server =
/ibm_http_server(\/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?( *apache\/[0-9]+\.[0-9]+\.[0-9]+ \(unix\))?/;
# ---------------------------------------------------------------------
# Servers values for which we don't retain all values.
# ---------------------------------------------------------------------
const zope_server =
/zope\/\(zope ([0-9]+\.)*[0-9]+-[a-z0-9]{1,2}\, python ([0-9]+\.)*[0-9]+\, linux[0-9]\)/;
const thttp_server = /thttpd\/[0-9]+\.[0-9]+(beta[0-9]+)?/;
const weblogic_server = /weblogic server [0-9]+\.[0-9]+/;
const zedo_server = /zedo 3g(\/([0-9]+\.)*[0-9]+)?/;
const jetty_server = /jetty\/[0-9]+\.[0-9]+/;
# ---------------------------------------------------------------------
# Misc Servers
# ---------------------------------------------------------------------
const misc_server =
/dclk creative/
| /gws\/[0-9]+\.[0-9]+/
| /nfe\/[0-9]+\.[0-9]+/
| /gfe\/[0-9]+\.[0-9]+/
| /dclk-adsvr/
| /rsi/
| /swcd\/([0-9]+\.)*[0-9]+/
| /microsoft-iis\/[0-9]{1,2}\.[0-9]{1,2}/
| /cafe\/[0-9]+\.[0-9]+/
| /artblast\/([0-9]+\.)*[0-9]+/
| /aolserver\/([0-9]+\.)*[0-9]+/
| /resin\/([0-9]+\.)*s?[0-9]+/
| /netscape-enterprise\/([0-9]+\.)*[0-9a-z]{1,2}+ *(aol)?/
| /mapquest listener/
| /miixpc\/[0-9]+\.[0-9]+/
| /sun-one-web-server\/[0-9]+\.[0-9]+/
| /appledotmacserver/
| /cj\/[0-9]+\.[0-9]+/
| /jigsaw\/([0-9]+\.)*[0-9]+/
| /boa\/[0-9]+\.[0-9]+(\.[0-9]+(rc[0-9]+)?)?/
| /tux\/[0-9]+\.[0-9]+ *\(linux\)/
| /igfe/
| /trafficmarketplace-jforce\/([0-9]+\.)*[0-9]+/
| /lighttpd/
| /hitbox gateway ([0-9]+\.)*[0-9]+ [a-z][0-9]/
| /jbird\/[0-9]+\.[0-9a-z]{1,2}/
| /perlbal/
| /big-ip/
| /konichiwa\/[0-9]+\.[0-9]+/
| /footprint [0-9]+\.[0-9]+\/fpmc/
| /iii [0-9]+/
| /clickability web server\/([0-9]+\.)*[0-9]+ *\(unix\)/
| /accipiter-directserver\/([0-9]+\.)*[0-9]+ \(nt; pentium\)/
| /ibm-proxy-wte\/([0-9]+\.)*[0-9]+/
| /netscape-commerce\/[0-9]+\.[0-9]+/
| /nde/
;
function do_apache_server(server: string): string
{
local apache_parts = split_all(server, apache_server);
if ( apache_parts[3] == "" )
return apache_parts[2];
local apache_return_string = apache_parts[2];
local mod_parts = split(apache_parts[3], / /);
for ( part in mod_parts )
{
if ( mod_parts[part] == apache_mod_pat )
{
apache_return_string =
string_cat(apache_return_string,
" ");
apache_return_string =
string_cat(apache_return_string,
mod_parts[part]);
}
else
print http_anon_log, fmt("** unknown Apache mod: %s:%s", mod_parts[part], server);
}
return apache_return_string;
}
function check_server(server: string, server_pat: pattern): bool
{
return server_pat in server;
}
function do_server(server: string, server_pat: pattern): string
{
return split_all(server, server_pat)[2];
}
function filter_in_http_server(server: string): string
{
# Vanilla Apache is a hard one and a special case. Let's get the
# nastiness over first.
if ( apache_server in server )
return do_apache_server(server);
if ( check_server(server, apache_misc) )
return do_server(server, apache_misc);
if ( check_server(server, IBM_server) )
return do_server(server, IBM_server);
if ( check_server(server, zedo_server) )
return do_server(server, zedo_server);
if ( check_server(server, zope_server) )
return do_server(server, zope_server);
if ( check_server(server, jetty_server) )
return do_server(server, jetty_server);
if ( check_server(server, thttp_server) )
return do_server(server, thttp_server);
if ( check_server(server, weblogic_server) )
return do_server(server, weblogic_server);
# Grab bag.
if ( misc_server in server )
return server;
# Best guess - unknown Apache variant of some sort.
if ( apache_basic in server )
{
print http_anon_log,
fmt("** unknown Apache variant: %s", server);
return fmt("(bro: unknown) %s %s",
split_all(server, apache_basic)[2],
split_all(server, apache_platforms)[2]);
}
print http_anon_log, fmt("** unknown server: %s", server);
return fmt("(bro: unknown) %s", anonymize_arg("server", server));
}

View file

@ -1,111 +0,0 @@
# $Id:$
# Filter-in known "USER-AGENT:" values.
@load anon
@load http-anon-utils
# ---------------------------------------------------------------------
# Mozilla (and friends)
# ---------------------------------------------------------------------
const mozilla_full_pat =
/mozilla\/[0-9]\.[0-9] \(( *|;|iebar| freebsd i[0-9]{1,4}|fr|-|windows|windows 98|sunos sun4u|compatible|msie [0-9]\.[0-9]|windows nt [0-9]\.[0-9]|google-tr-1|sv1|\.net clr ([0-9]\.)*[0-9]+|x11|en|ppc mac os x|macintosh|u|linux i[0-9]{1,4}|en-us|rv\:([0-9]+\.)*[0-9]+|aol [0-9]\.[0-9]|gnotify ([0-9]+\.)*[0-9]+)*\) *(gecko\/[0-9]+)? *(firefox\/([0-9]+.)*[0-9]+)?/;
const mozilla_head_pat = /mozilla\/[0-9]\.[0-9]/;
const misc_user_pat =
/spiderman/
| /w3m\/([0-9]+\.)*[0-9]+/
| /java([0-9]+\.)*[0-9]+(_[0-9]+)?/
| /java\/([0-9]+\.)*[0-9]+(_[0-9]+)?/
| /freecorder/
| /industry update control/
| /microsoft-cryptoapi\/([0-9]+\.)*[0-9]+/
| /ruriko\/([0-9]+\.)*[0-9]+/
| /crawler[0-9]\.[0-9]/
| /w3search/
| /symantec liveupdate/
| /davkit\/[0-9]\.[0-9]/
| /windows-media-player\/([0-9]+\.)*[0-9]+/
| /winamp\/([0-9]+\.)*[0-9]+/
| /headdump/
;
const misc_cmplx_user_pat =
/lynx\/([0-9]+\.)*[0-9]+.*/
| /wget\/([0-9]+\.)*[0-9]+.*/
| /yahooseeker\/([0-9]+\.)*[0-9]+.*/
| /rma\/([0-9]+\.)*[0-9]+.*/
| /aim\/[0-9]+.*/
| /ichiro\/([0-9]+\.)*[0-9]+.*/
| /unchaos.*/
| /irlbot\/[0-9]\.[0-9]+.*/
| /msnbot\/([0-9]+\.)*[0-9]+.*/
| /opera\/([0-9]+\.)*[0-9]+.*/
| /netnewswire\/([0-9]+\.)*[0-9]+.*/
| /nsplayer\/([0-9]+\.)*[0-9]+.*/
| /aipbot\/([0-9]+\.)*[0-9]+.*/
| /mac os x; webservicescore\.framework.*/
| /fast-webcrawler\/([0-9]+\.)*[0-9]+.*/
| /skype.*/
| /googlebot\/([0-9]+\.)*[0-9]+.*/
;
const misc_cmplx_user_start =
/lynx\/([0-9]+\.)*[0-9]+/
| /wget\/([0-9]+\.)*[0-9]+/
| /yahooseeker\/([0-9]+\.)*[0-9]+/
| /rma\/([0-9]+\.)*[0-9]+/
| /aim\/[0-9]+/
| /ichiro\/([0-9]+\.)*[0-9]+/
| /unchaos/
| /irlbot\/[0-9]\.[0-9]+/
| /opera\/([0-9]+\.)*[0-9]+/
| /msnbot\/([0-9]+\.)*[0-9]+/
| /netnewswire\/([0-9]+\.)*[0-9]+/
| /nsplayer\/([0-9]+\.)*[0-9]+/
| /aipbot\/([0-9]+\.)*[0-9]+/
| /mac os x; webservicescore\.framework/
| /fast-webcrawler\/([0-9]+\.)*[0-9]+/
| /skype/
| /googlebot\/([0-9]+\.)*[0-9]+/
;
function filter_in_http_useragent(user: string): string
{
# Check for an exact match for Mozilla.
if ( mozilla_full_pat in user )
return split_all(user, mozilla_full_pat)[2];
# Look for popular Mozilla-compatible crawlers.
if ( mozilla_head_pat in user )
{
local crawler = "(bro: unknown)";
if ( /.*yahoo\! slurp/ in user )
crawler = "(yahoo! slurp)";
else if ( /.*ask jeeves/ in user )
crawler = "(ask jeeves)";
else
print http_anon_log,
fmt("*** unknown Mozilla user-agent %s\n", user);
return fmt("%s %s", split_all(user, mozilla_head_pat)[2],
crawler);
}
# Some simple, common user names.
if ( misc_user_pat in user )
return user;
# Require some info removal.
if ( misc_cmplx_user_pat in user )
return split_all(user, misc_cmplx_user_pat)[2];
print http_anon_log,fmt("*** unknown user agent %s\n", user);
return fmt("(bro: unknown) %s", anonymize_arg("user-agent", user));
}

View file

@ -1,164 +0,0 @@
# $Id:$
@load anon
global http_anon_log = open_log_file("http-anon") &redef;
const URI_proto_pat = /^ *([a-zA-Z]+)\:\/\// ;
const known_URI_proto_pat = /^ *(http|https|ftp|ssh)\:\/\// ;
const host_pat = / *^([\-0-9a-zA-Z]+\.)+([\_\-0-9a-zA-Z])*/ ;
const port_pat = /^ *(\:[0-9]+\.)/ ;
const query_pat = /\?/ ;
function anonymize_http_URI(URI: string): string
{
URI = to_lower(URI);
# Strip off protocol.
local proto = "";
if ( URI_proto_pat in URI )
{
local proto_part = split(URI, /\:\/\//);
# Check if we know the protocol. If not, flag it so we
# can update our protocol database.
if ( known_URI_proto_pat !in URI )
{
print http_anon_log,
fmt("*** protocol %s unknown ", proto_part[1]);
proto_part[1] =
string_cat(" (bro: unknown) ",
anonymize_arg("proto", proto_part[1]));
}
proto = string_cat(proto_part[1],"://");
URI = proto_part[2];
}
# Strip off domain.
local host = "";
if ( host_pat in URI )
{
local base_parts =
split_all(URI, / *^([\-\_0-9a-z]+\.)+[\-\_0-9a-z]*/);
if ( |base_parts| < 2 )
{
print http_anon_log,
fmt (" XXXXXXXXXXXXXXXXXXXXXX BASE %s", URI);
return " XXXX processing error XXXX";
}
if ( |base_parts| == 2 )
URI = "";
else if ( |base_parts| == 3)
URI = base_parts[3];
else if ( |base_parts| > 3)
{
local patch_me = "";
local hack = base_parts[2];
local i = 1;
for ( part in base_parts )
{
if ( i != 2 )
patch_me = string_cat(patch_me,
base_parts[i]);
i += 1;
}
URI = patch_me;
}
if ( host == simple_filename )
host = anonymize_path(host);
else
host = anonymize_host(base_parts[2]);
}
# Strip off port (if it exists).
local pport = "";
if ( port_pat in URI )
{
print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
print "XXXXX anon.bro doing nothing with port XXXXXXXXXXX ";
print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
}
# Handle query (if exists).
local tail = "";
if ( URI == "/" )
{
# -- pass
}
else if ( query_pat in URI )
{
local query_part = split(URI, /\?/);
tail = fmt("%s?%s",
anonymize_path(query_part[1]),
anonymize_path(query_part[2]));
}
else
tail = anonymize_path(URI);
tail = string_cat("/", tail);
return fmt("%s%s%s%s", proto, host, pport, tail);
}
const a_href_pat = /.*\< *a *href.*\>.*/ ;
#/.*\< *a *href *= *\"[[:print:]]+\" *\>.*/;
# Doesn't get everything ... but works for most.
const a_href_split =
/\< *a *href *= *(\\)?(\"|\')?([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+(\\)?(\"|\')?/ ;
# Elegant ... yeah ... really .. :-/
const file_split =
/(\"|\')([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+(\"|\')/ ;
const file_strip_split = /([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+/ ;
function http_doc_link_list(abstract: string): string
{
abstract = to_lower(abstract);
if ( abstract == "" )
return abstract;
local concat_key = "";
local href_parts = split_all(abstract, a_href_split);
for ( part in href_parts )
{
if ( href_parts[part] == a_href_split )
{
local file_parts =
split_all(href_parts[part], file_split);
for ( a_part in file_parts )
{
if ( file_parts[a_part] == file_split )
{
local file_strip_parts =
split_all(file_parts[a_part],
file_strip_split);
concat_key = fmt("%s %s", concat_key,
anonymize_http_URI(file_strip_parts[2]));
}
}
}
}
return concat_key;
}

View file

@ -1,60 +0,0 @@
# $Id: http-body.bro 5230 2008-01-14 01:38:18Z vern $
# Counts length of data.
#
# If log_HTTP_data = T, it also outputs an abstract of data.
@load http
module HTTP;
redef process_HTTP_data = T;
redef log_HTTP_data = T;
export {
# If the following is > 0, then when logging contents, they will be
# truncated beyond this many bytes.
global content_truncation_limit = 40 &redef;
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
local len = byte_len(data);
msg$data_length = msg$data_length + length;
if ( log_HTTP_data )
{
local abstract: string;
if ( content_truncation_limit > 0 &&
len > content_truncation_limit )
abstract = cat(sub_bytes(data, 1, content_truncation_limit), "...");
else
abstract = data;
print http_log, fmt("%.6f %s %s %d bytes: \"%s\"",
network_time(), s$id,
is_orig ? "=>" : "<=", length,
abstract);
}
}
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
# This is for debugging purpose only
if ( msg$data_length > 0 &&
stat$body_length != msg$data_length + stat$content_gap_length)
{
# This can happen for multipart messages with a
# 'content-length' header, which is not required for multipart
# messages.
# Log::warning(fmt("length mismatch: %s %d %d %d",
# id_string(c$id), stat$body_length, msg$data_length,
# stat$content_gap_length));
}
}

View file

@ -1,45 +0,0 @@
@load http
module HTTP;
export {
redef enum Notice += {
PasswordFullFetch, # they got back the whole thing
PasswordShadowFetch, # they got back a shadowed version
};
# Pattern to search for in replies indicating that a full password
# file was returned.
const full_fetch =
/[[:alnum:]]+\:[[:alnum:]]+\:[[:digit:]]+\:[[:digit:]]+\:/
&redef;
# Same, but indicating a shadow password file was returned.
const shadow_fetch =
/[[:alnum:]]+\:\*\:[[:digit:]]+\:[[:digit:]]+\:/
&redef;
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
local s = lookup_http_request_stream(c);
local n = s$first_pending_request;
if ( n !in s$requests )
return;
local req = s$requests[n];
local passwd_request = req$passwd_req;
if ( ! passwd_request )
return;
if ( full_fetch in data )
NOTICE([$note=PasswordFullFetch,
$conn=c, $method=req$method, $URL=req$URI,
$msg=fmt("%s %s: %s %s", id_string(c$id), c$addl,
req$method, req$URI)]);
else if ( shadow_fetch in data )
NOTICE([$note=PasswordShadowFetch,
$conn=c, $method=req$method, $URL=req$URI,
$msg=fmt("%s %s: %s %s", id_string(c$id), c$addl,
req$method, req$URI)]);
}

View file

@ -1,20 +0,0 @@
# $Id: http-entity.bro 6 2004-04-30 00:31:26Z jason $
# Counts entity_level.
module HTTP;
event http_begin_entity(c: connection, is_orig: bool)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
++msg$entity_level;
}
event http_end_entity(c: connection, is_orig: bool)
{
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
if ( msg$entity_level > 0 )
--msg$entity_level;
}

View file

@ -1,12 +0,0 @@
# $Id: http-event.bro 6 2004-04-30 00:31:26Z jason $
@load http
module HTTP;
event http_event(c: connection, event_type: string, detail: string)
{
print http_log, fmt("%.6f %s HTTP event: [%s] \"%s\"",
network_time(), id_string(c$id),
event_type, detail);
}

View file

@ -1,41 +0,0 @@
# $Id:$
# Extracts the items from HTTP traffic, one per file.
# Files are named:
#
# <prefix>.<n>.<orig-addr>_<orig-port>.<resp-addr>_<resp-port>.<is-orig>
#
# where <prefix> is a redef'able prefix (default: "http-item"), <n> is
# a number uniquely identifying the item, the next four are describe
# the connection tuple, and <is-orig> is "orig" if the item was transferred
# from the originator to the responder, "resp" otherwise.
@load http-reply
module HTTP_extract_items;
global prefix = "http-item" &redef;
global item_file: table[conn_id] of file;
global nitems = 0;
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
local id = c$id;
if ( id !in item_file )
{
# Create a new file for this one.
local fname = fmt("%s.%d.%s_%d.%s_%d.%s",
prefix, ++nitems,
id$orig_h, id$orig_p,
id$resp_h, id$resp_p,
is_orig ? "orig" : "resp");
item_file[id] = open(fname);
}
write_file(item_file[id], data);
}
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
{
delete item_file[c$id];
}

View file

@ -1,36 +0,0 @@
# $Id: http-header.bro 7073 2010-09-13 00:45:02Z vern $
# Prints out detailed HTTP headers.
@load http
module HTTP;
export {
# The following lets you specify headers that you don't want
# to print out.
global skip_header: set[string] &redef;
# If you add anything to the following table, *only* the headers
# included will be recorded.
global include_header: set[string] &redef;
# For example:
# redef skip_header += { "COOKIE", "SET-COOKIE" };
# will refrain from printing cookies.
}
event http_header(c: connection, is_orig: bool, name: string, value: string)
{
if ( name in skip_header )
return;
if ( |include_header| > 0 && name !in include_header )
return;
local s = lookup_http_request_stream(c);
print http_log, fmt("%.6f %s %s %s: %s",
network_time(), s$id,
is_orig ? ">" : "<", name, value);
}

View file

@ -1,115 +0,0 @@
# $Id:$
#
# Analyze HTTP entities for sensitive types (e.g., executables).
#
# Contributed by Seth Hall.
@load http-reply
module HTTP;
const http_identified_log = open_log_file("http-id");
export {
# Base the libmagic analysis on this many bytes. Currently,
# we will in fact use fewer (basically, just what's in the
# first data packet).
const magic_content_limit = 1024 &redef;
# These MIME types are logged and generate a Notice. The patterns
# need to match the entire description as returned by libMagic.
# For example, for plain text it can return
# "text/plain charset=us-ascii", so you might want to use
# /text\/plain.*/.
const watched_mime_types =
/application\/x-dosexec/ # Windows and DOS executables
| /application\/x-executable/ # *NIX executable binary
&redef;
const watched_descriptions = /PHP script text/ &redef;
# URLs included here are not logged and notices are not generated.
# Take care when defining patterns to not be overly broad.
const ignored_urls =
/^http:\/\/www\.download\.windowsupdate\.com\// &redef;
redef enum Notice += {
# Generated when we see a MIME type we flagged for watching.
HTTP_WatchedMIMEType,
# Generated when the file extension doesn't match
# the file contents.
HTTP_IncorrectFileType,
};
# Create patterns that *should* be in the URLs for specific MIME types.
# Notices are generated if the pattern doesn't match.
const mime_types_extensions = {
["application/x-dosexec"] = /\.([eE][xX][eE]|[dD][lL][lL])/,
} &redef;
}
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
if ( is_orig )
# For now we only inspect server responses.
return;
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
@ifndef ( content_truncation_limit )
# This is only done if http-body.bro is not loaded.
msg$data_length = msg$data_length + length;
@endif
# For the time being, we'll just use the data from the first packet.
# Don't continue until we have enough data.
# if ( msg$data_length < magic_content_limit )
# return;
# Right now, only try this for the first chunk of data
if ( msg$data_length > length )
return;
local abstract = sub_bytes(data, 1, magic_content_limit);
local magic_mime = identify_data(abstract, T);
local magic_descr = identify_data(abstract, F);
if ( (magic_mime == watched_mime_types ||
watched_descriptions in magic_descr) &&
s$first_pending_request in s$requests )
{
local r = s$requests[s$first_pending_request];
local host = (s$next_request$host=="") ?
fmt("%s", c$id$resp_h) : s$next_request$host;
event file_transferred(c, abstract, magic_descr, magic_mime);
local url = fmt("http://%s%s", host, r$URI);
if ( ignored_urls in url )
return;
local file_type = "";
if ( magic_mime == watched_mime_types )
file_type = magic_mime;
else
file_type = magic_descr;
local message = fmt("%s %s %s %s",
id_string(c$id), file_type, r$method, url);
NOTICE([$note=HTTP_WatchedMIMEType, $msg=message, $conn=c,
$method=r$method, $URL=url]);
print http_identified_log, fmt("%.06f %s %s",
network_time(), s$id, message);
if ( (magic_mime in mime_types_extensions &&
mime_types_extensions[magic_mime] !in url) ||
(magic_descr in mime_types_extensions &&
mime_types_extensions[magic_descr] !in url) )
NOTICE([$note=HTTP_IncorrectFileType, $msg=message,
$conn=c, $method=r$method, $URL=url]);
}
}

View file

@ -1,117 +0,0 @@
# $Id: http-reply.bro 2694 2006-04-02 22:50:00Z vern $
@load http-request
module HTTP;
redef capture_filters += {
["http-reply"] = "tcp src port 80 or tcp src port 8080 or tcp src port 8000"
};
redef process_HTTP_replies = T;
event http_reply(c: connection, version: string, code: count, reason: string)
{
local s = lookup_http_request_stream(c);
local msg = s$next_reply;
init_http_message(msg);
msg$initiated = T;
msg$code = code;
msg$reason = reason;
}
function http_request_done(c: connection, stat: http_message_stat)
{
local s = lookup_http_request_stream(c);
local msg = s$next_request;
msg$initiated = F;
}
function http_reply_done(c: connection, stat: http_message_stat)
{
local s = lookup_http_request_stream(c);
local req_msg = s$next_request;
local msg = s$next_reply;
local req: string;
local have_request = F;
local log_it: bool;
if ( s$num_pending_requests == 0 )
{
# Weird - reply w/o request - perhaps due to cold start?
req = "<unknown request>";
log_it = F;
}
else
{
local r = s$requests[s$first_pending_request];
have_request = T;
# Remove pending request.
delete s$requests[s$first_pending_request];
--s$num_pending_requests;
++s$first_pending_request;
req = fmt("%s %s", r$method, r$URI);
log_it = r$log_it;
}
local req_rep =
fmt("%s (%d \"%s\" [%d%s]%s)",
req, msg$code, string_escape(msg$reason, "\""),
stat$body_length,
stat$interrupted ? " (interrupted)" : "",
have_request ? fmt(" %s", req_msg$host) : "");
# The following is a more verbose form:
# local req_rep =
# fmt("%s (%d \"%s\" [\"%s\", %d%s%s])",
# req, msg$code, msg$reason,
# msg$content_length, stat$body_length,
# stat$interrupted ? " (interrupted)" : "",
# stat$content_gap_length > 0 ?
# fmt(" (gap = %d bytes)", stat$content_gap_length) : "");
if ( log_it )
NOTICE([$note=HTTP_SensitiveURI, $conn=c,
$method = r$method, $URL = r$URI,
$n = msg$code,
$msg = fmt("%s %s: %s",
id_string(c$id), c$addl, req_rep)]);
print http_log, fmt("%.6f %s %s", network_time(), s$id, req_rep);
msg$initiated = F;
}
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
{
if ( is_orig )
http_request_done(c, stat);
else
http_reply_done(c, stat);
}
@load http-entity
event http_header(c: connection, is_orig: bool, name: string, value: string)
{
# Only rewrite top-level headers.
local s = lookup_http_request_stream(c);
local msg = get_http_message(s, is_orig);
if ( msg$entity_level == 1 )
{
if ( name == "CONTENT-LENGTH" )
msg$content_length = value;
else if ( is_orig && name == "HOST" )
{ # suppress leading blank
if ( /^ / in value )
msg$host = sub_bytes(value, 2, -1);
else
msg$host = value;
}
}
}

View file

@ -1,104 +0,0 @@
# $Id: http-request.bro 6726 2009-06-07 22:09:55Z vern $
# Analysis of HTTP requests.
@load http
module HTTP;
export {
const sensitive_URIs =
/etc\/(passwd|shadow|netconfig)/
| /IFS[ \t]*=/
| /nph-test-cgi\?/
| /(%0a|\.\.)\/(bin|etc|usr|tmp)/
| /\/Admin_files\/order\.log/
| /\/carbo\.dll/
| /\/cgi-bin\/(phf|php\.cgi|test-cgi)/
| /\/cgi-dos\/args\.bat/
| /\/cgi-win\/uploader\.exe/
| /\/search97\.vts/
| /tk\.tgz/
| /ownz/ # somewhat prone to false positives
| /viewtopic\.php.*%.*\(.*\(/ # PHP attack, 26Nov04
# a bunch of possible rootkits
| /sshd\.(tar|tgz).*/
| /[aA][dD][oO][rR][eE][bB][sS][dD].*/
# | /[tT][aA][gG][gG][eE][dD].*/ # prone to FPs
| /shv4\.(tar|tgz).*/
| /lrk\.(tar|tgz).*/
| /lyceum\.(tar|tgz).*/
| /maxty\.(tar|tgz).*/
| /rootII\.(tar|tgz).*/
| /invader\.(tar|tgz).*/
&redef;
# Used to look for attempted password file fetches.
const passwd_URI = /passwd/ &redef;
# URIs that match sensitive_URIs but can be generated by worms,
# and hence should not be flagged (because they're so common).
const worm_URIs =
/.*\/c\+dir/
| /.*cool.dll.*/
| /.*Admin.dll.*Admin.dll.*/
&redef;
# URIs that should not be considered sensitive if accessed by
# a local client.
const skip_remote_sensitive_URIs =
/\/cgi-bin\/(phf|php\.cgi|test-cgi)/
&redef;
const sensitive_post_URIs = /wwwroot|WWWROOT/ &redef;
}
redef capture_filters += {
["http-request"] = "tcp dst port 80 or tcp dst port 8080 or tcp dst port 8000"
};
event http_request(c: connection, method: string, original_URI: string,
unescaped_URI: string, version: string)
{
local log_it = F;
local URI = unescaped_URI;
if ( (sensitive_URIs in URI && URI != worm_URIs) ||
(method == "POST" && sensitive_post_URIs in URI) )
{
if ( is_local_addr(c$id$orig_h) &&
skip_remote_sensitive_URIs in URI )
; # don't flag it after all
else
log_it = T;
}
local s = lookup_http_request_stream(c);
if ( process_HTTP_replies )
{
# To process HTTP replies, we need to record the corresponding
# requests.
local n = s$first_pending_request + s$num_pending_requests;
s$requests[n] = [$method=method, $URI=URI, $log_it=log_it,
$passwd_req=passwd_URI in URI];
++s$num_pending_requests;
# if process_HTTP_messages
local msg = s$next_request;
init_http_message(msg);
msg$initiated = T;
}
else
{
if ( log_it )
NOTICE([$note=HTTP_SensitiveURI, $conn=c,
$method = method, $URL = URI,
$msg=fmt("%s %s: %s %s",
id_string(c$id), c$addl, method, URI)]);
print http_log,
fmt("%.6f %s %s %s", network_time(), s$id, method, URI);
}
}

View file

@ -1,203 +0,0 @@
# $Id: http.bro 6726 2009-06-07 22:09:55Z vern $
@load notice
@load site
@load conn-id
module HTTP;
export {
redef enum Notice += {
HTTP_SensitiveURI, # sensitive URI in GET/POST/HEAD
};
}
# DPM configuration.
global http_ports = {
80/tcp, 81/tcp, 631/tcp, 1080/tcp, 3138/tcp,
8000/tcp, 8080/tcp, 8888/tcp,
};
redef dpd_config += { [ANALYZER_HTTP] = [$ports = http_ports] };
redef dpd_config += { [ANALYZER_HTTP_BINPAC] = [$ports = http_ports] };
# HTTP processing options.
export {
const process_HTTP_replies = F &redef;
const process_HTTP_data = F &redef;
const include_HTTP_abstract = F &redef;
const log_HTTP_data = F &redef;
}
type http_pending_request: record {
method: string;
URI: string;
log_it: bool;
# Whether we determined it's an attempted passwd file fetch.
passwd_req: bool;
};
# Eventually we will combine http_pending_request and http_message.
type http_message: record {
initiated: bool;
code: count; # for HTTP reply message
reason: string; # for HTTP reply message
entity_level: count; # depth of enclosing MIME entities
data_length: count; # actual length of data delivered
content_length: string; # length specified in CONTENT-LENGTH header
header_slot: count; # rewrite slot at the end of headers
abstract: string; # data abstract
skip_abstract: bool; # to skip abstract for certain content types
host: string; # host indicated in Host header
};
type http_pending_request_stream: record {
# Number of first pending request.
first_pending_request: count &default = 0;
# Total number of pending requests.
num_pending_requests: count &default = 0;
# Indexed from [first_pending_request ..
# (first_pending_request + num_pending_requests - 1)]
requests: table[count] of http_pending_request;
next_request: http_message; # the on-going request
next_reply: http_message; # the on-going reply
# len_next_reply: count; # 0 means unspecified
# len_next_request: count;
id: string; # repeated from http_session_info, for convenience
};
type http_session_info: record {
id: string;
request_stream: http_pending_request_stream;
};
const http_log = open_log_file("http") &redef;
export {
global http_sessions: table[conn_id] of http_session_info;
}
global http_session_id = 0;
function init_http_message(msg: http_message)
{
msg$initiated = F;
msg$code = 0;
msg$reason = "";
msg$entity_level = 0;
msg$data_length = 0;
msg$content_length = "";
msg$header_slot = 0;
msg$abstract = "";
msg$skip_abstract = F;
msg$host = "";
}
function new_http_message(): http_message
{
local msg: http_message;
init_http_message(msg);
return msg;
}
function new_http_session(c: connection): http_session_info
{
local session = c$id;
local new_id = ++http_session_id;
local info: http_session_info;
info$id = fmt("%%%s", prefixed_id(new_id));
local rs: http_pending_request_stream;
rs$first_pending_request = 1;
rs$num_pending_requests = 0;
rs$id = info$id;
rs$next_request = new_http_message();
rs$next_reply = new_http_message();
rs$requests = table();
info$request_stream = rs;
http_sessions[session] = info;
print http_log, fmt("%.6f %s start %s:%d > %s:%d", network_time(),
info$id, c$id$orig_h,
c$id$orig_p, c$id$resp_h, c$id$resp_p);
return info;
}
function lookup_http_session(c: connection): http_session_info
{
local s: http_session_info;
local id = c$id;
s = id in http_sessions ? http_sessions[id] : new_http_session(c);
append_addl(c, s$id);
return s;
}
function lookup_http_request_stream(c: connection): http_pending_request_stream
{
local s = lookup_http_session(c);
return s$request_stream;
}
function get_http_message(s: http_pending_request_stream, is_orig: bool): http_message
{
return is_orig ? s$next_request : s$next_reply;
}
function finish_stream(session: conn_id, id: string,
rs: http_pending_request_stream)
{
### We really want to do this in sequential order, not table order.
for ( i in rs$requests )
{
local req = rs$requests[i];
if ( req$log_it )
NOTICE([$note=HTTP_SensitiveURI,
$src=session$orig_h, $dst=session$resp_h,
$URL=req$URI,
$method=req$method,
$msg=fmt("%s:%d -> %s:%d %s: <no reply>",
session$orig_h, session$orig_p,
session$resp_h, session$resp_p, id)]);
local msg = fmt("%s %s <no reply>", req$method, req$URI);
print http_log, fmt("%.6f %s %s", network_time(), rs$id, msg);
}
}
event connection_state_remove(c: connection)
{
local id = c$id;
if ( id !in http_sessions )
return;
local s = http_sessions[id];
finish_stream(id, s$id, s$request_stream);
delete http_sessions[c$id];
}
# event http_stats(c: connection, stats: http_stats_rec)
# {
# if ( stats$num_requests == 0 && stats$num_replies == 0 )
# return;
#
# c$addl = fmt("%s (%d v%.1f v%.1f)", c$addl, stats$num_requests, stats$request_version, stats$reply_version);
# }

View file

@ -1,306 +0,0 @@
# $Id: icmp.bro 6883 2009-08-19 21:08:09Z vern $
@load hot
@load weird
@load conn
@load scan
global icmp_file = open_log_file("icmp");
redef capture_filters += { ["icmp"] = "icmp" };
module ICMP;
export {
redef enum Notice += {
ICMPAsymPayload, # payload in echo req-resp not the same
ICMPConnectionPair, # too many ICMPs between hosts
ICMPAddressScan,
# The following isn't presently sufficiently useful due
# to cold start and packet drops.
# ICMPUnpairedEchoReply, # no EchoRequest seen for EchoReply
};
# Whether to log detailed information icmp.log.
const log_details = T &redef;
# ICMP scan detection.
const detect_scans = T &redef;
const scan_threshold = 25 &redef;
# Analysis of connection pairs.
const detect_conn_pairs = F &redef; # switch for connection pair
const detect_payload_asym = F &redef; # switch for echo payload
const conn_pair_threshold = 200 &redef;
}
global conn_pair:table[addr] of set[addr] &create_expire = 1 day;
global conn_pair_thresh_reached: table[addr] of bool &default=F;
type flow_id: record {
orig_h: addr;
resp_h: addr;
id: count;
};
type flow_info: record {
start_time: time;
last_time: time;
orig_bytes: count;
resp_bytes: count;
payload: string;
};
const names: table[count] of string = {
[0] = "echo_reply",
[3] = "unreach",
[4] = "quench",
[5] = "redirect",
[8] = "echo_req",
[9] = "router_adv",
[10] = "router_sol",
[11] = "time_xcd",
[12] = "param_prob",
[13] = "tstamp_req",
[14] = "tstamp_reply",
[15] = "info_req",
[16] = "info_reply",
[17] = "mask_req",
[18] = "mask_reply",
} &default = function(n: count): string { return fmt("icmp-%d", n); };
# Map IP protocol number to the protocol's name.
const IP_proto_name: table[count] of string = {
[1] = "ICMP",
[2] = "IGMP",
[6] = "TCP",
[17] = "UDP",
[41] = "IPV6",
} &default = function(n: count): string { return fmt("%s", n); }
&redef;
# Print a report for the given ICMP flow.
function generate_flow_summary(flow: flow_id, fi: flow_info)
{
local local_init = is_local_addr(flow$orig_h);
local local_addr = local_init ? flow$orig_h : flow$resp_h;
local remote_addr = local_init ? flow$resp_h : flow$orig_h;
local flags = local_init ? "L" : "";
local state: string;
if ( fi$orig_bytes > 0 )
{
if ( fi$resp_bytes > 0 )
state = "SF";
else
state = "SH";
}
else if ( fi$resp_bytes > 0 )
state = "SHR";
else
state = "OTH";
print icmp_file, fmt("%.6f %.6f %s %s %s %s %s %s %s",
fi$start_time, fi$last_time - fi$start_time,
flow$orig_h, flow$resp_h, "icmp_echo",
fi$orig_bytes, fi$resp_bytes, state, flags);
}
# Called when a flow is expired in order to generate a report for it.
function flush_flow(ft: table[flow_id] of flow_info, fi: flow_id): interval
{
generate_flow_summary(fi, ft[fi]);
return 0 sec;
}
# Table to track each active flow.
global flows: table[flow_id] of flow_info
&read_expire = 45 sec
&expire_func = flush_flow;
event icmp_sent(c: connection, icmp: icmp_conn)
{
print icmp_file, fmt("%.6f %.6f %s %s %s %s %s %s %s %s %s",
network_time(), 0.0, icmp$orig_h, icmp$resp_h,
names[icmp$itype], icmp$itype, icmp$icode, "icmp",
icmp$len, "0", "SH");
}
event flow_summary(flow: flow_id, last_time: time)
{
if ( flow !in flows )
return;
local fi = flows[flow];
if ( fi$last_time == last_time )
{
generate_flow_summary(flow, fi);
delete flows[flow];
}
}
function update_flow(icmp: icmp_conn, id: count, is_orig: bool, payload: string)
{
local fid: flow_id;
fid$orig_h = is_orig ? icmp$orig_h : icmp$resp_h;
fid$resp_h = is_orig ? icmp$resp_h : icmp$orig_h;
fid$id = id;
if ( fid !in flows )
{
local info: flow_info;
info$start_time = network_time();
info$orig_bytes = info$resp_bytes = 0;
info$payload = payload; # checked in icmp_echo_reply
flows[fid] = info;
}
local fi = flows[fid];
fi$last_time = network_time();
if ( is_orig )
fi$orig_bytes = fi$orig_bytes + byte_len(payload);
else
fi$resp_bytes = fi$resp_bytes + byte_len(payload);
schedule +30sec { flow_summary(fid, fi$last_time) };
}
event icmp_echo_request(c: connection, icmp: icmp_conn, id: count, seq: count, payload: string)
{
update_flow(icmp, id, T, payload);
local orig = icmp$orig_h;
local resp = icmp$resp_h;
# Simple ping scan detector.
if ( detect_scans &&
(orig !in Scan::distinct_peers ||
resp !in Scan::distinct_peers[orig]) )
{
if ( orig !in Scan::distinct_peers )
{
local empty_peer_set: set[addr] &mergeable;
Scan::distinct_peers[orig] = empty_peer_set;
}
if ( resp !in Scan::distinct_peers[orig] )
add Scan::distinct_peers[orig][resp];
if ( ! Scan::shut_down_thresh_reached[orig] &&
orig !in Scan::skip_scan_sources &&
orig !in Scan::skip_scan_nets &&
|Scan::distinct_peers[orig]| >= scan_threshold )
{
NOTICE([$note=ICMPAddressScan, $src=orig,
$n=scan_threshold,
$msg=fmt("%s has icmp echo scanned %s hosts",
orig, scan_threshold)]);
Scan::shut_down_thresh_reached[orig] = T;
}
}
if ( detect_conn_pairs )
{
if ( orig !in conn_pair )
{
local empty_peer_set2: set[addr] &mergeable;
conn_pair[orig] = empty_peer_set2;
}
if ( resp !in conn_pair[orig] )
add conn_pair[orig][resp];
if ( ! conn_pair_thresh_reached[orig] &&
|conn_pair[orig]| >= conn_pair_threshold )
{
NOTICE([$note=ICMPConnectionPair,
$msg=fmt("ICMP connection threshold exceeded : %s -> %s",
orig, resp)]);
conn_pair_thresh_reached[orig] = T;
}
}
}
event icmp_echo_reply(c: connection, icmp: icmp_conn, id: count,
seq: count, payload: string)
{
# Check payload with the associated flow.
local fid: flow_id;
fid$orig_h = icmp$resp_h; # We know the expected results since
fid$resp_h = icmp$orig_h; # it's an echo reply.
fid$id = id;
if ( fid !in flows )
{
# NOTICE([$note=ICMPUnpairedEchoReply,
# $msg=fmt("ICMP echo reply w/o request: %s -> %s",
# icmp$orig_h, icmp$resp_h)]);
}
else
{
if ( detect_payload_asym )
{
local fi = flows[fid];
local pl = fi$payload;
if ( pl != payload )
{
NOTICE([$note=ICMPAsymPayload,
$msg=fmt("ICMP payload inconsistancy: %s(%s) -> %s(%s)",
icmp$orig_h, byte_len(fi$payload),
icmp$resp_h, byte_len(payload))]);
}
}
}
update_flow(icmp, id, F, payload);
}
event icmp_unreachable(c: connection, icmp: icmp_conn, code: count,
context: icmp_context)
{
if ( active_connection(context$id) )
{
# This section allows Bro to act on ICMP-unreachable packets
# that happen in the context of an active connection. It is
# not currently used.
local c2 = connection_record(context$id);
local os = c2$orig$state;
local rs = c2$resp$state;
local is_attempt =
is_tcp_port(c2$id$orig_p) ?
(os == TCP_SYN_SENT && rs == TCP_INACTIVE) :
(os == UDP_ACTIVE && rs == UDP_INACTIVE);
# Insert action here.
}
if ( log_details )
{
# ICMP unreachable packets are the only ones currently
# logged. Due to the connection data contained *within*
# them, each log line will contain two connections' worth
# of data. The initial ICMP connection info is the same
# as logged for connections.
print icmp_file, fmt("%.6f %.6f %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
network_time(), 0.0, icmp$orig_h, icmp$resp_h,
names[icmp$itype], icmp$itype, icmp$icode, "icmp",
icmp$len, "0", "EncapPkt:",
# This is the encapsulated packet:
context$id$orig_h, context$id$orig_p,
context$id$resp_h, context$id$resp_p,
context$len, IP_proto_name[context$proto],
context$len, context$bad_hdr_len,
context$bad_checksum);
}
}

View file

@ -1,68 +0,0 @@
# $Id: ident.bro 5948 2008-07-11 22:29:49Z vern $
@load notice
@load hot-ids
module Ident;
export {
redef enum Notice += {
IdentSensitiveID, # sensitive username in Ident lookup
};
const hot_ident_ids = { always_hot_ids, } &redef;
const hot_ident_exceptions = { "uucp", "nuucp", "daemon", } &redef;
}
redef capture_filters += { ["ident"] = "tcp port 113" };
global ident_ports = { 113/tcp } &redef;
redef dpd_config += { [ANALYZER_IDENT] = [$ports = ident_ports] };
global pending_ident_requests: set[addr, port, addr, port, port, port];
event ident_request(c: connection, lport: port, rport: port)
{
local id = c$id;
add pending_ident_requests[id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport];
}
function add_ident_tag(c: connection, lport: port, rport: port, tag: string)
: connection
{
local id = c$id;
if ( [id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport] in
pending_ident_requests )
delete pending_ident_requests[id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport];
else
tag = fmt("orphan-%s", tag);
local c_orig_id = [$orig_h = id$resp_h, $orig_p = rport,
$resp_h = id$orig_h, $resp_p = lport];
local c_orig = active_connection(c_orig_id) ?
connection_record(c_orig_id) : c;
append_addl(c_orig, tag);
return c_orig;
}
event ident_reply(c: connection, lport: port, rport: port,
user_id: string, system: string)
{
local c_orig = add_ident_tag(c, lport, rport, fmt("ident/%s", user_id));
if ( user_id in hot_ident_ids && user_id !in hot_ident_exceptions )
{
++c_orig$hot;
NOTICE([$note=IdentSensitiveID, $conn=c,
$msg=fmt("%s hot ident: %s",
$user=c_orig$addl, id_string(c_orig$id))]);
}
}
event ident_error(c: connection, lport: port, rport: port, line: string)
{
add_ident_tag(c, lport, rport, fmt("iderr/%s", line));
}

View file

@ -1,31 +0,0 @@
# $Id: inactivity.bro 7073 2010-09-13 00:45:02Z vern $
@load port-name
const inactivity_timeouts: table[port] of interval = {
# For interactive services, allow longer periods of inactivity.
[[telnet, rlogin, ssh, ftp]] = 1 hrs,
} &redef;
function determine_inactivity_timeout(c: connection)
{
local service = c$id$resp_p;
# Determine service (adapted from hot.bro)
if ( c$orig$state == TCP_INACTIVE )
{
# We're seeing a half-established connection. Use the
# service of the originator if it's well-known and the
# responder isn't.
if ( service !in port_names && c$id$orig_p in port_names )
service = c$id$orig_p;
}
if ( service in inactivity_timeouts )
set_inactivity_timeout(c$id, inactivity_timeouts[service]);
}
event connection_established(c: connection)
{
determine_inactivity_timeout(c);
}

View file

@ -1,318 +0,0 @@
# $Id: interconn.bro 3997 2007-02-23 00:31:19Z vern $
#
# interconn - generic detection of interactive connections.
@load port-name
@load demux
# The following must be defined for the event engine to generate
# interconn events.
redef interconn_min_interarrival = 0.01 sec;
redef interconn_max_interarrival = 2.0 sec;
redef interconn_max_keystroke_pkt_size = 20;
redef interconn_default_pkt_size = 512;
redef interconn_stat_period = 15.0 sec;
redef interconn_stat_backoff = 1.5;
const interconn_min_num_pkts = 10 &redef; # min num of pkts sent
const interconn_min_duration = 2.0 sec &redef; # min duration for the connection
const interconn_ssh_len_disabled = T &redef;
const interconn_min_ssh_pkts_ratio = 0.6 &redef;
const interconn_min_bytes = 10 &redef;
const interconn_min_7bit_ascii_ratio = 0.75 &redef;
const interconn_min_num_lines = 2 &redef;
const interconn_min_normal_line_ratio = 0.5 &redef;
# alpha: portion of interarrival times within range
# [interconn_min_interarrival, interconn_max_interarrival]
#
# alpha should be >= interconn_min_alpha
#
# gamma: num_keystrokes_two_in_row / num_pkts
# gamma indicates the portion of keystrokes in the overall traffic
#
# gamma should be >= interconn_min_gamma
const interconn_min_alpha = 0.2 &redef; # minimum required alpha
const interconn_min_gamma = 0.2 &redef; # minimum required gamma
const interconn_standard_ports = { telnet, rlogin, ftp, ssh, smtp, 143/tcp, 110/tcp } &redef;
const interconn_ignore_standard_ports = F &redef;
const interconn_demux_disabled = T &redef;
const INTERCONN_UNKNOWN = 0; # direction/interactivity is unknown
const INTERCONN_FORWARD = 1; # forward: a conn's orig is true originator
const INTERCONN_BACKWARD = 2; # backward: a conn's resp is true originator
const INTERCONN_INTERACTIVE = 1; # a conn is interactive
const INTERCONN_STANDARD_PORT = 2; # conn involves a standard port to ignore
type conn_info : record {
interactive: count; # interactivity: unknown/interactive/standard_port
dir: count; # direction: unknown/forward/backward
};
global interconn_conns: table [conn_id] of conn_info; # table for all connections
# Table for resp_endp's of those established (non-partial) conn's.
# If a partial conn connects to one of such resp's, we can infer
# its direction.
global interconn_resps: table [addr, port] of count &default = 0;
global interconn_log = open_log_file("interconn") &redef;
global num_interconns = 0;
function interconn_conn_string(c: connection): string
{
return fmt("%.6f %s.%d > %s.%d",
c$start_time,
c$id$orig_h, c$id$orig_p,
c$id$resp_h, c$id$resp_p);
}
function interconn_weird(c: connection, s: string)
{
print fmt("%s interconn_weird: %s %s", network_time(), interconn_conn_string(c), s);
}
function get_direction(c: connection): count
{
local id = c$id;
if ( interconn_conns[id]$dir != INTERCONN_UNKNOWN )
return interconn_conns[id]$dir;
# The connection is not established yet, but one endpoint
# is a known resp_endp
if ( [id$resp_h, id$resp_p] in interconn_resps )
{
interconn_conns[id]$dir = INTERCONN_FORWARD;
++interconn_resps[id$resp_h, id$resp_p];
return INTERCONN_FORWARD;
}
else if ( [id$orig_h, id$orig_p] in interconn_resps )
{
interconn_conns[id]$dir = INTERCONN_BACKWARD;
++interconn_resps[id$orig_h, id$orig_p];
return INTERCONN_BACKWARD;
}
return INTERCONN_UNKNOWN;
}
function comp_gamma(s: interconn_endp_stats): double
{
return s$num_pkts >= interconn_min_num_pkts ?
(1.0 * s$num_keystrokes_two_in_row) / s$num_pkts : 0.0;
}
function comp_alpha(s: interconn_endp_stats) : double
{
return ( s$num_keystrokes_two_in_row > 0 ) ?
(1.0 * s$num_normal_interarrivals / s$num_keystrokes_two_in_row) : 0.0;
}
function skip_further_interconn_processing(c: connection)
{
# This used to call skip_further_processing()
# (if active_connection(c$id) returned T). But that's
# clearly wrong *if* we're also doing additional analysis
# on the connection. So do nothing.
}
function log_interconn(c: connection, tag: string)
{
print interconn_log, fmt("%s %s", interconn_conn_string(c), tag);
local id = c$id;
if ( interconn_demux_disabled )
skip_further_interconn_processing(c);
else
demux_conn(id, tag, "orig", "resp");
}
function is_interactive_endp(s: interconn_endp_stats): bool
{
# Criteria 1: num_pkts >= interconn_min_num_pkts.
if ( s$num_pkts < interconn_min_num_pkts )
return F;
# Criteria 2: gamma >= interconn_min_gamma.
if ( comp_gamma(s) < interconn_min_gamma )
return F;
# Criteria 3: alpha >= interconn_min_alpha.
if ( comp_alpha(s) < interconn_min_alpha )
return F;
return T;
}
event connection_established(c: connection)
{
local id = c$id;
local dir = interconn_conns[id]$dir;
if ( dir == INTERCONN_FORWARD )
return;
if ( dir == INTERCONN_BACKWARD )
{
interconn_weird(c, "inconsistent direction");
return;
}
interconn_conns[id]$dir = INTERCONN_FORWARD;
++interconn_resps[id$resp_h, id$resp_p];
}
event new_connection(c: connection)
{
local id = c$id;
local info: conn_info;
info$dir = INTERCONN_UNKNOWN;
if ( interconn_ignore_standard_ports &&
(id$orig_p in interconn_standard_ports ||
id$resp_p in interconn_standard_ports) )
{
info$interactive = INTERCONN_STANDARD_PORT;
skip_further_interconn_processing(c);
}
else
info$interactive = INTERCONN_UNKNOWN;
interconn_conns[id] = info;
}
event interconn_remove_conn(c: connection)
{
local id = c$id;
if ( id !in interconn_conns )
# This can happen for weird connections such as those
# with an initial SYN+FIN packet.
return;
local dir = interconn_conns[id]$dir;
delete interconn_conns[id];
delete demuxed_conn[c$id];
if ( dir == INTERCONN_FORWARD )
{
if ( --interconn_resps[id$resp_h, id$resp_p] == 0 )
delete interconn_resps[id$resp_h, id$resp_p];
}
else if ( dir == INTERCONN_BACKWARD )
{
if ( --interconn_resps[id$orig_h, id$orig_p] == 0 )
delete interconn_resps[id$orig_h, id$orig_p];
}
}
event interconn_stats(c: connection,
os: interconn_endp_stats, rs: interconn_endp_stats)
{
local id = c$id;
if ( id !in interconn_conns )
return;
if ( interconn_conns[id]$interactive != INTERCONN_UNKNOWN )
return; # already classified
if ( c$duration < interconn_min_duration )
# forget about excessively short connections
return;
local dir = get_direction(c);
# Criteria:
#
# if ( dir == FORWARD )
# (os) is interactive
# else if ( dir == BACKWARD )
# (rs) is interactive
# else
# either (os) or (rs) is interactive
if ( dir == INTERCONN_FORWARD )
{
if ( ! is_interactive_endp(os) )
return;
}
else if ( dir == INTERCONN_BACKWARD )
{
if ( ! is_interactive_endp(rs) )
return;
}
else
{
if ( ! is_interactive_endp(os) && ! is_interactive_endp(rs) )
return;
}
local tag: string;
if ( ! interconn_ssh_len_disabled && (os$is_partial || rs$is_partial) )
{
local num_pkts = os$num_pkts + rs$num_pkts;
local num_8k0_pkts = os$num_8k0_pkts + rs$num_8k0_pkts;
local num_8k4_pkts = os$num_8k4_pkts + rs$num_8k4_pkts;
if ( num_8k0_pkts > num_pkts * interconn_min_ssh_pkts_ratio )
{
# c now considered as interactive.
interconn_conns[id]$interactive = INTERCONN_INTERACTIVE;
tag = fmt("interconn.%d.ssh2", ++num_interconns);
}
else if ( num_8k4_pkts > num_pkts * interconn_min_ssh_pkts_ratio )
{
# c now considered as interactive.
interconn_conns[id]$interactive = INTERCONN_INTERACTIVE;
tag = fmt("interconn.%d.ssh1", ++num_interconns);
}
}
# Criteria 4: num_7bit_ascii / num_bytes is big enough; AND
# enough number of normal lines
if ( interconn_conns[id]$interactive != INTERCONN_INTERACTIVE )
{
local num_bytes = os$num_bytes + rs$num_bytes;
local num_7bit_ascii = os$num_7bit_ascii + rs$num_7bit_ascii;
if ( num_bytes < interconn_min_bytes ||
num_7bit_ascii < num_bytes * interconn_min_7bit_ascii_ratio )
return;
local num_lines = os$num_lines + rs$num_lines;
local num_normal_lines = os$num_normal_lines +
rs$num_normal_lines;
if ( num_lines < interconn_min_num_lines ||
num_normal_lines < num_lines * interconn_min_normal_line_ratio )
return;
# c now considered as interactive.
interconn_conns[id]$interactive = INTERCONN_INTERACTIVE;
tag = fmt("interconn.%d", ++num_interconns);
}
log_interconn(c, tag);
}

View file

@ -1,79 +0,0 @@
# $Id: irc-bot-syslog.bro,v 1.1.4.2 2006/05/31 00:16:21 sommer Exp $
#
# Passes current bot-state to syslog.
#
# - When a new server/client is found, we syslog it immediately.
# - Every IrcBot::summary_interval we dump the current set.
@load irc-bot
module IrcBotSyslog;
export {
# Prefix for all messages for easy grepping.
const prefix = "irc-bots" &redef;
}
# For debugging, everything which goes to syslog also goes here.
global syslog_file = open_log_file("irc-bots.syslog");
function fmt_time(t: time) : string
{
return strftime("%Y-%m-%d-%H-%M-%S", t);
}
function log_server(ip: addr, new: bool)
{
local s = IrcBot::servers[ip];
local ports = IrcBot::portset_to_str(s$p);
local msg = fmt("%s ip=%s new=%d local=%d server=1 first_seen=%s last_seen=%s ports=%s",
prefix, ip, new, is_local_addr(ip),
fmt_time(s$first_seen), fmt_time(s$last_seen), ports);
syslog(msg);
print syslog_file, fmt("%.6f %s", network_time(), msg);
}
function log_client(ip: addr, new: bool)
{
local c = IrcBot::clients[ip];
local servers = IrcBot::addrset_to_str(c$servers);
local msg = fmt("%s ip=%s new=%d local=%d server=0 first_seen=%s last_seen=%s user=%s nick=%s realname=%s servers=%s",
prefix, ip, new, is_local_addr(ip),
fmt_time(c$first_seen), fmt_time(c$last_seen),
c$user, c$nick, c$realname, servers);
syslog(msg);
print syslog_file, fmt("%.6f %s", network_time(), msg);
}
event print_bot_state()
{
for ( s in IrcBot::confirmed_bot_servers )
log_server(s, F);
for ( c in IrcBot::confirmed_bot_clients )
log_client(c, F);
}
event bro_init()
{
set_buf(syslog_file, F);
}
redef notice_policy += {
[$pred(a: notice_info) =
{
if ( a$note == IrcBot::IrcBotServerFound )
log_server(a$src, T);
if ( a$note == IrcBot::IrcBotClientFound )
log_client(a$src, T);
return F;
},
$result = NOTICE_FILE,
$priority = 1]
};

View file

@ -1,566 +0,0 @@
# $Id:$
@load conn
@load notice
@load weird
module IrcBot;
export {
global detailed_log = open_log_file("irc.detailed") &redef;
global bot_log = open_log_file("irc-bots") &redef;
global summary_interval = 1 min &redef;
global detailed_logging = T &redef;
global content_dir = "irc-bots" &redef;
global bot_nicks =
/^\[([^\]]+\|)+[0-9]{2,}]/ # [DEU|XP|L|00]
| /^\[[^ ]+\]([^ ]+\|)+([0-9a-zA-Z-]+)/ # [0]CHN|3436036 [DEU][1]3G-QE
| /^DCOM[0-9]+$/ # DCOM7845
| /^\{[A-Z]+\}-[0-9]+/ # {XP}-5021040
| /^\[[0-9]+-[A-Z0-9]+\][a-z]+/ # [0058-X2]wpbnlgwf
| /^\[[a-zA-Z0-9]\]-[a-zA-Z0-9]+$/ # [SD]-743056826
| /^[a-z]+[A-Z]+-[0-9]{5,}$/
| /^[A-Z]{3}-[0-9]{4}/ # ITD-1119
;
global bot_cmds =
/(^| *)[.?#!][^ ]{0,5}(scan|ndcass|download|cvar\.|execute|update|dcom|asc|scanall) /
| /(^| +\]\[ +)\* (ipscan|wormride)/
| /(^| *)asn1/
;
global skip_msgs =
/.*AUTH .*/
| /.*\*\*\* Your host is .*/
| /.*\*\*\* If you are having problems connecting .*/
;
redef enum Notice += {
IrcBotServerFound,
IrcBotClientFound,
};
type channel: record {
name: string;
passwords: set[string];
topic: string &default="";
topic_history: vector of string;
};
type bot_client: record {
host: addr;
p: port;
nick: string &default="";
user: string &default="";
realname: string &default="";
channels: table[string] of channel;
servers: set[addr] &optional;
first_seen: time;
last_seen: time;
};
type bot_server: record {
host: addr;
p: set[port];
clients: table[addr] of bot_client;
global_users: string &default="";
passwords: set[string];
channels: table[string] of channel;
first_seen: time;
last_seen: time;
};
type bot_conn: record {
client: bot_client;
server: bot_server;
conn: connection;
fd: file;
ircx: bool &default=F;
};
# We keep three sets of clients/servers:
# (1) tables containing all IRC clients/servers
# (2) sets containing potential bot hosts
# (3) sets containing confirmend bot hosts
#
# Hosts are confirmed when a connection is established between
# potential bot hosts.
#
# FIXME: (1) should really be moved into the general IRC script.
global expire_server:
function(t: table[addr] of bot_server, idx: addr): interval;
global expire_client:
function(t: table[addr] of bot_client, idx: addr): interval;
global servers: table[addr] of bot_server &write_expire=24 hrs
&expire_func=expire_server &persistent;
global clients: table[addr] of bot_client &write_expire=24 hrs
&expire_func=expire_client &persistent;
global potential_bot_clients: set[addr] &persistent;
global potential_bot_servers: set[addr] &persistent;
global confirmed_bot_clients: set[addr] &persistent;
global confirmed_bot_servers: set[addr] &persistent;
# All IRC connections.
global conns: table[conn_id] of bot_conn &persistent;
# Connections between confirmed hosts.
global bot_conns: set[conn_id] &persistent;
# Helper functions for readable output.
global strset_to_str: function(s: set[string]) : string;
global portset_to_str: function(s: set[port]) : string;
global addrset_to_str: function(s: set[addr]) : string;
}
function strset_to_str(s: set[string]) : string
{
if ( |s| == 0 )
return "<none>";
local r = "";
for ( i in s )
{
if ( r != "" )
r = cat(r, ",");
r = cat(r, fmt("\"%s\"", i));
}
return r;
}
function portset_to_str(s: set[port]) : string
{
if ( |s| == 0 )
return "<none>";
local r = "";
for ( i in s )
{
if ( r != "" )
r = cat(r, ",");
r = cat(r, fmt("%d", i));
}
return r;
}
function addrset_to_str(s: set[addr]) : string
{
if ( |s| == 0 )
return "<none>";
local r = "";
for ( i in s )
{
if ( r != "" )
r = cat(r, ",");
r = cat(r, fmt("%s", i));
}
return r;
}
function fmt_time(t: time) : string
{
return strftime("%y-%m-%d-%H-%M-%S", t);
}
event print_bot_state()
{
local bot_summary_log = open_log_file("irc-bots.summary");
disable_print_hook(bot_summary_log);
print bot_summary_log, "---------------------------";
print bot_summary_log, strftime("%y-%m-%d-%H-%M-%S", network_time());
print bot_summary_log, "---------------------------";
print bot_summary_log;
print bot_summary_log, "Known servers";
for ( h in confirmed_bot_servers )
{
local s = servers[h];
print bot_summary_log,
fmt(" %s %s - clients: %d ports %s password(s) %s last-seen %s first-seen %s global-users %s",
(is_local_addr(s$host) ? "L" : "R"),
s$host, length(s$clients), portset_to_str(s$p),
strset_to_str(s$passwords),
fmt_time(s$last_seen), fmt_time(s$first_seen),
s$global_users);
for ( name in s$channels )
{
local ch = s$channels[name];
print bot_summary_log,
fmt(" channel %s: topic \"%s\", password(s) %s",
ch$name, ch$topic,
strset_to_str(ch$passwords));
}
}
print bot_summary_log, "\nKnown clients";
for ( h in confirmed_bot_clients )
{
local c = clients[h];
print bot_summary_log,
fmt(" %s %s - server(s) %s user %s nick %s realname %s last-seen %s first-seen %s",
(is_local_addr(h) ? "L" : "R"), h,
addrset_to_str(c$servers),
c$user, c$nick, c$realname,
fmt_time(c$last_seen), fmt_time(c$first_seen));
}
close(bot_summary_log);
if ( summary_interval != 0 secs )
schedule summary_interval { print_bot_state() };
}
event bro_init()
{
if ( summary_interval != 0 secs )
schedule summary_interval { print_bot_state() };
}
function do_log_force(c: connection, msg: string)
{
local id = c$id;
print bot_log, fmt("%.6f %s:%d > %s:%d %s %s",
network_time(), id$orig_h, id$orig_p,
id$resp_h, id$resp_p, c$addl, msg);
}
function do_log(c: connection, msg: string)
{
if ( c$id !in bot_conns )
return;
do_log_force(c, msg);
}
function log_msg(c: connection, cmd: string, prefix: string, msg: string)
{
if ( skip_msgs in msg )
return;
do_log(c, fmt("MSG command=%s prefix=%s msg=\"%s\"", cmd, prefix, msg));
}
function update_timestamps(c: connection) : bot_conn
{
local conn = conns[c$id];
conn$client$last_seen = network_time();
conn$server$last_seen = network_time();
# To prevent the set of entries from premature expiration,
# we need to make a write access (can't use read_expire as we
# iterate over the entries on a regular basis).
clients[c$id$orig_h] = conn$client;
servers[c$id$resp_h] = conn$server;
return conn;
}
function add_server(c: connection) : bot_server
{
local s_h = c$id$resp_h;
if ( s_h in servers )
return servers[s_h];
local empty_table1: table[addr] of bot_client;
local empty_table2: table[string] of channel;
local empty_set: set[string];
local empty_set2: set[port];
local server = [$host=s_h, $p=empty_set2, $clients=empty_table1,
$channels=empty_table2, $passwords=empty_set,
$first_seen=network_time(), $last_seen=network_time()];
servers[s_h] = server;
return server;
}
function add_client(c: connection) : bot_client
{
local c_h = c$id$orig_h;
if ( c_h in clients )
return clients[c_h];
local empty_table: table[string] of channel;
local empty_set: set[addr];
local client = [$host=c_h, $p=c$id$resp_p, $servers=empty_set,
$channels=empty_table, $first_seen=network_time(),
$last_seen=network_time()];
clients[c_h] = client;
return client;
}
function check_bot_conn(c: connection)
{
if ( c$id in bot_conns )
return;
local client = c$id$orig_h;
local server = c$id$resp_h;
if ( client !in potential_bot_clients || server !in potential_bot_servers )
return;
# New confirmed bot_conn.
add bot_conns[c$id];
if ( server !in confirmed_bot_servers )
{
NOTICE([$note=IrcBotServerFound, $src=server, $p=c$id$resp_p, $conn=c,
$msg=fmt("ircbot server found: %s:%d", server, $p=c$id$resp_p)]);
add confirmed_bot_servers[server];
}
if ( client !in confirmed_bot_clients )
{
NOTICE([$note=IrcBotClientFound, $src=client, $p=c$id$orig_p, $conn=c,
$msg=fmt("ircbot client found: %s:%d", client, $p=c$id$orig_p)]);
add confirmed_bot_clients[client];
}
}
function get_conn(c: connection) : bot_conn
{
local conn: bot_conn;
if ( c$id in conns )
{
check_bot_conn(c);
return update_timestamps(c);
}
local c_h = c$id$orig_h;
local s_h = c$id$resp_h;
local client : bot_client;
local server : bot_server;
if ( c_h in clients )
client = clients[c_h];
else
client = add_client(c);
if ( s_h in servers )
server = servers[s_h];
else
server = add_server(c);
server$clients[c_h] = client;
add server$p[c$id$resp_p];
add client$servers[s_h];
conn$server = server;
conn$client = client;
conn$conn = c;
conns[c$id] = conn;
update_timestamps(c);
return conn;
}
function expire_server(t: table[addr] of bot_server, idx: addr): interval
{
local server = t[idx];
for ( c in server$clients )
{
local client = server$clients[c];
delete client$servers[idx];
}
delete potential_bot_servers[idx];
delete confirmed_bot_servers[idx];
return 0secs;
}
function expire_client(t: table[addr] of bot_client, idx: addr): interval
{
local client = t[idx];
for ( s in client$servers )
if ( s in servers )
delete servers[s]$clients[idx];
delete potential_bot_clients[idx];
delete confirmed_bot_clients[idx];
return 0secs;
}
function remove_connection(c: connection)
{
local conn = conns[c$id];
delete conns[c$id];
delete bot_conns[c$id];
}
event connection_state_remove(c: connection)
{
if ( c$id !in conns )
return;
remove_connection(c);
}
event bro_init()
{
set_buf(detailed_log, F);
set_buf(bot_log, F);
}
event irc_client(c: connection, prefix: string, data: string)
{
if ( detailed_logging )
print detailed_log, fmt("%.6f %s > (%s) %s", network_time(), id_string(c$id), prefix, data);
local conn = get_conn(c);
if ( data == /^ *[iI][rR][cC][xX] *$/ )
conn$ircx = T;
}
event irc_server(c: connection, prefix: string, data: string)
{
if ( detailed_logging )
print detailed_log, fmt("%.6f %s < (%s) %s", network_time(), id_string(c$id), prefix, data);
local conn = get_conn(c);
}
event irc_user_message(c: connection, user: string, host: string, server: string, real_name: string)
{
local conn = get_conn(c);
conn$client$user = user;
conn$client$realname = real_name;
do_log(c, fmt("USER user=%s host=%s server=%s real_name=%s", user, host, server, real_name));
}
function get_channel(conn: bot_conn, channel: string) : channel
{
if ( channel in conn$server$channels )
return conn$server$channels[channel];
else
{
local empty_set: set[string];
local empty_vec: vector of string;
local ch = [$name=channel, $passwords=empty_set, $topic_history=empty_vec];
conn$server$channels[ch$name] = ch;
return ch;
}
}
event irc_join_message(c: connection, info_list: irc_join_list)
{
local conn = get_conn(c);
for ( i in info_list )
{
local ch = get_channel(conn, i$channel);
if ( i$password != "" )
add ch$passwords[i$password];
conn$client$channels[ch$name] = ch;
do_log(c, fmt("JOIN channel=%s password=%s", i$channel, i$password));
}
}
global urls: set[string] &read_expire = 7 days &persistent;
event http_request(c: connection, method: string, original_URI: string,
unescaped_URI: string, version: string)
{
if ( original_URI in urls )
do_log_force(c, fmt("Request for URL %s", original_URI));
}
event irc_channel_topic(c: connection, channel: string, topic: string)
{
if ( bot_cmds in topic )
{
do_log_force(c, fmt("Matching TOPIC %s", topic));
add potential_bot_servers[c$id$resp_h];
}
local conn = get_conn(c);
local ch = get_channel(conn, channel);
ch$topic_history[|ch$topic_history|] = ch$topic;
ch$topic = topic;
if ( c$id in bot_conns )
{
do_log(c, fmt("TOPIC channel=%s topic=\"%s\"", channel, topic));
local s = split(topic, / /);
for ( i in s )
{
local w = s[i];
if ( w == /[a-zA-Z]+:\/\/.*/ )
{
add urls[w];
do_log(c, fmt("URL channel=%s url=\"%s\"",
channel, w));
}
}
}
}
event irc_nick_message(c: connection, who: string, newnick: string)
{
if ( bot_nicks in newnick )
{
do_log_force(c, fmt("Matching NICK %s", newnick));
add potential_bot_clients[c$id$orig_h];
}
local conn = get_conn(c);
conn$client$nick = newnick;
do_log(c, fmt("NICK who=%s nick=%s", who, newnick));
}
event irc_password_message(c: connection, password: string)
{
local conn = get_conn(c);
add conn$server$passwords[password];
do_log(c, fmt("PASS password=%s", password));
}
event irc_privmsg_message(c: connection, source: string, target: string,
message: string)
{
log_msg(c, "privmsg", source, fmt("->%s %s", target, message));
}
event irc_notice_message(c: connection, source: string, target: string,
message: string)
{
log_msg(c, "notice", source, fmt("->%s %s", target, message));
}
event irc_global_users(c: connection, prefix: string, msg: string)
{
local conn = get_conn(c);
# Better would be to parse the message to extract the counts.
conn$server$global_users = msg;
log_msg(c, "globalusers", prefix, msg);
}

View file

@ -1,689 +0,0 @@
# $Id: irc.bro 4758 2007-08-10 06:49:23Z vern $
@load conn-id
@load notice
@load weird
@load signatures
module IRC;
export {
const log_file = open_log_file("irc") &redef;
type irc_user: record {
u_nick: string; # nick name
u_real: string; # real name
u_host: string; # client host
u_channels: set[string]; # channels the user is member of
u_is_operator: bool; # user is server operator
u_conn: connection; # connection handle
};
type irc_channel: record {
c_name: string; # channel name
c_users: set[string]; # users in channel
c_ops: set[string]; # channel operators
c_type: string; # channel type
c_modes: string; # channel modes
c_topic: string; # channel topic
};
global expired_user:
function(t: table[string] of irc_user, idx: string): interval;
global expired_channel:
function(t: table[string] of irc_channel, idx: string): interval;
# Commands to ignore in irc_request/irc_message.
const ignore_in_other_msgs = { "PING", "PONG", "ISON" } &redef;
# Return codes to ignore in irc_response
const ignore_in_other_responses: set[count] = {
303 # RPL_ISON
} &redef;
# Active users, indexed by nick.
global active_users: table[string] of irc_user &read_expire = 6 hrs
&expire_func = expired_user &redef;
# Active channels, indexed by channel name.
global active_channels: table[string] of irc_channel
&read_expire = 6 hrs
&expire_func = expired_channel &redef;
# Strings that generate a notice if found in session dialog.
const hot_words =
/.*etc\/shadow.*/
| /.*etc\/ldap.secret.*/
| /.*phatbot.*/
| /.*botnet.*/
&redef;
redef enum Notice += {
IRC_HotWord,
};
}
# IRC ports. This could be widened to 6660-6669, say.
redef capture_filters += { ["irc-6666"] = "port 6666" };
redef capture_filters += { ["irc-6667"] = "port 6667" };
# DPM configuration.
global irc_ports = { 6666/tcp, 6667/tcp } &redef;
redef dpd_config += { [ANALYZER_IRC] = [$ports = irc_ports] };
redef Weird::weird_action += {
["irc_invalid_dcc_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_invite_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_kick_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_line"] = Weird::WEIRD_FILE,
["irc_invalid_mode_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_names_line"] = Weird::WEIRD_FILE,
["irc_invalid_njoin_line"] = Weird::WEIRD_FILE,
["irc_invalid_notice_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_oper_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_privmsg_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_reply_number"] = Weird::WEIRD_FILE,
["irc_invalid_squery_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_who_line"] = Weird::WEIRD_FILE,
["irc_invalid_who_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_whois_channel_line"] = Weird::WEIRD_FILE,
["irc_invalid_whois_message_format"] = Weird::WEIRD_FILE,
["irc_invalid_whois_operator_line"] = Weird::WEIRD_FILE,
["irc_invalid_whois_user_line"] = Weird::WEIRD_FILE,
["irc_line_size_exceeded"] = Weird::WEIRD_FILE,
["irc_line_too_short"] = Weird::WEIRD_FILE,
["irc_partial_request"] = Weird::WEIRD_FILE,
["irc_too_many_invalid"] = Weird::WEIRD_FILE,
};
# # IRC servers to identify server-to-server connections.
# redef irc_servers = {
# # German IRCnet servers
# irc.leo.org,
# irc.fu-berlin.de,
# irc.uni-erlangen.de,
# irc.belwue.de,
# irc.freenet.de,
# irc.tu-ilmenau.de,
# irc.rz.uni-karlsruhe.de,
# };
global conn_list: table[conn_id] of count;
global conn_ID = 0;
global check_connection: function(c: connection);
function irc_check_hot(c: connection, s: string, context: string)
{
if ( s == hot_words )
NOTICE([$note=IRC_HotWord, $conn=c,
$msg=fmt("IRC hot word in: %s", context)]);
}
function log_activity(c: connection, msg: string)
{
print log_file, fmt("%.6f #%s %s",
network_time(), conn_list[c$id], msg);
}
event connection_state_remove(c: connection)
{
delete conn_list[c$id];
}
event irc_request(c: connection, prefix: string,
command: string, arguments: string)
{
check_connection(c);
local context = fmt("%s %s", command, arguments);
irc_check_hot(c, command, context);
irc_check_hot(c, arguments, context);
if ( command !in ignore_in_other_msgs )
log_activity(c, fmt("other request%s%s: %s",
prefix == "" ? "" : " ",
prefix, context));
}
event irc_reply(c: connection, prefix: string, code: count, params: string)
{
check_connection(c);
local context = fmt("%s %s", code, params);
irc_check_hot(c, params, context);
if ( code !in ignore_in_other_responses )
log_activity(c, fmt("other response from %s: %s",
prefix, context));
}
event irc_message(c: connection, prefix: string,
command: string, message: string)
{
check_connection(c);
# Sanity checks whether this is indeed IRC.
#
# If we happen to parse an HTTP connection, the server "commands" will
# end with ":".
if ( command == /.*:$/ )
{
local aid = current_analyzer();
event protocol_violation(c, ANALYZER_IRC, aid, "broken server response");
return;
}
local context = fmt("%s %s", command, message);
irc_check_hot(c, command, context);
irc_check_hot(c, message, context);
if ( command !in ignore_in_other_msgs )
log_activity(c, fmt("other server message from %s: %s",
prefix, context));
}
event irc_user_message(c: connection, user: string, host: string,
server: string, real_name: string)
{
check_connection(c);
log_activity(c, fmt("new user, user='%s', host='%s', server='%s', real = '%s'",
user, host, server, real_name));
if ( user in active_users )
active_users[user]$u_conn = c;
else
{
local u: irc_user;
u$u_nick = user;
u$u_real = real_name;
u$u_conn = c;
u$u_host = "";
u$u_is_operator = F;
active_users[user] = u;
}
}
event irc_quit_message(c: connection, nick: string, message: string)
{
check_connection(c);
log_activity(c, fmt("user '%s' leaving%s", nick,
message == "" ? "" : fmt(", \"%s\"", message)));
# Remove from lists.
if ( nick in active_users )
{
delete active_users[nick];
for ( my_channel in active_channels )
delete active_channels[my_channel]$c_users[nick];
}
}
function check_message(c: connection, source: string, target: string,
msg: string, msg_type: string)
{
check_connection(c);
irc_check_hot(c, msg, msg);
log_activity(c, fmt("%s%s to '%s': %s", msg_type,
source != "" ? fmt(" from '%s'", source) : "",
target, msg));
}
event irc_privmsg_message(c: connection, source: string, target: string,
message: string)
{
check_message(c, source, target, message, "message");
}
event irc_notice_message(c: connection, source: string, target: string,
message: string)
{
check_message(c, source, target, message, "notice");
}
event irc_squery_message(c: connection, source: string, target: string,
message: string)
{
check_message(c, source, target, message, "squery");
}
event irc_join_message(c: connection, info_list: irc_join_list)
{
check_connection(c);
for ( l in info_list )
{
log_activity(c, fmt("user '%s' joined '%s'%s",
l$nick, l$channel,
l$password != "" ?
fmt("with password '%s'",
l$password) : ""));
if ( l$nick == "" )
next;
if ( l$nick in active_users )
add (active_users[l$nick]$u_channels)[l$channel];
else
{
local user: irc_user;
user$u_nick = l$nick;
user$u_real = "";
user$u_conn = c;
user$u_host = "";
user$u_is_operator = F;
add user$u_channels[l$channel];
active_users[l$nick] = user;
}
# Add channel to lists.
if ( l$channel in active_channels )
add (active_channels[l$channel]$c_users)[l$nick];
else
{
local my_c: irc_channel;
my_c$c_name = l$channel;
add my_c$c_users[l$nick];
my_c$c_type = my_c$c_modes = "";
active_channels[l$channel] = my_c;
}
}
}
event irc_part_message(c: connection, nick: string,
chans: string_set, message: string)
{
check_connection(c);
local channel_str = "";
for ( ch in chans )
channel_str = channel_str == "" ?
ch : fmt("%s, %s", channel_str, ch);
log_activity(c, fmt("%s channel '%s'%s",
nick == "" ? "leaving" :
fmt("user '%s' leaving", nick),
channel_str,
message == "" ?
"" : fmt("with message '%s'", message)));
# Remove user from channel.
if ( nick == "" )
return;
for ( ch in active_channels )
{
delete (active_channels[ch]$c_users)[nick];
delete (active_channels[ch]$c_ops)[nick];
if ( nick in active_users )
delete (active_users[nick]$u_channels)[ch];
}
}
event irc_nick_message(c: connection, who: string, newnick: string)
{
check_connection(c);
log_activity(c, fmt("%s nick name to '%s'",
who == "" ? "changing" :
fmt("user '%s' changing", who),
newnick));
}
event irc_invalid_nick(c: connection)
{
check_connection(c);
log_activity(c, "changing nick name failed");
}
event irc_network_info(c: connection, users: count, services: count,
servers: count)
{
check_connection(c);
log_activity(c, fmt("network includes %d users, %d services, %d servers",
users, services, servers));
}
event irc_server_info(c: connection, users: count, services: count,
servers: count)
{
check_connection(c);
log_activity(c, fmt("server includes %d users, %d services, %d peers",
users, services, servers));
}
event irc_channel_info(c: connection, chans: count)
{
check_connection(c);
log_activity(c, fmt("network includes %d channels", chans));
}
event irc_who_line(c: connection, target_nick: string, channel: string,
user: string, host: string, server: string,
nick: string, params: string, hops: count,
real_name: string)
{
check_connection(c);
log_activity(c, fmt("channel '%s' includes '%s' on %s connected to %s with nick '%s', real name '%s', params %s",
channel, user, host, server,
nick, real_name, params));
if ( nick == "" || channel == "" )
return;
if ( nick in active_users )
active_users[nick]$u_conn = c;
else
{
local myuser: irc_user;
myuser$u_nick = nick;
myuser$u_real = real_name;
myuser$u_conn = c;
myuser$u_host = host;
myuser$u_is_operator = F;
add myuser$u_channels[channel];
active_users[nick] = myuser;
if ( channel in active_channels )
add (active_channels[channel]$c_users)[nick];
else
{
local my_c: irc_channel;
my_c$c_name = channel;
add my_c$c_users[nick];
my_c$c_type = "";
my_c$c_modes = "";
active_channels[channel] = my_c;
}
}
}
event irc_who_message(c: connection, mask: string, oper: bool)
{
check_connection(c);
log_activity(c, fmt("WHO with mask %s%s", mask,
oper ? ", only operators" : ""));
}
event irc_whois_message(c: connection, server: string, users: string)
{
check_connection(c);
log_activity(c, fmt("WHOIS%s for user(s) %s",
server == "" ?
server : fmt(" to server %s", server),
users));
}
event irc_whois_user_line(c: connection, nick: string,
user: string, host: string, real_name: string)
{
check_connection(c);
log_activity(c, fmt("user '%s' with nick '%s' on host %s has real name '%s'",
user, nick, host, real_name));
if ( nick in active_users )
{
active_users[nick]$u_real = real_name;
active_users[nick]$u_host = host;
}
else
{
local u: irc_user;
u$u_nick = nick;
u$u_real = real_name;
u$u_conn = c;
u$u_host = host;
u$u_is_operator = F;
active_users[nick] = u;
}
}
event irc_whois_operator_line(c: connection, nick: string)
{
check_connection(c);
log_activity(c, fmt("user '%s' is an IRC operator", nick));
if ( nick in active_users )
active_users[nick]$u_is_operator = T;
else
{
local u: irc_user;
u$u_nick = nick;
u$u_real = "";
u$u_conn = c;
u$u_host = "";
u$u_is_operator = T;
active_users[nick] = u;
}
}
event irc_whois_channel_line(c: connection, nick: string, chans: string_set)
{
check_connection(c);
local message = fmt("user '%s' is on channels:", nick);
for ( channel in chans )
message = fmt("%s %s", message, channel);
log_activity(c, message);
if ( nick in active_users )
{
for ( ch in chans )
add active_users[nick]$u_channels[ch];
}
else
{
local u: irc_user;
u$u_nick = nick;
u$u_real = "";
u$u_conn = c;
u$u_host = "";
u$u_is_operator = F;
u$u_channels = chans;
active_users[nick] = u;
}
for ( ch in chans )
{
if ( ch in active_channels )
add (active_channels[ch]$c_users)[nick];
else
{
local my_c: irc_channel;
my_c$c_name = ch;
add my_c$c_users[nick];
my_c$c_type = "";
my_c$c_modes = "";
active_channels[ch] = my_c;
}
}
}
event irc_oper_message(c: connection, user: string, password: string)
{
check_connection(c);
log_activity(c, fmt("user requests operator status with name '%s', password '%s'",
user, password));
}
event irc_oper_response(c: connection, got_oper: bool)
{
check_connection(c);
log_activity(c, fmt("user %s operator status",
got_oper ? "received" : "did not receive"));
}
event irc_kick_message(c: connection, prefix: string, chans: string,
users: string, comment: string)
{
check_connection(c);
log_activity(c, fmt("user '%s' requested to kick '%s' from channel(s) %s with comment %s",
prefix, users, chans, comment));
}
event irc_error_message(c: connection, prefix: string, message: string)
{
check_connection(c);
log_activity(c, fmt("error message%s: %s",
prefix == "" ? "" : fmt("from '%s'", prefix),
message));
}
event irc_invite_message(c: connection, prefix: string,
nickname: string, channel: string)
{
check_connection(c);
log_activity(c, fmt("'%s' invited to channel %s%s",
nickname, channel,
prefix == "" ? "" : fmt(" by %s", prefix)));
}
event irc_mode_message(c: connection, prefix: string, params: string)
{
check_connection(c);
log_activity(c, fmt("mode command%s: %s",
prefix == "" ? "" : fmt(" from '%s'", prefix),
params));
}
event irc_squit_message(c: connection, prefix: string,
server: string, message: string)
{
check_connection(c);
log_activity(c, fmt("server disconnect attempt%s for %s with comment %s",
prefix == "" ? "" : fmt(" from '%s'", prefix),
server, message));
}
event irc_names_info(c: connection, c_type: string, channel: string,
users: string_set)
{
check_connection(c);
local chan_type =
c_type == "@" ? "secret" :
(c_type == "*" ? "private" : "public");
local message = fmt("channel '%s' (%s) contains users:",
channel, chan_type);
for ( user in users )
message = fmt("%s %s", message, user);
log_activity(c, message);
if ( channel in active_channels )
{
for ( u in users )
add (active_channels[channel]$c_users)[u];
}
else
{
local my_c: irc_channel;
my_c$c_name = channel;
my_c$c_users = users;
my_c$c_type = "";
my_c$c_modes = "";
active_channels[channel] = my_c;
}
for ( nick in users )
{
if ( nick in active_users )
add (active_users[nick]$u_channels)[channel];
else
{
local usr: irc_user;
usr$u_nick = nick;
usr$u_real = "";
usr$u_conn = c;
usr$u_host = "";
usr$u_is_operator = F;
add usr$u_channels[channel];
active_users[nick] = usr;
}
}
}
event irc_dcc_message(c: connection, prefix: string, target: string,
dcc_type: string, argument: string,
address: addr, dest_port: count, size: count)
{
check_connection(c);
log_activity(c, fmt("DCC %s invitation for '%s' to host %s on port %s%s",
dcc_type, target, address, dest_port,
dcc_type == "SEND" ?
fmt(" (%s: %s bytes)", argument, size) :
""));
}
event irc_channel_topic(c: connection, channel: string, topic: string)
{
check_connection(c);
log_activity(c, fmt("topic for %s is '%s'", channel, topic));
}
event irc_password_message(c: connection, password: string)
{
check_connection(c);
log_activity(c, fmt("password %s", password));
}
function expired_user(t: table[string] of irc_user, idx: string): interval
{
for ( my_c in active_users[idx]$u_channels )
{
suspend_state_updates();
delete active_channels[my_c]$c_users[idx];
delete active_channels[my_c]$c_ops[idx];
resume_state_updates();
}
return 0 secs;
}
function expired_channel(t:table[string] of irc_channel, idx: string): interval
{
for ( my_u in active_channels[idx]$c_users )
if ( my_u in active_users )
delete active_users[my_u]$u_channels[idx];
# Else is there a possible state leak? How could it not
# be in active_users? Yet sometimes it isn't, which
# is why we needed to add the above test.
return 0 secs;
}
function check_connection(c: connection)
{
if ( c$id !in conn_list )
{
++conn_ID;
append_addl(c, fmt("#%d", conn_ID));
conn_list[c$id] = conn_ID;
log_activity(c, fmt("new connection %s", id_string(c$id)));
}
}

View file

@ -1,336 +0,0 @@
# $Id: large-conns.bro 1332 2005-09-07 17:39:17Z vern $
# Written by Chema Gonzalez.
# Estimates the size of large "flows" (i.e., each direction of a TCP
# connection) by noting when their sequence numbers cross a set of regions
# in the sequence space. This can be done using a static packet filter,
# so is very efficient. It works for (TCP) traffic that Bro otherwise doesn't
# see.
# Usage
#
# 1) Set the appropriate number_of_regions and region_size:
#
# Modify the number_of_regions and (perhaps) region_size global
# variables. You do this *prior* to loading this script, so
# for example:
#
# const number_of_regions = 32;
# @load large-conns
#
# You do *not* redef them like you would with other script variables
# (this is because they need to be used directly in the initializations
# of other variables used by this script).
#
# Note that number_of_regions affects the granularity
# and definition of the script (see below).
#
# 2) To get an estimate of the true size of a flow, call:
#
# function estimate_flow_size_and_remove(cid: conn_id, orig: bool):
# flow_size_est
#
# If orig=T, then an estimate of the size of the forward (originator)
# direction is returned. If orig=F, then the reverse (responder)
# direction is returned. In both cases, what's returned is a
# flow_size_est, which includes a flag indicating whether there was
# any estimate formed, and, if the flag is T, a lower bound, an upper bound,
# and an inconsistency-count (which, if > 0, means that the estimates
# came from sequence numbers that were inconsistent, and thus something
# is wrong - perhaps packet drops by the secondary filter). Finally,
# calling this function causes the flow's record to be deleted. Perhaps
# at some point we'll need to add a version that just retrieves the
# estimate.
type flow_size_est: record {
have_est: bool;
lower: double &optional;
upper: double &optional;
num_inconsistent: count &optional;
};
global estimate_flow_size_and_remove:
function(cid: conn_id, orig: bool): flow_size_est;
module LargeConn;
# Rationale
#
# One of the mechanisms that Bro uses to detect large TCP flows is
# to calculate the difference in the sequence number (seq) field contents
# between the last packet (FIN or RST) and the first packet (SYN). This
# method may be wrong if a) the seq number is busted (which can happen
# frequently with RST termination), or b) the seq number wraps around
# the 4GB sequence number space (note that this is OK for TCP while
# there is no ambiguity on what a packet's sequence number means,
# due to its use of a window <= 2 GB in size).
#
# The purpose of this script is to resolve these ambiguities. In other
# words, help with differentiating truly large flows from flows with
# a busted seq, and detecting very large flows that wrap around the
# 4GB seq space.
#
# To do so, large-flow listens to a small group of thin regions in
# the sequence space, located at equal distances from each other. The idea
# is that a truly large flow will pass through the regions in
# an orderly fashion, maybe several times. This script keeps track of
# all packets that pass through any of the regions, counting the number
# of times a packet from a given flow passes through consecutive regions.
#
# Note that the exact number of regions, and the size of each region, can
# be controlled by redefining the global variables number_of_regions
# and region_size, respectively. Both should be powers of two (if not,
# they are rounded to be such), and default to 4 and 16KB, respectively.
# The effect of varying these parameters is the following:
#
# - Increasing number_of_regions will increase the granularity of the
# script, at the cost of elevating its cost in both processing (more
# packets will be seen) and memory (more flows will be seen).
# The granularity of the script is defined as the minimum variation
# in size the script can see. Its value is:
#
# granularity = (4GB / number_of_regions)
#
# For example, if we're using 4 regions, the minimum flow size difference
# that the script can see is 1GB.
#
# number_of_regions also affects the script definition, defined as the
# smallest size of a flow which ensures that the flow will be seen by
# the script. The script definition is:
#
# definition = (2 * granularity)
#
# The script sees no flow smaller than the granularity, some flows with
# size between granularity and definition, and all flows larger than
# definition. In our example, the script definition is 2GB (it will see
# for sure only flows bigger than 2GB).
#
# - Increasing region_size will only increase the resilience of the script
# to lost packets, at the cost of augmenting the cost in both processing
# and memory (see above). The default value of 16 KB is chosen to work
# in the presence of largish packets without too much additional work.
# Set up defaults, unless the user has already specified these. Note that
# these variables are *not* redef'able, since they are used in initializations
# later in this script (so a redef wouldn't be "seen" in time).
@ifndef ( number_of_regions )
const number_of_regions = 4;
@endif
@ifndef ( region_size )
const region_size = 16 * 1024; # 16 KB
@endif
# Track the regions visited for each flow.
type t_info: record {
last_region: count; # last region visited
num_regions: count; # number of regions visited
num_inconsistent: count; # num. inconsistent region crossings
};
# The state expiration for this table needs to be generous, as it's
# for tracking very large flows, which could be quite long-lived.
global flow_region_info: table[conn_id] of t_info &write_expire = 6 hr;
# Returns the integer logarithm in base b.
function logarithm(base: count, x: count): count
{
if ( x < base )
return 0;
else
return 1 + logarithm(base, x / base);
}
# Function used to get around Bro's lack of real ordered loop.
function do_while(i: count, max: count, total: count,
f: function(i: count, total: count): count): count
{
if ( i >= max )
return total;
else
return do_while(++i, max, f(--i, total), f);
}
function fn_mask_location(i: count, total: count): count
{
return total * 2 + 1;
}
function fn_filter_location(i: count, total: count): count
{
# The location pattern is 1010101010...
return total * 2 + (i % 2 == 0 ? 1 : 0);
}
function fn_common_region_size(i: count, total: count): count
{
return total * 2;
}
function get_interregion_distance(number_of_regions: count,
region_size: count): count
{
local bits_number_of_regions = logarithm(2, number_of_regions);
local bits_other = int_to_count(32 - bits_number_of_regions);
return do_while(0, bits_other, 1, fn_common_region_size);
}
global interregion_distance =
get_interregion_distance(number_of_regions, region_size);
# Returns an estiamte of size of the flow (one direction of a TCP connection)
# that this script has seen. This is based on the number of consecutive
# regions a flow has visited, weighted with the distance between regions.
#
# We know that the full sequence number space accounts for 4GB. This
# space comprises number_of_regions regions, separated from each other
# a (4GB / number_of_regions) distance. If a flow has been seen
# in X consecutive regions, it means that the size of the flow is
# greater than ((X - 1) * distance_between_regions) GB.
#
# Note that seeing a flow in just one region is no different from
# not seeing it at all.
function estimate_flow_size_and_remove(cid: conn_id, orig: bool): flow_size_est
{
local id = orig ? cid :
[$orig_h = cid$resp_h, $orig_p = cid$resp_p,
$resp_h = cid$orig_h, $resp_p = cid$orig_p];
if ( id !in flow_region_info )
return [$have_est = F];
local regions_crossed =
int_to_count(flow_region_info[id]$num_regions - 1);
local lower = regions_crossed * interregion_distance * 1.0;
local upper = lower + interregion_distance * 2.0;
local num_inconsis = flow_region_info[id]$num_inconsistent;
delete flow_region_info[id];
return [$have_est = T, $lower = lower, $upper = upper,
$num_inconsistent = num_inconsis];
}
# Returns a tcpdump filter corresponding to the number of regions and
# region size requested by the user.
#
# How to calculate the tcpdump filter used to hook packet_event to the
# secondary filter system? We are interested only in TCP packets whose
# seq number belongs to any of the test slices. Let's focus on the case
# of 4 regions, 16KB per region.
#
# The mask should be: [ x x L L L ... L L L x x ... x ]
# <---><---------------><--------->
# | | |
# | | +-> suffix: region size
# | +-> location: remaining bits
# +-> prefix: number of equidistant regions
#
# The 32-bit seq number is masked as follows:
#
# - suffix: defines size of the regions (16KB implies log_2(16KB) = 14 bits)
#
# - location: defines the exact location of the 4 regions. Note that, to
# minimize the amount of data we keep, the location will be distinct from
# zero, so segments with seq == 0 are not in a valid region
#
# - prefix: defines number of regions (4 implies log_2(4) = 2 bits)
#
# E.g., the mask will be seq_number & 0011...1100..00_2 = 00LL..LL00..00_2,
# which, by setting the location to 1010101010101010, will finally be
# seq_number & 0011...1100..00_2 = 00101010101010101000..00_2, i.e.,
# seq_number & 0x3fffc000 = 0x2aaa8000.
#
# For that particular parameterization, we'd like to wind up with a
# packet event filter of "(tcp[4:4] & 0x3fffc000) == 0x2aaa8000".
function get_event_filter(number_of_regions: count, region_size: count): string
{
local bits_number_of_regions = logarithm(2, number_of_regions);
local bits_region_size = logarithm(2, region_size);
local bits_remaining =
int_to_count(32 - bits_number_of_regions - bits_region_size);
# Set the bits corresponding to the location:
# i = 0;
# while ( i < bits_remaining )
# {
# mask = (mask * 2) + 1;
# filter = (filter * 2) + (((i % 2) == 0) ? 1 : 0);
# ++i;
# }
local mask = do_while(0, bits_remaining, 0, fn_mask_location);
local filter = do_while(0, bits_remaining, 0, fn_filter_location);
# Set the bits corrsponding to the region size
# i = 0;
# while ( i < bits_region_size )
# {
# mask = mask * 2;
# filter = filter * 2;
# ++i;
# }
mask = do_while(0, bits_region_size, mask, fn_common_region_size);
filter = do_while(0, bits_region_size, filter, fn_common_region_size);
return fmt("(tcp[4:4] & 0x%x) == 0x%x", mask, filter);
}
# packet_event --
#
# This event is raised once per (TCP) packet falling into any of the regions.
# It updates the flow_region_info table.
event packet_event(filter: string, pkt: pkt_hdr)
{
# Distill the region from the seq number.
local region = pkt$tcp$seq / interregion_distance;
# Get packet info and update global counters.
local cid = [$orig_h = pkt$ip$src, $orig_p = pkt$tcp$sport,
$resp_h = pkt$ip$dst, $resp_p = pkt$tcp$dport];
if ( cid !in flow_region_info )
{
flow_region_info[cid] =
[$last_region = region, $num_regions = 1,
$num_inconsistent = 0];
return;
}
local info = flow_region_info[cid];
local next_region = (info$last_region + 1) % number_of_regions;
if ( region == next_region )
{ # flow seen in the next region
info$last_region = region;
++info$num_regions;
}
else if ( region == info$last_region )
{ # flow seen in the same region, ignore
}
else
{
# Flow seen in another region (not the next one).
info$last_region = region;
info$num_regions = 1; # restart accounting
++info$num_inconsistent;
}
}
# Glue the filter into the secondary filter hookup.
global packet_event_filter = get_event_filter(number_of_regions, region_size);
redef secondary_filters += { [packet_event_filter] = packet_event };

View file

@ -1,16 +0,0 @@
# $Id: listen-clear.bro 416 2004-09-17 03:52:28Z vern $
#
# Listen for other Bros (non-SSL).
@load remote
# On which port to listen.
const listen_port_clear = Remote::default_port_clear &redef;
# On which IP to bind (0.0.0.0 for any interface)
const listen_if_clear = 0.0.0.0 &redef;
event bro_init()
{
listen(listen_if_clear, listen_port_clear, F);
}

View file

@ -1,16 +0,0 @@
# $Id: listen-ssl.bro 1015 2005-01-31 13:46:50Z kreibich $
#
# Listen for other Bros (SSL).
@load remote
# On which port to listen.
const listen_port_ssl = Remote::default_port_ssl &redef;
# On which IP to bind (0.0.0.0 for any interface)
const listen_if_ssl = 0.0.0.0 &redef;
event bro_init()
{
listen(listen_if_ssl, listen_port_ssl, T);
}

View file

@ -1,194 +0,0 @@
# $Id: load-level.bro 1904 2005-12-14 03:27:15Z vern $
#
# Support for shedding/reinstating load.
@load notice
# If no load_level is given, a filter is always activated.
#
# If a level is given for a filter (using the same ID than in
# {capture,restrict}_filter), then:
#
# - a capture_filter is activated if current load_level is <=
# - a restrict_filter is activated if current load_level is >=
global capture_load_levels: table[string] of PcapFilterID &redef;
global restrict_load_levels: table[string] of PcapFilterID &redef;
redef enum PcapFilterID += {
LoadLevel1, LoadLevel2, LoadLevel3, LoadLevel4, LoadLevel5,
LoadLevel6, LoadLevel7, LoadLevel8, LoadLevel9, LoadLevel10,
};
const Levels = {
LoadLevel1, LoadLevel2, LoadLevel3, LoadLevel4, LoadLevel5,
LoadLevel6, LoadLevel7, LoadLevel8, LoadLevel9, LoadLevel10
};
# The load-level cannot not leave this interval.
const MinLoad = LoadLevel1;
const MaxLoad = LoadLevel10;
# The initial load-level.
global default_load_level = LoadLevel10 &redef;
# Set to 0 to turn off any changes of the filter.
global can_adjust_filter = T &redef;
global current_load_level = DefaultPcapFilter;
global ll_file = open_log_file("load-level");
# Interface functions for switching load levels.
function set_load_level(level: PcapFilterID): bool
{
if ( level == current_load_level )
return T;
if ( ! can_adjust_filter )
{
print ll_file, fmt("%.6f can't set %s (load-levels are turned off)", network_time(), level);
return F;
}
if ( ! install_pcap_filter(level) )
{
print ll_file, fmt("%.6f can't set %s (install failed)", network_time(), level);
# Don't try again.
can_adjust_filter = F;
return F;
}
current_load_level = level;
print ll_file, fmt("%.6f switched to %s", network_time(), level);
return T;
}
# Too bad that we can't use enums like integers...
const IncreaseLoadLevelTab = {
[LoadLevel1] = LoadLevel2,
[LoadLevel2] = LoadLevel3,
[LoadLevel3] = LoadLevel4,
[LoadLevel4] = LoadLevel5,
[LoadLevel5] = LoadLevel6,
[LoadLevel6] = LoadLevel7,
[LoadLevel7] = LoadLevel8,
[LoadLevel8] = LoadLevel9,
[LoadLevel9] = LoadLevel10,
[LoadLevel10] = LoadLevel10,
};
const DecreaseLoadLevelTab = {
[LoadLevel1] = LoadLevel1,
[LoadLevel2] = LoadLevel1,
[LoadLevel3] = LoadLevel2,
[LoadLevel4] = LoadLevel3,
[LoadLevel5] = LoadLevel4,
[LoadLevel6] = LoadLevel5,
[LoadLevel7] = LoadLevel6,
[LoadLevel8] = LoadLevel7,
[LoadLevel9] = LoadLevel8,
[LoadLevel10] = LoadLevel9,
};
const LoadLevelToInt = {
[DefaultPcapFilter] = 0,
[LoadLevel1] = 1,
[LoadLevel2] = 2,
[LoadLevel3] = 3,
[LoadLevel4] = 4,
[LoadLevel5] = 5,
[LoadLevel6] = 6,
[LoadLevel7] = 7,
[LoadLevel8] = 8,
[LoadLevel9] = 9,
[LoadLevel10] = 10,
};
function increase_load_level()
{
set_load_level(IncreaseLoadLevelTab[current_load_level]);
}
function decrease_load_level()
{
set_load_level(DecreaseLoadLevelTab[current_load_level]);
}
# Internal functions.
function load_level_error()
{
print ll_file, fmt("%.6f Error, switching back to DefaultPcapFilter",
network_time());
install_default_pcap_filter();
# Don't try changing the load level any more.
can_adjust_filter = F;
}
function build_load_level_filter(level: PcapFilterID): string
{
# Build up capture_filter.
local cfilter = "";
for ( id in capture_filters )
{
if ( id !in capture_load_levels ||
LoadLevelToInt[level] <= LoadLevelToInt[capture_load_levels[id]] )
cfilter = add_to_pcap_filter(cfilter, capture_filters[id], "or");
}
# Build up restrict_filter.
local rfilter = "";
for ( id in restrict_filters )
{
if ( id !in restrict_load_levels ||
LoadLevelToInt[level] >= LoadLevelToInt[restrict_load_levels[id]] )
rfilter = add_to_pcap_filter(rfilter, restrict_filters[id], "and");
}
return join_filters(cfilter, rfilter);
}
function precompile_load_level_filters(): bool
{
print ll_file, fmt("%.6f <<< Begin of precompilation", network_time() );
for ( i in Levels )
{
local filter = build_load_level_filter(i);
if ( ! precompile_pcap_filter(i, filter) )
{
print ll_file, fmt("%.6f Level %d: %s",
network_time(), LoadLevelToInt[i], pcap_error());
load_level_error();
return F;
}
print ll_file, fmt("%.6f Level %2d: %s", network_time(), LoadLevelToInt[i], filter);
}
print ll_file, fmt("%.6f >>> End of precompilation", network_time() );
return T;
}
event bro_init()
{
set_buf(ll_file, F);
precompile_load_level_filters();
set_load_level(default_load_level);
# Don't adjust the filter when reading a trace.
if ( ! reading_live_traffic() )
can_adjust_filter = F;
}

View file

@ -1,43 +0,0 @@
# $Id: load-sample.bro 1758 2005-11-22 00:58:10Z vern $
# A simple form of profiling based on sampling the work done per-packet.
# load_sample() is generated every load_sample_freq packets (roughly;
# it's randomized). For each sampled packet, "samples" contains a set
# of the functions, event handlers, and their source files that were accessed
# during the processing of that packet, along with an estimate of the
# CPU cost of processing the packet and (currently broken) memory allocated/
# freed.
global sampled_count: table[string] of count &default = 0;
global sampled_CPU: table[string] of interval &default = 0 sec;
global sampled_mem: table[string] of int &default = +0;
global num_samples = 0;
global total_sampled_CPU = 0 sec;
global total_sampled_mem = +0;
event load_sample(samples: load_sample_info, CPU: interval, dmem: int)
{
++num_samples;
total_sampled_CPU += CPU;
total_sampled_mem += dmem;
if ( |samples| == 0 )
add samples["<nothing>"];
for ( i in samples )
{
++sampled_count[i];
sampled_CPU[i] += CPU;
sampled_mem[i] += dmem;
}
}
event bro_done()
{
for ( i in sampled_CPU )
print fmt("%s: %d%% pkts, %.1f%% CPU",
i, sampled_count[i] * 100 / num_samples,
sampled_CPU[i] * 100 / total_sampled_CPU);
# sampled_mem[i] / total_sampled_mem;
}

View file

@ -1,10 +0,0 @@
# $Id: log-append.bro 2797 2006-04-23 05:56:24Z vern $
# By default, logs are overwritten when opened, deleting the contents
# of any existing log of the same name. Loading this module changes the
# behavior to appending.
function open_log_file(tag: string): file
{
return open_for_append(log_file_name(tag));
}

View file

@ -1,677 +0,0 @@
# $Id: login.bro 6481 2008-12-15 00:47:57Z vern $
@load notice
@load weird
@load hot-ids
@load conn
# scan.bro is needed for "account_tried" event.
@load scan
@load demux
@load terminate-connection
module Login;
global telnet_ports = { 23/tcp } &redef;
redef dpd_config += { [ANALYZER_TELNET] = [$ports = telnet_ports] };
global rlogin_ports = { 513/tcp } &redef;
redef dpd_config += { [ANALYZER_RLOGIN] = [$ports = rlogin_ports] };
export {
redef enum Notice += {
SensitiveLogin, # interactive login using sensitive username
# Interactive login seen using forbidden username, but the analyzer
# was confused in following the login dialog, so may be in error.
LoginForbiddenButConfused,
# During a login dialog, a sensitive username (e.g., "rewt") was
# seen in the user's *password*. This is reported as a notice
# because it could be that the login analyzer didn't track the
# authentication dialog correctly, and in fact what it thinks is
# the user's password is instead the user's username.
SensitiveUsernameInPassword,
};
# If these patterns appear anywhere in the user's keystrokes, do a notice.
const input_trouble =
/rewt/
| /eggdrop/
| /\/bin\/eject/
| /oir##t/
| /ereeto/
| /(shell|xploit)_?code/
| /execshell/
| /ff\.core/
| /unset[ \t]+(histfile|history|HISTFILE|HISTORY)/
| /neet\.tar/
| /r0kk0/
| /su[ \t]+(daemon|news|adm)/
| /\.\/clean/
| /rm[ \t]+-rf[ \t]+secure/
| /cd[ \t]+\/dev\/[a-zA-Z]{3}/
| /solsparc_lpset/
| /\.\/[a-z]+[ \t]+passwd/
| /\.\/bnc/
| /bnc\.conf/
| /\"\/bin\/ksh\"/
| /LAST STAGE OF DELIRIUM/
| /SNMPXDMID_PROG/
| /snmpXdmid for solaris/
| /\"\/bin\/uname/
| /gcc[ \t]+1\.c/
| />\/etc\/passwd/
| /lynx[ \t]+-source[ \t]+.*(packetstorm|shellcode|linux|sparc)/
| /gcc.*\/bin\/login/
| /#define NOP.*0x/
| /printf\(\"overflowing/
| /exec[a-z]*\(\"\/usr\/openwin/
| /perl[ \t]+.*x.*[0-9][0-9][0-9][0-9]/
| /ping.*-s.*%d/
&redef;
# If this pattern appears anywhere in the user's input after applying
# <backspace>/<delete> editing, do a notice ...
const edited_input_trouble =
/[ \t]*(cd|pushd|more|less|cat|vi|emacs|pine)[ \t]+((['"]?\.\.\.)|(["'](\.*)[ \t]))/
&redef;
# ... *unless* the corresponding output matches this:
const output_indicates_input_not_trouble = /No such file or directory/ &redef;
# NOTICE on these, but only after waiting for the corresponding output,
# so it can be displayed at the same time.
const input_wait_for_output = edited_input_trouble &redef;
# If the user's entire input matches this pattern, do a notice. Putting
# "loadmodule" here rather than in input_trouble is just to illustrate
# the idea, it could go in either.
const full_input_trouble = /.*loadmodule.*/ &redef;
# If the following appears anywhere in the user's output, do a notice.
const output_trouble =
/^-r.s.*root.*\/bin\/(sh|csh|tcsh)/
| /Jumping to address/
| /Jumping Address/
| /smashdu\.c/
| /PATH_UTMP/
| /Log started at =/
| /www\.anticode\.com/
| /www\.uberhax0r\.net/
| /smurf\.c by TFreak/
| /Super Linux Xploit/
| /^# \[root@/
| /^-r.s.*root.*\/bin\/(time|sh|csh|tcsh|bash|ksh)/
| /invisibleX/
| /PATH_(UTMP|WTMP|LASTLOG)/
| /[0-9]{5,} bytes from/
| /(PATH|STAT):\ .*=>/
| /----- \[(FIN|RST|DATA LIMIT|Timed Out)\]/
| /IDLE TIMEOUT/
| /DATA LIMIT/
| /-- TCP\/IP LOG --/
| /STAT: (FIN|TIMED_OUT) /
| /(shell|xploit)_code/
| /execshell/
| /x86_bsd_compaexec/
| /\\xbf\\xee\\xee\\xee\\x08\\xb8/ # from x.c worm
| /Coded by James Seter/
| /Irc Proxy v/
| /Daemon port\.\.\.\./
| /BOT_VERSION/
| /NICKCRYPT/
| /\/etc\/\.core/
| /exec.*\/bin\/newgrp/
| /deadcafe/
| /[ \/]snap\.sh/
| /Secure atime,ctime,mtime/
| /Can\'t fix checksum/
| /Promisc Dectection/
| /ADMsn0ofID/
| /(cd \/; uname -a; pwd; id)/
| /drw0rm/
| /[Rr][Ee3][Ww][Tt][Ee3][Dd]/
| /rpc\.sadmin/
| /AbraxaS/
| /\[target\]/
| /ID_SENDSYN/
| /ID_DISTROIT/
| /by Mixter/
| /rap(e?)ing.*using weapons/
| /spsiod/
| /[aA][dD][oO][rR][eE][bB][sS][dD]/ # rootkit
&redef;
# Same, but must match entire output.
const full_output_trouble = /.*Trojaning in progress.*/ &redef;
const backdoor_prompts =
/^[!-~]*( ?)[#%$] /
| /.*no job control/
| /WinGate>/
&redef;
const non_backdoor_prompts = /^ *#.*#/ &redef;
const hot_terminal_types = /VT666|007/ &redef;
const hot_telnet_orig_ports = { 53982/tcp, } &redef;
const router_prompts: set[string] &redef;
const non_ASCII_hosts: set[addr] &redef;
const skip_logins_to = { non_ASCII_hosts, } &redef;
const always_hot_login_ids = { always_hot_ids } &redef;
const hot_login_ids = { hot_ids } &redef;
const rlogin_id_okay_if_no_password_exposed = { "root", } &redef;
const BS = "\x08";
const DEL = "\x7f";
global new_login_session:
function(c: connection, pid: peer_id, output_line: count);
global remove_login_session: function(c: connection, pid: peer_id);
global ext_set_login_state:
function(cid: conn_id, pid: peer_id, state: count);
global ext_get_login_state:
function(cid: conn_id, pid: peer_id): count;
}
redef capture_filters += { ["login"] = "port telnet or tcp port 513" };
redef skip_authentication = {
"WELCOME TO THE BERKELEY PUBLIC LIBRARY",
};
redef direct_login_prompts = { "TERMINAL?", };
redef login_prompts = {
"Login:", "login:", "Name:", "Username:", "User:", "Member Name",
"User Access Verification", "Cisco Systems Console",
direct_login_prompts
};
redef login_non_failure_msgs = {
"Failures", "failures", # probably is "<n> failures since last login"
"failure since last successful login",
"failures since last successful login",
};
redef login_non_failure_msgs = {
"Failures", "failures", # probably is "<n> failures since last login"
"failure since last successful login",
"failures since last successful login",
} &redef;
redef login_failure_msgs = {
"invalid", "Invalid", "incorrect", "Incorrect", "failure", "Failure",
# "Unable to authenticate", "unable to authenticate",
"User authorization failure",
"Login failed",
"INVALID", "Sorry.", "Sorry,",
};
redef login_success_msgs = {
"Last login",
"Last successful login", "Last successful login",
"checking for disk quotas", "unsuccessful login attempts",
"failure since last successful login",
"failures since last successful login",
router_prompts,
};
redef login_timeouts = {
"timeout", "timed out", "Timeout", "Timed out",
"Error reading command input", # VMS
};
type check_info: record {
expanded_line: string; # line with all possible editing seqs
hot: bool; # whether any editing sequence was a hot user id
hot_id: string; # the ID considered hot
forbidden: bool; # same, but forbidden user id
};
type login_session_info: record {
user: string;
output_line: count; # number of lines seen
# input string for which we want to match the output.
waiting_for_output: string;
waiting_for_output_line: count; # output line we want to match it to
state: count; # valid for external connections only
};
global login_sessions: table[peer_id, conn_id] of login_session_info;
# The next two functions are "external-to-the-event-engine",
# hence the ext_ prefix. They're used by the script to manage
# login state so that they can work with login sessions unknown
# to the event engine (such as those received from remote peers).
function ext_get_login_state(cid: conn_id, pid: peer_id): count
{
if ( pid == PEER_ID_NONE )
return get_login_state(cid);
return login_sessions[pid, cid]$state;
}
function ext_set_login_state(cid: conn_id, pid: peer_id, state: count)
{
if ( pid == PEER_ID_NONE )
set_login_state(cid, state);
else
login_sessions[pid, cid]$state = state;
}
function new_login_session(c: connection, pid: peer_id, output_line: count)
{
local s: login_session_info;
s$waiting_for_output = s$user = "";
s$output_line = output_line;
s$state = LOGIN_STATE_AUTHENTICATE;
login_sessions[pid, c$id] = s;
}
function remove_login_session(c: connection, pid: peer_id)
{
delete login_sessions[pid, c$id];
}
function is_login_conn(c: connection): bool
{
return c$id$resp_p == telnet || c$id$resp_p == rlogin;
}
function hot_login(c: connection, pid: peer_id, msg: string, tag: string)
{
if ( [pid, c$id] in login_sessions )
NOTICE([$note=SensitiveLogin, $conn=c,
$user=login_sessions[pid, c$id]$user, $msg=msg]);
else
NOTICE([$note=SensitiveLogin, $conn=c, $msg=msg]);
++c$hot;
demux_conn(c$id, tag, "keys", service_name(c));
}
function is_hot_id(id: string, successful: bool, confused: bool): bool
{
return successful ? id in hot_login_ids :
(confused ? id in forbidden_ids :
id in always_hot_login_ids);
}
function is_forbidden_id(id: string): bool
{
return id in forbidden_ids || id == forbidden_id_patterns;
}
function edit_and_check_line(c: connection, pid: peer_id, line: string,
successful: bool): check_info
{
line = to_lower(line);
local ctrl_H_edit = edit(line, BS);
local del_edit = edit(line, DEL);
local confused =
(ext_get_login_state(c$id, pid) == LOGIN_STATE_CONFUSED);
local hot = is_hot_id(line, successful, confused);
local hot_id = hot ? line : "";
local forbidden = is_forbidden_id(line);
local eline = line;
if ( ctrl_H_edit != line )
{
eline = fmt("%s,%s", eline, ctrl_H_edit);
if ( ! hot && is_hot_id(ctrl_H_edit, successful, confused) )
{
hot = T;
hot_id = ctrl_H_edit;
}
forbidden = forbidden || is_forbidden_id(ctrl_H_edit);
}
if ( del_edit != line )
{
eline = fmt("%s,%s", eline, del_edit);
if ( ! hot && is_hot_id(del_edit, successful, confused) )
{
hot = T;
hot_id = del_edit;
}
forbidden = forbidden || is_forbidden_id(del_edit);
}
local results: check_info;
results$expanded_line = eline;
results$hot = hot;
results$hot_id = hot_id;
results$forbidden = forbidden;
return results;
}
function edit_and_check_user(c: connection, pid: peer_id, user: string,
successful: bool, fmt_s: string): bool
{
local check = edit_and_check_line(c, pid, user, successful);
if ( [pid, c$id] !in login_sessions )
new_login_session(c, pid, 9999);
login_sessions[pid, c$id]$user = check$expanded_line;
c$addl = fmt(fmt_s, c$addl, check$expanded_line);
if ( check$hot )
{
++c$hot;
demux_conn(c$id, check$hot_id, "keys", service_name(c));
}
if ( check$forbidden )
{
if ( ext_get_login_state(c$id, pid) == LOGIN_STATE_CONFUSED )
NOTICE([$note=LoginForbiddenButConfused, $conn=c,
$user = user,
$msg=fmt("not terminating %s because confused about state", full_id_string(c))]);
else
TerminateConnection::terminate_connection(c);
}
return c$hot > 0;
}
function edit_and_check_password(c: connection, pid: peer_id, password: string)
{
local check = edit_and_check_line(c, pid, password, T);
if ( check$hot )
{
++c$hot;
NOTICE([$note=SensitiveUsernameInPassword, $conn=c,
$user=password,
$msg=fmt("%s password: \"%s\"",
id_string(c$id), check$expanded_line)]);
}
}
event login_failure(c: connection, user: string, client_user: string,
password: string, line: string)
{
local pid = get_event_peer()$id;
event account_tried(c, user, password);
edit_and_check_password(c, pid, password);
if ( c$hot == 0 && password == "" &&
! edit_and_check_line(c, pid, user, F)$hot )
# Don't both reporting it, this was clearly a half-hearted
# attempt and it's not a sensitive username.
return;
local user_hot = edit_and_check_user(c, pid, user, F, "%sfail/%s ");
if ( client_user != "" && client_user != user &&
edit_and_check_user(c, pid, client_user, F, "%s(%s) ") )
user_hot = T;
if ( user_hot || c$hot > 0 )
NOTICE([$note=SensitiveLogin, $conn=c,
$user=user, $sub=client_user,
$msg=fmt("%s %s", id_string(c$id), c$addl)]);
}
event login_success(c: connection, user: string, client_user: string,
password: string, line: string)
{
local pid = get_event_peer()$id;
Hot::check_hot(c, Hot::APPL_ESTABLISHED);
event account_tried(c, user, password);
edit_and_check_password(c, pid, password);
# Look for whether the user name is sensitive; but allow for
# some ids being okay if no password was exposed accessing them.
local user_hot = F;
if ( c$id$resp_p == rlogin && password == "<none>" &&
user in rlogin_id_okay_if_no_password_exposed )
append_addl(c, fmt("\"%s\"", user));
else
user_hot = edit_and_check_user(c, pid, user, T, "%s\"%s\" ");
if ( c$id$resp_p == rlogin && client_user in always_hot_login_ids )
{
append_addl(c, fmt("(%s)", client_user));
demux_conn(c$id, client_user, "keys", service_name(c));
user_hot = T;
}
if ( user_hot || c$hot > 0 )
NOTICE([$note=SensitiveLogin, $conn=c,
$user=user, $sub=client_user,
$msg=fmt("%s %s", id_string(c$id), c$addl)]);
# else if ( password == "" )
# alarm fmt("%s %s <no password>", id_string(c$id), c$addl);
### use the following if no login_input_line/login_output_line
# else
# {
# set_record_packets(c$id, F);
# skip_further_processing(c$id);
# }
}
event login_input_line(c: connection, line: string)
{
local pid = get_event_peer()$id;
local BS_line = edit(line, BS);
local DEL_line = edit(line, DEL);
if ( input_trouble in line ||
### need to merge input_trouble and edited_input_trouble here
### ideally, match on input_trouble would tell whether we need
### to invoke the edit functions, as an attribute of a .*(^H|DEL)
### rule.
input_trouble in BS_line || input_trouble in DEL_line ||
(edited_input_trouble in BS_line &&
# If one is in but the other not, then the one that's not
# is presumably the correct edit, and the one that is, isn't
# in fact edited at all
edited_input_trouble in DEL_line) ||
line == full_input_trouble )
{
if ( [pid, c$id] !in login_sessions )
new_login_session(c, pid, 9999);
if ( edited_input_trouble in BS_line &&
edited_input_trouble in DEL_line )
{
login_sessions[pid, c$id]$waiting_for_output = line;
login_sessions[pid, c$id]$waiting_for_output_line =
# We don't want the *next* line, that's just
# the echo of this input.
login_sessions[pid, c$id]$output_line + 2;
}
else if ( ++c$hot <= 2 )
hot_login(c, pid, fmt("%s input \"%s\"", id_string(c$id), line), "trb");
}
}
event login_output_line(c: connection, line: string)
{
local pid = get_event_peer()$id;
if ( [pid, c$id] !in login_sessions )
new_login_session(c, pid, 9999);
local s = login_sessions[pid, c$id];
if ( line != "" && ++s$output_line == 1 )
{
if ( byte_len(line) < 40 &&
backdoor_prompts in line && non_backdoor_prompts !in line )
hot_login(c, pid, fmt("%s possible backdoor \"%s\"", id_string(c$id), line), "trb");
}
if ( s$waiting_for_output != "" &&
s$output_line >= s$waiting_for_output_line )
{
if ( output_indicates_input_not_trouble !in line )
hot_login(c, pid,
fmt("%s input \"%s\" yielded output \"%s\"",
id_string(c$id),
s$waiting_for_output,
line),
"trb");
s$waiting_for_output = "";
}
if ( byte_len(line) < 256 &&
(output_trouble in line || line == full_output_trouble) &&
++c$hot <= 2 )
hot_login(c, pid, fmt("%s output \"%s\"", id_string(c$id), line), "trb");
}
event login_confused(c: connection, msg: string, line: string)
{
Hot::check_hot(c, Hot::APPL_ESTABLISHED);
append_addl(c, "<confused>");
event conn_weird_addl(msg, c, line);
set_record_packets(c$id, T);
}
event login_confused_text(c: connection, line: string)
{
local pid = get_event_peer()$id;
if ( c$hot == 0 && edit_and_check_line(c, pid, line, F)$hot )
{
local ignore =
edit_and_check_user(c, pid, line, F, "%sconfused/%s ");
NOTICE([$note=SensitiveLogin, $conn=c,
$user=line,
$msg=fmt("%s %s", id_string(c$id), c$addl)]);
set_record_packets(c$id, T);
}
}
event login_terminal(c: connection, terminal: string)
{
local pid = get_event_peer()$id;
if ( hot_terminal_types in terminal )
hot_login(c, pid,
fmt("%s term %s", id_string(c$id), terminal), "trb");
}
event login_prompt(c: connection, prompt: string)
{
# Could check length >= 6, per Solaris exploit ...
local pid = get_event_peer()$id;
hot_login(c, pid,
fmt("%s $TTYPROMPT %s", id_string(c$id), prompt), "trb");
}
event excessive_line(c: connection)
{
if ( is_login_conn(c) )
{
local pid = get_event_peer()$id;
if ( ! c$hot && c$id$resp_h in non_ASCII_hosts )
{
ext_set_login_state(c$id, pid, LOGIN_STATE_SKIP);
set_record_packets(c$id, F);
}
else if ( ext_get_login_state(c$id, pid) == LOGIN_STATE_AUTHENTICATE )
{
event login_confused(c, "excessive_line", "");
ext_set_login_state(c$id, pid, LOGIN_STATE_CONFUSED);
}
}
}
event inconsistent_option(c: connection)
{
print Weird::weird_file, fmt("%.6f %s inconsistent option", network_time(), id_string(c$id));
}
event bad_option(c: connection)
{
print Weird::weird_file, fmt("%.6f %s bad option", network_time(), id_string(c$id));
}
event bad_option_termination(c: connection)
{
print Weird::weird_file, fmt("%.6f %s bad option termination", network_time(), id_string(c$id));
}
event authentication_accepted(name: string, c: connection)
{
local addl_msg = fmt("auth/%s", name);
append_addl(c, addl_msg);
}
event authentication_rejected(name: string, c: connection)
{
append_addl(c, fmt("auth-failed/%s", name));
}
event authentication_skipped(c: connection)
{
append_addl(c, "(skipped)");
skip_further_processing(c$id);
if ( ! c$hot )
set_record_packets(c$id, F);
}
event connection_established(c: connection)
{
if ( is_login_conn(c) )
{
local pid = get_event_peer()$id;
new_login_session(c, pid, 0);
if ( c$id$resp_h in skip_logins_to )
event authentication_skipped(c);
if ( c$id$resp_p == telnet &&
c$id$orig_p in hot_telnet_orig_ports )
hot_login(c, pid, fmt("%s hot_orig_port", id_string(c$id)), "orig");
}
}
event partial_connection(c: connection)
{
if ( is_login_conn(c) )
{
local pid = get_event_peer()$id;
new_login_session(c, pid, 9999);
ext_set_login_state(c$id, pid, LOGIN_STATE_CONFUSED);
if ( c$id$resp_p == telnet &&
c$id$orig_p in hot_telnet_orig_ports )
hot_login(c, pid, fmt("%s hot_orig_port", id_string(c$id)), "orig");
}
}
event connection_finished(c: connection)
{
local pid = get_event_peer()$id;
remove_login_session(c, pid);
}
event activating_encryption(c: connection)
{
if ( is_login_conn(c) )
append_addl(c, "(encrypted)");
}

View file

@ -1,180 +0,0 @@
# $Id: mime-pop.bro 4758 2007-08-10 06:49:23Z vern $
#
# A stripped-down version of mime.bro adapted to work on POP3 events.
#
# FIXME: What's the best way to merge mime.bro and mime-pop3.bro?
@load pop3
module MIME_POP3;
const mime_log = open_log_file("mime-pop") &redef;
type mime_session_info: record {
id: count;
connection_id: conn_id;
level: count;
data_offset: count;
};
global mime_session_id = 0;
global mime_sessions: table[conn_id] of mime_session_info;
function mime_session_string(session: mime_session_info): string
{
return fmt("#%s %s +%d", prefixed_id(session$id),
id_string(session$connection_id), session$level);
}
function mime_log_warning(what: string)
{
print mime_log, fmt("%.6f warning: %s", network_time(), what);
}
function mime_log_msg(session: mime_session_info, where: string, what: string)
{
print mime_log, fmt("%.6f %s: [%s] %s",
network_time(),
mime_session_string(session),
where,
what);
}
function new_mime_session(c: connection)
{
local id = c$id;
local session_id = ++mime_session_id;
local info: mime_session_info;
info$id = session_id;
info$connection_id = id;
info$level = 0;
info$data_offset = 0;
mime_sessions[id] = info;
mime_log_msg(info, "start", "");
}
function get_mime_session(c: connection, new_session_ok: bool): mime_session_info
{
local id = c$id;
if ( id !in mime_sessions )
{
if ( ! new_session_ok )
mime_log_warning(fmt("begin_entity missing for new MIME session %s", id_string(id)));
new_mime_session(c);
}
return mime_sessions[id];
}
function end_mime_session(session: mime_session_info)
{
mime_log_msg(session, "finish", "");
delete mime_sessions[session$connection_id];
}
event connection_state_remove(c: connection)
{
if ( c$id$resp_p != 110/tcp )
return;
local id = c$id;
if ( id in mime_sessions )
{
mime_log_msg(mime_sessions[id], "state remove", "");
delete mime_sessions[id];
}
}
function do_mime_begin_entity(c: connection)
{
local session = get_mime_session(c, T);
++session$level;
session$data_offset = 0;
mime_log_msg(session, "begin entity", "");
}
event mime_begin_entity(c: connection)
{
if ( c$id$resp_p != 110/tcp )
return;
do_mime_begin_entity(c);
}
function do_mime_end_entity(c: connection)
{
local session = get_mime_session(c, T);
mime_log_msg(session, "end entity", "");
if ( session$level > 0 )
{
--session$level;
if ( session$level == 0 )
end_mime_session(session);
}
else
mime_log_warning(fmt("unmatched end_entity for MIME session %s",
mime_session_string(session)));
}
event mime_end_entity(c: connection)
{
if ( c$id$resp_p != 110/tcp )
return;
do_mime_end_entity(c);
}
event mime_next_entity(c: connection)
{
if ( c$id$resp_p != 110/tcp )
return;
do_mime_end_entity(c);
do_mime_begin_entity(c);
}
event mime_all_headers(c: connection, hlist: mime_header_list)
{
if ( c$id$resp_p != 110/tcp )
return;
local session = get_mime_session(c, T);
local i = 0;
for ( i in hlist )
{
local h = hlist[i];
mime_log_msg(session, "header",
fmt("%s: \"%s\"", h$name, h$value));
}
}
event mime_segment_data(c: connection, length: count, data: string)
{
if ( c$id$resp_p != 110/tcp )
return;
local session = get_mime_session(c, T);
if ( session$data_offset < 256 )
mime_log_msg(session, "data", fmt("%d: %s", length, data));
session$data_offset = session$data_offset + length;
}
event mime_event(c: connection, event_type: string, detail: string)
{
if ( c$id$resp_p != 110/tcp )
return;
local session = get_mime_session(c, T);
mime_log_msg(session, "event", fmt("%s: %s", event_type, detail));
}

View file

@ -1,15 +0,0 @@
# $Id: mt.bro 340 2004-09-09 06:38:27Z vern $
@load dns-lookup
@load hot
@load frag
@load tcp
@load scan
@load weird
@load finger
@load ident
@load ftp
@load login
@load portmapper
@load ntp
@load tftp

View file

@ -1,101 +0,0 @@
# $Id:$
@load conn-id
module NCP;
global ncp_log = open_log_file("ncp") &redef;
redef capture_filters += {["ncp"] = "tcp port 524"};
export {
const ncp_frame_type_name = {
[ 0x1111 ] = "NCP_ALLOC_SLOT",
[ 0x2222 ] = "NCP_REQUEST",
[ 0x3333 ] = "NCP_REPLY",
[ 0x5555 ] = "NCP_DEALLOC_SLOT",
[ 0x7777 ] = "NCP_BURST",
[ 0x9999 ] = "NCP_ACK",
} &default = function(code: count): string
{
return fmt("NCP_UNKNOWN_FRAME_TYPE(%x)", code);
};
const ncp_function_name = {
[ 0x01 ] = "NCP_FILE_SET_LOCK",
[ 0x02 ] = "NCP_FILE_RELEASE_LOCK",
[ 0x03 ] = "NCP_LOG_FILE",
[ 0x04 ] = "NCP_LOCK_FILE_SET",
[ 0x05 ] = "NCP_RELEASE_FILE",
[ 0x06 ] = "NCP_RELEASE_FILE_SET",
[ 0x07 ] = "NCP_CLEAR_FILE",
[ 0x08 ] = "NCP_CLEAR_FILE_SET",
[ 0x09 ] = "NCP_LOG_LOGICAL_RECORD",
[ 0x0a ] = "NCP_LOCK_LOGICAL_RECORD_SET",
[ 0x0b ] = "NCP_CLEAR_LOGICAL_RECORD",
[ 0x0c ] = "NCP_RELEASE_LOGICAL_RECORD",
[ 0x0d ] = "NCP_RELEASE_LOGICAL_RECORD_SET",
[ 0x0e ] = "NCP_CLEAR_LOGICAL_RECORD_SET",
[ 0x0f ] = "NCP_ALLOC_RESOURCE",
[ 0x10 ] = "NCP_DEALLOC_RESOURCE",
[ 0x11 ] = "NCP_PRINT",
[ 0x15 ] = "NCP_MESSAGE",
[ 0x16 ] = "NCP_DIRECTORY",
[ 0x17 ] = "NCP_BINDARY_AND_MISC",
[ 0x18 ] = "NCP_END_OF_JOB",
[ 0x19 ] = "NCP_LOGOUT",
[ 0x1a ] = "NCP_LOG_PHYSICAL_RECORD",
[ 0x1b ] = "NCP_LOCK_PHYSICAL_RECORD_SET",
[ 0x1c ] = "NCP_RELEASE_PHYSICAL_RECORD",
[ 0x1d ] = "NCP_RELEASE_PHYSICAL_RECORD_SET",
[ 0x1e ] = "NCP_CLEAR_PHYSICAL_RECORD",
[ 0x1f ] = "NCP_CLEAR_PHYSICAL_RECORD_SET",
[ 0x20 ] = "NCP_SEMAPHORE",
[ 0x22 ] = "NCP_TRANSACTION_TRACKING",
[ 0x23 ] = "NCP_AFP",
[ 0x42 ] = "NCP_CLOSE_FILE",
[ 0x47 ] = "NCP_GET_FILE_SIZE",
[ 0x48 ] = "NCP_READ_FILE",
[ 0x49 ] = "NCP_WRITE_FILE",
[ 0x56 ] = "NCP_EXT_ATTR",
[ 0x57 ] = "NCP_FILE_DIR",
[ 0x58 ] = "NCP_AUDITING",
[ 0x5a ] = "NCP_MIGRATION",
[ 0x60 ] = "NCP_PNW",
[ 0x61 ] = "NCP_GET_MAX_PACKET_SIZE",
[ 0x68 ] = "NCP_NDS",
[ 0x6f ] = "NCP_SEMAPHORE_NEW",
[ 0x7b ] = "NCP_7B",
[ 0x5701 ] = "NCP_CREATE_FILE_DIR",
[ 0x5702 ] = "NCP_INIT_SEARCH",
[ 0x5703 ] = "NCP_SEARCH_FILE_DIR",
[ 0x5704 ] = "NCP_RENAME_FILE_DIR",
[ 0x5706 ] = "NCP_OBTAIN_FILE_DIR_INFO",
[ 0x5707 ] = "NCP_MODIFY_FILE_DIR_DOS_INFO",
[ 0x5708 ] = "NCP_DELETE_FILE_DIR",
[ 0x5709 ] = "NCP_SET_SHORT_DIR_HANDLE",
[ 0x5714 ] = "NCP_SEARCH_FOR_FILE_DIR_SET",
[ 0x5718 ] = "NCP_GET_NAME_SPACE_LOADED_LIST",
[ 0x5742 ] = "NCP_GET_CURRENT_SIZE_OF_FILE",
} &default = function(code: count): string
{
return fmt("NCP_UNKNOWN_FUNCTION(%x)", code);
};
} # export
event ncp_request(c: connection, frame_type: count, length: count, func: count)
{
print ncp_log, fmt("%.6f %s NCP request type=%s function=%s",
network_time(), id_string(c$id),
ncp_frame_type_name[frame_type],
ncp_function_name[func]);
}
event ncp_reply(c: connection, frame_type: count, length: count,
req_frame: count, req_func: count, completion_code: count)
{
}

View file

@ -1,106 +0,0 @@
# $Id:$
#
# Netflow data-dumper and proof-of-concept flow restitcher.
# Written by Bernhard Ager (2007).
module NetFlow;
export {
# Perform flow restitching?
global netflow_restitch = T &redef;
# How long to wait for additional flow records after a RST or FIN,
# so we can compress multiple RST/FINs for the same flow rather than
# treating them as separate flows. It's not clear what's the best
# setting for this timer, but for now we use something larger
# than the NetFlow inactivity timeout (5 minutes).
global netflow_finished_conn_expire = 310 sec &redef;
}
global netflow_log = open_log_file("netflow") &redef;
# Should be larger than activity timeout. Setting only affects table
# declaration, therefore &redef useless.
const netflow_table_expire = 31 min;
type flow: record {
cnt: count;
pkts: count;
octets: count;
syn: bool;
fin: bool;
first: time;
last: time;
};
function new_flow(r: nf_v5_record): flow
{
return [ $cnt = 1,
$pkts = r$pkts,
$octets = r$octets,
$syn = r$tcpflag_syn,
$fin = r$tcpflag_fin,
$first = r$first,
$last = r$last ];
}
function update_flow(f: flow, r: nf_v5_record)
{
f$pkts += r$pkts;
f$octets += r$octets;
++f$cnt;
f$syn = f$syn || r$tcpflag_syn;
f$fin = f$fin || r$tcpflag_fin;
if ( r$first < f$first )
f$first = r$first;
if ( r$last > f$last )
f$last = r$last;
}
function print_flow(t: table[conn_id] of flow, idx: conn_id): interval
{
print netflow_log, fmt("%.6f flow %s: %s", network_time(), idx, t[idx]);
return -1 sec;
}
event v5flow_finished(t: table[conn_id] of flow, idx: conn_id)
{
if ( idx in t )
{
print_flow(t, idx);
delete t[idx];
}
}
global flows: table[conn_id] of flow &write_expire = netflow_table_expire
&expire_func = print_flow;
event netflow_v5_header(h: nf_v5_header)
{
print netflow_log, fmt("%.6f header %s", network_time(), h);
}
event netflow_v5_record (r: nf_v5_record)
{
if ( netflow_restitch )
{
if ( r$id in flows )
update_flow (flows[r$id], r);
else
flows[r$id] = new_flow (r);
if ( r$tcpflag_fin || r$tcpflag_rst )
schedule netflow_finished_conn_expire {
v5flow_finished (flows, r$id)
};
}
print netflow_log, fmt("%.6f record %s", network_time(), r);
}
event bro_done ()
{
for ( f_id in flows )
print_flow(flows, f_id);
}

View file

@ -1,32 +0,0 @@
# $Id: netstats.bro 564 2004-10-23 02:27:57Z vern $
@load notice
redef enum Notice += {
DroppedPackets, # Bro reported packets dropped by the packet filter
};
const stats_collection_interval = 10secs;
event net_stats_update(last_stat: NetStats)
{
local ns = net_stats();
local new_dropped = ns$pkts_dropped - last_stat$pkts_dropped;
if ( new_dropped > 0 )
{
local new_recvd = ns$pkts_recvd - last_stat$pkts_recvd;
local new_link = ns$pkts_link - last_stat$pkts_link;
NOTICE([$note=DroppedPackets,
$msg=fmt("%d packets dropped after filtering, %d received%s",
new_dropped, new_recvd + new_dropped,
new_link != 0 ?
fmt(", %d on link", new_link) : "")]);
}
schedule stats_collection_interval { net_stats_update(ns) };
}
event bro_init()
{
schedule stats_collection_interval { net_stats_update(net_stats()) };
}

View file

@ -1,408 +0,0 @@
@load udp
module NFS3;
export {
global log_file = open_log_file("nfs") &redef;
global names_log_file = open_log_file("nfs-files") &redef;
global readdir_log = open_log_file("nfs-readdir") &redef;
# We want to estimate how long it takes to lookup a chain of FH (directories)
# until we reach a FH that is used in a read or write operation. Whenever we
# get a new FH, we check how long ago we got the FH's parent. If this is less
# than fh_chain_maxtime, we assume that they belong to a lookup chain and set
# the dt value for the FH accordingly.
global fh_chain_maxtime = 100 msec;
}
redef capture_filters += {
["nfs"] = "port 2049",
# NFS UDP packets are often fragmented.
["nfs-frag"] = "(ip[6:2] & 0x3fff != 0) and udp",
};
global nfs_ports = { 2049/tcp, 2049/udp } &redef;
redef dpd_config += { [ANALYZER_NFS] = [$ports = nfs_ports] };
# Information about a filehandle
type fh_info : record {
id: count; # A unique ID (counter) for more readable representation of the FH
pathname: string &default="@"; # the path leading to this FH
basename: string &default=""; # the name of this FHs file or directory
mimetype: string &default="";
t0: time &default=double_to_time(0); # time when we first saw this FH
dt: interval &default=0 sec; # time it took to get this FH (assuming a chain of
# procedures that ultimately yield the FH for the file
# a client is interested in
chainlen: count &default=0;
attr: fattr_t &optional;
};
# Maps opaque file handles to numbers for easier tracking.
global num_fhs = 0;
global fh_map: table[addr,string] of fh_info;
# Maps connids to number for easier post processing
global num_nfs_conns = 0;
global nfs_conns: table[conn_id] of count;
# Get the FH info. Create a new info if it doesn't exists
function get_fh_info(c: connection, fh: string): fh_info
{
if ( [c$id$resp_h, fh] !in fh_map )
{
# Don't have a mapping for this FH yet. E.g., a root FH
local newfhinfo: fh_info = [ $id=++num_fhs ];
newfhinfo$pathname = fmt("@%d", newfhinfo$id);
newfhinfo$t0 = network_time();
fh_map[c$id$resp_h, fh] = newfhinfo;
}
return fh_map[c$id$resp_h, fh];
}
function log_filename(proc: string, info: fh_info)
{
print names_log_file, fmt("%.6f %s path FH%d %s/%s", network_time(), proc,
info$id, info$pathname, info$basename);
##print fmt("%.6f FH%d <%s> <%s>", network_time(), info$id, info$pathname, info$basename);
}
function fmt_attr(a: fattr_t): string
{
local s = fmt("%s %s %d %d %d %d %d %d %d %d %d %.2f %.2f %.2f",
a$ftype, mode2string(a$mode), a$nlink, a$uid, a$gid, a$size, a$used, a$rdev1, a$rdev2,
a$fsid, a$fileid, a$atime, a$mtime, a$ctime);
return s;
}
function log_attributes(c: connection, proc: string, fh: string, attr: fattr_t)
{
local info = get_fh_info(c,fh);
local did_change = F;
# check whether the attributes have changes
if (info?$attr)
{
# We can't compare records for equality :-(. So we use a hack.
# We add the two instance we want to compare to a set. If there
# are two elements in the set, the records are not equal...
local dummy: set[fattr_t];
add dummy[info$attr];
add dummy[attr];
if (|dummy| > 1)
did_change = T;
}
else
did_change=T;
if (did_change)
{
info$attr = attr;
print names_log_file, fmt("%.6f %s attr FH%d %s", network_time(), proc,
info$id, fmt_attr(attr));
}
}
# Update (or add) a filehandle mapping.
# parentfh ... parent (directory)
# name ....... the name for this FH
# fh ......... the new FH
function add_update_fh(c: connection, proc: string, parentfh: string, name: string, fh: string)
{
local info = get_fh_info(c, fh);
# TODO: we could/should check if we already have a pathname and/or basename
# for this FH and if so whether it matches the parent we just got!
if (name == ".")
return;
info$basename = name;
if (parentfh != "")
{
local parentinfo = get_fh_info(c, parentfh);
info$pathname = cat(parentinfo$pathname, "/", parentinfo$basename);
if ( (network_time() - parentinfo$t0) < fh_chain_maxtime
&& info$dt < 0 sec )
{
# The FH is part of lookup chain and it doesn't yet have a dt value
# TODO: this should probably be moved to get_fh_info(). But then get_fh_info()
# would need information about a FH's parent....
# TODO: We are using network_time(), but we really should use request
# and reply time!!!
info$dt = parentinfo$dt + (network_time() - parentinfo$t0);
info$chainlen = parentinfo$chainlen + 1;
}
}
log_filename(proc, info);
}
function set_fh_mimetype(c: connection, fh: string, proc:string, data: string)
{
local info = get_fh_info(c,fh);
local mimetype = identify_data(data, T);
if (info$mimetype != mimetype)
{
info$mimetype = mimetype;
print names_log_file, fmt("%.6f %s type FH%d %s/%s %s", network_time(), proc,
info$id, info$pathname, info$basename, (mimetype!="") ? mimetype : "X/X");
}
}
# Get the total time of the lookup chain for this FH to the
# current network time. Returns a negative interal if no
# lookup chain was found
function get_fh_chaintime_str(c:connection, fh:string): string
{
local info = get_fh_info(c, fh);
if ((network_time() - info$t0) < fh_chain_maxtime)
return fmt("%d %.6f", info$chainlen, info$dt + (network_time() - info$t0));
else
return fmt("%d %.6f", 0, 0.0);
}
# Get a FH ID
function get_fh_id(c:connection, fh: string): string
{
return cat("FH", get_fh_info(c, fh)$id);
}
# Get the basename for the FH
function get_fh_basename(c:connection, fh: string): string
{
return get_fh_info(c, fh)$basename;
}
# Get the fullname for the FH
function get_fh_fullname(c:connection, fh: string): string
{
local info = get_fh_info(c, fh);
return cat(info$pathname, "/", info$basename);
}
function print_attr(attr: fattr_t): string
{
return fmt("%s", attr);
}
function map_conn(cid: conn_id): count
{
if (cid !in nfs_conns)
nfs_conns[cid] = ++num_nfs_conns;
return nfs_conns[cid];
}
function is_success(info: info_t): bool
{
return (info$rpc_stat == RPC_SUCCESS && info$nfs_stat == NFS3ERR_OK);
}
function is_rpc_success(info: info_t): bool
{
return (info$rpc_stat == RPC_SUCCESS);
}
function nfs_get_log_prefix(c: connection, info: info_t, proc: string): string
{
local nfs_stat_str = (info$rpc_stat == RPC_SUCCESS) ? fmt("%s", info$nfs_stat) : "X";
return fmt("%.06f %.06f %d %.06f %.06f %d %s %s %d %s %s %s",
info$req_start, info$req_dur, info$req_len,
info$rep_start, info$rep_dur, info$rep_len,
id_string(c$id), get_port_transport_proto(c$id$orig_p),
map_conn(c$id),
proc, info$rpc_stat, nfs_stat_str);
}
event nfs_proc_not_implemented(c: connection, info: info_t, proc: proc_t)
{
local prefix = nfs_get_log_prefix(c, info, fmt("%s", proc));
print log_file, fmt("%s Not_implemented", prefix);
}
event nfs_proc_null(c: connection, info: info_t)
{
local prefix = nfs_get_log_prefix(c, info, "null");
print log_file, prefix;
}
event nfs_proc_getattr (c: connection, info: info_t, fh: string, attrs: fattr_t)
{
local prefix = nfs_get_log_prefix(c, info, "getattr");
if (is_success(info))
log_attributes(c, "getattr", fh, attrs);
print log_file, fmt("%s %s", prefix, get_fh_id(c,fh));
}
event nfs_proc_lookup(c: connection, info: info_t, req: diropargs_t, rep: lookup_reply_t)
{
local prefix = nfs_get_log_prefix(c, info, "lookup");
if (! is_success(info) )
{
print log_file, fmt("%s %s + %s", prefix, get_fh_id(c, req$dirfh), req$fname);
# could print dir_attr, if they are set ....
return;
}
if (rep?$dir_attr)
log_attributes(c, "lookup", req$dirfh, rep$dir_attr);
if (is_rpc_success(info) && rep?$obj_attr)
log_attributes(c, "lookup", rep$fh, rep$obj_attr);
add_update_fh(c, "lookup", req$dirfh, req$fname, rep$fh);
print log_file, fmt("%s %s + %s => %s", prefix, get_fh_id(c, req$dirfh), req$fname, get_fh_id(c, rep$fh));
}
event nfs_proc_read(c: connection, info: info_t, req: readargs_t, rep: read_reply_t)
{
local msg = nfs_get_log_prefix(c, info, "read");
msg = fmt("%s %s @%d: %d", msg, get_fh_id(c, req$fh), req$offset, req$size);
if (is_success(info))
{
msg = fmt("%s got %d bytes %s %s", msg, rep$size, (rep$eof) ? "<eof>" : "x",
get_fh_chaintime_str(c, req$fh));
if (rep?$data && req$offset==0 && rep$size>0)
set_fh_mimetype(c, req$fh, "read", rep$data);
if (is_rpc_success(info) && rep?$attr)
log_attributes(c, "read", req$fh, rep$attr);
}
print log_file, msg;
}
event nfs_proc_readlink(c: connection, info: info_t, fh: string, rep: readlink_reply_t)
{
local msg = nfs_get_log_prefix(c, info, "readlink");
msg = fmt("%s %s", msg, get_fh_id(c, fh));
if (is_success(info))
{
msg = fmt("%s : %s", msg, rep$nfspath);
if (rep?$attr)
log_attributes(c, "readlink", fh, rep$attr);
}
print log_file, msg;
}
event nfs_proc_write(c: connection, info: info_t, req: writeargs_t, rep: write_reply_t)
{
local msg = nfs_get_log_prefix(c, info, "write");
msg = fmt("%s %s @%d: %d %s", msg, get_fh_id(c, req$fh), req$offset, req$size, req$stable);
if (is_success(info))
{
msg = fmt("%s wrote %d bytes %s %s", msg, rep$size, rep$commited,
get_fh_chaintime_str(c, req$fh));
if (req?$data && req$offset==0 && rep$size>0)
set_fh_mimetype(c, req$fh, "write", req$data);
if (rep?$postattr)
log_attributes(c, "write", req$fh, rep$postattr);
}
print log_file, msg;
}
function nfs_newobj(c: connection, info: info_t, proc: string, req: diropargs_t, rep: newobj_reply_t)
{
local prefix = nfs_get_log_prefix(c, info, proc);
local newfh_str: string;
if (! is_success(info) )
{
print log_file, fmt("%s %s + %s", prefix, get_fh_id(c, req$dirfh), req$fname);
# could print dir_attr, if they are set ....
return;
}
if (is_rpc_success(info) && rep?$dir_post_attr)
log_attributes(c, proc, req$dirfh, rep$dir_post_attr);
# TODO: could print dir_pre_attr
if (is_rpc_success(info) && rep?$obj_attr)
log_attributes(c, proc, rep$fh, rep$obj_attr);
add_update_fh(c, proc, req$dirfh, req$fname, rep$fh);
newfh_str = (rep?$fh) ? get_fh_id(c, rep$fh) : "FH??";
print log_file, fmt("%s %s + %s => %s", prefix, get_fh_id(c, req$dirfh), req$fname, get_fh_id(c, rep$fh));
}
event nfs_proc_create(c: connection, info: info_t, req: diropargs_t, rep: newobj_reply_t)
{
# TODO: create request attributes not implemented in core
nfs_newobj(c, info, "create", req, rep);
}
event nfs_proc_mkdir(c: connection, info: info_t, req: diropargs_t, rep: newobj_reply_t)
{
# TODO: mkidir request attributes not implemented in core
nfs_newobj(c, info, "mkdir", req, rep);
}
function nfs_delobj(c: connection, info: info_t, proc: string, req: diropargs_t, rep: delobj_reply_t)
{
local prefix = nfs_get_log_prefix(c, info, proc);
print log_file, fmt("%s %s - %s", prefix, get_fh_id(c, req$dirfh), req$fname);
if (is_rpc_success(info) && rep?$dir_post_attr)
log_attributes(c, proc, req$dirfh, rep$dir_post_attr);
# TODO: could print dir_pre_attr
}
event nfs_proc_remove(c: connection, info: info_t, req: diropargs_t, rep: delobj_reply_t)
{
nfs_delobj(c, info, "remove", req, rep);
}
event nfs_proc_rmdir(c: connection, info: info_t, req: diropargs_t, rep: delobj_reply_t)
{
nfs_delobj(c, info, "rmdir", req, rep);
}
function fmt_direntry(c: connection, e: direntry_t): string
{
local rv = "";
rv = fmt("%d %s %d", e$fileid, e$fname, e$cookie);
if (e?$fh)
rv = fmt("%s %s", rv, get_fh_id(c, e$fh));
return rv;
}
event nfs_proc_readdir(c: connection, info: info_t, req: readdirargs_t, rep: readdir_reply_t)
{
local isplus = req$isplus;
local proc = (isplus) ? "readdirplus" : "readdir";
local msg = nfs_get_log_prefix(c, info, proc);
msg = fmt("%s %s @%d (%x)", msg, get_fh_id(c, req$dirfh), req$cookie, req$cookieverf);
if (is_success(info))
{
msg = fmt("%s %d entries %d", msg, |rep$entries|, rep$eof);
print readdir_log, msg;
for (i in rep$entries)
{
local curentry = rep$entries[i];
if (curentry?$attr && curentry?$fh)
log_attributes(c, proc, curentry$fh, curentry$attr);
if (curentry?$fh)
add_update_fh(c, proc, req$dirfh, curentry$fname, curentry$fh);
print readdir_log,fmt(" %s", fmt_direntry(c, curentry));
}
if (rep?$dir_attr)
log_attributes(c, proc, req$dirfh, rep$dir_attr);
}
else if (is_rpc_success(info) && rep?$dir_attr)
{
log_attributes(c, proc, req$dirfh, rep$dir_attr);
}
print log_file, msg;
}
event connection_state_remove(c: connection)
{
if ( c$id !in nfs_conns )
return;
delete nfs_conns[c$id];
}

View file

@ -1,72 +0,0 @@
# $Id: notice-policy.bro 4758 2007-08-10 06:49:23Z vern $
# Examples of using notice_policy and other mechanisms to filter out
# alarms that are not interesting.
# Note: this file is not self-contained, in that it refers to Notice
# names that will only be defined if you've loaded other files (e.g.,
# print-resources for the ResourceSummary notice). The full list of
# policy files it needs is:
#
# blaster.bro
# conn.bro
# http-request.bro
# netstats.bro
# print-resources.bro
# trw.bro
# weird.bro
# Remove these notices from logging since they can be too noisy.
redef notice_action_filters += {
[[Weird::ContentGap, Weird::AckAboveHole]] = ignore_notice,
};
# Send these only to the notice log, not the alarm log.
redef notice_action_filters += {
[[Drop::AddressDropIgnored, DroppedPackets,
ResourceSummary, W32B_SourceRemote,
TRW::TRWScanSummary, Scan::BackscatterSeen,
Weird::WeirdActivity,
Weird::RetransmissionInconsistency]] = file_notice,
};
# Other example use of notice_action_filters:
#
# To just get a summary Notice when Bro is shutdown/checkpointed, use
# tally_notice_type, such as:
#redef notice_action_filters += {
# [[RetransmissionInconsistency, ContentGap, AckAboveHole]] =
# tally_notice_type,
#};
# To get a summary once every hour per originator, use notice_alarm_per_orig,
# such as:
#redef notice_action_filters += {
# [[ BackscatterSeen, RetransmissionInconsistency]] =
# notice_alarm_per_orig,
#};
# Fine-grained filtering of specific alarms.
redef notice_policy += {
# Connections to 2766/tcp ("Solaris listen service") appear
# nearly always actually due to P2P apps.
[$pred(n: notice_info) =
{
return n$note == SensitiveConnection &&
/Solaris listen service/ in n$msg;
},
$result = NOTICE_FILE,
$priority = 1],
# Ignore sensitive URLs that end in .gif, .jpg, .png
[$pred(n: notice_info) =
{
return n$note == HTTP::HTTP_SensitiveURI &&
n$URL == /.*\.(gif|GIF|png|PNG|jpg|JPG)/;
},
$result = NOTICE_FILE,
$priority = 1],
};

View file

@ -1,53 +0,0 @@
# $Id: ntp.bro 4758 2007-08-10 06:49:23Z vern $
@load udp-common
redef capture_filters += { ["ntp"] = "udp port 123" };
module NTP;
export {
const excessive_ntp_request = 48 &redef;
const allow_excessive_ntp_requests: set[addr] &redef;
}
# DPM configuration.
global ntp_ports = { 123/udp } &redef;
redef dpd_config += { [ANALYZER_NTP] = [$ports = ntp_ports] };
const ntp_code: table[count] of string = {
[0] = "unspec",
[1] = "sym_act",
[2] = "sym_psv",
[3] = "client",
[4] = "server",
[5] = "bcast",
[6] = "rsv1",
[7] = "rsv2",
};
event ntp_message(u: connection, msg: ntp_msg, excess: string)
{
local id = u$id;
if ( id !in udp_rep_count && id !in udp_req_count )
{
Hot::check_hot(u, Hot::CONN_ATTEMPTED);
Scan::check_scan(u, F, F);
}
if ( msg$code == 4 )
# "server"
++udp_rep_count[id];
else
# anything else
++udp_req_count[id];
local n_excess = byte_len(excess);
if ( n_excess > excessive_ntp_request &&
id$orig_h !in allow_excessive_ntp_requests )
{
append_addl_marker(u, fmt("%s", n_excess), ",");
++u$hot;
}
}

View file

@ -1,29 +0,0 @@
# $Id: passwords.bro 688 2004-11-02 23:59:55Z vern $
# Generates notices of exposed passwords. Currently just works
# on telnet/rlogin access. Should be extended to do FTP, HTTP, etc.
@load login
redef enum Notice += {
PasswordExposed,
};
# Usernames which we ignore.
global okay_usernames: set[string] &redef;
# Passwords which we ignore.
global okay_passwords = { "", "<none>" } &redef;
event login_success(c:connection, user: string, client_user: string,
password: string, line: string)
{
if ( user in okay_usernames || password in okay_passwords )
return;
NOTICE([$note=PasswordExposed,
$conn=c,
$user=user,
$sub=password,
$msg="login exposed user's password"]);
}

View file

@ -1,84 +0,0 @@
# $Id: peer-status.bro 5954 2008-07-15 00:07:50Z vern $
#
# Emits process status "update" event periodically.
module PeerStatus;
export {
type peer_status: record {
res: bro_resources;
stats: net_stats;
current_time: time;
cpu: double; # average CPU load since last update
default_filter: string; # default capture filter
};
# Event sent periodically.
global update: event(status: peer_status);
# Update interval.
const update_interval = 1 min;
# This keeps track of all (local and remote) updates
# (indexed by peer ID).
global peers: table[peer_id] of peer_status;
}
global start_time = 0;
global cpu_last_proc_time = 0 secs;
global cpu_last_wall_time: time = 0;
global stats: net_stats;
global default_filter : string;
event net_stats_update(t: time, ns: net_stats)
{
stats = ns;
}
event emit_update()
{
# Get CPU load.
local res = resource_usage();
local proc_time = res$user_time + res$system_time;
local wall_time = current_time();
local dproc = proc_time - cpu_last_proc_time;
local dwall = wall_time - cpu_last_wall_time;
local load = dproc / dwall * 100.0;
cpu_last_proc_time = proc_time;
cpu_last_wall_time = wall_time;
local status: peer_status;
status$res = res;
status$stats = stats;
status$current_time = current_time();
status$cpu = load;
status$default_filter = default_filter;
event PeerStatus::update(status);
schedule update_interval { emit_update() };
}
event bro_init()
{
default_filter = build_default_pcap_filter();
local res = resource_usage();
cpu_last_proc_time = res$user_time + res$system_time;
cpu_last_wall_time = current_time();
stats = [$pkts_recvd=0, $pkts_dropped=0, $pkts_link=0];
schedule update_interval { emit_update() };
}
event update(status: peer_status)
{
local peer = get_event_peer();
peers[peer$id] = status;
}
event remote_connection_closed(p: event_peer)
{
if ( p$id in peers )
delete peers[p$id];
}

View file

@ -1,5 +0,0 @@
# $Id: pkt-profile.bro 325 2004-09-03 01:33:15Z vern $
redef pkt_profile_file = open_log_file("pkt-prof");
redef pkt_profile_mode = PKT_PROFILE_MODE_SECS;
redef pkt_profile_freq = 1.0;

View file

@ -1,155 +0,0 @@
# $Id: pop3.bro 4758 2007-08-10 06:49:23Z vern $
#
# Analyzer for Post Office Protocol, version 3.
#
# If you want to decode the mail itself, also load mime-pop.bro.
@load login
module POP3;
export {
# Report if source triggers more ERR messages.
const error_threshold: count = 3 &redef;
# Don't log these commands.
const ignore_commands: set[string] = { "STAT" } &redef;
}
redef capture_filters += { ["pop3"] = "port 110" };
global pop3_ports = { 110/tcp } &redef;
redef dpd_config += { [ANALYZER_POP3] = [$ports = pop3_ports] };
const log_file = open_log_file("pop3") &redef;
type pop3_session_info: record {
id: count; # Unique session ID.
quit_sent: bool; # Client issued a QUIT.
last_command: string; # Last command of client.
};
global pop_log: function(conn: pop3_session_info,
command: string, message: string);
global get_connection: function(id: conn_id): pop3_session_info;
global pop_connections:
table[conn_id] of pop3_session_info &read_expire = 60 mins;
global pop_connection_weirds:
table[addr] of count &default=0 &read_expire = 60 mins;
global pop_session_id = 0;
event pop3_request(c: connection, is_orig: bool, command: string, arg: string)
{
local conn = get_connection(c$id);
pop_log(conn, command, fmt("%.6f #%s > %s %s",
network_time(), prefixed_id(conn$id), command, arg));
conn$last_command = command;
if ( command == "QUIT" )
conn$quit_sent = T;
}
event pop3_reply(c: connection, is_orig: bool, cmd: string, msg: string)
{
local conn = get_connection(c$id);
pop_log(conn, cmd,
fmt("%.6f #%s < %s %s", network_time(), prefixed_id(conn$id), cmd, msg));
if ( cmd == "OK" )
{
if ( conn$quit_sent )
delete pop_connections[c$id];
}
else if ( cmd == "ERR" )
{
++pop_connection_weirds[c$id$orig_h];
if ( pop_connection_weirds[c$id$orig_h] > error_threshold )
print log_file, fmt("%.6f #%s %s/%d > %s/%d WARNING: error count exceeds threshold",
network_time(), prefixed_id(conn$id),
c$id$orig_h, c$id$orig_p,
c$id$resp_h, c$id$resp_p);
}
}
event pop3_login_success(c: connection, is_orig: bool,
user: string, password: string)
{
local conn = get_connection(c$id);
local pw = byte_len(password) != 0 ? password : "<not seen>";
print log_file, fmt("%.6f #%s > login successful: user %s password: %s",
network_time(), prefixed_id(conn$id), user, pw);
event login_success(c, user, "", password, "");
}
event pop3_login_failure(c: connection, is_orig: bool,
user: string, password: string)
{
local conn = get_connection(c$id);
print log_file, fmt("%.6f #%s > login failed: user %s password: %s",
network_time(), prefixed_id(conn$id), user, password);
event login_failure(c, user, "", password, "");
}
# event pop3_data(c: connection, is_orig: bool, data: string)
# {
# # We could instantiate partial connections here if we wished,
# # but at considerable cost in terms of event counts.
# local conn = get_connection(c$id);
# }
event pop3_unexpected(c: connection, is_orig: bool, msg: string, detail: string)
{
local conn = get_connection(c$id);
print log_file, fmt("%.6f #%s unexpected cmd: %s detail: %s",
network_time(), prefixed_id(conn$id),
msg, detail);
}
event pop3_terminate(c: connection, is_orig: bool, msg: string)
{
delete pop_connections[c$id];
}
function pop_log(conn: pop3_session_info, command: string, message: string)
{
if ( command !in ignore_commands )
{
if ( (command == "OK" || command == "ERR") &&
conn$last_command in ignore_commands )
;
else
print log_file, message;
}
}
function get_connection(id: conn_id): pop3_session_info
{
if ( id in pop_connections )
return pop_connections[id];
local conn: pop3_session_info;
conn$id = ++pop_session_id;
conn$quit_sent = F;
conn$last_command = "INIT";
pop_connections[id] = conn;
print log_file, fmt("%.6f #%s %s/%d > %s/%d: new connection",
network_time(), prefixed_id(conn$id),
id$orig_h, id$orig_p, id$resp_h, id$resp_p);
return conn;
}

View file

@ -1,63 +0,0 @@
const port_names: table[port] of string = {
[0/icmp] = "icmp-echo",
[3/icmp] = "icmp-unreach",
[8/icmp] = "icmp-echo",
[7/tcp] = "echo",
[9/tcp] = "discard",
[20/tcp] = "ftp-data",
[21/tcp] = "ftp",
[22/tcp] = "ssh",
[23/tcp] = "telnet",
[25/tcp] = "smtp",
[37/tcp] = "time",
[43/tcp] = "whois",
[53/tcp] = "dns",
[79/tcp] = "finger",
[80/tcp] = "http",
[109/tcp] = "pop-2",
[110/tcp] = "pop-3",
[111/tcp] = "portmap",
[113/tcp] = "ident",
[119/tcp] = "nntp",
[135/tcp] = "epmapper",
[139/tcp] = "netbios-ssn",
[143/tcp] = "imap4",
[179/tcp] = "bgp",
[389/tcp] = "ldap",
[443/tcp] = "https",
[445/tcp] = "smb",
[512/tcp] = "exec",
[513/tcp] = "rlogin",
[514/tcp] = "shell",
[515/tcp] = "printer",
[524/tcp] = "ncp",
[543/tcp] = "klogin",
[544/tcp] = "kshell",
[631/tcp] = "ipp",
[993/tcp] = "simap",
[995/tcp] = "spop",
[1521/tcp] = "oracle-sql",
[2049/tcp] = "nfs",
[6000/tcp] = "X11",
[6001/tcp] = "X11",
[6667/tcp] = "IRC",
[53/udp] = "dns",
[69/udp] = "tftp",
[111/udp] = "portmap",
[123/udp] = "ntp",
[137/udp] = "netbios-ns",
[138/udp] = "netbios-dgm",
[161/udp] = "snmp",
[2049/udp] = "nfs",
} &redef;
function endpoint_id(h: addr, p: port): string
{
if ( p in port_names )
return fmt("%s/%s", h, port_names[p]);
else
return fmt("%s/%d", h, p);
}

View file

@ -1,468 +0,0 @@
# $Id: portmapper.bro 4758 2007-08-10 06:49:23Z vern $
@load notice
@load hot
@load conn
@load weird
@load scan
module Portmapper;
export {
redef enum Notice += {
# Some combination of the service looked up, the host doing the
# request, and the server contacted is considered sensitive.
SensitivePortmapperAccess,
};
# Kudos to Job de Haas for a lot of these entries.
const rpc_programs = {
[200] = "aarp",
[100000] = "portmapper", [100001] = "rstatd",
[100002] = "rusersd", [100003] = "nfs", [100004] = "ypserv",
[100005] = "mountd", [100007] = "ypbind", [100008] = "walld",
[100009] = "yppasswdd", [100010] = "etherstatd",
[100011] = "rquotad", [100012] = "sprayd",
[100013] = "3270_mapper", [100014] = "rje_mapper",
[100015] = "selection_svc", [100016] = "database_svc",
[100017] = "rexd", [100018] = "alis", [100019] = "sched",
[100020] = "llockmgr", [100021] = "nlockmgr",
[100022] = "x25.inr", [100023] = "statmon",
[100024] = "status", [100026] = "bootparam",
[100028] = "ypupdate", [100029] = "keyserv",
[100033] = "sunlink_mapper", [100036] = "pwdauth",
[100037] = "tfsd", [100038] = "nsed",
[100039] = "nsemntd", [100041] = "pnpd",
[100042] = "ipalloc", [100043] = "filehandle",
[100055] = "ioadmd", [100062] = "NETlicense",
[100065] = "sunisamd", [100066] = "debug_svc",
[100068] = "cms", [100069] = "ypxfrd",
[100071] = "bugtraqd", [100078] = "kerbd",
[100083] = "tooltalkdb", [100087] = "admind",
[100099] = "autofsd",
[100101] = "event", [100102] = "logger", [100104] = "sync",
[100105] = "diskinfo", [100106] = "iostat",
[100107] = "hostperf", [100109] = "activity",
[100111] = "lpstat", [100112] = "hostmem",
[100113] = "sample", [100114] = "x25", [100115] = "ping",
[100116] = "rpcnfs", [100117] = "hostif", [100118] = "etherif",
[100119] = "ippath", [100120] = "iproutes",
[100121] = "layers", [100122] = "snmp", [100123] = "traffic",
[100131] = "layers2", [100135] = "etherif2",
[100136] = "hostmem2", [100137] = "iostat2",
[100138] = "snmpv2", [100139] = "sender",
[100221] = "kcms", [100227] = "nfs_acl", [100229] = "metad",
[100230] = "metamhd", [100232] = "sadmind", [100233] = "ufsd",
[100235] = "cachefsd", [100249] = "snmpXdmid",
[100300] = "nisd", [100301] = "nis_cache",
[100302] = "nis_callback", [100303] = "nispasswd",
[120126] = "nf_snmd", [120127] = "nf_snmd",
[150001] = "pcnfsd",
[300004] = "frameuser", [300009] = "stdfm", [300019] = "amd",
[300433] = "bssd", [300434] = "drdd",
[300598] = "dmispd",
[390100] = "prestoctl_svc",
[390600] = "arserverd", [390601] = "ntserverd",
[390604] = "arservtcd",
[391000] = "SGI_snoopd", [391001] = "SGI_toolkitbus",
[391002] = "SGI_fam", [391003] = "SGI_notepad",
[391004] = "SGI_mountd", [391005] = "SGI_smtd",
[391006] = "SGI_pcsd", [391007] = "SGI_nfs",
[391008] = "SGI_rfind", [391009] = "SGI_pod",
[391010] = "SGI_iphone", [391011] = "SGI_videod",
[391012] = "SGI_testcd", [391013] = "SGI_ha_hb",
[391014] = "SGI_ha_nc", [391015] = "SGI_ha_appmon",
[391016] = "SGI_xfsmd", [391017] = "SGI_mediad",
# 391018 - 391063 = "SGI_reserved"
[545580417] = "bwnfsd",
[555555554] = "inetray.start", [555555555] = "inetray",
[555555556] = "inetray", [555555557] = "inetray",
[555555558] = "inetray", [555555559] = "inetray",
[555555560] = "inetray",
[600100069] = "fypxfrd",
[1342177279] = "Solaris/CDE", # = 0x4fffffff
# Some services that choose numbers but start often at these values.
[805306368] = "dmispd",
[824395111] = "cfsd", [1092830567] = "cfsd",
} &redef;
const NFS_services = {
"mountd", "nfs", "pcnfsd", "nlockmgr", "rquotad", "status"
} &redef;
# Indexed by the host providing the service, the host requesting it,
# and the service.
const RPC_okay: set[addr, addr, string] &redef;
const RPC_okay_nets: set[subnet] &redef;
const RPC_okay_services: set[string] &redef;
const NFS_world_servers: set[addr] &redef;
# Indexed by the portmapper request and a boolean that's T if
# the request was answered, F it was attempted but not answered.
# If there's an entry in the set, then the access won't lead to a
# NOTICE (unless the connection is hot for some other reason).
const RPC_do_not_complain: set[string, bool] = {
["pm_null", [T, F]],
} &redef;
# Indexed by the host requesting the dump and the host from which it's
# requesting it.
const RPC_dump_okay: set[addr, addr] &redef;
# Indexed by the host providing the service - any host can request it.
const any_RPC_okay = {
[NFS_world_servers, NFS_services],
[sun-rpc.mcast.net, "ypserv"], # sigh
} &redef;
# Logs all portmapper activity as readable "messages"
# Format: timestamp orig_p resp_h resp_p proto localInit PortmapProcedure success details
const log_file = open_log_file("portmapper") &redef;
# Logs all portmapper mappings that we observe (i.e., getport and
# dump replies. Format:
# timestamp orig_h orig_p resp_h resp_p proto localInit PortmapProcedure RPCprogram version port proto
# the mapping is then: <resp_h> accepts <RPCprogram> with <version>
# calls on <port> <proto>. We learned this mapping via <PortmapProcedure>
const mapping_log_file = open_log_file("portmapper-maps") &redef;
}
redef capture_filters += { ["portmapper"] = "port 111" };
const portmapper_ports = { 111/tcp, 111/udp } &redef;
redef dpd_config += { [ANALYZER_PORTMAPPER] = [$ports = portmapper_ports] };
# Indexed by source and destination addresses, plus the portmapper service.
# If the tuple is in the set, then we already created a NOTICE for it and
# shouldn't do so again.
global did_pm_notice: set[addr, addr, string];
# Indexed by source and portmapper service. If set, we already created
# a notice and shouldn't do so again.
global suppress_pm_notice: set[addr, string];
function RPC_weird_action_filter(c: connection): Weird::WeirdAction
{
if ( c$id$orig_h in RPC_okay_nets )
return Weird::WEIRD_FILE;
else
return Weird::WEIRD_UNSPECIFIED;
}
redef Weird::weird_action_filters += {
[["bad_RPC", "excess_RPC", "multiple_RPCs", "partial_RPC"]] =
RPC_weird_action_filter,
};
function rpc_prog(p: count): string
{
if ( p in rpc_programs )
return rpc_programs[p];
else
return fmt("unknown-%d", p);
}
function pm_get_conn_string(cid: conn_id) : string
{
return fmt("%s %d %s %d %s %s",
cid$orig_h, cid$orig_p,
cid$resp_h, cid$resp_p,
get_port_transport_proto(cid$resp_p),
is_local_addr(cid$orig_h) ? "L" : "X"
);
}
# Log a pm_request or pm_attempt to the log file
function pm_log(r: connection, proc: string, msg: string, success: bool)
{
print log_file, fmt("%f %s %s %s %s", network_time(),
pm_get_conn_string(r$id),
proc, success, msg);
}
# Log portmapper mappings received from a dump procedure
function pm_log_mapping_dump(r: connection, m: pm_mappings)
{
# TODO: sort by program and version
for ( mp in m )
{
local prog = rpc_prog(m[mp]$program);
local ver = m[mp]$version;
local p = m[mp]$p;
print mapping_log_file, fmt("%f %s pm_dump %s %d %d %s", network_time(),
pm_get_conn_string(r$id),
prog, ver, p, get_port_transport_proto(p));
}
}
# Log portmapper mappings received from a getport procedure
# Unfortunately, pm_request_getport doesn't return pm_mapping,
# but returns the parameters separately ....
function pm_log_mapping_getport(r: connection, pr: pm_port_request, p: port)
{
local prog = rpc_prog(pr$program);
local ver = pr$version;
print mapping_log_file, fmt("%f %s pm_getport %s %d %d %s", network_time(),
pm_get_conn_string(r$id),
prog, ver, p, get_port_transport_proto(p));
}
function pm_check_getport(r: connection, prog: string): bool
{
if ( prog in RPC_okay_services ||
[r$id$resp_h, prog] in any_RPC_okay ||
[r$id$resp_h, r$id$orig_h, prog] in RPC_okay )
return F;
if ( r$id$orig_h in RPC_okay_nets )
return F;
return T;
}
function pm_activity(r: connection, do_notice: bool, proc: string)
{
local id = r$id;
if ( do_notice &&
[id$orig_h, id$resp_h, proc] !in did_pm_notice &&
[id$orig_h, proc] !in suppress_pm_notice )
{
NOTICE([$note=SensitivePortmapperAccess, $conn=r,
$msg=fmt("rpc: %s %s: %s",
id_string(r$id), proc, r$addl)]);
add did_pm_notice[id$orig_h, id$resp_h, proc];
}
}
function pm_request(r: connection, proc: string, addl: string, do_notice: bool)
{
if ( [proc, T] in RPC_do_not_complain )
do_notice = F;
if ( ! is_tcp_port(r$id$orig_p) )
{
# It's UDP, so no connection_established event - check for
# scanning, hot access, here, instead.
Scan::check_scan(r, T, F);
Hot::check_hot(r, Hot::CONN_ESTABLISHED);
}
if ( r$addl == "" )
r$addl = addl;
else
{
if ( byte_len(r$addl) > 80 )
{
# r already has a lot of annotation. We can sometimes
# get *zillions* of successive pm_request's with the
# same connection ID, depending on how the RPC client
# behaves. For those, don't add any further, except
# add an ellipses if we don't already have one.
append_addl(r, "...");
}
else
append_addl_marker(r, addl, ", ");
}
add r$service[proc];
Hot::check_hot(r, Hot::CONN_FINISHED);
pm_activity(r, do_notice || r$hot > 0, proc);
pm_log(r, proc, addl, T);
}
event pm_request_null(r: connection)
{
pm_request(r, "pm_null", "", F);
}
event pm_request_set(r: connection, m: pm_mapping, success: bool)
{
pm_request(r, "pm_set", fmt("%s %d (%s)",
rpc_prog(m$program), m$p, success ? "ok" : "failed"), T);
}
event pm_request_unset(r: connection, m: pm_mapping, success: bool)
{
pm_request(r, "pm_unset", fmt("%s %d (%s)",
rpc_prog(m$program), m$p, success ? "ok" : "failed"), T);
}
function update_RPC_server_map(server: addr, p: port, prog: string)
{
if ( [server, p] in RPC_server_map )
{
if ( prog !in RPC_server_map[server, p] )
{
RPC_server_map[server, p] =
fmt("%s/%s", RPC_server_map[server, p], prog);
}
}
else
RPC_server_map[server, p] = prog;
}
event pm_request_getport(r: connection, pr: pm_port_request, p: port)
{
local prog = rpc_prog(pr$program);
local do_notice = pm_check_getport(r, prog);
update_RPC_server_map(r$id$resp_h, p, prog);
pm_request(r, "pm_getport", fmt("%s -> %s", prog, p), do_notice);
pm_log_mapping_getport(r, pr, p);
}
# Note, this function has the side effect of updating the
# RPC_server_map
function pm_mapping_to_text(server: addr, m: pm_mappings): string
{
# Used to suppress multiple entries for multiple versions.
local mapping_seen: set[count, port];
local addls: vector of string;
local num_addls = 0;
for ( mp in m )
{
local prog = m[mp]$program;
local p = m[mp]$p;
if ( [prog, p] !in mapping_seen )
{
add mapping_seen[prog, p];
addls[num_addls] = fmt("%s -> %s", rpc_prog(prog), p);
++num_addls;
update_RPC_server_map(server, p, rpc_prog(prog));
}
}
local addl_str = fmt("%s", sort(addls, strcmp));
# Lop off surrounding []'s for compatibility with previous
# format.
addl_str = sub(addl_str, /^\[/, "");
addl_str = sub(addl_str, /\]$/, "");
return addl_str;
}
event pm_request_dump(r: connection, m: pm_mappings)
{
local do_notice = [r$id$orig_h, r$id$resp_h] !in RPC_dump_okay;
# pm_mapping_to_text has the side-effect of updating RPC_server_map
pm_request(r, "pm_dump",
length(m) == 0 ? "(nil)" : pm_mapping_to_text(r$id$resp_h, m),
do_notice);
pm_log_mapping_dump(r, m);
}
event pm_request_callit(r: connection, call: pm_callit_request, p: port)
{
local orig_h = r$id$orig_h;
local prog = rpc_prog(call$program);
local do_notice = [orig_h, prog] !in suppress_pm_notice;
pm_request(r, "pm_callit", fmt("%s/%d (%d bytes) -> %s",
prog, call$proc, call$arg_size, p), do_notice);
if ( prog == "walld" )
add suppress_pm_notice[orig_h, prog];
}
function pm_attempt(r: connection, proc: string, status: rpc_status,
addl: string, do_notice: bool)
{
if ( [proc, F] in RPC_do_not_complain )
do_notice = F;
if ( ! is_tcp_port(r$id$orig_p) )
{
# It's UDP, so no connection_attempt event - check for
# scanning here, instead.
Scan::check_scan(r, F, F);
Hot::check_hot(r, Hot::CONN_ATTEMPTED);
}
add r$service[proc];
append_addl(r, fmt("(%s)", RPC_status[status]));
# Current policy is ignore any failed attempts.
pm_activity(r, F, proc);
pm_log(r, proc, addl, F);
}
event pm_attempt_null(r: connection, status: rpc_status)
{
pm_attempt(r, "pm_null", status, "", T);
}
event pm_attempt_set(r: connection, status: rpc_status, m: pm_mapping)
{
pm_attempt(r, "pm_set", status, fmt("%s %d", rpc_prog(m$program), m$p), T);
}
event pm_attempt_unset(r: connection, status: rpc_status, m: pm_mapping)
{
pm_attempt(r, "pm_unset", status, fmt("%s %d", rpc_prog(m$program), m$p), T);
}
event pm_attempt_getport(r: connection, status: rpc_status, pr: pm_port_request)
{
local prog = rpc_prog(pr$program);
local do_notice = pm_check_getport(r, prog);
pm_attempt(r, "pm_getport", status, prog, do_notice);
}
event pm_attempt_dump(r: connection, status: rpc_status)
{
local do_notice = [r$id$orig_h, r$id$resp_h] !in RPC_dump_okay;
pm_attempt(r, "pm_dump", status, "", do_notice);
}
event pm_attempt_callit(r: connection, status: rpc_status,
call: pm_callit_request)
{
local orig_h = r$id$orig_h;
local prog = rpc_prog(call$program);
local do_notice = [orig_h, prog] !in suppress_pm_notice;
pm_attempt(r, "pm_callit", status,
fmt("%s/%d (%d bytes)", prog, call$proc, call$arg_size),
do_notice);
if ( prog == "walld" )
add suppress_pm_notice[orig_h, prog];
}
event pm_bad_port(r: connection, bad_p: count)
{
event conn_weird_addl("bad_pm_port", r, fmt("port %d", bad_p));
}

View file

@ -1,26 +0,0 @@
# $Id: print-filter.bro 4506 2007-06-27 14:40:34Z vern $
module PrintFilter;
export {
# If true, terminate Bro after printing the filter.
const terminate_bro = T &redef;
# If true, write to log file instead of stdout.
const to_file = F &redef;
}
event bro_init()
{
if ( to_file )
{
local f = open_log_file("pcap_filter");
print f, build_default_pcap_filter();
close(f);
}
else
print build_default_pcap_filter();
if ( terminate_bro )
exit();
}

View file

@ -1,4 +0,0 @@
event bro_done()
{
print global_sizes();
}

View file

@ -1,21 +0,0 @@
# $Id: print-resources.bro 6703 2009-05-13 22:27:44Z vern $
# Logs Bro resource usage information upon termination.
@load notice
redef enum Notice += {
ResourceSummary, # Notice type for this event
};
event bro_done()
{
local res = resource_usage();
NOTICE([$note=ResourceSummary,
$msg=fmt("elapsed time = %s, total CPU = %s, maximum memory = %d KB, peak connections = %d, peak timers = %d, peak fragments = %d",
res$real_time, res$user_time + res$system_time,
res$mem / 1024,
res$max_TCP_conns + res$max_UDP_conns + res$max_ICMP_conns,
res$max_timers, res$max_fragments)]);
}

View file

@ -1,18 +0,0 @@
# $Id: print-sig-states.bro 491 2004-10-05 05:44:59Z vern $
#
# Simple profiling script for periodicaly dumping out signature-matching
# statistics.
global sig_state_stats_interval = 5 mins;
global sig_state_file = open_log_file("sig-states");
event dump_sig_state_stats()
{
dump_rule_stats(sig_state_file);
schedule sig_state_stats_interval { dump_sig_state_stats() };
}
event bro_init()
{
schedule sig_state_stats_interval { dump_sig_state_stats() };
}

Some files were not shown because too many files have changed in this diff Show more