From 340805fe00d06736085eb17b80029e6608c7af6d Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Tue, 26 Oct 2010 17:03:19 -0400 Subject: [PATCH 1/3] Adding some of the initial scripts that are going to be merged from my script repository. --- policy/dns-ext.bro | 213 +++++++++++++++++++++++++++++ policy/functions.bro | 268 +++++++++++++++++++++++++++++++++++++ policy/global.bro | 33 +++++ policy/logging.dns-ext.bro | 82 ++++++++++++ 4 files changed, 596 insertions(+) create mode 100644 policy/dns-ext.bro create mode 100644 policy/functions.bro create mode 100644 policy/global.bro create mode 100644 policy/logging.dns-ext.bro diff --git a/policy/dns-ext.bro b/policy/dns-ext.bro new file mode 100644 index 0000000000..58b41976f8 --- /dev/null +++ b/policy/dns-ext.bro @@ -0,0 +1,213 @@ +@load global-ext +@load dns + +type dns_ext_session_info: record { + id: conn_id; + ts: time; + trans_id: count; + query: string; + qtype: count; + qclass: count; + total_answers: count &default=0; + rcode: count &default = 65536; + QR: bool &default=F; + Z: bool &default=F; + AA: bool &default=F; + RD: bool &default=F; + RA: bool &default=F; + TC: bool &default=F; + TTL: interval &default=0secs; + replies: set[string]; +}; + +# Define the generic dns-ext event that can be handled from other scripts +global dns_ext: event(id: conn_id, di: dns_ext_session_info); + +module DNS; + +export { + # Follow this example to define domains that you would consider "local". + # This is primarily useful if you are monitoring authoritative nameservers, + # but also useful for any zones that *should* be pointing at your + # network. + # e.g. + #const local_domains = /(^|\.)(osu|ohio-state)\.edu$/ | + # /(^|\.)akamai(tech)?\.net$/ &redef; + const local_domains = /(^|\.)akamai(tech)?\.net$/ &redef; + + redef enum Notice += { + # Raised when a non-local name is found to be pointing at a local host. + # This only works appropriately when all of your authoritative DNS + # servers are located in your "local_nets". + DNSExternalName, + }; +} + + +global dns_sessions_ext: table[addr, addr, count] of dns_ext_session_info; + +# This doesn't work with live traffic yet. +# It's waiting for support to dynamically construct pattern variables at init time. +#global dns_suffix_regex = build_regex(local_domains, "(^|\.)~~$"); +#event bro_init() +# { +# local i: count = 0; +# local tmp_pattern: pattern; +# for ( d in local_domains ) +# { +# tmp_pattern = string_to_pattern( fmt("=%s@", d), T ); +# +# if ( i == 0 ) +# pat = tmp_pattern; +# else +# pat = merge_pattern(tmp_pattern, pat); +# ++i; +# } +# } + +event expire_DNS_session_ext(orig: addr, resp: addr, trans_id: count) + { + if ( [orig, resp, trans_id] in dns_sessions_ext ) + { + local session = dns_sessions[orig, resp, trans_id]; + local session_ext = dns_sessions_ext[orig, resp, trans_id]; + + event dns_ext(session_ext$id, session_ext); + } + } + +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 session_ext: dns_ext_session_info; + if ( [orig, resp, msg$id] !in dns_sessions_ext ) + { + session_ext$id = c$id; + session_ext$trans_id = msg$id; + session_ext$ts = network_time(); + session_ext$RD = msg$RD; + session_ext$TC = msg$TC; + session_ext$qtype = qtype; + session_ext$qclass = qclass; + session_ext$query = query; + local strings: set[string] = set(); + session_ext$replies = strings; + dns_sessions_ext[orig, resp, msg$id] = session_ext; + + # This needs to expire before the original dns.bro script expires the + # the data from the dns_session variable. + schedule 14secs { expire_DNS_session_ext(orig, resp, msg$id) }; + } + } + + +event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) + { + local id = c$id; + local orig = id$orig_h; + local resp = id$resp_h; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [orig, resp, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[orig, resp, msg$id]; + add session_ext$replies[fmt("%s",a)]; + session_ext$RA = msg$RA; + session_ext$TTL = ans$TTL; + session_ext$rcode = msg$rcode; + } + + # Check for out of place domain names + if ( is_local_addr(a) && # referring to a local host + !is_local_addr(c$id$resp_h) && # response from a remote host + local_domains !in ans$query ) # drop known names + { + NOTICE([$note=DNSExternalName, + $msg=fmt("%s is pointing to a local host - %s.", ans$query, a), + $conn=c]); + } + } + +event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, str: string) + { + local id = c$id; + local orig = id$orig_h; + local resp = id$resp_h; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [orig, resp, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[orig, resp, msg$id]; + session_ext$rcode = msg$rcode; + add session_ext$replies[str]; + } + } + +event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr, + astr: string) + { + local id = c$id; + local orig = id$orig_h; + local resp = id$resp_h; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [orig, resp, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[orig, resp, msg$id]; + session_ext$rcode = msg$rcode; + add session_ext$replies[fmt("%s", a)]; + } + } + + +event dns_MX_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string, + preference: count) + { + local id=c$id; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [id$orig_h, id$resp_h, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[id$orig_h, id$resp_h, msg$id]; + session_ext$rcode = msg$rcode; + add session_ext$replies[name]; + } + } + +event dns_PTR_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) + { + local id = c$id; + local orig = id$orig_h; + local resp = id$resp_h; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [orig, resp, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[orig, resp, msg$id]; + session_ext$rcode = msg$rcode; + add session_ext$replies[name]; + } + } + +event dns_end(c: connection, msg: dns_msg) + { + local id = c$id; + local orig = id$orig_h; + local resp = id$resp_h; + local session = lookup_DNS_session(c, msg$id); + local session_ext: dns_ext_session_info; + + if ( [orig, resp, msg$id] in dns_sessions_ext ) + { + session_ext = dns_sessions_ext[orig, resp, msg$id]; + session_ext$rcode = msg$rcode; + } + } diff --git a/policy/functions.bro b/policy/functions.bro new file mode 100644 index 0000000000..5a7685feeb --- /dev/null +++ b/policy/functions.bro @@ -0,0 +1,268 @@ +function numeric_id_string(id: conn_id): string + { + return fmt("%s:%d > %s:%d", + id$orig_h, id$orig_p, + id$resp_h, id$resp_p); + } + +function fmt_addr_set(input: addr_set): string + { + local output = ""; + local tmp = ""; + local len = length(input); + local i = 1; + + for ( item in input ) + { + tmp = fmt("%s", item); + if ( len != i ) + tmp = fmt("%s ", tmp); + i = i+1; + output = fmt("%s%s", output, tmp); + } + return fmt("%s", output); + } + +function fmt_str_set(input: string_set, strip: pattern): string + { + local len = length(input); + if ( len == 0 ) + return "{}"; + + local output = "{"; + local tmp = ""; + local i = 1; + + for ( item in input ) + { + tmp = fmt("%s", gsub(item, strip, "")); + if ( len != i ) + tmp = fmt("%s, ", tmp); + i = i+1; + output = fmt("%s%s", output, tmp); + } + return fmt("%s}", output); + } + +# Regular expressions for matching IP addresses in strings. +const ipv4_addr_regex = /[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}/; +const ipv6_8hex_regex = /([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/; +const ipv6_compressed_hex_regex = /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)/; +const ipv6_hex4dec_regex = /(([0-9A-Fa-f]{1,4}:){6,6})([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; +const ipv6_compressed_hex4dec_regex = /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}:)*)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; + +# These are only commented out until this bug is fixed: +# http://www.bro-ids.org/wiki/index.php/Known_Issues#Bug_with_OR-ing_together_pattern_variables +#const ipv6_addr_regex = ipv6_8hex_regex | +# ipv6_compressed_hex_regex | +# ipv6_hex4dec_regex | +# ipv6_compressed_hex4dec_regex; +#const ip_addr_regex = ipv4_addr_regex | ipv6_addr_regex; + +const ipv6_addr_regex = + /([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/ | + /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)/ | # IPv6 Compressed Hex + /(([0-9A-Fa-f]{1,4}:){6,6})([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ | # 6Hex4Dec + /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}:)*)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; # CompressedHex4Dec + +const ip_addr_regex = + /[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}/ | + /([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/ | + /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)/ | # IPv6 Compressed Hex + /(([0-9A-Fa-f]{1,4}:){6,6})([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/ | # 6Hex4Dec + /(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::(([0-9A-Fa-f]{1,4}:)*)([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/; # CompressedHex4Dec + +function is_valid_ip(ip_str: string): bool + { + if ( ip_str == ipv4_addr_regex ) + { + local octets = split(ip_str, /\./); + if ( |octets| != 4 ) + return F; + + local num=0; + for ( i in octets ) + { + num = to_count(octets[i]); + if ( num < 0 || 255 < num ) + return F; + } + return T; + } + else if ( ip_str == ipv6_addr_regex ) + { + # TODO: make this work correctly. + return T; + } + return F; + } + +# This outputs a string_array of ip addresses extracted from a string. +# given: "this is 1.1.1.1 a test 2.2.2.2 string with ip addresses 3.3.3.3" +# outputs: { [1] = 1.1.1.1, [2] = 2.2.2.2, [3] = 3.3.3.3 } +function find_ip_addresses(input: string): string_array + { + local parts = split_all(input, ip_addr_regex); + local output: string_array; + + for ( i in parts ) + { + if ( i % 2 == 0 && is_valid_ip(parts[i]) ) + output[|output|+1] = parts[i]; + } + return output; + } + +######################################## +# The following functions are for getting contact information (email) for an +# IP address in your organization. It will return data from nested subnets +# as well. +# Below is an example for using it: +# +# redef subnet_to_admin_table += { +# [146.128.0.0/16] = "security@yourorg.com", +# [146.128.94.0/24] = "someone@yourorg.com", +# }; +# +# event bro_init() +# { +# print fmt_email_string(find_all_emails(146.128.94.3)); +# => "security@yourorg.com, someone@yourorg.com" +# print fmt_email_string(find_all_emails(146.128.3.3)); +# => "security@yourorg.com" +# } +######################################## +# TODO: make this work with IPv6 +function find_all_emails(ip: addr): set[string] + { + if ( ip !in subnet_to_admin_table ) return set(); + + local output_values: set[string] = set(); + local tmp_ip: addr; + local i: count; + local emails: string; + for ( i in one_to_32 ) + { + tmp_ip = mask_addr(ip, one_to_32[i]); + emails = subnet_to_admin_table[tmp_ip]; + if ( emails != "" ) + add output_values[emails]; + } + return output_values; + } + +function fmt_email_string(emails: set[string]): string + { + local output=""; + for( email in emails ) + { + if ( output == "" ) + output = email; + else + output = fmt("%s, %s", output, email); + } + return output; + } + +######################################## + +# Get a software version instance full of zeros. +function get_default_software_version(): software_version + { + local tmp_int: int = 0; + local tmp_v: software_version = [$major=tmp_int, + $minor=tmp_int, + $minor2=tmp_int, + $addl=""]; + return tmp_v; + } + +function default_software_parsing(sw: string): software + { + local software_name = ""; + local v = get_default_software_version(); + + # The regular expression should match the complete version number + local version_parts = split_all(sw, /[0-9\-\._]{2,}/); + if ( |version_parts| >= 2 ) + { + # Remove the name/version separator + software_name = sub(version_parts[1], /.$/, ""); + local version_numbers = split_n(version_parts[2], /[\-\._[:blank:]]/, F, 4); + if ( |version_numbers| >= 4 ) + v$addl = version_numbers[4]; + if ( |version_numbers| >= 3 ) + v$minor2 = to_int(version_numbers[3]); + if ( |version_numbers| >= 2 ) + v$minor = to_int(version_numbers[2]); + if ( |version_numbers| >= 1 ) + v$major = to_int(version_numbers[1]); + } + local this_software: software = [$name=software_name, $version=v]; + return this_software; + } + +type track_count: record { + n: count &default=0; + index: count &default=0; +}; + +function default_track_count(a: addr): track_count + { + local x: track_count; + return x; + } + +const default_notice_thresholds: vector of count = { + 30, 100, 1000, 10000, 100000, 1000000, 10000000, +} &redef; + +# This is total rip off from scan.bro, but placed in the global namespace +# and slightly reworked to be easier to work with and more general. +function check_threshold(v: vector of count, tracker: track_count): bool + { + if ( tracker$index <= |v| && tracker$n >= v[tracker$index] ) + { + ++tracker$index; + return T; + } + return F; + } + +function default_check_threshold(tracker: track_count): bool + { + return check_threshold(default_notice_thresholds, tracker); + } + +# This can be used for &default values on tables when the index is an addr. +function addr_empty_string_set(a: addr): set[string] + { + return set(); + } + +# Some enums for deciding what and when to log. +type Directions_and_Hosts: enum { + Inbound, Outbound, + LocalHosts, RemoteHosts, + Enabled, Disabled +}; +const DIRECTIONS = set(Inbound, Outbound, Enabled, Disabled); +const HOSTS = set(LocalHosts, RemoteHosts, Enabled, Disabled); + +function id_matches_directions(id: conn_id, d: Directions_and_Hosts): bool + { + if ( d == Disabled ) return F; + + return ( d == Enabled || + (d == Outbound && is_local_addr(id$orig_h)) || + (d == Inbound && is_local_addr(id$resp_h)) ); + } + +function addr_matches_hosts(ip: addr, h: Directions_and_Hosts): bool + { + if ( h == Disabled ) return F; + + return ( h == Enabled || + (h == LocalHosts && is_local_addr(ip)) || + (h == RemoteHosts && !is_local_addr(ip)) ); + } diff --git a/policy/global.bro b/policy/global.bro new file mode 100644 index 0000000000..d459b5e83a --- /dev/null +++ b/policy/global.bro @@ -0,0 +1,33 @@ +@load site +@load signatures + +const private_address_space: set[subnet] = {10.0.0.0/8, 192.168.0.0/16, 127.0.0.0/8, 172.16.0.0/12}; + +# These go along with the functions in functions-ext.bro for mapping IP +# addresses to email contact information for IP addresses and subnets. +const one_to_32: vector of count = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32}; +const subnet_to_admin_table: table[subnet] of string = { + [0.0.0.0/0] = "", +} &redef; + +############################################################### +# Make the default signature action ignore certain signatures +############################################################### +const ignored_signatures = /INTENTIONALLY_BLANK/ &redef; +function default_signature_action(sig: string): SigAction + { + if ( ignored_signatures in sig ) + return SIG_IGNORE; + else + return SIG_ALARM; + } +redef signature_actions &default=default_signature_action; +############################################################### + +# This defines the event that is used by the bro-dblogger application +# to push data from Bro directly into a database. +# see: http://github.com/sethhall/bro-dblogger +global db_log: event(db_table: string, data: any); + +@load functions-ext +@load logging-ext diff --git a/policy/logging.dns-ext.bro b/policy/logging.dns-ext.bro new file mode 100644 index 0000000000..7ee5887db0 --- /dev/null +++ b/policy/logging.dns-ext.bro @@ -0,0 +1,82 @@ +@load global-ext +@load dns-ext + +module DNS; + +export { + # Which DNS queries to record. + # Choices are: Inbound, Outbound, Enabled, Disabled + const query_logging = Enabled &redef; + + # If set to T, this will split inbound and outbound requests + # into separate files. F merges everything into a single file. + const split_log_file = F &redef; + + # Make this value true to reduce the logs to only what's being + # queried for + const minimal_logging = F &redef; +} + +event bro_init() + { + LOG::create_logs("dns-ext", query_logging, split_log_file, T); + + if ( minimal_logging ) + LOG::define_header("dns-ext", cat_sep("\t", "\\N", + "ts", "orig_h", + "query_type", "query")); + else + LOG::define_header("dns-ext", cat_sep("\t", "", + "ts", + "orig_h", "orig_p", + "resp_h", "resp_p", + "proto", "query_type", "query_class", + "query", "transaction_id", + "ttl", "flags", "error", "replies")); + + } + +event dns_ext(id: conn_id, di: dns_ext_session_info) &priority=-10 + { + local log = LOG::get_file_by_id("dns-ext", id, F); + + if ( minimal_logging ) + { + print log, cat_sep("\t", "\\N", + di$ts, + id$orig_h, + query_types[di$qtype], + di$query); + } + else + { + local flags: set[string]; + if ( di$RD ) + add flags["RD"]; + if ( di$RA ) + add flags["RA"]; + if ( di$TC ) + add flags["TC"]; + if ( di$QR ) + add flags["QR"]; + if ( di$Z ) + add flags["Z"]; + if ( di$AA ) + add flags["AA"]; + + print log, cat_sep("\t", "\\N", + di$ts, + id$orig_h, port_to_count(id$orig_p), + id$resp_h, port_to_count(id$resp_p), + get_port_transport_proto(id$resp_p), + query_types[di$qtype], + dns_class[di$qclass], + di$query, + fmt("%04x", di$trans_id), + fmt("%.0f", interval_to_double(di$TTL)), + fmt_str_set(flags, /!!!!/), + base_error[di$rcode], + fmt_str_set(di$replies, /!!!!/) + ); + } + } \ No newline at end of file From cc7c3776ccab7bae73b56b68c7ce0552c2f9ad45 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Wed, 27 Oct 2010 15:37:00 -0400 Subject: [PATCH 2/3] Added the initial syslog analyzer and policy script. --- policy/logging.syslog.bro | 38 ++++++++++++++++ policy/syslog.bro | 57 ++++++++++++++++++++++++ src/Analyzer.cc | 4 ++ src/AnalyzerTags.h | 2 +- src/Makefile.am | 13 ++++-- src/Syslog-binpac.cc | 92 +++++++++++++++++++++++++++++++++++++++ src/Syslog-binpac.h | 58 ++++++++++++++++++++++++ src/event.bif | 2 + src/syslog-analyzer.pac | 27 ++++++++++++ src/syslog-protocol.pac | 15 +++++++ src/syslog.pac | 10 +++++ 11 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 policy/logging.syslog.bro create mode 100644 policy/syslog.bro create mode 100644 src/Syslog-binpac.cc create mode 100644 src/Syslog-binpac.h create mode 100644 src/syslog-analyzer.pac create mode 100644 src/syslog-protocol.pac create mode 100644 src/syslog.pac diff --git a/policy/logging.syslog.bro b/policy/logging.syslog.bro new file mode 100644 index 0000000000..606fd95071 --- /dev/null +++ b/policy/logging.syslog.bro @@ -0,0 +1,38 @@ +@load syslog + +module Syslog; + +export { + # If set to T, this will split inbound and outbound transactions + # into separate files. F merges everything into a single file. + const split_log_file = F &redef; + + # Which SSH logins to record. + # Choices are: Inbound, Outbound, Enabled, Disabled + const logging = Enabled &redef; + +} + +event bro_init() + { + LOG::create_logs("syslog", logging, split_log_file, T); + LOG::define_header("syslog", cat_sep("\t", "", + "ts", + "orig_h", "orig_p", + "resp_h", "resp_p", + "facility", "severity", + "msg")); + } + +event syslog_message(c: connection, facility: count, severity: count, msg: string) + { + local log = LOG::get_file_by_id("syslog", c$id, F); + local id = c$id; + + print log, cat_sep("\t", "\\N", + network_time(), + id$orig_h, port_to_count(id$orig_p), + id$resp_h, port_to_count(id$resp_p), + facility_codes[facility], severity_codes[severity], + msg); + } diff --git a/policy/syslog.bro b/policy/syslog.bro new file mode 100644 index 0000000000..988966f194 --- /dev/null +++ b/policy/syslog.bro @@ -0,0 +1,57 @@ +redef capture_filters += { ["syslog"] = "port 514" }; + +global syslog_ports = { 514/udp } &redef; +redef dpd_config += { [ANALYZER_SYSLOG_BINPAC] = [$ports = syslog_ports] }; + +module Syslog; + +export { + redef enum Notice += { + Syslog_New_Source, + Syslog_New_Destination, + }; + + const facility_codes: table[count] of string = { + [0] = "KERN", + [1] = "USER", + [2] = "MAIL", + [3] = "DAEMON", + [4] = "AUTH", + [5] = "SYSLOG", + [6] = "LPR", + [7] = "NEWS", + [8] = "UUCP", + [9] = "CRON", + [10] = "AUTHPRIV", + [11] = "FTP", + [12] = "NTP", + [13] = "AUDIT", + [14] = "ALERT", + [15] = "CLOCK", + [16] = "LOCAL0", + [17] = "LOCAL1", + [18] = "LOCAL2", + [19] = "LOCAL3", + [20] = "LOCAL4", + [21] = "LOCAL5", + [22] = "LOCAL6", + [23] = "LOCAL7", + }; + + const severity_codes: table[count] of string = { + [0] = "EMERG", + [1] = "ALERT", + [2] = "CRIT", + [3] = "ERR", + [4] = "WARNING", + [5] = "NOTICE", + [6] = "INFO", + [7] = "DEBUG", + }; + +} + +event syslog_message(c: connection, facility: count, severity: count, msg: string) + { + + } diff --git a/src/Analyzer.cc b/src/Analyzer.cc index 6ad1f7998f..14eaff20c1 100644 --- a/src/Analyzer.cc +++ b/src/Analyzer.cc @@ -36,6 +36,7 @@ #include "SSH.h" #include "SSLProxy.h" #include "SSL-binpac.h" +#include "Syslog-binpac.h" // Keep same order here as in AnalyzerTag definition! const Analyzer::Config Analyzer::analyzer_configs[] = { @@ -138,6 +139,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = { { AnalyzerTag::SSL_BINPAC, "SSL_BINPAC", SSL_Analyzer_binpac::InstantiateAnalyzer, SSL_Analyzer_binpac::Available, 0, false }, + { AnalyzerTag::SYSLOG_BINPAC, "SYSLOG_BINPAC", + Syslog_Analyzer_binpac::InstantiateAnalyzer, + Syslog_Analyzer_binpac::Available, 0, false }, { AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer, File_Analyzer::Available, 0, false }, diff --git a/src/AnalyzerTags.h b/src/AnalyzerTags.h index eafa5de300..06f5d636dc 100644 --- a/src/AnalyzerTags.h +++ b/src/AnalyzerTags.h @@ -36,7 +36,7 @@ namespace AnalyzerTag { // Application-layer analyzers, binpac-generated. DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC, - HTTP_BINPAC, RPC_UDP_BINPAC, SSL_BINPAC, + HTTP_BINPAC, RPC_UDP_BINPAC, SSL_BINPAC, SYSLOG_BINPAC, // Other File, Backdoor, InterConn, SteppingStone, TCPStats, diff --git a/src/Makefile.am b/src/Makefile.am index 48aa6536a8..d620f02856 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,7 @@ bifcl_SOURCES = bif_lex.cc bif_parse.cc bif_arg.cc BINPAC_SRC = binpac-lib.pac binpac_bro-lib.pac bittorrent.pac \ dce_rpc.pac dce_rpc_simple.pac dhcp.pac dns.pac \ dns_tcp.pac http.pac ncp.pac netflow.pac rpc.pac smb.pac \ - ssl.pac ssl-record-layer.pac + ssl.pac ssl-record-layer.pac syslog.pac BINPAC_H = $(BINPAC_SRC:.pac=_pac.h) BINPAC_CC = $(BINPAC_SRC:.pac=_pac.cc) @@ -49,7 +49,8 @@ BINPAC_RPC_AUXSRC = \ rpc-protocol.pac rpc-analyzer.pac \ smb-protocol.pac smb-mailslot.pac smb-pipe.pac \ ssl.pac ssl-analyzer.pac ssl-defs.pac \ - ssl-protocol.pac ssl-record-layer.pac + ssl-protocol.pac ssl-record-layer.pac \ + syslog.pac syslog-protocol.pac syslog-analyzer.pac BINPAC = @BINPAC@ BINPAC_FLAGS = -d $(top_builddir)/src -I $(srcdir) @@ -129,7 +130,7 @@ bro_SOURCES = \ Queue.cc RE.cc RPC.cc Reassem.cc RemoteSerializer.cc Rlogin.cc RSH.cc \ Rule.cc RuleAction.cc RuleCondition.cc RuleMatcher.cc \ ScriptAnaly.cc SmithWaterman.cc SMB.cc SMTP.cc \ - SSH.cc SSL-binpac.cc \ + SSH.cc SSL-binpac.cc Syslog-binpac.cc \ Scope.cc SerializationFormat.cc SerialObj.cc Serializer.cc \ Sessions.cc StateAccess.cc Stats.cc SteppingStone.cc Stmt.cc \ TCP.cc TCP_Endpoint.cc TCP_Reassembler.cc TCP_Rewriter.cc \ @@ -165,7 +166,7 @@ noinst_HEADERS = Active.h Analyzer.h AnalyzerTags.h Anon.h ARP.h Attr.h \ SSLInterpreter.h SSLProxy.h SSLv2.h SSLv3.h SSLv3Automaton.h Scope.h \ SerialInfo.h SerialObj.h SerialTypes.h \ SerializationFormat.h Serializer.h Sessions.h StateAccess.h \ - Stats.h SteppingStone.h Stmt.h StmtEnums.h \ + Stats.h SteppingStone.h Stmt.h StmtEnums.h Syslog-binpac.h \ TCP.h TCP_Endpoint.h TCP_Reassembler.h TCP_Rewriter.h Telnet.h Timer.h \ Traverse.h TraverseTypes.h Trigger.h TwoWise.h Type.h UDP.h \ Val.h Var.h X509.h XDR.h ZIP.h \ @@ -331,6 +332,10 @@ ssl_pac.h ssl_pac.cc: ssl.pac $(BINPAC) $(BINPAC_AUXSRC) $(BINPAC_AUXHDR) ssl-pr ssl-record-layer_pac.h ssl-record-layer_pac.cc: ssl-record-layer.pac $(BINPAC) $(BINPAC_AUXSRC) $(BINPAC_AUXHDR) $(BINPAC) $(BINPAC_FLAGS) $(srcdir)/ssl-record-layer.pac +syslog_pac.h syslog_pac.cc: syslog.pac $(BINPAC) $(BINPAC_AUXSRC) $(BINPAC_AUXHDR) syslog-protocol.pac syslog-analyzer.pac + $(BINPAC) $(BINPAC_FLAGS) $(srcdir)/syslog.pac + + patricia.o: patricia.c patricia.h $(CC) $(CFLAGS) -c $(srcdir)/patricia.c diff --git a/src/Syslog-binpac.cc b/src/Syslog-binpac.cc new file mode 100644 index 0000000000..28c0716aab --- /dev/null +++ b/src/Syslog-binpac.cc @@ -0,0 +1,92 @@ +// $Id:$ + +#include "Syslog-binpac.h" +#include "TCP_Reassembler.h" + +Syslog_Analyzer_binpac::Syslog_Analyzer_binpac(Connection* conn) +: Analyzer(AnalyzerTag::SYSLOG_BINPAC, conn) + { + interp = new binpac::Syslog::Syslog_Conn(this); + did_session_done = 0; + //ADD_ANALYZER_TIMER(&Syslog_Analyzer_binpac::ExpireTimer, + // network_time + Syslog_session_timeout, 1, TIMER_Syslog_EXPIRE); + } + +Syslog_Analyzer_binpac::~Syslog_Analyzer_binpac() + { + delete interp; + } + +void Syslog_Analyzer_binpac::Done() + { + Analyzer::Done(); + + if ( ! did_session_done ) + Event(udp_session_done); + } + +void Syslog_Analyzer_binpac::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); + interp->NewData(orig, data, data + len); + } + +//void Syslog_Analyzer_binpac::ExpireTimer(double t) +// { +// // The - 1.0 in the following is to allow 1 second for the +// // common case of a single request followed by a single reply, +// // so we don't needlessly set the timer twice in that case. +// if ( t - Conn()->LastTime() >= Syslog_session_timeout - 1.0 || terminating ) +// { +// Event(connection_timeout); +// sessions->Remove(Conn()); +// } +// else +// ADD_ANALYZER_TIMER(&Syslog_Analyzer_binpac::ExpireTimer, +// t + Syslog_session_timeout, 1, TIMER_Syslog_EXPIRE); +// } + +//Syslog_TCP_Analyzer_binpac::Syslog_TCP_Analyzer_binpac(Connection* conn) +//: TCP_ApplicationAnalyzer(AnalyzerTag::Syslog_TCP_BINPAC, conn) +// { +// interp = new binpac::Syslog_on_TCP::Syslog_TCP_Conn(this); +// } + +//Syslog_TCP_Analyzer_binpac::~Syslog_TCP_Analyzer_binpac() +// { +// delete interp; +// } + +//void Syslog_TCP_Analyzer_binpac::Done() +// { +// TCP_ApplicationAnalyzer::Done(); +// +// interp->FlowEOF(true); +// interp->FlowEOF(false); +// } + +//void Syslog_TCP_Analyzer_binpac::EndpointEOF(TCP_Reassembler* endp) +// { +// TCP_ApplicationAnalyzer::EndpointEOF(endp); +// interp->FlowEOF(endp->IsOrig()); +// } + +//void Syslog_TCP_Analyzer_binpac::DeliverStream(int len, const u_char* data, +// bool orig) +// { +// TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); +// +// assert(TCP()); +// +// if ( TCP()->IsPartial() || TCP()->HadGap(orig) ) +// // punt-on-partial or stop-on-gap. +// return; +// +// interp->NewData(orig, data, data + len); +// } + +//void Syslog_TCP_Analyzer_binpac::Undelivered(int seq, int len, bool orig) +// { +// TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); +// interp->NewGap(orig, len); +// } diff --git a/src/Syslog-binpac.h b/src/Syslog-binpac.h new file mode 100644 index 0000000000..c9a42ddbcf --- /dev/null +++ b/src/Syslog-binpac.h @@ -0,0 +1,58 @@ +// $Id:$ + +#ifndef Syslog_binpac_h +#define Syslog_binpac_h + +#include "UDP.h" +#include "TCP.h" + +#include "syslog_pac.h" + +class Syslog_Analyzer_binpac : public Analyzer { +public: + Syslog_Analyzer_binpac(Connection* conn); + virtual ~Syslog_Analyzer_binpac(); + + virtual void Done(); + virtual void DeliverPacket(int len, const u_char* data, bool orig, + int seq, const IP_Hdr* ip, int caplen); + + static Analyzer* InstantiateAnalyzer(Connection* conn) + { return new Syslog_Analyzer_binpac(conn); } + + static bool Available() + { return true; } + //{ return (Syslog_request || Syslog_full_request) && FLAGS_use_binpac; } + +protected: + friend class AnalyzerTimer; + void ExpireTimer(double t); + + int did_session_done; + + binpac::Syslog::Syslog_Conn* interp; +}; + +// #include "Syslog_tcp_pac.h" +// +//class Syslog_TCP_Analyzer_binpac : public TCP_ApplicationAnalyzer { +//public: +// Syslog_TCP_Analyzer_binpac(Connection* conn); +// virtual ~Syslog_TCP_Analyzer_binpac(); +// +// virtual void Done(); +// virtual void DeliverStream(int len, const u_char* data, bool orig); +// virtual void Undelivered(int seq, int len, bool orig); +// virtual void EndpointEOF(TCP_Reassembler* endp); +// +// static Analyzer* InstantiateAnalyzer(Connection* conn) +// { return new Syslog_TCP_Analyzer_binpac(conn); } +// +// static bool Available() +// { return (Syslog_request || Syslog_full_request) && FLAGS_use_binpac; } +// +//protected: +// binpac::Syslog_on_TCP::Syslog_TCP_Conn* interp; +//}; +// +#endif diff --git a/src/event.bif b/src/event.bif index 3171b02dde..ce5bd16c03 100644 --- a/src/event.bif +++ b/src/event.bif @@ -392,6 +392,8 @@ event irc_password_message%(c: connection, password: string%); event file_transferred%(c: connection, prefix: string, descr: string, mime_type: string%); event file_virus%(c: connection, virname: string%); +event syslog_message%(c: connection, facility: count, severity: count, msg: string%); + event signature_match%(state: signature_state, msg: string, data: string%); # Generated if a handler finds an identification of the software diff --git a/src/syslog-analyzer.pac b/src/syslog-analyzer.pac new file mode 100644 index 0000000000..5256b1d294 --- /dev/null +++ b/src/syslog-analyzer.pac @@ -0,0 +1,27 @@ + +connection Syslog_Conn(bro_analyzer: BroAnalyzer) +{ + upflow = Syslog_Flow; + downflow = Syslog_Flow; +}; + +flow Syslog_Flow +{ + datagram = Syslog_Message withcontext(connection, this); + + function process_syslog_message(m: Syslog_Message): bool + %{ + bro_event_syslog_message(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${m.PRI.facility}, + ${m.PRI.severity}, + new StringVal(${m.msg}.length(), (const char*) ${m.msg}.begin()) + ); + return true; + %} + +}; + +refine typeattr Syslog_Message += &let { + proc_syslog_message = $context.flow.process_syslog_message(this); +}; diff --git a/src/syslog-protocol.pac b/src/syslog-protocol.pac new file mode 100644 index 0000000000..a2bf8a31da --- /dev/null +++ b/src/syslog-protocol.pac @@ -0,0 +1,15 @@ +type Syslog_Message = record { + PRI: Syslog_Priority; + msg: bytestring &restofdata; +} &byteorder = littleendian; + +type Syslog_Priority = record { + lt : uint8 &check(lt == "<"); + val : RE/[[:digit:]]+/; + gt : uint8 &check(gt == ">"); +} &let { + val_length: int = sizeof(val) - 1; + int_val: int = bytestring_to_int(val, 10); + severity: int = (int_val & 0x07); + facility: int = (int_val & 0x03f8) >> 3; +}; diff --git a/src/syslog.pac b/src/syslog.pac new file mode 100644 index 0000000000..3c0ecfb10d --- /dev/null +++ b/src/syslog.pac @@ -0,0 +1,10 @@ +%include binpac.pac +%include bro.pac + +analyzer Syslog withcontext { + connection: Syslog_Conn; + flow: Syslog_Flow; +}; + +%include syslog-protocol.pac +%include syslog-analyzer.pac From 6322130c3741661cc73e441524856346da5990e7 Mon Sep 17 00:00:00 2001 From: Seth Hall Date: Fri, 3 Jun 2011 10:28:10 -0400 Subject: [PATCH 3/3] Updates for syslog analyzer to prepare it for merging. - Integrated with CMake. - Analyzer only support syslog over UDP right now. - Fixed small bug in the analyzer to make it generate events correctly. --- policy/syslog.bro | 10 +++++----- src/CMakeLists.txt | 3 +++ src/Syslog-binpac.cc | 2 -- src/Syslog-binpac.h | 5 +---- src/syslog-analyzer.pac | 12 ++++++------ 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/policy/syslog.bro b/policy/syslog.bro index 988966f194..258e73496f 100644 --- a/policy/syslog.bro +++ b/policy/syslog.bro @@ -6,10 +6,10 @@ redef dpd_config += { [ANALYZER_SYSLOG_BINPAC] = [$ports = syslog_ports] }; module Syslog; export { - redef enum Notice += { - Syslog_New_Source, - Syslog_New_Destination, - }; + #redef enum Notice += { + # Syslog_New_Source, + # Syslog_New_Destination, + #}; const facility_codes: table[count] of string = { [0] = "KERN", @@ -53,5 +53,5 @@ export { event syslog_message(c: connection, facility: count, severity: count, msg: string) { - + print msg; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcf15db597..24b359a4a6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -201,6 +201,8 @@ binpac_target(ssl.pac ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac) binpac_target(ssl-record-layer.pac ssl-defs.pac ssl.pac) +binpac_target(syslog.pac + syslog-protocol.pac syslog-analyzer.pac) ######################################################################## ## bro target @@ -391,6 +393,7 @@ set(bro_SRCS Stats.cc SteppingStone.cc Stmt.cc + Syslog-binpac.cc TCP.cc TCP_Endpoint.cc TCP_Reassembler.cc diff --git a/src/Syslog-binpac.cc b/src/Syslog-binpac.cc index 28c0716aab..c8697d0f3f 100644 --- a/src/Syslog-binpac.cc +++ b/src/Syslog-binpac.cc @@ -1,5 +1,3 @@ -// $Id:$ - #include "Syslog-binpac.h" #include "TCP_Reassembler.h" diff --git a/src/Syslog-binpac.h b/src/Syslog-binpac.h index c9a42ddbcf..fcd75edf0e 100644 --- a/src/Syslog-binpac.h +++ b/src/Syslog-binpac.h @@ -1,5 +1,3 @@ -// $Id:$ - #ifndef Syslog_binpac_h #define Syslog_binpac_h @@ -21,8 +19,7 @@ public: { return new Syslog_Analyzer_binpac(conn); } static bool Available() - { return true; } - //{ return (Syslog_request || Syslog_full_request) && FLAGS_use_binpac; } + { return syslog_message; } protected: friend class AnalyzerTimer; diff --git a/src/syslog-analyzer.pac b/src/syslog-analyzer.pac index 5256b1d294..2648319568 100644 --- a/src/syslog-analyzer.pac +++ b/src/syslog-analyzer.pac @@ -11,12 +11,12 @@ flow Syslog_Flow function process_syslog_message(m: Syslog_Message): bool %{ - bro_event_syslog_message(connection()->bro_analyzer(), - connection()->bro_analyzer()->Conn(), - ${m.PRI.facility}, - ${m.PRI.severity}, - new StringVal(${m.msg}.length(), (const char*) ${m.msg}.begin()) - ); + BifEvent::generate_syslog_message(connection()->bro_analyzer(), + connection()->bro_analyzer()->Conn(), + ${m.PRI.facility}, + ${m.PRI.severity}, + new StringVal(${m.msg}.length(), (const char*) ${m.msg}.begin()) + ); return true; %}