├── scripts ├── todo │ ├── needs_review │ │ ├── summaries │ │ │ ├── ipp-summary.bro │ │ │ ├── dns-summary.bro │ │ │ ├── nfs-summary.bro │ │ │ ├── netbios-ns-summary.bro │ │ │ ├── conn-app.bro │ │ │ ├── ncp-tag.bro │ │ │ ├── rexmit-summary.bro │ │ │ ├── conn-app-reduced.bro │ │ │ ├── sun-rpc-summary.bro │ │ │ ├── conn-util.bro │ │ │ ├── dce-rpc-tag.bro │ │ │ ├── app-summary.bro │ │ │ ├── ncp-summary.bro │ │ │ ├── conn-size.bro │ │ │ ├── dce-rpc-summary.bro │ │ │ ├── conn-summary.bro │ │ │ ├── netbios-ssn-summary.bro │ │ │ ├── smb-tag.bro │ │ │ ├── http-rps-summary.bro │ │ │ ├── dns-common-summary.bro │ │ │ ├── smb-summary.bro │ │ │ └── http-summary.bro │ │ ├── trw.bro │ │ ├── dce.bro │ │ ├── loaders │ │ │ ├── mt.bro │ │ │ ├── all.bro │ │ │ └── brolite.bro │ │ ├── flag-warez.bro │ │ ├── analy.bro │ │ ├── flag-irc.bro │ │ ├── time-machine │ │ │ ├── tm-class.bro │ │ │ ├── tm-http.bro │ │ │ ├── tm-ftp.bro │ │ │ ├── tm-capture.bro │ │ │ ├── tm-contents.bro │ │ │ └── tm-gap.bro │ │ ├── contents.bro │ │ ├── hot-ids.bro │ │ ├── clear-passwords.bro │ │ ├── secondary-filter.bro │ │ ├── ntp.bro │ │ ├── http-detect-passwd.bro │ │ ├── ssh-stepping.bro │ │ ├── dns-lookup.bro │ │ ├── gnutella.bro │ │ ├── conn-flood.bro │ │ ├── terminate-connection.bro │ │ ├── ident.bro │ │ ├── sigs │ │ │ ├── http-bots.sig │ │ │ └── dpd.sig │ │ ├── finger.bro │ │ ├── capture-loss.bro │ │ ├── proxy.bro │ │ ├── nfs.bro │ │ ├── service-probe.bro │ │ ├── http-request.bro │ │ ├── rsh.bro │ │ ├── targeted-scan.bro │ │ ├── ncp.bro │ │ ├── dns-anonymizer.bro │ │ ├── worm.bro │ │ ├── firewall.bro │ │ ├── http-anon-utils.bro │ │ ├── detect-protocols-http.bro │ │ ├── mime-pop.bro │ │ ├── pop3.bro │ │ ├── hot.bro │ │ ├── software.bro │ │ ├── anon.bro │ │ ├── bt-tracker.bro │ │ ├── trw-impl.bro │ │ ├── smtp-relay.bro │ │ └── http-anon-server.bro │ ├── possible │ │ ├── passwords.bro │ │ ├── inactivity.bro │ │ └── arp.bro │ └── relies_on_synchronized │ │ └── roam.bro └── policy │ └── protocols │ ├── conn │ └── conn-add-geodata.bro │ └── http │ └── http-exe-bad-attributes.bro ├── Makefile └── README.rst /scripts/todo/needs_review/summaries/ipp-summary.bro: -------------------------------------------------------------------------------- 1 | @load http-summary 2 | 3 | redef HTTP_summary::log = open_log_file("ipp-summary") &redef; 4 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/trw.bro: -------------------------------------------------------------------------------- 1 | # $Id: trw.bro 3297 2006-06-18 00:56:58Z vern $ 2 | # 3 | # Load this file to actiate TRW analysis. 4 | 5 | @load trw-impl 6 | 7 | redef TRW::use_TRW_algorithm = T; 8 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/dce.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | redef capture_filters += { ["dce"] = "port 135" }; 4 | 5 | global dce_ports = { 135/tcp } &redef; 6 | redef dpd_config += { [ANALYZER_DCE_RPC] = [$ports = dce_ports] }; 7 | 8 | # No default implementation for events. 9 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/dns-summary.bro: -------------------------------------------------------------------------------- 1 | @load dns-common-summary 2 | 3 | redef DNS_common_summary::log = open_log_file("dns-summary"); 4 | redef DNS_common_summary::server_ports = { 53/udp, 53/tcp }; 5 | 6 | redef capture_filters = { 7 | ["dns"] = "port 53", 8 | }; 9 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/nfs-summary.bro: -------------------------------------------------------------------------------- 1 | @load sun-rpc-summary 2 | 3 | redef SUN_RPC_summary::log = open_log_file("nfs-summary"); 4 | 5 | redef capture_filters = { 6 | ["nfs"] = "port 2049", 7 | # UDP packets are often fragmented 8 | ["nfs-frag"] = "ip[6:2] & 0x1fff != 0", 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/netbios-ns-summary.bro: -------------------------------------------------------------------------------- 1 | @load dns-common-summary 2 | 3 | redef DNS_common_summary::dns_summary_log = open_log_file("netbios-ns-summary"); 4 | redef DNS_common_summary::server_ports = { 137/udp }; 5 | 6 | redef capture_filters += { 7 | ["netbios-ns"] = "udp port 137", 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/loaders/mt.bro: -------------------------------------------------------------------------------- 1 | # $Id: mt.bro 340 2004-09-09 06:38:27Z vern $ 2 | 3 | @load alarm 4 | @load dns-lookup 5 | @load hot 6 | @load frag 7 | @load tcp 8 | @load scan 9 | @load weird 10 | @load finger 11 | @load ident 12 | @load ftp 13 | @load login 14 | @load portmapper 15 | @load ntp 16 | @load tftp 17 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/flag-warez.bro: -------------------------------------------------------------------------------- 1 | # $Id: flag-warez.bro 416 2004-09-17 03:52:28Z vern $ 2 | # 3 | # include this module to flag various forms of Warez access. 4 | 5 | @load hot-ids 6 | @load ftp 7 | 8 | redef FTP::hot_files += /.*[wW][aA][rR][eE][zZ].*/ ; 9 | 10 | redef always_hot_ids += { "warez", "hanzwarez", "zeraw", }; 11 | redef hot_ids += { "warez", "hanzwarez", "zeraw", }; 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This makefile is only meant to be used by Bro packagers to create 2 | # a tarball of contributed scripts. Resulting tarballs are named like: 3 | # bro-scripts-.tar.gz 4 | 5 | DATE=`date "+%Y%m%d"` 6 | NAME="bro-scripts-$(DATE)" 7 | 8 | all: dist 9 | 10 | dist: 11 | mkdir $(NAME) 12 | cp -R scripts README.rst $(NAME) 13 | tar czf $(NAME).tar.gz $(NAME) 14 | rm -r $(NAME) 15 | 16 | distclean: 17 | rm -r bro-scripts* 18 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/analy.bro: -------------------------------------------------------------------------------- 1 | # Statistical analysis of TCP connection in terms of the packet streams 2 | # in each direction. 3 | 4 | @load dns-lookup 5 | @load udp 6 | 7 | 8 | event conn_stats(c: connection, os: endpoint_stats, rs: endpoint_stats) 9 | { 10 | local id = c$id; 11 | 12 | print fmt("%.6f %s %s %s %s %s %s %s %s %s", 13 | c$start_time, c$duration, id$orig_p, id$resp_p, 14 | conn_size(c$orig, tcp), conn_size(c$resp, tcp), 15 | id$orig_h, id$resp_h, os, rs); 16 | } 17 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/flag-irc.bro: -------------------------------------------------------------------------------- 1 | # $Id: flag-irc.bro 4758 2007-08-10 06:49:23Z vern $ 2 | # 3 | # include this module to flag various forms of IRC access. 4 | 5 | @load ftp 6 | 7 | redef FTP::hot_files += 8 | /.*eggdrop.*/ 9 | | /.*eggsun.*/ 10 | ; 11 | 12 | redef Hot::flag_successful_inbound_service: table[port] of string += { 13 | [[6666/tcp, 6667/tcp]] = "inbound IRC", 14 | }; 15 | 16 | redef Hot::hot_dsts: table[addr] of string += { 17 | [bitchx.com] = "IRC source sites", 18 | }; 19 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-class.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | # 3 | # Changes the class for addresses that have generated alerts. 4 | 5 | @load time-machine 6 | @load notice 7 | @load scan 8 | 9 | event notice_alarm(n: notice_info, action: NoticeAction) 10 | { 11 | if ( ! n?$src ) 12 | return; 13 | 14 | if ( n?$conn && is_external_connection(n$conn) ) 15 | return; 16 | 17 | local class = "alarm"; 18 | if ( n$note == Scan::AddressScan || n$note == Scan::PortScan ) 19 | class = "scanner"; 20 | 21 | TimeMachine::set_class(n$src, class, TimeMachine::BOTH, "tm-class"); 22 | } 23 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-http.bro: -------------------------------------------------------------------------------- 1 | # $Id: tm-http.bro,v 1.1.2.1 2005/11/29 21:39:05 sommer Exp $ 2 | # 3 | # Requests connections from time-machine for which we have seen a sensitive URI. 4 | 5 | @load http 6 | @load time-machine 7 | 8 | redef notice_policy += { 9 | [$pred(a: notice_info) = 10 | { 11 | if ( a$note == HTTP::HTTP_SensitiveURI && 12 | a?$conn && ! is_external_connection(a$conn) ) 13 | TimeMachine::request_connection(a$conn, T, "tm-http"); 14 | return F; 15 | }, 16 | $result = NOTICE_FILE, # irrelevant, since we always return F 17 | $priority = 1] 18 | }; 19 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/conn-app.bro: -------------------------------------------------------------------------------- 1 | @load conn-app-reduced 2 | 3 | @load ftp 4 | @load dce-rpc 5 | 6 | event new_connection(c: connection) 7 | { 8 | local id = c$id; 9 | if ( [id$resp_h, id$resp_p] in DCE_RPC::dce_rpc_endpoint ) 10 | { 11 | # local uuid = DCE_RPC::dce_rpc_endpoint[id$resp_h, id$resp_p]; 12 | # conn_app[id] = fmt("dce-rpc-%s", 13 | # ( uuid in DCE_RPC::dce_rpc_uuid_name ) ? 14 | # DCE_RPC::dce_rpc_uuid_name[uuid] : "unknown"); 15 | conn_app[id] = "dce-rpc"; 16 | } 17 | else if ( FTP::is_ftp_data_connection(c) ) 18 | { 19 | conn_app[id] = "ftp-data"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/ncp-tag.bro: -------------------------------------------------------------------------------- 1 | @load conn-id 2 | @load ncp 3 | 4 | module NCP_tag; 5 | 6 | global log = open_log_file("ncp-tag") &redef; 7 | 8 | const ncp_request_type = { 9 | [ 0x11 ] = "print", 10 | [ 0x16, 0x68 ] = "directory", 11 | } &default = function(code: count): string 12 | { 13 | return fmt("unknown(%x)", code); 14 | }; 15 | 16 | event ncp_request(c: connection, frame_type: count, length: count, func: count) 17 | { 18 | print log, fmt("%.6f %s NCP request type=%s function=%s", 19 | network_time(), id_string(c$id), 20 | NCP::ncp_frame_type_name[frame_type], 21 | NCP::ncp_function_name[func]); 22 | } 23 | 24 | event ncp_reply(c: connection, frame_type: count, length: count, completion_code: count) 25 | { 26 | } 27 | -------------------------------------------------------------------------------- /scripts/todo/possible/passwords.bro: -------------------------------------------------------------------------------- 1 | # $Id: passwords.bro 688 2004-11-02 23:59:55Z vern $ 2 | 3 | # Generates notices of exposed passwords. Currently just works 4 | # on telnet/rlogin access. Should be extended to do FTP, HTTP, etc. 5 | 6 | @load login 7 | 8 | redef enum Notice += { 9 | PasswordExposed, 10 | }; 11 | 12 | # Usernames which we ignore. 13 | global okay_usernames: set[string] &redef; 14 | 15 | # Passwords which we ignore. 16 | global okay_passwords = { "", "" } &redef; 17 | 18 | event login_success(c:connection, user: string, client_user: string, 19 | password: string, line: string) 20 | { 21 | if ( user in okay_usernames || password in okay_passwords ) 22 | return; 23 | 24 | NOTICE([$note=PasswordExposed, 25 | $conn=c, 26 | $user=user, 27 | $sub=password, 28 | $msg="login exposed user's password"]); 29 | } 30 | -------------------------------------------------------------------------------- /scripts/policy/protocols/conn/conn-add-geodata.bro: -------------------------------------------------------------------------------- 1 | ##! conn-add-geodata.bro 2 | ##! 3 | ##! Add countries for the originator and responder of a connection 4 | ##! to the connection logs. 5 | ##! 6 | ##! Requirements: GeoIP database 7 | ##! 8 | ##! Author: Seth Hall 9 | 10 | module Conn; 11 | 12 | export { 13 | redef record Conn::Info += { 14 | ## Country code for the originator of the connection based 15 | ## on a GeoIP lookup. 16 | orig_cc: string &optional &log; 17 | ## Country code for the responser of the connection based 18 | ## on a GeoIP lookup. 19 | resp_cc: string &optional &log; 20 | }; 21 | } 22 | 23 | event connection_state_remove(c: connection) 24 | { 25 | local orig_loc = lookup_location(c$id$orig_h); 26 | if ( orig_loc?$country_code ) 27 | c$conn$orig_cc = orig_loc$country_code; 28 | 29 | local resp_loc = lookup_location(c$id$resp_h); 30 | if ( resp_loc?$country_code ) 31 | c$conn$resp_cc = resp_loc$country_code; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /scripts/todo/possible/inactivity.bro: -------------------------------------------------------------------------------- 1 | # $Id: inactivity.bro 7073 2010-09-13 00:45:02Z vern $ 2 | 3 | @load port-name 4 | 5 | const inactivity_timeouts: table[port] of interval = { 6 | # For interactive services, allow longer periods of inactivity. 7 | [[telnet, rlogin, ssh, ftp]] = 1 hrs, 8 | } &redef; 9 | 10 | function determine_inactivity_timeout(c: connection) 11 | { 12 | local service = c$id$resp_p; 13 | 14 | # Determine service (adapted from hot.bro) 15 | if ( c$orig$state == TCP_INACTIVE ) 16 | { 17 | # We're seeing a half-established connection. Use the 18 | # service of the originator if it's well-known and the 19 | # responder isn't. 20 | if ( service !in port_names && c$id$orig_p in port_names ) 21 | service = c$id$orig_p; 22 | } 23 | 24 | if ( service in inactivity_timeouts ) 25 | set_inactivity_timeout(c$id, inactivity_timeouts[service]); 26 | } 27 | 28 | event connection_established(c: connection) 29 | { 30 | determine_inactivity_timeout(c); 31 | } 32 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/rexmit-summary.bro: -------------------------------------------------------------------------------- 1 | # Statistical analysis of TCP connection in terms of the packet streams 2 | # in each direction. 3 | 4 | @load conn-util 5 | 6 | redef capture_filters = { ["tcp"] = "tcp" }; 7 | redef ignore_keep_alive_rexmit = T; 8 | 9 | global log = open_log_file("rexmit-summary") &redef; 10 | 11 | const min_num_pkts = 0; 12 | 13 | event conn_stats(c: connection, os: endpoint_stats, rs: endpoint_stats) 14 | { 15 | if ( os$num_pkts < min_num_pkts && rs$num_pkts < min_num_pkts ) 16 | return; 17 | 18 | print log, fmt("conn %s start %.6f duration %.6f pkt_^ %d rexmit_pkt_^ %d pyld_^ %d rexmit_pyld_^ %d pkt_v %d rexmit_pkt_v %d pyld_v %d rexmit_pyld_v %d", 19 | conn_id_string(c$id), c$start_time, c$duration, 20 | os$num_pkts, os$num_rxmit, 21 | # os$num_pkts == 0 ? 0.0 : 1.0 * os$num_rxmit / os$num_pkts, 22 | c$orig$size, os$num_rxmit_bytes, 23 | rs$num_pkts, rs$num_rxmit, 24 | # rs$num_pkts == 0 ? 0.0 : 1.0 * rs$num_rxmit / rs$num_pkts, 25 | c$resp$size, rs$num_rxmit_bytes); 26 | } 27 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/contents.bro: -------------------------------------------------------------------------------- 1 | # $Id: contents.bro 47 2004-06-11 07:26:32Z vern $ 2 | 3 | redef capture_filters += { ["contents"] = "tcp" }; 4 | 5 | # Keeps track of to which given contents files we've written. 6 | global contents_files: set[string]; 7 | 8 | event new_connection_contents(c: connection) 9 | { 10 | local id = c$id; 11 | 12 | local orig_file = 13 | fmt("contents.%s.%d-%s.%d", 14 | id$orig_h, id$orig_p, id$resp_h, id$resp_p); 15 | local resp_file = 16 | fmt("contents.%s.%d-%s.%d", 17 | id$resp_h, id$resp_p, id$orig_h, id$orig_p); 18 | 19 | local orig_f: file; 20 | local resp_f: file; 21 | 22 | if ( orig_file !in contents_files ) 23 | { 24 | add contents_files[orig_file]; 25 | orig_f = open(orig_file); 26 | } 27 | else 28 | orig_f = open_for_append(orig_file); 29 | 30 | if ( resp_file !in contents_files ) 31 | { 32 | add contents_files[resp_file]; 33 | resp_f = open(resp_file); 34 | } 35 | else 36 | resp_f = open_for_append(resp_file); 37 | 38 | set_contents_file(id, CONTENTS_ORIG, orig_f); 39 | set_contents_file(id, CONTENTS_RESP, resp_f); 40 | } 41 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/hot-ids.bro: -------------------------------------------------------------------------------- 1 | # @(#) $Id: hot-ids.bro 785 2004-11-24 05:56:06Z rwinslow $ (LBL) 2 | 3 | # If these ids are seen, the corresponding connection is terminated. 4 | const forbidden_ids = { 5 | "uucp", "daemon", "rewt", "nuucp", 6 | "EZsetup", "OutOfBox", "4Dgifts", 7 | "ezsetup", "outofbox", "4dgifts", "sgiweb", 8 | "r00t", "ruut", "bomb", "backdoor", 9 | "bionic", "warhead", "check_mate", "checkmate", "check_made", 10 | "themage", "darkmage", "y0uar3ownd", "netfrack", "netphrack", 11 | } &redef; 12 | 13 | const forbidden_ids_if_no_password = { "lp" } &redef; 14 | 15 | const forbidden_id_patterns = /(y[o0]u)(r|ar[e3])([o0]wn.*)/ &redef; 16 | 17 | const always_hot_ids = { 18 | "sync", "tutor", "tour", 19 | "retro", "milk", "moof", "own", "gdm", "anacnd", 20 | "lp", "demos", forbidden_ids, 21 | } &redef; 22 | 23 | # The ones here that aren't in always_hot_ids are only hot upon 24 | # success. 25 | const hot_ids = { 26 | "root", "system", "smtp", "sysadm", "diag", "sysdiag", "sundiag", 27 | "operator", "sys", "toor", "issadmin", "msql", "sysop", "sysoper", 28 | "wank", always_hot_ids, 29 | } &redef; 30 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/clear-passwords.bro: -------------------------------------------------------------------------------- 1 | # $Id: clear-passwords.bro 4758 2007-08-10 06:49:23Z vern $ 2 | 3 | # Monitoring for use of cleartext passwords. 4 | 5 | @load ftp 6 | @load login 7 | @load pop3 8 | @load irc 9 | 10 | const passwd_file = open_log_file("passwords") &redef; 11 | 12 | # ftp, login and pop3 call login_{success,failure}, which in turn 13 | # calls account_tried(), so we can snarf all at once here: 14 | event account_tried(c: connection, user: string, passwd: string) 15 | { 16 | print passwd_file, fmt("%s account name '%s', password '%s': %s", 17 | is_local_addr(c$id$orig_h) ? "local" : "remote", 18 | user, passwd, id_string(c$id)); 19 | } 20 | 21 | # IRC raises a different event on login, so we hook into it here: 22 | event irc_join_message(c: connection, info_list: irc_join_list) 23 | { 24 | for ( l in info_list) 25 | { 26 | print passwd_file, fmt("IRC JOIN name '%s', password '%s'", 27 | l$nick, l$password); 28 | } 29 | } 30 | 31 | # Raised if IRC user tries to become operator: 32 | event irc_oper_message(c: connection, user: string, password: string) 33 | { 34 | print passwd_file, fmt("IRC OPER name '%s', password '%s'", 35 | user, password); 36 | } 37 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/conn-app-reduced.bro: -------------------------------------------------------------------------------- 1 | @load port-name 2 | 3 | # Used to annotate apps for connections on ephemeral ports 4 | global conn_app: table[conn_id] of string &default = 5 | function(id: conn_id): string 6 | { 7 | local p = is_icmp_port(id$resp_p) ? id$orig_p : id$resp_p; 8 | if ( p in port_names ) 9 | return port_names[p]; 10 | else 11 | return fmt("%s", p); 12 | }; 13 | 14 | redef port_names += { 15 | [0/icmp] = "icmp-echo", 16 | [8/icmp] = "icmp-echo", 17 | [3/icmp] = "icmp-unreach", 18 | 19 | [497/tcp] = "dantz", 20 | [554/tcp] = "rtsp", 21 | [5730/tcp] = "steltor", # calendar 22 | [[7501/tcp, 7502/tcp, 7503/tcp, 7504/tcp, 7505/tcp, 23 | 7506/tcp, 7507/tcp, 7508/tcp, 7509/tcp, 7510/tcp]] 24 | = "hpss", 25 | [[3128/tcp, 8000/tcp, 8080/tcp, 8888/tcp]] = "http", 26 | [8443/tcp] = "https", 27 | [3396/tcp] = "printer-agent", 28 | [13782/tcp] = "veritas-backup-ctrl", 29 | [16384/tcp] = "connected-backup", 30 | 31 | [67/udp] = "dhcp-s", # bootstrap for diskless hosts 32 | [68/udp] = "dhcp-c", # reply-port 33 | [427/udp] = "srvloc", 34 | [11001/udp] = "metasys", # cardkey 35 | [38293/udp] = "nav-ping", # norton anti-virus host discovery 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/secondary-filter.bro: -------------------------------------------------------------------------------- 1 | # $Id: secondary-filter.bro 6022 2008-07-25 19:15:00Z vern $ 2 | 3 | # Examples of using the secondary-filter matching path. 4 | 5 | event rst_syn_fin_flag(filter: string, pkt: pkt_hdr) 6 | { 7 | print "rst_syn_fin_flag()"; 8 | print fmt(" %s:%s -> %s:%s", pkt$ip$src, pkt$tcp$sport, 9 | pkt$ip$dst, pkt$tcp$dport); 10 | } 11 | 12 | event a_udp_event(filter: string, pkt: pkt_hdr) 13 | { 14 | print "a_udp_event()"; 15 | print fmt(" %s:%s -> %s:%s", pkt$ip$src, pkt$udp$sport, 16 | pkt$ip$dst, pkt$udp$dport); 17 | } 18 | 19 | event a_tcp_event(filter: string, pkt: pkt_hdr) 20 | { 21 | print "a_tcp_event()"; 22 | print fmt(" %s:%s -> %s:%s", pkt$ip$src, pkt$tcp$sport, 23 | pkt$ip$dst, pkt$tcp$dport); 24 | } 25 | 26 | event sampled_1_in_1024_packet(filter: string, pkt: pkt_hdr) 27 | { 28 | print "sampled packet:"; 29 | print "ip", pkt$ip; 30 | 31 | if ( pkt?$tcp ) 32 | print "tcp", pkt$tcp; 33 | if ( pkt?$udp ) 34 | print "udp", pkt$udp; 35 | if ( pkt?$icmp ) 36 | print "icmp", pkt$icmp; 37 | } 38 | 39 | redef secondary_filters += { 40 | ["tcp[13] & 7 != 0"] = rst_syn_fin_flag, 41 | ["udp"] = a_udp_event, 42 | ["tcp"] = a_tcp_event, 43 | ["ip[10:2] & 0xffc == 0x398"] = sampled_1_in_1024_packet, 44 | }; 45 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/ntp.bro: -------------------------------------------------------------------------------- 1 | # $Id: ntp.bro 4758 2007-08-10 06:49:23Z vern $ 2 | 3 | @load udp-common 4 | 5 | redef capture_filters += { ["ntp"] = "udp port 123" }; 6 | 7 | module NTP; 8 | 9 | export { 10 | const excessive_ntp_request = 48 &redef; 11 | const allow_excessive_ntp_requests: set[addr] &redef; 12 | } 13 | 14 | # DPM configuration. 15 | global ntp_ports = { 123/udp } &redef; 16 | redef dpd_config += { [ANALYZER_NTP] = [$ports = ntp_ports] }; 17 | 18 | const ntp_code: table[count] of string = { 19 | [0] = "unspec", 20 | [1] = "sym_act", 21 | [2] = "sym_psv", 22 | [3] = "client", 23 | [4] = "server", 24 | [5] = "bcast", 25 | [6] = "rsv1", 26 | [7] = "rsv2", 27 | }; 28 | 29 | event ntp_message(u: connection, msg: ntp_msg, excess: string) 30 | { 31 | local id = u$id; 32 | 33 | if ( id !in udp_rep_count && id !in udp_req_count ) 34 | { 35 | Hot::check_hot(u, Hot::CONN_ATTEMPTED); 36 | Scan::check_scan(u, F, F); 37 | } 38 | 39 | if ( msg$code == 4 ) 40 | # "server" 41 | ++udp_rep_count[id]; 42 | else 43 | # anything else 44 | ++udp_req_count[id]; 45 | 46 | local n_excess = byte_len(excess); 47 | if ( n_excess > excessive_ntp_request && 48 | id$orig_h !in allow_excessive_ntp_requests ) 49 | { 50 | append_addl_marker(u, fmt("%s", n_excess), ","); 51 | ++u$hot; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/sun-rpc-summary.bro: -------------------------------------------------------------------------------- 1 | @load app-summary 2 | @load rpc 3 | 4 | redef capture_filters += { 5 | ["port-map"] = "port 111", 6 | ["nfs"] = "port 2049", 7 | # UDP packets are often fragmented 8 | ["nfs-frag"] = "ip[6:2] & 0x1fff != 0", 9 | }; 10 | 11 | module SUN_RPC_summary; 12 | 13 | export { 14 | global log = open_log_file("sun-rpc-summary") &redef; 15 | } 16 | 17 | global nfs_status: table[conn_id] of count; 18 | 19 | event nfs_reply_status(n: connection, status: count) 20 | { 21 | # print fmt("%.6f status = %d", network_time(), status); 22 | nfs_status[n$id] = status; 23 | } 24 | 25 | event rpc_call(c: connection, prog: count, ver: count, proc: count, status: count, 26 | start_time: time, call_len: count, reply_len: count) 27 | { 28 | # print fmt("%.6f rpc_call", network_time()); 29 | local prog_name = RPC::program_name(prog); 30 | local nfs_st = "n/a"; 31 | if ( c$id in nfs_status ) 32 | { 33 | nfs_st = fmt("%d", nfs_status[c$id]); 34 | # print fmt("%.6f get_status = %s", network_time(), nfs_st); 35 | delete nfs_status[c$id]; 36 | } 37 | 38 | print_app_summary(log, c$id, c$start_time, 39 | fmt("%sv%d/%s", 40 | prog_name, 41 | ver, 42 | RPC::procedure_name(prog, ver, proc)), 43 | start_time, 44 | 1, call_len, status == RPC_TIMEOUT ? 0 : 1, reply_len, 45 | fmt("rpc_status %s nfs_status %s", status, nfs_st)); 46 | } 47 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Contributed Bro Scripts 3 | ======================== 4 | 5 | This is an unofficial collection of Bro scripts that have been contributed by the Bro community. Scripts that have been included here have been reviewed and approved. Please note that the review is only for security issues, and not for syntax, logic, or performance. 6 | 7 | This file only contains a short summary about each script. Each script is accompanied by important documentation, that should be carefully read before attempting to use the script. This documentation is located in the script file itself, as a comment at the top of the file. 8 | 9 | To contribute to this repository, fork this repo, and submit a pull request. 10 | 11 | Script List: 12 | ----------- 13 | 14 | ``conn-add-geodata.bro`` 15 | Adds countries for the originator and responder of a connection to the connection logs. 16 | 17 | ``http-exe-bad-attributes.bro`` 18 | Detects bad executable downloaded by watching for attributes of the connection or request. 19 | 20 | ``roam.bro`` 21 | Collects IP-to-MAC mappings of machines that may have more than one IP address over time due to a DHCP server on the network. 22 | 23 | ``sidejack.bro`` 24 | Detects the reuse of session cookies in different contexts. 25 | 26 | ``scan.bro`` 27 | The Bro 1.5 scan detector ported to Bro 2.0. For using this in a cluster, see ``scan.cluster.bro``. 28 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-ftp.bro: -------------------------------------------------------------------------------- 1 | # $Id: tm-ftp.bro,v 1.1.2.1 2006/01/04 03:55:48 sommer Exp $ 2 | # 3 | # For sensitive FTP connections, request the data connection from the TM. 4 | # When we get it, we store the reassembled payload and run the file-analyzer 5 | # (the latter is automatically done by ftp.bro). 6 | 7 | @load time-machine 8 | @load tm-contents 9 | @load ftp 10 | 11 | module TimeMachineFTP; 12 | 13 | global data_conns: table[count] of conn_id; 14 | 15 | event ftp_sensitive_file(c: connection, session: FTP::ftp_session_info, 16 | filename: string) 17 | { 18 | if ( is_external_connection(c) ) 19 | return; 20 | 21 | if ( session$id !in data_conns ) 22 | # Should not happen, as transfer parameters need to be 23 | # negotiated first. We let ftp.bro deal with this, though. 24 | return; 25 | 26 | local id = data_conns[session$id]; 27 | TimeMachine::save_contents(fmt("ftp.%s", session$id), c, T, "tm-ftp"); 28 | } 29 | 30 | event ftp_connection_expected(c: connection, orig_h: addr, resp_h: addr, 31 | resp_p: port, session: FTP::ftp_session_info) 32 | { 33 | data_conns[session$id] = 34 | [$orig_h=orig_h, $orig_p=0/tcp, $resp_h=resp_h, $resp_p=resp_p]; 35 | } 36 | 37 | event connection_state_remove(c: connection) 38 | &priority = 5 # to be called before FTP's handler 39 | { 40 | if ( c$id in FTP::ftp_sessions ) 41 | delete data_conns[FTP::ftp_sessions[c$id]$id]; 42 | } 43 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/http-detect-passwd.bro: -------------------------------------------------------------------------------- 1 | @load http 2 | 3 | module HTTP; 4 | 5 | export { 6 | redef enum Notice += { 7 | PasswordFullFetch, # they got back the whole thing 8 | PasswordShadowFetch, # they got back a shadowed version 9 | }; 10 | 11 | # Pattern to search for in replies indicating that a full password 12 | # file was returned. 13 | const full_fetch = 14 | /[[:alnum:]]+\:[[:alnum:]]+\:[[:digit:]]+\:[[:digit:]]+\:/ 15 | &redef; 16 | 17 | # Same, but indicating a shadow password file was returned. 18 | const shadow_fetch = 19 | /[[:alnum:]]+\:\*\:[[:digit:]]+\:[[:digit:]]+\:/ 20 | &redef; 21 | } 22 | 23 | event http_entity_data(c: connection, is_orig: bool, length: count, data: string) 24 | { 25 | local s = lookup_http_request_stream(c); 26 | local n = s$first_pending_request; 27 | if ( n !in s$requests ) 28 | return; 29 | 30 | local req = s$requests[n]; 31 | local passwd_request = req$passwd_req; 32 | if ( ! passwd_request ) 33 | return; 34 | 35 | if ( full_fetch in data ) 36 | NOTICE([$note=PasswordFullFetch, 37 | $conn=c, $method=req$method, $URL=req$URI, 38 | $msg=fmt("%s %s: %s %s", id_string(c$id), c$addl, 39 | req$method, req$URI)]); 40 | else if ( shadow_fetch in data ) 41 | NOTICE([$note=PasswordShadowFetch, 42 | $conn=c, $method=req$method, $URL=req$URI, 43 | $msg=fmt("%s %s: %s %s", id_string(c$id), c$addl, 44 | req$method, req$URI)]); 45 | } 46 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/ssh-stepping.bro: -------------------------------------------------------------------------------- 1 | @load stepping 2 | 3 | redef capture_filters += { ["ssh-stepping"] = "tcp port 22" }; 4 | 5 | module SSH_Stepping; 6 | 7 | # Keeps track of how many connections each source is responsible for. 8 | global ssh_src_cnt: table[addr] of count &default=0 &write_expire=15sec; 9 | 10 | export { 11 | # Threshold above which we stop analyzing a source. 12 | # Use 0 to never stop. 13 | global src_fanout_no_stp_analysis_thresh = 100 &redef; 14 | } 15 | 16 | event connection_established(c: connection) 17 | { 18 | if ( c$id$resp_p == ssh ) 19 | { 20 | # No point recording these, and they're potentially huge 21 | # due to use of ssh for file transfers. 22 | set_record_packets(c$id, F); 23 | 24 | # Keep track of sources that create lots of connections 25 | # so we can skip analyzing them - they're very likely 26 | # uninteresting for stepping stones, and can present 27 | # a large state burden. 28 | local src = c$id$orig_h; 29 | if ( ++ssh_src_cnt[src] == src_fanout_no_stp_analysis_thresh ) 30 | add stp_skip_src[src]; 31 | 32 | if ( ssh_src_cnt[src] == 1 ) 33 | # First entry. It's possible this entry was set 34 | # before and has now expired. If so, stop skipping it. 35 | delete stp_skip_src[src]; 36 | } 37 | } 38 | 39 | event partial_connection(c: connection) 40 | { 41 | if ( c$id$orig_p == ssh || c$id$resp_p == ssh ) 42 | # No point recording these, and they're potentially huge 43 | # due to use of ssh for file transfers. 44 | set_record_packets(c$id, F); 45 | } 46 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/conn-util.bro: -------------------------------------------------------------------------------- 1 | function conn_id_string(id: conn_id): string 2 | { 3 | return fmt("%s/%d=>%s/%s", 4 | id$orig_h, id$orig_p, 5 | id$resp_h, id$resp_p); 6 | } 7 | 8 | function connection_state(c: connection, trans: transport_proto): string 9 | { 10 | local os = c$orig$state; 11 | local rs = c$resp$state; 12 | 13 | local o_inactive = os == TCP_INACTIVE || os == TCP_PARTIAL; 14 | local r_inactive = rs == TCP_INACTIVE || rs == TCP_PARTIAL; 15 | 16 | if ( trans == tcp ) 17 | { 18 | if ( rs == TCP_RESET ) 19 | { 20 | if ( os == TCP_SYN_SENT || os == TCP_SYN_ACK_SENT || 21 | (os == TCP_RESET && 22 | c$orig$size == 0 && c$resp$size == 0) ) 23 | return "REJ"; 24 | else if ( o_inactive ) 25 | return "RSTRH"; 26 | else 27 | return "RSTR"; 28 | } 29 | else if ( os == TCP_RESET ) 30 | return r_inactive ? "RSTOS0" : "RSTO"; 31 | else if ( rs == TCP_CLOSED && os == TCP_CLOSED ) 32 | return "SF"; 33 | else if ( os == TCP_CLOSED ) 34 | return r_inactive ? "SH" : "S2"; 35 | else if ( rs == TCP_CLOSED ) 36 | return o_inactive ? "SHR" : "S3"; 37 | else if ( os == TCP_SYN_SENT && rs == TCP_INACTIVE ) 38 | return "S0"; 39 | else if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) 40 | return "S1"; 41 | else 42 | return "OTH"; 43 | } 44 | 45 | else if ( trans == udp ) 46 | { 47 | if ( os == UDP_ACTIVE ) 48 | return rs == UDP_ACTIVE ? "SF" : "S0"; 49 | else 50 | return rs == UDP_ACTIVE ? "SHR" : "OTH"; 51 | } 52 | 53 | else 54 | return "OTH"; 55 | } 56 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/dce-rpc-tag.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | @load dce-rpc 3 | 4 | redef capture_filters += { 5 | ["dce-rpc"] = "tcp or udp", 6 | }; 7 | 8 | global dce_rpc_tag: table[conn_id] of string &default = ""; 9 | 10 | const log_dce_rpc_tags = T &redef; 11 | function get_dce_rpc_tag(id: conn_id): string 12 | { 13 | if ( id in dce_rpc_tag ) 14 | return dce_rpc_tag[id]; 15 | else 16 | return ""; 17 | } 18 | 19 | module DCE_RPC_tag; 20 | 21 | global log = open_log_file("dce_rpc-tag") &redef; 22 | 23 | function add_to_dce_rpc_tag(c: connection, name: string): bool 24 | { 25 | local id = c$id; 26 | local orig_tag = dce_rpc_tag[id]; 27 | 28 | if ( orig_tag == "" ) 29 | { 30 | dce_rpc_tag[id] = name; 31 | } 32 | else if ( strstr(orig_tag, name) == 0 ) 33 | { 34 | dce_rpc_tag[id] = cat(orig_tag, ",", name); 35 | } 36 | 37 | return T; 38 | } 39 | 40 | # Deficiency: it only looks at the bind request, but not the reply, so we 41 | # do not know if the bind is successful. 42 | 43 | event dce_rpc_bind(c: connection, uuid: string) 44 | { 45 | local if_name = DCE_RPC::dce_rpc_uuid_name[uuid]; 46 | if ( log_dce_rpc_tags ) 47 | print log, fmt("%.6f %s DCE_RPC_Bind: %s", 48 | network_time(), id_string(c$id), if_name); 49 | add_to_dce_rpc_tag(c, if_name); 50 | } 51 | 52 | event delete_dce_rpc_tag(c: connection) 53 | { 54 | delete dce_rpc_tag[c$id]; 55 | } 56 | 57 | event connection_state_remove(c: connection) 58 | { 59 | if ( c$id in dce_rpc_tag ) 60 | { 61 | if ( log_dce_rpc_tags ) 62 | print log, fmt("conn %s start %.6f DCE/RPC [%s]", 63 | conn_id_string(c$id), 64 | c$start_time, 65 | dce_rpc_tag[c$id]); 66 | event delete_dce_rpc_tag(c); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/dns-lookup.bro: -------------------------------------------------------------------------------- 1 | # $Id: dns-lookup.bro 340 2004-09-09 06:38:27Z vern $ 2 | 3 | @load notice 4 | 5 | redef enum Notice += { 6 | DNS_MappingChanged, # some sort of change WRT previous Bro lookup 7 | }; 8 | 9 | const dns_interesting_changes = { 10 | "unverified", "old name", "new name", "mapping", 11 | } &redef; 12 | 13 | function dump_dns_mapping(msg: string, dm: dns_mapping): bool 14 | { 15 | if ( msg in dns_interesting_changes || 16 | 127.0.0.1 in dm$addrs ) 17 | { 18 | local req = dm$req_host == "" ? 19 | fmt("%As", dm$req_addr) : dm$req_host; 20 | NOTICE([$note=DNS_MappingChanged, 21 | $msg=fmt("DNS %s: %s/%s %s-> %As", msg, req, 22 | dm$hostname, dm$valid ? 23 | "" : "(invalid) ", dm$addrs), 24 | $sub=msg]); 25 | 26 | return T; 27 | } 28 | else 29 | return F; 30 | } 31 | 32 | event dns_mapping_valid(dm: dns_mapping) 33 | { 34 | dump_dns_mapping("valid", dm); 35 | } 36 | 37 | event dns_mapping_unverified(dm: dns_mapping) 38 | { 39 | dump_dns_mapping("unverified", dm); 40 | } 41 | 42 | event dns_mapping_new_name(dm: dns_mapping) 43 | { 44 | dump_dns_mapping("new name", dm); 45 | } 46 | 47 | event dns_mapping_lost_name(dm: dns_mapping) 48 | { 49 | dump_dns_mapping("lost name", dm); 50 | } 51 | 52 | event dns_mapping_name_changed(old_dm: dns_mapping, new_dm: dns_mapping) 53 | { 54 | if ( dump_dns_mapping("old name", old_dm) ) 55 | dump_dns_mapping("new name", new_dm); 56 | } 57 | 58 | event dns_mapping_altered(dm: dns_mapping, 59 | old_addrs: set[addr], new_addrs: set[addr]) 60 | { 61 | if ( dump_dns_mapping("mapping", dm) ) 62 | NOTICE([$note=DNS_MappingChanged, 63 | $msg=fmt("changed addresses: %As -> %As", old_addrs, new_addrs), 64 | $sub="changed addresses"]); 65 | } 66 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/gnutella.bro: -------------------------------------------------------------------------------- 1 | # $Id: gnutella.bro 4017 2007-02-28 07:11:54Z vern $ 2 | 3 | redef capture_filters += { ["gnutella"] = "port 6346 or port 8436" }; 4 | 5 | global gnutella_ports = { 6346/tcp, 8436/tcp } &redef; 6 | redef dpd_config += { [ANALYZER_GNUTELLA] = [$ports = gnutella_ports] }; 7 | 8 | event gnutella_text_msg(c: connection, orig: bool, headers: string) 9 | { 10 | if ( orig ) 11 | print fmt("gnu txt %s -> %s %s", c$id$orig_h, c$id$resp_h, headers); 12 | else 13 | print fmt("gnu txt %s -> %s %s", c$id$resp_h, c$id$orig_h, headers); 14 | } 15 | 16 | 17 | event gnutella_binary_msg(c: connection, orig: bool, msg_type: count, 18 | ttl: count, hops: count, msg_len: count, 19 | payload: string, payload_len: count, 20 | trunc: bool, complete: bool) 21 | { 22 | local s = ""; 23 | 24 | if ( orig ) 25 | s = fmt("gnu bin %s -> %s", c$id$orig_h, c$id$resp_h); 26 | else 27 | s = fmt("gnu bin %s -> %s", c$id$resp_h, c$id$orig_h); 28 | 29 | print fmt("%s %d %d %d %d %d %d %d %s", 30 | s, msg_type, ttl, hops, msg_len, 31 | trunc, complete, payload_len, payload); 32 | } 33 | 34 | 35 | event gnutella_partial_binary_msg(c: connection, orig: bool, 36 | msg: string, len: count) 37 | { 38 | if ( orig ) 39 | print fmt("gnu pbin %s -> %s", c$id$orig_h, c$id$resp_h); 40 | else 41 | print fmt("gnu pbin %s -> %s", c$id$resp_h, c$id$orig_h); 42 | } 43 | 44 | 45 | event gnutella_establish(c: connection) 46 | { 47 | print fmt("gnu est %s <-> %s", c$id$orig_h, c$id$resp_h); 48 | } 49 | 50 | 51 | event gnutella_not_establish(c: connection) 52 | { 53 | print fmt("gnu !est %s <-> %s", c$id$orig_h, c$id$resp_h); 54 | } 55 | 56 | 57 | event gnutella_http_notify(c: connection) 58 | { 59 | print fmt("gnu http %s/%s <-> %s/%s", c$id$orig_h, c$id$orig_p, 60 | c$id$resp_h, c$id$resp_p); 61 | } 62 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/conn-flood.bro: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Script which alarms if the number of connections per time interval 4 | # exceeds a threshold. 5 | # 6 | # This script is mainly meant as a demonstration; it hasn't been hardened 7 | # with/for operational use. 8 | 9 | @load notice 10 | 11 | module ConnFlood; 12 | 13 | export { 14 | redef enum Notice += { 15 | ConnectionFloodStart, ConnectionFloodEnd, 16 | }; 17 | 18 | # Thresholds to reports (conns/sec). 19 | const thresholds: set[count] = 20 | { 1000, 2000, 4000, 6000, 8000, 10000, 20000, 50000 } 21 | &redef; 22 | 23 | # Average over this time interval. 24 | const avg_interval = 10 sec &redef; 25 | } 26 | 27 | global conn_counter = 0; 28 | global last_thresh = 0; 29 | 30 | # Note: replace with connection_attempt if too expensive. 31 | event new_connection(c: connection) 32 | { 33 | ++conn_counter; 34 | } 35 | 36 | event check_flood() 37 | { 38 | local thresh = 0; 39 | local rate = double_to_count(interval_to_double((conn_counter / avg_interval))); 40 | 41 | # Find the largest threshold reached this interval. 42 | for ( i in thresholds ) 43 | { 44 | if ( rate >= i && rate > thresh ) 45 | thresh = i; 46 | } 47 | 48 | # Report if larger than last reported threshold. 49 | if ( thresh > last_thresh ) 50 | { 51 | NOTICE([$note=ConnectionFloodStart, $n=thresh, 52 | $msg=fmt("flood begins at rate %d conns/sec", rate)]); 53 | last_thresh = thresh; 54 | } 55 | 56 | # If no threshold was reached, the flood is over. 57 | else if ( thresh == 0 && last_thresh > 0 ) 58 | { 59 | NOTICE([$note=ConnectionFloodEnd, $n=thresh, 60 | $msg=fmt("flood ends at rate %d conns/sec", rate)]); 61 | last_thresh = 0; 62 | } 63 | 64 | conn_counter = 0; 65 | schedule avg_interval { check_flood() }; 66 | } 67 | 68 | event bro_init() 69 | { 70 | schedule avg_interval { check_flood() }; 71 | } 72 | -------------------------------------------------------------------------------- /scripts/policy/protocols/http/http-exe-bad-attributes.bro: -------------------------------------------------------------------------------- 1 | ##! http-exe-bad-attributes.bro 2 | ##! 3 | ##! Detect bad executable downloaded by watching for attributes of the 4 | ##! connection or request. 5 | ##! 6 | ##! Authors: Justin Azoff and Seth Hall 7 | 8 | @load base/protocols/http 9 | 10 | module HTTPExecBadAttributes; 11 | 12 | export { 13 | redef enum Notice::Type += { 14 | ## Indicates detection of a Windows executable downloaded over HTTP 15 | ## with one or more of a number of attributes. 16 | Detected 17 | }; 18 | 19 | ## Pattern matching URLs that tend to be malicious if they return a 20 | ## Windows executable. 21 | const bad_exec_urls = 22 | /php\.adv=/ 23 | | /^http:\/\/[^\/]c[oxz]\.cc\// 24 | | /^http:\/\/www1/ 25 | | /^http:\/\/[0-9]{1,3}\.[0-9]{1,3}.*\/index\.php\?[^=]+=[^=]+$/ #try to match http://1.2.3.4/index.php?foo=bar 26 | | /load\.php/ &redef; 27 | 28 | ## Pattern matching user-agents that will tend to be bad to see downloading 29 | ## Windows executables. 30 | const bad_user_agents = /Java\/1/ &redef; 31 | } 32 | 33 | event log_http(rec: HTTP::Info) 34 | { 35 | if ( ! rec?resp_fuids ) 36 | return; 37 | 38 | local dosexec = false; 39 | for ( i in rec$resp_fuids ) 40 | { 41 | if ( dosexec || rec$resp_fuids[i] == "application/x-dosexec" ) 42 | dosexec = true; 43 | } 44 | 45 | if ( ! dosexec ) 46 | return; 47 | 48 | local reason = ""; 49 | local value = ""; 50 | local url = HTTP::build_url_http(rec); 51 | if ( bad_user_agents in rec$user_agent ) 52 | { 53 | reason = "user-agent"; 54 | value = rec$user_agent; 55 | } 56 | else if ( bad_exec_urls in url ) 57 | { 58 | reason = "url"; 59 | value = url; 60 | } 61 | 62 | if ( reason != "" ) 63 | { 64 | NOTICE([$note=Detected, 65 | $src=rec$id$orig_h, 66 | $msg=fmt("%s downloaded a Windows executable and the connection had a potentially bad %s.", rec$id$orig_h, reason), 67 | $sub=value, 68 | $identifier=cat(rec$id$orig_h,reason,value)]); 69 | } 70 | } -------------------------------------------------------------------------------- /scripts/todo/needs_review/terminate-connection.bro: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | @load site 4 | @load notice 5 | 6 | # Ugly: we need the following from conn.bro, but we can't soundly load 7 | # it because it in turn loads us. 8 | global full_id_string: function(c: connection): string; 9 | 10 | module TerminateConnection; 11 | 12 | export { 13 | redef enum Notice += { 14 | TerminatingConnection, # connection will be terminated 15 | TerminatingConnectionIgnored, # connection terminated disabled 16 | }; 17 | 18 | # Whether we're allowed (and/or are capable) to terminate connections 19 | # using "rst". 20 | const activate_terminate_connection = F &redef; 21 | 22 | # Terminate the given connection. 23 | global terminate_connection: function(c: connection); 24 | 25 | } 26 | 27 | function terminate_connection(c: connection) 28 | { 29 | local id = c$id; 30 | 31 | if ( activate_terminate_connection ) 32 | { 33 | local local_init = is_local_addr(id$orig_h); 34 | 35 | local term_cmd = fmt("rst %s -n 32 -d 20 %s %d %d %s %d %d", 36 | local_init ? "-R" : "", 37 | id$orig_h, id$orig_p, get_orig_seq(id), 38 | id$resp_h, id$resp_p, get_resp_seq(id)); 39 | 40 | if ( reading_live_traffic() ) 41 | system(term_cmd); 42 | else 43 | NOTICE([$note=TerminatingConnection, $conn=c, 44 | $msg=term_cmd, $sub="first termination command"]); 45 | 46 | term_cmd = fmt("rst %s -r 2 -n 4 -s 512 -d 20 %s %d %d %s %d %d", 47 | local_init ? "-R" : "", 48 | id$orig_h, id$orig_p, get_orig_seq(id), 49 | id$resp_h, id$resp_p, get_resp_seq(id)); 50 | 51 | if ( reading_live_traffic() ) 52 | system(term_cmd); 53 | else 54 | NOTICE([$note=TerminatingConnection, $conn=c, 55 | $msg=term_cmd, $sub="second termination command"]); 56 | 57 | NOTICE([$note=TerminatingConnection, $conn=c, 58 | $msg=fmt("terminating %s", full_id_string(c))]); 59 | } 60 | 61 | else 62 | NOTICE([$note=TerminatingConnectionIgnored, $conn=c, 63 | $msg=fmt("ignoring request to terminate %s", 64 | full_id_string(c))]); 65 | } 66 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/ident.bro: -------------------------------------------------------------------------------- 1 | # $Id: ident.bro 5948 2008-07-11 22:29:49Z vern $ 2 | 3 | @load notice 4 | @load hot-ids 5 | 6 | module Ident; 7 | 8 | export { 9 | redef enum Notice += { 10 | IdentSensitiveID, # sensitive username in Ident lookup 11 | }; 12 | 13 | const hot_ident_ids = { always_hot_ids, } &redef; 14 | const hot_ident_exceptions = { "uucp", "nuucp", "daemon", } &redef; 15 | } 16 | 17 | redef capture_filters += { ["ident"] = "tcp port 113" }; 18 | 19 | global ident_ports = { 113/tcp } &redef; 20 | redef dpd_config += { [ANALYZER_IDENT] = [$ports = ident_ports] }; 21 | 22 | global pending_ident_requests: set[addr, port, addr, port, port, port]; 23 | 24 | event ident_request(c: connection, lport: port, rport: port) 25 | { 26 | local id = c$id; 27 | add pending_ident_requests[id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport]; 28 | } 29 | 30 | function add_ident_tag(c: connection, lport: port, rport: port, tag: string) 31 | : connection 32 | { 33 | local id = c$id; 34 | if ( [id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport] in 35 | pending_ident_requests ) 36 | delete pending_ident_requests[id$orig_h, id$orig_p, id$resp_h, id$resp_p, lport, rport]; 37 | else 38 | tag = fmt("orphan-%s", tag); 39 | 40 | local c_orig_id = [$orig_h = id$resp_h, $orig_p = rport, 41 | $resp_h = id$orig_h, $resp_p = lport]; 42 | 43 | local c_orig = active_connection(c_orig_id) ? 44 | connection_record(c_orig_id) : c; 45 | 46 | append_addl(c_orig, tag); 47 | 48 | return c_orig; 49 | } 50 | 51 | event ident_reply(c: connection, lport: port, rport: port, 52 | user_id: string, system: string) 53 | { 54 | local c_orig = add_ident_tag(c, lport, rport, fmt("ident/%s", user_id)); 55 | 56 | if ( user_id in hot_ident_ids && user_id !in hot_ident_exceptions ) 57 | { 58 | ++c_orig$hot; 59 | NOTICE([$note=IdentSensitiveID, $conn=c, 60 | $msg=fmt("%s hot ident: %s", 61 | $user=c_orig$addl, id_string(c_orig$id))]); 62 | } 63 | } 64 | 65 | event ident_error(c: connection, lport: port, rport: port, line: string) 66 | { 67 | add_ident_tag(c, lport, rport, fmt("iderr/%s", line)); 68 | } 69 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/app-summary.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | @load conn-app-reduced 3 | 4 | global conn_size_table: table[conn_id] of count; 5 | global conn_size_log = open_log_file("conn-size") &redef; 6 | 7 | function add_to_conn_size(id: conn_id, size: count) 8 | { 9 | if ( id !in conn_size_table ) 10 | conn_size_table[id] = 0; 11 | local previous_size = conn_size_table[id]; 12 | conn_size_table[id] = conn_size_table[id] + size; 13 | if ( conn_size_table[id] < previous_size ) 14 | { 15 | print conn_size_log, fmt("ERROR: %.6f size wrapping around: %s, prev_size = %d, add = %d", 16 | network_time(), conn_id_string(id), previous_size, size); 17 | } 18 | } 19 | 20 | event after_connections_state_remove(c: connection) 21 | { 22 | local id = c$id; 23 | local app_size: count; 24 | local transport_size: count; 25 | if ( id !in conn_size_table ) 26 | conn_size_table[id] = 0; 27 | app_size = conn_size_table[id]; 28 | transport_size = c$orig$size + c$resp$size; 29 | local size_delta: int = transport_size - app_size; 30 | local annotation: string = "none"; 31 | if ( app_size > transport_size ) 32 | annotation = "negative_transport_overhead"; 33 | else if ( size_delta > 1000 && 1.0 * size_delta / transport_size > 0.3 ) 34 | annotation = "suspicious_transport_overhead"; 35 | 36 | print conn_size_log, fmt("conn %s app_size %d conn_size %d annotation %s", conn_id_string(id), app_size, transport_size, annotation); 37 | 38 | delete conn_size_table[id]; 39 | } 40 | 41 | event connection_state_remove(c: connection) 42 | { 43 | event after_connections_state_remove(c); 44 | } 45 | 46 | function print_app_summary(log: file, 47 | id: conn_id, conn_start: time, func: string, start: time, 48 | num_req: count, req_size: count, num_resp: count, resp_size: count, 49 | extra: string) 50 | { 51 | add_to_conn_size(id, req_size + resp_size); 52 | print log, fmt("conn %s conn_start %.6f app %s app_func %s start %.6f req %d pyld_^ %d reply %d pyld_v %d%s", 53 | conn_id_string(id), conn_start, conn_app[id], func, start, 54 | num_req, req_size, 55 | num_resp, resp_size, 56 | byte_len(extra) > 0 ? cat(" ", extra) : ""); 57 | } 58 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/ncp-summary.bro: -------------------------------------------------------------------------------- 1 | @load ncp 2 | @load app-summary 3 | 4 | module NCP_summary; 5 | 6 | global ncp_summary_log = open_log_file("ncp-summary") &redef; 7 | 8 | type ncp_transaction: record { 9 | connection_id: conn_id; 10 | conn_start: time; 11 | func: string; 12 | start: time; 13 | num_req: count; 14 | req_size: count; 15 | num_resp: count; 16 | resp_size: count; 17 | completion_code: int; # ... of the first reply 18 | }; 19 | 20 | global ncp_trans_table: table[conn_id] of ncp_transaction; 21 | 22 | function end_ncp_transaction(id: conn_id) 23 | { 24 | if ( id !in ncp_trans_table ) 25 | return; 26 | 27 | local t = ncp_trans_table[id]; 28 | print_app_summary(ncp_summary_log, t$connection_id, t$conn_start, t$func, t$start, 29 | t$num_req, t$req_size, 30 | t$num_resp, t$resp_size, 31 | fmt("completion_code %d", t$completion_code)); 32 | } 33 | 34 | function new_ncp_transaction(c: connection, func: string): ncp_transaction 35 | { 36 | local id = c$id; 37 | 38 | # End any previous trans 39 | end_ncp_transaction(id); 40 | 41 | local t = [ 42 | $connection_id = id, 43 | $conn_start = c$start_time, 44 | $func = func, 45 | $start = network_time(), 46 | $num_req = 0, $req_size = 0, 47 | $num_resp = 0, $resp_size = 0, 48 | $completion_code = -1]; 49 | 50 | ncp_trans_table[id] = t; 51 | return t; 52 | } 53 | 54 | event ncp_request(c: connection, frame_type: count, length: count, func: count) 55 | { 56 | local f = ( frame_type == 0x2222 ) ? 57 | NCP::ncp_function_name[func] : 58 | NCP::ncp_frame_type_name[frame_type]; 59 | 60 | local t = new_ncp_transaction(c, f); 61 | ++t$num_req; 62 | t$req_size = t$req_size + length; 63 | } 64 | 65 | event ncp_reply(c: connection, frame_type: count, length: count, req_frame: count, req_func: count, completion_code: count) 66 | { 67 | local t = ncp_trans_table[c$id]; 68 | ++t$num_resp; 69 | if ( t$num_resp == 1 ) 70 | t$completion_code = completion_code; 71 | t$resp_size = t$resp_size + length; 72 | } 73 | 74 | event connection_state_remove(c: connection) 75 | { 76 | if ( c$id in ncp_trans_table ) 77 | end_ncp_transaction(c$id); 78 | } 79 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/sigs/http-bots.sig: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | # 3 | # Some signatures for detecting certain HTTP-based botnet activity. 4 | 5 | signature nethell { 6 | http-request /.*php\?userid=/ 7 | http-request-body /userid=[0-9]{8}_/ 8 | event "Nethell request" 9 | } 10 | 11 | signature bzub { 12 | http-request /.*ver=.*&lg=.*&phid=.*&r=/ 13 | http-request-body /phid=[A-F0-9]{64}/ 14 | event "bzub request" 15 | } 16 | 17 | signature iebho { 18 | http-request /.*ver=.*&lg=.*&phid=/ 19 | http-request-body /phid=[A-F0-9]{32}/ 20 | event "IEBHO request" 21 | } 22 | 23 | signature bebloh { 24 | payload /^GET/ 25 | http-request /.*get\.php\?type=slg&id=/ 26 | event "Bebloh request" 27 | } 28 | 29 | signature black_enery { 30 | payload /^POST/ 31 | http-request-header /Cache-Control: no-cache/ 32 | http-request-body /.*id=.*&build_id=.*id=x.+_[0-9A-F]{8}&build_id=.+/ 33 | event "Black energy request" 34 | } 35 | 36 | signature waledec { 37 | payload /^POST/ 38 | http-request /\/[A-Za-z0-9]+\.[pP][nN][gG]/ 39 | event "Waledec request" 40 | } 41 | 42 | signature silentbanker { 43 | payload /^POST/ 44 | http-request /.*\/getcfg\.php/ 45 | event "SilentBanker request" 46 | } 47 | 48 | signature icepack { 49 | payload /^GET/ 50 | http-request /.*\/exe\.php/ 51 | event "Icepack request" 52 | } 53 | 54 | signature torpig { 55 | payload /^POST/ 56 | http-request /.*\/gate\.php/ 57 | event "Torpig request" 58 | } 59 | 60 | signature peed { 61 | http-request /.*\/controller\.php\?action=/ 62 | http-request /.*&entity/ 63 | http-request /.*&rnd=/ 64 | event "Peed request" 65 | } 66 | 67 | signature gozi { 68 | payload /^GET/ 69 | http-request /.*\?user_id=/ 70 | http-request /.*&version_id=/ 71 | http-request /.*&crc=/ 72 | event "Gozi request" 73 | } 74 | 75 | signature wsnpoem { 76 | payload /^GET/ 77 | http-request /.*\/((cfg|config)[0-9]*)\.bin$/ 78 | event "wsnpoem request" 79 | } 80 | 81 | signature pinch { 82 | payload /^POST/ 83 | http-request /.*\?act=online&.*s4=.*&s5=.*&nickname=/ 84 | http-request-body /.*msg_out=/ 85 | event "pinch request" 86 | } 87 | 88 | signature grum { 89 | payload /^GET/ 90 | http-request /.*s_alive\.php/ 91 | event "Grum request" 92 | } 93 | 94 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/conn-size.bro: -------------------------------------------------------------------------------- 1 | # const number_of_regions = 32; 2 | const region_size = 1024 * 1024; # 1MB 3 | @load large-conns 4 | 5 | global conn_size_log = open_log_file("conn-size") &redef; 6 | 7 | function conn_id_string(id: conn_id): string 8 | { 9 | return fmt("%s/%d=>%s/%s", 10 | id$orig_h, id$orig_p, 11 | id$resp_h, id$resp_p); 12 | } 13 | 14 | function report_size_error(c: connection, msg: string) 15 | { 16 | print conn_size_log, fmt("conn %s start %.6f duration %.6f pkt_^ %d pyld_^ %d pkt_v %d pyld_v %d size_error [%s]", 17 | conn_id_string(c$id), 18 | c$start_time, 19 | c$duration, 20 | c$orig$num_pkts, c$orig$size, 21 | c$resp$num_pkts, c$resp$size, 22 | msg); 23 | } 24 | 25 | function conn_size(c: connection, is_orig: bool): string 26 | { 27 | local endp = is_orig ? c$orig : c$resp; 28 | local endp_name = is_orig ? "orig" : "resp"; 29 | local size = endp$size; 30 | 31 | if ( is_tcp_port(c$id$resp_p) ) 32 | # double check TCP sizes 33 | { 34 | local est = estimate_flow_size_and_remove(c$id, is_orig); 35 | if ( est$have_est ) 36 | { 37 | print conn_size_log, 38 | fmt("conn %s endpoint %s size %d low %.0fMB high %.0fMB inconsistent %d", 39 | conn_id_string(c$id), endp_name, 40 | endp$size, 41 | est$lower / 1e6, 42 | est$upper / 1e6, 43 | est$num_inconsistent); 44 | 45 | if ( est$num_inconsistent > 0 ) 46 | { 47 | report_size_error(c, 48 | fmt("%s size error inconsistent %d", 49 | endp_name, 50 | est$num_inconsistent)); 51 | return "-"; 52 | } 53 | 54 | if ( size < est$lower || size > est$upper ) 55 | { 56 | report_size_error(c, 57 | fmt("%s size error estimates: %.0fMB - %.0fMB", 58 | endp_name, 59 | est$lower / 1e6, 60 | est$upper / 1e6)); 61 | return "-"; 62 | } 63 | } 64 | } 65 | else if ( is_udp_port(c$id$resp_p) ) 66 | { 67 | if ( endp$num_pkts > size && size != 0 ) 68 | { 69 | report_size_error(c, 70 | fmt("%s size error: pkt > size", 71 | endp_name)); 72 | return "-"; 73 | } 74 | } 75 | 76 | return fmt("%d", size); 77 | } 78 | 79 | event connection_state_remove(c: connection) 80 | { 81 | local orig_size = conn_size(c, T); 82 | local resp_size = conn_size(c, F); 83 | } 84 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/finger.bro: -------------------------------------------------------------------------------- 1 | # $Id: finger.bro 4758 2007-08-10 06:49:23Z vern $ 2 | 3 | module Finger; 4 | 5 | export { 6 | const hot_names = { 7 | "root", "lp", "uucp", "nuucp", "demos", "operator", "sync", 8 | "r00t", "tutor", "tour", "admin", "system", "guest", "visitor", 9 | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 10 | } &redef; 11 | 12 | const max_finger_request_len = 80 &redef; 13 | } 14 | 15 | redef capture_filters += { ["finger"] = "port finger" }; 16 | 17 | # DPM configuration. 18 | global finger_ports = { 79/tcp } &redef; 19 | redef dpd_config += { [ANALYZER_FINGER] = [$ports = finger_ports] }; 20 | 21 | function public_user(user: string): bool 22 | { 23 | return T; 24 | } 25 | 26 | function authorized_client(host: addr): bool 27 | { 28 | return T; 29 | } 30 | 31 | event finger_request(c: connection, full: bool, username: string, hostname: string) 32 | { 33 | local id = c$id; 34 | local request: string; 35 | 36 | if ( hostname != "" ) 37 | request = cat(username, "@", hostname); 38 | else 39 | request = username; 40 | 41 | if ( byte_len(request) > max_finger_request_len ) 42 | { 43 | request = fmt("%s...", sub_bytes(request, 1, max_finger_request_len)); 44 | ++c$hot; 45 | } 46 | 47 | if ( hostname != "" ) 48 | ++c$hot; 49 | 50 | if ( username in hot_names ) 51 | ++c$hot; 52 | 53 | local req = request == "" ? "ALL" : fmt("\"%s\"", request); 54 | 55 | if ( full ) 56 | req = fmt("%s (/W)", req); 57 | 58 | if ( c$addl != "" ) 59 | # This is an additional request. 60 | req = fmt("(%s)", req); 61 | 62 | append_addl_marker(c, req, " *"); 63 | 64 | if ( rewriting_finger_trace ) 65 | rewrite_finger_request(c, full, 66 | public_user(username) ? username : "private user", 67 | hostname); 68 | } 69 | 70 | event finger_reply(c: connection, reply_line: string) 71 | { 72 | local id = c$id; 73 | if ( rewriting_finger_trace ) 74 | rewrite_finger_reply(c, 75 | authorized_client(id$orig_h) ? "finger reply ..." : reply_line); 76 | } 77 | 78 | function is_finger_conn(c: connection): bool 79 | { 80 | return c$id$resp_p == finger; 81 | } 82 | 83 | event connection_state_remove(c: connection) 84 | { 85 | if ( rewriting_finger_trace && requires_trace_commitment && 86 | is_finger_conn(c) ) 87 | # Commit queued packets and all packets in future. 88 | rewrite_commit_trace(c, T, T); 89 | } 90 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/capture-loss.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | # Logs evidence regarding the degree to which the packet capture process 4 | # suffers from measurment loss. 5 | # 6 | # By default, only reports loss computed in terms of number of "gap events" 7 | # (ACKs for a sequence number that's above a gap). You can also get an 8 | # estimate in terms of number of bytes missing; this however is sometimes 9 | # heavily affected by miscomputations due to broken packets with incorrect 10 | # sequence numbers. (These packets also affect the first estimator, but 11 | # only to a quite minor degree.) 12 | 13 | @load notice 14 | 15 | module CaptureLoss; 16 | 17 | export { 18 | redef enum Notice += { 19 | CaptureLossReport, # interval report 20 | CaptureLossSummary, # end-of-run summary 21 | }; 22 | 23 | # Whether to also report byte-weighted estimates. 24 | global report_byte_based_estimates = F &redef; 25 | 26 | # Whether to generate per-interval reports even if there 27 | # was no evidence of loss. 28 | global report_if_none = F &redef; 29 | 30 | # Whether to generate a summary even if there was no 31 | # evidence of loss. 32 | global summary_if_none = F &redef; 33 | } 34 | 35 | 36 | # Redefine this to be non-zero to get per-interval reports. 37 | redef gap_report_freq = 0 sec; 38 | 39 | event gap_report(dt: interval, info: gap_info) 40 | { 41 | if ( info$gap_events > 0 || report_if_none ) 42 | { 43 | local msg = report_byte_based_estimates ? 44 | fmt("gap-dt=%.6f acks=%d bytes=%d gaps=%d gap-bytes=%d", 45 | dt, info$ack_events, info$ack_bytes, 46 | info$gap_events, info$gap_bytes) : 47 | fmt("gap-dt=%.6f acks=%d gaps=%d", 48 | dt, info$ack_events, info$gap_events); 49 | 50 | NOTICE([$note=CaptureLossReport, $msg=msg]); 51 | } 52 | } 53 | 54 | event bro_done() 55 | { 56 | local g = get_gap_summary(); 57 | 58 | local gap_rate = 59 | g$ack_events == 0 ? 0.0 : 60 | (1.0 * g$gap_events) / (1.0 * g$ack_events); 61 | local gap_bytes = 62 | g$ack_bytes == 0 ? 0.0 : 63 | (1.0 * g$gap_bytes) / (1.0 * g$ack_bytes); 64 | 65 | if ( gap_rate == 0.0 && gap_bytes == 0.0 && ! summary_if_none ) 66 | return; 67 | 68 | local msg = report_byte_based_estimates ? 69 | fmt("estimated rate = %g / %g (events/bytes)", 70 | gap_rate, gap_bytes) : 71 | fmt("estimated rate = %g", gap_rate); 72 | 73 | NOTICE([$note=CaptureLossSummary, $msg=msg]); 74 | } 75 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/dce-rpc-summary.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | @load dce-rpc 3 | @load app-summary 4 | 5 | module DCE_RPC_summary; 6 | 7 | global log = open_log_file("dce-rpc-summary") &redef; 8 | 9 | type dce_rpc_transaction: record { 10 | connection_id: conn_id; 11 | conn_start: time; 12 | uuid: string; 13 | opnum: count; 14 | start: time; 15 | num_req: count; 16 | req_size: count; 17 | num_resp: count; 18 | resp_size: count; 19 | }; 20 | 21 | global conn_uuid: table[conn_id] of string &default = DCE_RPC::null_uuid; 22 | global dce_rpc_trans_table: table[conn_id] of dce_rpc_transaction; 23 | # global msg_size: table[conn_id, bool] of count; 24 | 25 | function end_dce_rpc_transaction(id: conn_id) 26 | { 27 | if ( id !in dce_rpc_trans_table ) 28 | return; 29 | 30 | local t = dce_rpc_trans_table[id]; 31 | local ifname = DCE_RPC::dce_rpc_uuid_name[t$uuid]; 32 | local func_name = DCE_RPC::dce_rpc_func_name[ifname, t$opnum]; 33 | print_app_summary(log, 34 | t$connection_id, 35 | t$conn_start, 36 | fmt("%s/%s", ifname, func_name), 37 | t$start, 38 | t$num_req, t$req_size, 39 | t$num_resp, t$resp_size, 40 | fmt("ifname %s", ifname)); 41 | 42 | delete dce_rpc_trans_table[id]; 43 | } 44 | 45 | function new_dce_rpc_transaction(c: connection, uuid: string, opnum: count): dce_rpc_transaction 46 | { 47 | local id = c$id; 48 | 49 | # End any previous trans 50 | end_dce_rpc_transaction(id); 51 | 52 | local t = [ 53 | $connection_id = id, $conn_start = c$start_time, 54 | $uuid = uuid, $opnum = opnum, 55 | $start = network_time(), 56 | $num_req = 0, $req_size = 0, 57 | $num_resp = 0, $resp_size = 0]; 58 | 59 | dce_rpc_trans_table[id] = t; 60 | return t; 61 | } 62 | 63 | event dce_rpc_message(c: connection, is_orig: bool, ptype: dce_rpc_ptype, msg: string) 64 | { 65 | # msg_size[c$id, is_orig] = byte_len(msg); 66 | } 67 | 68 | event dce_rpc_bind(c: connection, uuid: string) 69 | { 70 | conn_uuid[c$id] = uuid; 71 | } 72 | 73 | event dce_rpc_request(c: connection, opnum: count, stub: string) 74 | { 75 | local t = new_dce_rpc_transaction(c, conn_uuid[c$id], opnum); 76 | ++t$num_req; 77 | t$req_size = t$req_size + byte_len(stub); 78 | # t$req_size = t$req_size + msg_size[c$id, T]; 79 | } 80 | 81 | event dce_rpc_response(c: connection, opnum: count, stub: string) 82 | { 83 | local t = dce_rpc_trans_table[c$id]; 84 | ++t$num_resp; 85 | t$resp_size = t$resp_size + byte_len(stub); 86 | # t$resp_size = t$resp_size + msg_size[c$id, F]; 87 | } 88 | 89 | event connection_state_remove(c: connection) 90 | { 91 | if ( c$id in dce_rpc_trans_table ) 92 | end_dce_rpc_transaction(c$id); 93 | } 94 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/proxy.bro: -------------------------------------------------------------------------------- 1 | # $Id: proxy.bro,v 1.1.4.2 2006/05/31 00:16:22 sommer Exp $ 2 | # 3 | # Finds open proxies by matching incoming HTTP requests with outgoing ones. 4 | 5 | @load notice 6 | 7 | module Proxy; 8 | 9 | export { 10 | const KnownProxies: set[addr] = { }; 11 | 12 | redef enum Notice += { 13 | HTTPProxyFound, 14 | }; 15 | } 16 | 17 | 18 | type request: record { 19 | p: port; 20 | paths: set[string]; 21 | }; 22 | 23 | # Maps the address of the potential proxy to the paths that 24 | # have been requested from it. 25 | global requests: table[addr] of request; 26 | 27 | # A parsed URL. 28 | type url: record { 29 | host: string; 30 | path: string; 31 | }; 32 | 33 | global found_proxies: set[addr] &create_expire = 24 hrs; 34 | 35 | function parse_url(u: string) : url 36 | { 37 | # The URL parsing is imperfect, but should work sufficiently well. 38 | local a = split1(u, /:\/\//); 39 | if ( |a| == 1 ) 40 | return [$host="", $path=a[1]]; 41 | 42 | local b = split1(a[2], /\//); 43 | return [$host=b[1], $path=(|b| == 2 ? cat("/", b[2]) : "/")]; 44 | } 45 | 46 | event http_request(c: connection, method: string, original_URI: string, 47 | unescaped_URI: string, version: string) 48 | { 49 | if ( method != "GET" && method != "CONNECT" ) 50 | return; 51 | 52 | local client = c$id$orig_h; 53 | local server = c$id$resp_h; 54 | 55 | if ( server in KnownProxies ) 56 | return; 57 | 58 | # FIXME: Which one? original_URI or unescaped_URI? 59 | local u = parse_url(original_URI); 60 | 61 | if ( client in requests ) 62 | { 63 | # We have already seen requests to this host. Let's see 64 | # any matches the one we're very currently seeing. 65 | local r = requests[client]; 66 | if ( u$path in r$paths ) 67 | { 68 | if ( client !in found_proxies ) 69 | { 70 | NOTICE([$note=HTTPProxyFound, 71 | $conn=c, $src=client, 72 | $p=r$p, $URL=original_URI, 73 | $msg=fmt("HTTP proxy found %s:%d (%s)", 74 | client, r$p, original_URI)]); 75 | add found_proxies[client]; 76 | } 77 | 78 | return; 79 | } 80 | } 81 | 82 | if ( u$host == "" ) 83 | # A relative URL. That's fine. 84 | return; 85 | 86 | # An absolute URL. Remember path for later. 87 | # 88 | # Note: using "when", could even lookup the destination 89 | # host and remember that one, too! 90 | 91 | if ( server !in requests ) 92 | { 93 | local empty_set: set[string] &read_expire = 15 secs; 94 | local req = [$p=c$id$resp_p, $paths=empty_set]; 95 | requests[server] = req; 96 | } 97 | 98 | add requests[server]$paths[u$path]; 99 | } 100 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-capture.bro: -------------------------------------------------------------------------------- 1 | # $Id: tm-capture.bro,v 1.1.2.1 2006/01/04 03:52:02 sommer Exp $ 2 | # 3 | # For each non-scan alert, we can 4 | # (a) tell the time-machine to permanently store the connection's packets 5 | # (b) request the connection, to store the (reassembled) payload ourselves 6 | # (c) request all other traffic from that IP within the last X hours 7 | # (d) store all other traffic from that IP within the last X hours 8 | 9 | @load time-machine 10 | @load tm-contents 11 | @load notice 12 | @load scan 13 | 14 | module TimeMachineCapture; 15 | 16 | export { 17 | # Request past traffic. Set to 0 to disable. 18 | # This does on-disk queries, potentially expensive. 19 | const history_interval = 0 hrs &redef; 20 | 21 | # Capture past traffic. Set to 0 to disable. 22 | # This does on-disk queries, potentially expensive. 23 | const history_capture_interval = 0 hrs &redef; 24 | 25 | const ignore_notices: set[Notice] = { 26 | Scan::AddressScan, 27 | Scan::PortScan, 28 | } &redef; 29 | } 30 | 31 | @ifdef ( TimeMachineGap::ContentGapTmAndLink ) 32 | redef ignore_notices += { 33 | TimeMachineGap::ContentGapTmAndLink, 34 | TimeMachineGap::ContentGapSolved, 35 | }; 36 | @endif 37 | 38 | global hosts: set[addr] &create_expire = history_capture_interval; 39 | 40 | global dbg = open_log_file("tm-capture"); 41 | 42 | event notice_alarm(n: notice_info, action: NoticeAction) 43 | { 44 | if ( n$note in ignore_notices ) 45 | return; 46 | 47 | if ( ! n?$id ) 48 | return; 49 | 50 | if ( n?$conn && is_external_connection(n$conn) ) 51 | return; 52 | 53 | local id = n$id; 54 | local start: time; 55 | 56 | if ( n?$conn ) 57 | start = n$conn$start_time; 58 | else if ( connection_exists(id) ) 59 | start = lookup_connection(id)$start_time; 60 | else 61 | start = network_time() - 5 min; # shouldn't usually get here 62 | 63 | local tag = fmt("conn.%s", n$tag); 64 | n$captured = tag; 65 | 66 | # It should be in the TM's memory. 67 | TimeMachine::capture_connection_id(fmt("%s.pcap", tag), id, start, 68 | T, "tm-capture"); 69 | 70 | if ( get_port_transport_proto(id$resp_p) == tcp ) 71 | { 72 | n$captured += " (contents)"; 73 | TimeMachine::save_contents_id(tag, id, start, T, "tm-capture"); 74 | } 75 | 76 | if ( n$src !in hosts ) 77 | { 78 | if ( history_interval != 0 sec ) 79 | TimeMachine::request_addr(n$src, 80 | network_time() - history_interval, F, 81 | "tm-capture"); 82 | 83 | if ( history_capture_interval != 0secs ) 84 | TimeMachine::capture_addr(fmt("host.%s.%s.pcap", 85 | n$src, n$tag), n$src, 86 | network_time() - history_capture_interval, F, 87 | "tm-capture"); 88 | 89 | add hosts[n$src]; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/conn-summary.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | # @load conn-app 3 | # @load smb-tag 4 | # @load dce-rpc-tag 5 | 6 | module ConnSummary; 7 | 8 | # redef capture_filters += { ["TUI"] = "tcp or udp or icmp" }; 9 | redef capture_filters = { ["ip"] = "ip" }; # to also capture IP fragments 10 | # redef SMB_tag::log_smb_tags = F; 11 | # redef DCE_RPC_tag::log_dce_rpc_tags = F; 12 | 13 | global conn_summary_log = open_log_file("conn-summary") &redef; 14 | 15 | global conn_annotation: table[conn_id] of string &default = ""; 16 | 17 | function add_to_conn_annotation(cid: conn_id, new_annotation: string) 18 | { 19 | local a: string; 20 | if ( cid in conn_annotation ) 21 | conn_annotation[cid] = 22 | cat(conn_annotation[cid], ",", new_annotation); 23 | else 24 | conn_annotation[cid] = new_annotation; 25 | } 26 | 27 | # II. Annotation events 28 | event new_connection(c: connection) 29 | { 30 | if ( is_tcp_port(c$id$resp_p) ) 31 | { 32 | if ( c$orig$state != TCP_SYN_SENT ) 33 | { 34 | # add_to_conn_annotation(c$id, "partial"); 35 | } 36 | } 37 | } 38 | 39 | event partial_connection(c: connection) 40 | { 41 | add_to_conn_annotation(c$id, "partial"); 42 | } 43 | 44 | event connection_established(c: connection) 45 | { 46 | if ( c$orig$state == TCP_ESTABLISHED && c$resp$state == TCP_ESTABLISHED ) 47 | { 48 | add_to_conn_annotation(c$id, "established"); 49 | } 50 | } 51 | 52 | event connection_rejected(c: connection) 53 | { 54 | add_to_conn_annotation(c$id, "rejected"); 55 | } 56 | 57 | event connection_reset(c: connection) 58 | { 59 | add_to_conn_annotation(c$id, "reset"); 60 | } 61 | 62 | event connection_attempt(c: connection) 63 | { 64 | add_to_conn_annotation(c$id, "attempt"); 65 | } 66 | 67 | event connection_finished(c: connection) 68 | { 69 | add_to_conn_annotation(c$id, "finished"); 70 | } 71 | 72 | event icmp_unreachable(c: connection, icmp: icmp_conn, code: count, context: icmp_context) 73 | { 74 | add_to_conn_annotation(context$id, "unreach"); 75 | } 76 | 77 | event icmp_time_exceeded(c: connection, icmp: icmp_conn, code: count, context: icmp_context) 78 | { 79 | add_to_conn_annotation(context$id, "time_exceeded"); 80 | } 81 | 82 | event connection_state_remove(c: connection) 83 | { 84 | # local tag_smb = get_smb_tag(c$id); 85 | # local tag_dce_rpc = get_dce_rpc_tag(c$id); 86 | 87 | print conn_summary_log, fmt("conn %s start %.6f duration %.6f app %s pkt_^ %d pyld_^ %d pkt_v %d pyld_v %d state %s notes [%s]", 88 | conn_id_string(c$id), 89 | c$start_time, 90 | c$duration, 91 | conn_app[c$id], 92 | c$orig$num_pkts, c$orig$size, 93 | c$resp$num_pkts, c$resp$size, 94 | conn_state(c, get_port_transport_proto(c$id$resp_p)), 95 | conn_annotation[c$id]); 96 | 97 | delete conn_annotation[c$id]; 98 | delete conn_app[c$id]; 99 | } 100 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/nfs.bro: -------------------------------------------------------------------------------- 1 | # $Id: nfs.bro 4017 2007-02-28 07:11:54Z vern $ 2 | 3 | @load udp 4 | 5 | module NFS; 6 | 7 | export { 8 | global log_file = open_log_file("nfs") &redef; 9 | } 10 | 11 | redef capture_filters += { 12 | ["nfs"] = "port 2049", 13 | # NFS UDP packets are often fragmented. 14 | ["nfs-frag"] = "(ip[6:2] & 0x3fff != 0) and udp", 15 | }; 16 | 17 | global nfs_ports = { 2049/tcp, 2049/udp } &redef; 18 | redef dpd_config += { [ANALYZER_NFS] = [$ports = nfs_ports] }; 19 | 20 | # Maps opaque file handles to numbers for easier tracking. 21 | global num_fhs = 0; 22 | global fh_map: table[string] of count; 23 | 24 | function map_fh(fh: string): string 25 | { 26 | if ( fh !in fh_map ) 27 | fh_map[fh] = ++num_fhs; 28 | 29 | return cat("FH", fh_map[fh]); 30 | } 31 | 32 | 33 | function NFS_request(n: connection, req: string, addl: string) 34 | { 35 | print log_file, fmt("%.06f %s NFS %s: %s", 36 | network_time(), id_string(n$id), req, addl); 37 | } 38 | 39 | function NFS_attempt(n: connection, req: string, status: count, addl: string) 40 | { 41 | print log_file, fmt("%.06f %s NFS attempt %s (%d): %s", 42 | network_time(), id_string(n$id), req, status, addl); 43 | } 44 | 45 | 46 | event nfs_request_null(n: connection) 47 | { 48 | NFS_request(n, "null", ""); 49 | } 50 | 51 | event nfs_attempt_null(n: connection, status: count) 52 | { 53 | NFS_attempt(n, "null", status, ""); 54 | } 55 | 56 | 57 | event nfs_request_getattr(n: connection, fh: string, attrs: nfs3_attrs) 58 | { 59 | NFS_request(n, "getattr", fmt("%s -> %s", map_fh(fh), attrs)); 60 | } 61 | 62 | event nfs_attempt_getattr(n: connection, status: count, fh: string) 63 | { 64 | NFS_attempt(n, "getattr", status, map_fh(fh)); 65 | } 66 | 67 | 68 | function opt_attr_fmt(a: nfs3_opt_attrs): string 69 | { 70 | return a?$attrs ? fmt("%s", a$attrs) : ""; 71 | } 72 | 73 | event nfs_request_lookup(n: connection, req: nfs3_lookup_args, rep: nfs3_lookup_reply) 74 | { 75 | NFS_request(n, "lookup", fmt("%s -> %s (file-attr: %s, dir-attr: %s)", 76 | req, rep$fh, 77 | opt_attr_fmt(rep$file_attr), 78 | opt_attr_fmt(rep$dir_attr))); 79 | } 80 | 81 | event nfs_attempt_lookup(n: connection, status: count, req: nfs3_lookup_args) 82 | { 83 | NFS_attempt(n, "lookup", status, fmt("%s", req)); 84 | } 85 | 86 | 87 | event nfs_request_fsstat(n: connection, root_fh: string, stat: nfs3_fsstat) 88 | { 89 | NFS_request(n, "fsstat", fmt("%s -> attr: %s, tbytes: %s, fbytes: %s, abytes: %s, tfiles: %s, ffiles: %s, afiles: %s, invarsec: %s", 90 | map_fh(root_fh), 91 | opt_attr_fmt(stat$attrs), 92 | stat$tbytes, stat$fbytes, stat$abytes, 93 | stat$tfiles, stat$ffiles, stat$afiles, 94 | stat$invarsec)); 95 | } 96 | 97 | event nfs_attempt_fsstat(n: connection, status: count, root_fh: string) 98 | { 99 | NFS_attempt(n, "fsstat", status, map_fh(root_fh)); 100 | } 101 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/service-probe.bro: -------------------------------------------------------------------------------- 1 | # $Id: service-probe.bro 5892 2008-07-01 02:37:03Z vern $ 2 | # 3 | # Detects hosts that continually bang away at a particular service 4 | # of a local host, for example for brute-forcing passwords. 5 | # 6 | # Written by Jim Mellander, LBNL. 7 | # Updated by Robin Sommer, ICSI. 8 | 9 | @load conn 10 | 11 | module ServiceProbe; 12 | 13 | export { 14 | redef enum Notice += { ServiceProbe }; 15 | 16 | # No work gets done unless this is set. 17 | global detect_probes = F &redef; 18 | 19 | # By default, look for service probes targeting MySQL and SSH. 20 | global probe_ports = { 1433/tcp, 22/tcp, } &redef; 21 | 22 | # They have to connect to this many to be flagged. 23 | global connect_threshold: table[port] of count &default=100 &redef; 24 | 25 | # How many bytes the connection must have to be considered potentially 26 | # a probe. If missing, then there's no lower/upper bound. 27 | # 28 | # Note, the attack that motivated including these was SSH password 29 | # guessing, where it was empirically determined that connections 30 | # with > 1KB and < 2KB bytes transferred appear to be unsuccessful 31 | # password guesses. 32 | # 33 | global min_bytes: table[port] of int &default=-1 &redef; 34 | global max_bytes: table[port] of int &default=-1 &redef; 35 | 36 | # How many tries a given originator host has made against a given 37 | # port on a given responder host. 38 | global tries: table[addr, addr, port] of count 39 | &default=0 &read_expire = 10 min; 40 | } 41 | 42 | global reported_hosts: set[addr] &read_expire = 1 day; 43 | 44 | function service_probe_check(c: connection) 45 | { 46 | if ( ! detect_probes ) 47 | return; 48 | 49 | local id = c$id; 50 | local orig = id$orig_h; 51 | local resp = id$resp_h; 52 | local service = (port_names[20/tcp] in c$service) ? 20/tcp : id$resp_p; 53 | 54 | if ( orig in reported_hosts ) 55 | # We've already blocked them. 56 | return; 57 | 58 | if ( is_local_addr(orig) ) 59 | # We only analyze probes of local servers. 60 | return; 61 | 62 | if ( service !in probe_ports ) 63 | # Not a port we care about. 64 | return; 65 | 66 | local enough_bytes = T; 67 | local bytes_xferred = c$orig$size + c$resp$size; 68 | 69 | if ( service in min_bytes && bytes_xferred < min_bytes[service] ) 70 | enough_bytes = F; 71 | 72 | if ( service in max_bytes && bytes_xferred > max_bytes[service] ) 73 | enough_bytes = F; 74 | 75 | if ( ! enough_bytes ) 76 | return; 77 | 78 | local cnt = ++tries[orig, resp, service]; 79 | if ( cnt == connect_threshold[service] ) 80 | { 81 | local svc = service_name(c); 82 | 83 | NOTICE([$note=ServiceProbe, $src=orig, 84 | $msg=fmt("service probing %s -> %s %s", 85 | orig, resp, svc)]); 86 | 87 | # Since we've dropped this host, we can now release the space. 88 | delete tries[orig, resp, service]; 89 | add reported_hosts[orig]; 90 | } 91 | } 92 | 93 | 94 | event connection_state_remove(c: connection) 95 | { 96 | service_probe_check(c); 97 | } 98 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/loaders/all.bro: -------------------------------------------------------------------------------- 1 | @load heavy-analysis 2 | @load OS-fingerprint 3 | @load adu 4 | @load alarm 5 | @load analy 6 | @load anon 7 | @load arp 8 | @load backdoor 9 | @load bittorrent 10 | @load blaster 11 | @load bt-tracker 12 | @load brolite-backdoor 13 | @load capture-events 14 | @load capture-loss 15 | @load capture-state-updates 16 | @load checkpoint 17 | @load clear-passwords 18 | @load conn-flood 19 | @load conn-id 20 | @load conn 21 | @load contents 22 | @load cpu-adapt 23 | @load dce 24 | @load demux 25 | @load detect-protocols-http 26 | @load detect-protocols 27 | @load dhcp 28 | @load dns-info 29 | @load dns-lookup 30 | @load dns 31 | @load dpd 32 | @load drop-adapt 33 | @load dyn-disable 34 | @load file-flush 35 | @load finger 36 | @load firewall 37 | @load flag-irc 38 | @load flag-warez 39 | @load frag 40 | @load ftp 41 | @load gnutella 42 | @load hot-ids 43 | @load hot 44 | @load http-abstract 45 | @load http-anon-server 46 | @load http-anon-useragent 47 | @load http-anon-utils 48 | @load http-body 49 | @load http-detect-passwd 50 | @load http-entity 51 | @load http-event 52 | @load http-header 53 | @load http-identified-files.bro 54 | @load http-reply 55 | @load http-request 56 | @load http-rewriter 57 | @load http 58 | @load icmp 59 | @load ident-rewriter 60 | @load ident 61 | @load inactivity 62 | @load interconn 63 | @load irc-bot-syslog 64 | @load irc-bot 65 | @load irc 66 | @load large-conns 67 | @load listen-clear 68 | @load listen-ssl 69 | @load load-level 70 | @load load-sample 71 | @load log-append 72 | @load login 73 | @load mime-pop 74 | @load mime 75 | @load mt 76 | @load ncp 77 | @load netflow 78 | @load netstats 79 | @load nfs 80 | @load notice-action-filters 81 | @load notice 82 | @load ntp 83 | @load passwords 84 | @load pcap 85 | @load pkt-profile 86 | @load pop3 87 | @load port-name 88 | @load portmapper 89 | @load print-filter 90 | @load print-globals 91 | @load print-resources 92 | @load print-sig-states 93 | @load profiling 94 | @load proxy 95 | @load remote-pcap 96 | @load remote-ping 97 | @load remote-print-id-reply 98 | @load remote-print-id 99 | @load remote-print 100 | @load remote-report-notices 101 | @load remote-send-id 102 | @load remote 103 | @load rotate-logs 104 | @load rsh 105 | @load scan 106 | @load secondary-filter 107 | @load sensor-sshd 108 | @load server-ports 109 | @load service-probe 110 | @load signatures 111 | @load site 112 | @load smb 113 | @load smtp-relay 114 | @load smtp-rewriter 115 | @load smtp 116 | @load snort 117 | @load software 118 | @load ssh 119 | @load ssh-stepping 120 | @load ssl-alerts 121 | @load ssl-ciphers 122 | @load ssl-errors 123 | @load ssl-worm 124 | @load ssl 125 | @load stats 126 | @load stepping 127 | @load synflood 128 | @load targeted-scan 129 | @load tcp 130 | @load tftp 131 | @load trw-impl 132 | @load trw 133 | @load udp-common 134 | @load udp 135 | @load vlan 136 | @load weird 137 | @load worm 138 | @load notice-policy 139 | 140 | # The following keeps us running after the bro_init event. 141 | redef PrintFilter::terminate_bro = F; 142 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/netbios-ssn-summary.bro: -------------------------------------------------------------------------------- 1 | @load app-summary 2 | 3 | redef capture_filters = { 4 | ["netbios-ssn"] = "tcp port 139", 5 | }; 6 | 7 | module NetbiosSSN_summary; 8 | 9 | global netbios_log = open_log_file("netbios-ssn-summary") &redef; 10 | 11 | const netbios_msg_types = { 12 | [0x0] = "ssn_message", 13 | [0x81] = "ssn_request", 14 | [0x82] = "positive_resp", 15 | [0x83] = "negative_resp", 16 | [0x84] = "retarget_resp", 17 | [0x85] = "keep_alive", 18 | } &default = function(msg_type: count): string 19 | { 20 | return fmt("unknown-0x%x", msg_type); 21 | }; 22 | 23 | type netbios_ssn_transaction: record { 24 | connection_id: conn_id; 25 | conn_start: time; 26 | start: time; 27 | num_req: count; 28 | req_size: count; 29 | num_resp: count; 30 | resp_size: count; 31 | req_type: string; 32 | resp_type: string; # ... of the first reply 33 | raw_ssn_msg: count; 34 | }; 35 | 36 | global netbios_ssn_trans_table: table[conn_id] of netbios_ssn_transaction; 37 | 38 | function end_netbios_ssn_transaction(id: conn_id) 39 | { 40 | if ( id !in netbios_ssn_trans_table ) 41 | return; 42 | 43 | local t = netbios_ssn_trans_table[id]; 44 | print_app_summary(netbios_log, t$connection_id, t$conn_start, 45 | t$req_type, t$start, 46 | t$num_req, t$req_size, 47 | t$num_resp, t$resp_size, 48 | fmt("req_type %s resp_type %s raw %d", 49 | t$req_type, t$resp_type, t$raw_ssn_msg)); 50 | 51 | delete netbios_ssn_trans_table[id]; 52 | } 53 | 54 | function lookup_netbios_ssn_transaction(c: connection, new_trans: bool): netbios_ssn_transaction 55 | { 56 | local id = c$id; 57 | 58 | if ( new_trans ) 59 | { 60 | # End any previous trans 61 | end_netbios_ssn_transaction(id); 62 | } 63 | 64 | if ( id !in netbios_ssn_trans_table ) 65 | { 66 | local t = [ 67 | $connection_id = id, 68 | $conn_start = c$start_time, 69 | $start = network_time(), 70 | $num_req = 0, $req_size = 0, 71 | $num_resp = 0, $resp_size = 0, 72 | $req_type = "none", $resp_type = "none", 73 | $raw_ssn_msg = 0]; 74 | netbios_ssn_trans_table[c$id] = t; 75 | } 76 | 77 | return netbios_ssn_trans_table[c$id]; 78 | } 79 | 80 | event netbios_ssn_message(c: connection, is_orig: bool, msg_type: count, data_len: count) 81 | { 82 | local msg_type_name = netbios_msg_types[msg_type]; 83 | local t: netbios_ssn_transaction; 84 | if ( is_orig ) 85 | { 86 | t = lookup_netbios_ssn_transaction(c, T); 87 | ++t$num_req; 88 | if ( t$num_req == 1 ) 89 | t$req_type = msg_type_name; 90 | t$req_size = t$req_size + data_len; 91 | } 92 | else 93 | { 94 | t = lookup_netbios_ssn_transaction(c, F); 95 | ++t$num_resp; 96 | if ( t$num_resp == 1 ) 97 | t$resp_type = msg_type_name; 98 | t$resp_size = t$resp_size + data_len; 99 | } 100 | } 101 | 102 | event netbios_session_raw_message(c: connection, is_orig: bool, msg: string) 103 | { 104 | local t = lookup_netbios_ssn_transaction(c, F); 105 | ++t$raw_ssn_msg; 106 | } 107 | 108 | event connection_state_remove(c: connection) 109 | { 110 | if ( c$id in netbios_ssn_trans_table ) 111 | end_netbios_ssn_transaction(c$id); 112 | } 113 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/http-request.bro: -------------------------------------------------------------------------------- 1 | # $Id: http-request.bro 6726 2009-06-07 22:09:55Z vern $ 2 | 3 | # Analysis of HTTP requests. 4 | 5 | @load http 6 | 7 | module HTTP; 8 | 9 | export { 10 | const sensitive_URIs = 11 | /etc\/(passwd|shadow|netconfig)/ 12 | | /IFS[ \t]*=/ 13 | | /nph-test-cgi\?/ 14 | | /(%0a|\.\.)\/(bin|etc|usr|tmp)/ 15 | | /\/Admin_files\/order\.log/ 16 | | /\/carbo\.dll/ 17 | | /\/cgi-bin\/(phf|php\.cgi|test-cgi)/ 18 | | /\/cgi-dos\/args\.bat/ 19 | | /\/cgi-win\/uploader\.exe/ 20 | | /\/search97\.vts/ 21 | | /tk\.tgz/ 22 | | /ownz/ # somewhat prone to false positives 23 | | /viewtopic\.php.*%.*\(.*\(/ # PHP attack, 26Nov04 24 | # a bunch of possible rootkits 25 | | /sshd\.(tar|tgz).*/ 26 | | /[aA][dD][oO][rR][eE][bB][sS][dD].*/ 27 | # | /[tT][aA][gG][gG][eE][dD].*/ # prone to FPs 28 | | /shv4\.(tar|tgz).*/ 29 | | /lrk\.(tar|tgz).*/ 30 | | /lyceum\.(tar|tgz).*/ 31 | | /maxty\.(tar|tgz).*/ 32 | | /rootII\.(tar|tgz).*/ 33 | | /invader\.(tar|tgz).*/ 34 | &redef; 35 | 36 | # Used to look for attempted password file fetches. 37 | const passwd_URI = /passwd/ &redef; 38 | 39 | # URIs that match sensitive_URIs but can be generated by worms, 40 | # and hence should not be flagged (because they're so common). 41 | const worm_URIs = 42 | /.*\/c\+dir/ 43 | | /.*cool.dll.*/ 44 | | /.*Admin.dll.*Admin.dll.*/ 45 | &redef; 46 | 47 | # URIs that should not be considered sensitive if accessed by 48 | # a local client. 49 | const skip_remote_sensitive_URIs = 50 | /\/cgi-bin\/(phf|php\.cgi|test-cgi)/ 51 | &redef; 52 | 53 | const sensitive_post_URIs = /wwwroot|WWWROOT/ &redef; 54 | } 55 | 56 | redef capture_filters += { 57 | ["http-request"] = "tcp dst port 80 or tcp dst port 8080 or tcp dst port 8000" 58 | }; 59 | 60 | event http_request(c: connection, method: string, original_URI: string, 61 | unescaped_URI: string, version: string) 62 | { 63 | local log_it = F; 64 | local URI = unescaped_URI; 65 | 66 | if ( (sensitive_URIs in URI && URI != worm_URIs) || 67 | (method == "POST" && sensitive_post_URIs in URI) ) 68 | { 69 | if ( is_local_addr(c$id$orig_h) && 70 | skip_remote_sensitive_URIs in URI ) 71 | ; # don't flag it after all 72 | else 73 | log_it = T; 74 | } 75 | 76 | local s = lookup_http_request_stream(c); 77 | 78 | if ( process_HTTP_replies ) 79 | { 80 | # To process HTTP replies, we need to record the corresponding 81 | # requests. 82 | local n = s$first_pending_request + s$num_pending_requests; 83 | 84 | s$requests[n] = [$method=method, $URI=URI, $log_it=log_it, 85 | $passwd_req=passwd_URI in URI]; 86 | ++s$num_pending_requests; 87 | 88 | # if process_HTTP_messages 89 | local msg = s$next_request; 90 | 91 | init_http_message(msg); 92 | msg$initiated = T; 93 | } 94 | else 95 | { 96 | if ( log_it ) 97 | NOTICE([$note=HTTP_SensitiveURI, $conn=c, 98 | $method = method, $URL = URI, 99 | $msg=fmt("%s %s: %s %s", 100 | id_string(c$id), c$addl, method, URI)]); 101 | print http_log, 102 | fmt("%.6f %s %s %s", network_time(), s$id, method, URI); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/rsh.bro: -------------------------------------------------------------------------------- 1 | # $Id: rsh.bro 4758 2007-08-10 06:49:23Z vern $ 2 | 3 | @load conn 4 | @load login 5 | 6 | module RSH; 7 | 8 | export { 9 | redef enum Notice += { 10 | # RSH client username and server username differ. 11 | DifferentRSH_Usernames, 12 | 13 | # Attempt to authenticate via RSH failed. 14 | FailedRSH_Authentication, 15 | 16 | # RSH session appears to be interactive - multiple lines of 17 | # user commands. 18 | InteractiveRSH, 19 | 20 | SensitiveRSH_Input, 21 | SensitiveRSH_Output, 22 | }; 23 | 24 | const failure_msgs = 25 | /^Permission denied/ 26 | | /Login failed/ 27 | &redef; 28 | } 29 | 30 | redef capture_filters += { ["rsh"] = "tcp port 514" }; 31 | 32 | global rsh_ports = { 514/tcp } &redef; 33 | redef dpd_config += { [ANALYZER_RSH] = [$ports = rsh_ports] }; 34 | 35 | type rsh_session_info: record { 36 | client_user: string; 37 | server_user: string; 38 | initial_cmd: string; 39 | output_line: count; # number of lines seen 40 | }; 41 | 42 | global rsh_sessions: table[conn_id] of rsh_session_info; 43 | 44 | function new_rsh_session(c: connection, client_user: string, 45 | server_user: string, line: string) 46 | { 47 | if ( c$id in rsh_sessions ) 48 | delete rsh_sessions[c$id]; 49 | 50 | local s: rsh_session_info; 51 | s$client_user = client_user; 52 | s$server_user = server_user; 53 | s$initial_cmd = line; 54 | s$output_line = 0; 55 | 56 | rsh_sessions[c$id] = s; 57 | } 58 | 59 | event rsh_request(c: connection, client_user: string, server_user: string, 60 | line: string, new_session: bool) 61 | { 62 | local id = c$id; 63 | 64 | local BS_line = edit(line, Login::BS); 65 | local DEL_line = edit(line, Login::DEL); 66 | 67 | if ( new_session ) 68 | { 69 | new_rsh_session(c, client_user, server_user, line); 70 | 71 | if ( client_user != server_user ) 72 | NOTICE([$note=DifferentRSH_Usernames, $conn=c, 73 | $msg=fmt("differing client/server usernames (%s/%s)", 74 | client_user, server_user), 75 | $sub=client_user, $user=server_user]); 76 | } 77 | 78 | local s = rsh_sessions[c$id]; 79 | if ( s$output_line > 0 ) 80 | NOTICE([$note=InteractiveRSH, $conn=c, 81 | $msg="interactive RSH session, input following output", 82 | $sub=s$client_user, $user=s$server_user]); 83 | 84 | if ( Login::input_trouble in line || 85 | Login::input_trouble in BS_line || 86 | Login::input_trouble in DEL_line || 87 | line == Login::full_input_trouble ) 88 | NOTICE([$note=SensitiveRSH_Input, $conn=c, 89 | $msg=line, $sub=s$client_user, $user=s$server_user]); 90 | } 91 | 92 | event rsh_reply(c: connection, client_user: string, server_user: string, 93 | line: string) 94 | { 95 | local s = rsh_sessions[c$id]; 96 | 97 | if ( line != "" && ++s$output_line == 1 && failure_msgs in line ) 98 | NOTICE([$note=FailedRSH_Authentication, $conn=c, 99 | $msg=line, $sub=s$client_user, $user=s$server_user]); 100 | 101 | if ( Login::output_trouble in line || 102 | line == Login::full_output_trouble ) 103 | NOTICE([$note=SensitiveRSH_Output, $conn=c, 104 | $msg=line, $sub=s$client_user, $user=s$server_user]); 105 | } 106 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/targeted-scan.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | # 3 | # Drop external hosts that continually bang away on a particular open port. 4 | # 5 | # Note that we time out identified scanners to avoid excessive memory 6 | # utilitization in the event of a wide scan across address space. 7 | 8 | @load notice 9 | @load site 10 | 11 | module TargetedScan; 12 | 13 | export { 14 | redef enum Notice += { TargetedScan, }; 15 | 16 | # If true, then only consider traffic from external sources. 17 | global external_only = T &redef; 18 | 19 | # Which ports to consider. 20 | const ports = { 1433/tcp, } &redef; 21 | 22 | # If set, at least/most this many bytes need to be transferred for 23 | # a connection using the given port. These are useful for example 24 | # for inferring that SSH connections reflect password-guessing 25 | # attempts. 26 | const min_bytes: table[port] of count &redef; 27 | const max_bytes: table[port] of count &redef; 28 | 29 | # If set, then this is the threshold for reportin accessing 30 | # for a given service. 31 | const port_threshold: table[port] of count &redef; 32 | 33 | # Otherwise, this is the threshold. 34 | const general_threshold = 1000 &redef; 35 | 36 | # The data structure we use to track targeted probing. 37 | # It's exported to enable redef'ing the &write_expire value. 38 | global targeted_tries: table[addr, addr, port] of count 39 | &default=0 &write_expire=10 min &redef; 40 | } 41 | 42 | function delete_targeted_data(orig: addr, resp: addr, service: port) 43 | { 44 | delete targeted_tries[orig, resp, service]; 45 | } 46 | 47 | function targeted_check(c: connection) 48 | { 49 | local id = c$id; 50 | local orig = id$orig_h; 51 | local resp = id$resp_h; 52 | local service = ("ftp-data" in c$service) ? 20/tcp : id$resp_p; 53 | 54 | if ( service !in ports || (external_only && is_local_addr(orig)) ) 55 | return; 56 | 57 | local bytes_xferred = c$orig$size + c$resp$size; 58 | 59 | if ( service in min_bytes && bytes_xferred < min_bytes[service] ) 60 | return; 61 | if ( service in max_bytes && bytes_xferred > max_bytes[service] ) 62 | return; 63 | 64 | local cnt = ++targeted_tries[orig, resp, service]; 65 | 66 | if ( service in port_threshold ) 67 | { 68 | if ( cnt != port_threshold[service] ) 69 | return; 70 | } 71 | 72 | else if ( cnt != general_threshold ) 73 | return; 74 | 75 | local svc = service in port_names ? 76 | port_names[service] : fmt("%s", service); 77 | 78 | NOTICE([$note=TargetedScan, $src=orig, $dst=resp, $p=service, 79 | $msg=fmt("targeted attack on service %s, count = %d", svc, cnt)]); 80 | 81 | # Since we've reported this host, we can stop tracking it. 82 | delete targeted_tries[orig, resp, service]; 83 | } 84 | 85 | 86 | event connection_finished(c: connection) 87 | { 88 | targeted_check(c); 89 | } 90 | 91 | event connection_rejected(c: connection) 92 | { 93 | targeted_check(c); 94 | } 95 | 96 | event connection_half_finished(c: connection) 97 | { 98 | targeted_check(c); 99 | } 100 | 101 | event connection_reset(c: connection) 102 | { 103 | targeted_check(c); 104 | } 105 | 106 | event connection_partial_close(c: connection) 107 | { 108 | targeted_check(c); 109 | } 110 | 111 | event connection_state_remove(c: connection) 112 | { 113 | targeted_check(c); 114 | } 115 | -------------------------------------------------------------------------------- /scripts/todo/relies_on_synchronized/roam.bro: -------------------------------------------------------------------------------- 1 | ##! roam.bro 2 | ##! 3 | ##! This script collects IP-to-MAC mappings (and vice versa) of machines 4 | ##! that may have more than one IP address over time due to a DHCP server 5 | ##! in the network. 6 | ##! 7 | ##! When keeping per-IP-address state, it could well be that the address 8 | ##! becomes invalid because the client's DHCP lease expired or because it 9 | ##! received a new IP address after rejoining the network. Ideally, this 10 | ##! state would roam with the user. But many Bro script data structures 11 | ##! use per-address indices and would mechanistically instantiate state 12 | ##! for a new client even though it merely reappeared under a new IP 13 | ##! address. To incorporate the notion of roaming, roam.bro makes 14 | ##! available two data structures that script writers can use: 15 | ##! 16 | ##! global ip_to_mac: table[addr] of string 17 | ##! &read_expire = alias_expiration &synchronized; 18 | ##! 19 | ##! global mac_to_ip: table[string] of set[addr] 20 | ##! &read_expire = alias_expiration &synchronized; 21 | ##! 22 | ##! Event handlers for the dhcp_ack and arp_reply events populate 23 | ##! these tables. For example, the sidejacking script (see below) makes 24 | ##! use of roam.bro to test whether a certain client IP address is an 25 | ##! alias of another IP address: 26 | ##! 27 | ##! function is_aliased(client: addr, ctx: cookie_context) : bool 28 | ##! { 29 | ##! if (client in Roam::ip_to_mac) 30 | ##! { 31 | ##! local mac = Roam::ip_to_mac[client]; 32 | ##! if (mac == ctx$mac && mac in Roam::mac_to_ip 33 | ##! && client in Roam::mac_to_ip[mac]) 34 | ##! return T; 35 | ##! } 36 | ##! 37 | ##! return F; 38 | ##! } 39 | ##! 40 | ##! If the two table are not accessed for more than the 41 | ##! alias_expiration, the entry will expire. It is possible to 42 | ##! redefine the expiration interval: 43 | ##! 44 | ##! redef Roam::alias_expiration = 7 days; 45 | ##! 46 | ##! Author: Matthias Vallentin 47 | 48 | module Roam; 49 | 50 | export 51 | { 52 | # Time after which observed MAC to IP mappings (and vice versa) expire. 53 | const alias_expiration = 1 day &redef; 54 | 55 | global ip_to_mac: table[addr] of string 56 | &read_expire = alias_expiration &synchronized; 57 | 58 | global mac_to_ip: table[string] of set[addr] 59 | &read_expire = alias_expiration &synchronized; 60 | } 61 | 62 | # Collect IP-to-MAC mappings and vice versa from DHCP ACKs. 63 | event DHCP::dhcp_ack(c: connection, msg: dhcp_msg, mask: addr, 64 | router: dhcp_router_list, lease: interval, serv_addr: addr) 65 | { 66 | local ip = msg$yiaddr; 67 | local mac = msg$h_addr; 68 | 69 | if (ip !in ip_to_mac) 70 | ip_to_mac[ip] = mac; 71 | 72 | if (mac !in mac_to_ip) 73 | mac_to_ip[mac] = set() &mergeable; 74 | 75 | add mac_to_ip[mac][ip]; 76 | } 77 | 78 | # Collect IP-to-MAC mappings and vice versa from ARP replies. 79 | event arp_reply(mac_src: string, mac_dst: string, SPA: addr, SHA: string, 80 | TPA: addr, THA: string) 81 | { 82 | local ip = SPA; 83 | local mac = mac_src; 84 | 85 | if (ip !in ip_to_mac) 86 | ip_to_mac[ip] = mac; 87 | 88 | if (mac !in mac_to_ip) 89 | mac_to_ip[mac] = set() &mergeable; 90 | 91 | add mac_to_ip[mac][ip]; 92 | } 93 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-contents.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | # 3 | # Provides a function that requests a particular connection from the 4 | # Time Machine and stores the subsequent reassembled payload into a 5 | # local file. 6 | 7 | @load time-machine 8 | 9 | module TimeMachine; 10 | 11 | export { 12 | global save_contents: 13 | function(filename_prefix: string, c: connection, 14 | in_mem: bool, descr: string); 15 | 16 | global save_contents_id: 17 | function(filename_prefix: string, id: conn_id, start: time, 18 | in_mem: bool, descr: string); 19 | 20 | # Raised when contents have been fully saved. 21 | global contents_saved: 22 | event(c: connection, orig_file: string, resp_file: string); 23 | 24 | const contents_dir = "tm-contents" &redef; 25 | } 26 | 27 | # Table associating TM tag with filename. 28 | global requested_conns: table[string] of string; 29 | 30 | type fnames: record { 31 | orig: string; 32 | resp: string; 33 | orig_f: file; 34 | resp_f: file; 35 | }; 36 | 37 | global external_conns: table[conn_id] of fnames; 38 | 39 | function save_contents(filename_prefix: string, c: connection, 40 | in_mem: bool, descr: string) 41 | { 42 | if ( is_external_connection(c) ) 43 | return; 44 | 45 | save_contents_id(filename_prefix, c$id, c$start_time, in_mem, descr); 46 | } 47 | 48 | function save_contents_id(filename_prefix: string, id: conn_id, start: time, 49 | in_mem: bool, descr: string) 50 | { 51 | TimeMachine::suspend_cut_off_id(id, descr); 52 | local qtag = TimeMachine::request_connection_id(id, start, in_mem, descr); 53 | if ( qtag == "" ) 54 | return; 55 | 56 | requested_conns[qtag] = filename_prefix; 57 | } 58 | 59 | event connection_external(c: connection, tag: string) 60 | { 61 | if ( tag !in requested_conns ) 62 | return; 63 | 64 | local fn = requested_conns[tag]; 65 | local id = c$id; 66 | local idstr = fmt("%s.%d-%s.%d", id$orig_h, id$orig_p, id$resp_h, id$resp_p); 67 | 68 | local orig_fn = fmt("%s/%s.%s.orig.dat", contents_dir, fn, idstr); 69 | local resp_fn = fmt("%s/%s.%s.resp.dat", contents_dir, fn, idstr); 70 | local orig_f = open(orig_fn); 71 | local resp_f = open(resp_fn); 72 | 73 | set_contents_file(c$id, CONTENTS_ORIG, orig_f); 74 | set_contents_file(c$id, CONTENTS_RESP, resp_f); 75 | 76 | delete requested_conns[tag]; 77 | external_conns[c$id] = 78 | [$orig=orig_fn, $resp=resp_fn, $orig_f=orig_f, $resp_f=resp_f]; 79 | } 80 | 81 | event delayed_contents_saved(c: connection, orig_file: string, resp_file: string) 82 | { 83 | schedule 2 min { TimeMachine::contents_saved(c, orig_file, resp_file) }; 84 | } 85 | 86 | event connection_state_remove(c: connection) 87 | { 88 | if ( ! is_external_connection(c) ) 89 | return; 90 | 91 | if ( c$id !in external_conns ) 92 | return; 93 | 94 | local fn = external_conns[c$id]; 95 | 96 | close(fn$orig_f); 97 | close(fn$resp_f); 98 | 99 | # FIXME: We delay this a bit as there seems to be some race-condition 100 | # with the file's data being flushed to disk. Not sure why, though. 101 | # However, we need to delay indirectly through another event to 102 | # install it into the global timer manager. 103 | event delayed_contents_saved(c, fn$orig, fn$resp); 104 | 105 | delete external_conns[c$id]; 106 | } 107 | 108 | event bro_init() 109 | { 110 | mkdir(contents_dir); 111 | } 112 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/ncp.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | @load conn-id 4 | 5 | module NCP; 6 | 7 | global ncp_log = open_log_file("ncp") &redef; 8 | 9 | redef capture_filters += {["ncp"] = "tcp port 524"}; 10 | 11 | export { 12 | 13 | const ncp_frame_type_name = { 14 | [ 0x1111 ] = "NCP_ALLOC_SLOT", 15 | [ 0x2222 ] = "NCP_REQUEST", 16 | [ 0x3333 ] = "NCP_REPLY", 17 | [ 0x5555 ] = "NCP_DEALLOC_SLOT", 18 | [ 0x7777 ] = "NCP_BURST", 19 | [ 0x9999 ] = "NCP_ACK", 20 | } &default = function(code: count): string 21 | { 22 | return fmt("NCP_UNKNOWN_FRAME_TYPE(%x)", code); 23 | }; 24 | 25 | const ncp_function_name = { 26 | [ 0x01 ] = "NCP_FILE_SET_LOCK", 27 | [ 0x02 ] = "NCP_FILE_RELEASE_LOCK", 28 | [ 0x03 ] = "NCP_LOG_FILE", 29 | [ 0x04 ] = "NCP_LOCK_FILE_SET", 30 | [ 0x05 ] = "NCP_RELEASE_FILE", 31 | [ 0x06 ] = "NCP_RELEASE_FILE_SET", 32 | [ 0x07 ] = "NCP_CLEAR_FILE", 33 | [ 0x08 ] = "NCP_CLEAR_FILE_SET", 34 | [ 0x09 ] = "NCP_LOG_LOGICAL_RECORD", 35 | [ 0x0a ] = "NCP_LOCK_LOGICAL_RECORD_SET", 36 | [ 0x0b ] = "NCP_CLEAR_LOGICAL_RECORD", 37 | [ 0x0c ] = "NCP_RELEASE_LOGICAL_RECORD", 38 | [ 0x0d ] = "NCP_RELEASE_LOGICAL_RECORD_SET", 39 | [ 0x0e ] = "NCP_CLEAR_LOGICAL_RECORD_SET", 40 | [ 0x0f ] = "NCP_ALLOC_RESOURCE", 41 | [ 0x10 ] = "NCP_DEALLOC_RESOURCE", 42 | [ 0x11 ] = "NCP_PRINT", 43 | [ 0x15 ] = "NCP_MESSAGE", 44 | [ 0x16 ] = "NCP_DIRECTORY", 45 | [ 0x17 ] = "NCP_BINDARY_AND_MISC", 46 | [ 0x18 ] = "NCP_END_OF_JOB", 47 | [ 0x19 ] = "NCP_LOGOUT", 48 | [ 0x1a ] = "NCP_LOG_PHYSICAL_RECORD", 49 | [ 0x1b ] = "NCP_LOCK_PHYSICAL_RECORD_SET", 50 | [ 0x1c ] = "NCP_RELEASE_PHYSICAL_RECORD", 51 | [ 0x1d ] = "NCP_RELEASE_PHYSICAL_RECORD_SET", 52 | [ 0x1e ] = "NCP_CLEAR_PHYSICAL_RECORD", 53 | [ 0x1f ] = "NCP_CLEAR_PHYSICAL_RECORD_SET", 54 | [ 0x20 ] = "NCP_SEMAPHORE", 55 | [ 0x22 ] = "NCP_TRANSACTION_TRACKING", 56 | [ 0x23 ] = "NCP_AFP", 57 | [ 0x42 ] = "NCP_CLOSE_FILE", 58 | [ 0x47 ] = "NCP_GET_FILE_SIZE", 59 | [ 0x48 ] = "NCP_READ_FILE", 60 | [ 0x49 ] = "NCP_WRITE_FILE", 61 | [ 0x56 ] = "NCP_EXT_ATTR", 62 | [ 0x57 ] = "NCP_FILE_DIR", 63 | [ 0x58 ] = "NCP_AUDITING", 64 | [ 0x5a ] = "NCP_MIGRATION", 65 | [ 0x60 ] = "NCP_PNW", 66 | [ 0x61 ] = "NCP_GET_MAX_PACKET_SIZE", 67 | [ 0x68 ] = "NCP_NDS", 68 | [ 0x6f ] = "NCP_SEMAPHORE_NEW", 69 | [ 0x7b ] = "NCP_7B", 70 | 71 | [ 0x5701 ] = "NCP_CREATE_FILE_DIR", 72 | [ 0x5702 ] = "NCP_INIT_SEARCH", 73 | [ 0x5703 ] = "NCP_SEARCH_FILE_DIR", 74 | [ 0x5704 ] = "NCP_RENAME_FILE_DIR", 75 | [ 0x5706 ] = "NCP_OBTAIN_FILE_DIR_INFO", 76 | [ 0x5707 ] = "NCP_MODIFY_FILE_DIR_DOS_INFO", 77 | [ 0x5708 ] = "NCP_DELETE_FILE_DIR", 78 | [ 0x5709 ] = "NCP_SET_SHORT_DIR_HANDLE", 79 | [ 0x5714 ] = "NCP_SEARCH_FOR_FILE_DIR_SET", 80 | [ 0x5718 ] = "NCP_GET_NAME_SPACE_LOADED_LIST", 81 | [ 0x5742 ] = "NCP_GET_CURRENT_SIZE_OF_FILE", 82 | 83 | } &default = function(code: count): string 84 | { 85 | return fmt("NCP_UNKNOWN_FUNCTION(%x)", code); 86 | }; 87 | 88 | } # export 89 | 90 | event ncp_request(c: connection, frame_type: count, length: count, func: count) 91 | { 92 | print ncp_log, fmt("%.6f %s NCP request type=%s function=%s", 93 | network_time(), id_string(c$id), 94 | ncp_frame_type_name[frame_type], 95 | ncp_function_name[func]); 96 | } 97 | 98 | event ncp_reply(c: connection, frame_type: count, length: count, 99 | req_frame: count, req_func: count, completion_code: count) 100 | { 101 | } 102 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/dns-anonymizer.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | @load dns 4 | @load anon 5 | 6 | module DNS; 7 | 8 | redef rewriting_dns_trace = T; 9 | 10 | event dns_message(c: connection, is_orig: bool, msg: dns_msg, len: count) 11 | { 12 | if ( get_conn_transport_proto(c$id) == udp ) 13 | rewrite_dns_message(c, is_orig, msg, len); 14 | } 15 | 16 | event dns_request(c: connection, msg: dns_msg, query: string, 17 | qtype: count, qclass: count) 18 | { 19 | if ( get_conn_transport_proto(c$id) == udp ) 20 | rewrite_dns_request(c, anonymize_host(query), 21 | msg, qtype, qclass); 22 | } 23 | 24 | event dns_end(c: connection, msg: dns_msg) 25 | { 26 | if ( get_conn_transport_proto(c$id) == udp ) 27 | rewrite_dns_end(c, T); 28 | } 29 | 30 | event dns_query_reply(c: connection, msg: dns_msg, query: string, 31 | qtype: count, qclass: count) 32 | { 33 | rewrite_dns_reply_question(c, msg, anonymize_host(query), 34 | qtype, qclass); 35 | } 36 | 37 | event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) 38 | { 39 | ans$query = anonymize_host(ans$query); 40 | rewrite_dns_A_reply(c, msg, ans, anonymize_address(a, c$id)); 41 | } 42 | 43 | #### FIXME: ANONYMIZE! 44 | event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, 45 | a: addr, astr: string) 46 | { 47 | ans$query = anonymize_host(ans$query); 48 | astr = "::"; 49 | a = anonymize_address(a, c$id); 50 | rewrite_dns_AAAA_reply(c, msg, ans, a, astr); 51 | } 52 | 53 | event dns_NS_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) 54 | { 55 | ans$query = anonymize_host(ans$query); 56 | rewrite_dns_NS_reply(c, msg, ans, anonymize_host(name)); 57 | } 58 | 59 | event dns_CNAME_reply(c: connection, msg: dns_msg, ans: dns_answer, 60 | name: string) 61 | { 62 | ans$query = anonymize_host(ans$query); 63 | rewrite_dns_CNAME_reply(c, msg, ans, anonymize_host(name)); 64 | } 65 | 66 | event dns_MX_reply(c: connection, msg: dns_msg, ans: dns_answer, 67 | name: string, preference: count) 68 | { 69 | ans$query = anonymize_host(ans$query); 70 | rewrite_dns_MX_reply(c, msg, ans, anonymize_host(name), preference); 71 | } 72 | 73 | event dns_PTR_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) 74 | { 75 | ans$query = anonymize_host(ans$query); 76 | rewrite_dns_PTR_reply(c, msg, ans, anonymize_host(name)); 77 | } 78 | 79 | event dns_SOA_reply(c: connection, msg: dns_msg, ans: dns_answer, soa: dns_soa) 80 | { 81 | soa$mname = anonymize_host(soa$mname); 82 | soa$rname = anonymize_host(soa$rname); 83 | ans$query = anonymize_host(ans$query); 84 | rewrite_dns_SOA_reply(c, msg, ans, soa); 85 | } 86 | 87 | event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, 88 | str: string) 89 | { 90 | str = anonymize_string(str); 91 | ans$query = anonymize_host(ans$query); 92 | rewrite_dns_TXT_reply(c, msg, ans, str); 93 | } 94 | 95 | event dns_EDNS_addl (c: connection, msg: dns_msg, ans: dns_edns_additional) 96 | { 97 | rewrite_dns_EDNS_addl(c, msg, ans); 98 | } 99 | 100 | event dns_rejected(c: connection, msg: dns_msg, query: string, 101 | qtype: count, qclass: count) 102 | { 103 | #### Hmmm, this is probably not right - we are going to have to look 104 | # at the question type to determine how to anonymize this. 105 | rewrite_dns_reply_question(c, msg, anonymize_host(query), 106 | qtype, qclass); 107 | } 108 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/smb-tag.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | 3 | redef capture_filters += { 4 | ["smb"] = "tcp port 445", 5 | ["netbios-ss"] = "tcp port 139", 6 | }; 7 | 8 | global smb_filename_tag: table[conn_id] of string &default = ""; 9 | 10 | const log_smb_tags = T &redef; 11 | function get_smb_tag(id: conn_id): string 12 | { 13 | if ( id in smb_filename_tag ) 14 | return smb_filename_tag[id]; 15 | else 16 | return ""; 17 | } 18 | 19 | module SMB_tag; 20 | 21 | global log = open_log_file("smb-tag") &redef; 22 | 23 | const well_known_files = { 24 | "\\IPC$", 25 | "\\print$", 26 | "\\LANMAN", 27 | "\\atsvc", 28 | "\\AudioSrv", 29 | "\\browser", 30 | "\\cert", 31 | "\\Ctx_Winstation_API_Service", 32 | "\\DAV", 33 | "\\dnsserver", 34 | "\\epmapper", 35 | "\\eventlog", 36 | "\\HydraLsPipe", 37 | "\\InitShutdown", 38 | "\\keysvc", 39 | "\\locator", 40 | "\\llsrpc", 41 | "\\lsarpc", 42 | "\\msgsvc", 43 | "\\netdfs", 44 | "\\netlogon", 45 | "\\ntsvcs", 46 | "\\policyagent", 47 | "\\ipsec", 48 | "\\ProfMapApi", 49 | "\\protected_storage", 50 | "\\ROUTER", 51 | "\\samr", 52 | "\\scerpc", 53 | "\\SECLOGON", 54 | "\\SfcApi", 55 | "\\spoolss", 56 | "\\srvsvc", 57 | "\\ssdpsrv", 58 | "\\svcctl", 59 | "\\tapsrv", 60 | "\\trkwks", 61 | "\\W32TIME", 62 | "\\W32TIME_ALT", 63 | "\\winlogonrpc", 64 | "\\winreg", 65 | "\\winspipe", 66 | "\\wkssvc", 67 | "\\lbl.gov", 68 | "\\LBL" 69 | }; 70 | 71 | function well_known_file(n: string): string 72 | { 73 | n = to_lower(n); 74 | local a = ""; 75 | for ( p in well_known_files ) 76 | { 77 | if ( strstr(n, to_lower(p)) > 0 ) 78 | if ( byte_len(p) > byte_len(a) ) 79 | a = p; 80 | } 81 | return a; 82 | } 83 | 84 | function add_to_smb_filename_tag(c: connection, name: string): bool 85 | { 86 | if ( name == "\\PIPE\\" || name == "" ) 87 | return F; 88 | 89 | local id = c$id; 90 | local orig_tag = smb_filename_tag[id]; 91 | 92 | local n = well_known_file(name); 93 | if ( n == "" ) 94 | { 95 | if ( log_smb_tags ) 96 | print log, fmt("%.6f %s regular file: \"%s\"", 97 | network_time(), conn_id_string(c$id), name); 98 | n = ""; 99 | } 100 | 101 | n = fmt("\"%s\"", n); 102 | 103 | if ( orig_tag == "" ) 104 | { 105 | smb_filename_tag[id] = n; 106 | } 107 | else if ( strstr(orig_tag, n) == 0 ) 108 | { 109 | smb_filename_tag[id] = cat(orig_tag, ",", n); 110 | } 111 | 112 | return T; 113 | } 114 | 115 | event smb_com_nt_create_andx(c: connection, name: string) 116 | { 117 | add_to_smb_filename_tag(c, name); 118 | } 119 | 120 | event smb_com_transaction(c: connection, is_orig: bool, subcmd: count, 121 | name: string, data: string) 122 | { 123 | add_to_smb_filename_tag(c, name); 124 | } 125 | 126 | event smb_com_transaction2(c: connection, is_orig: bool, subcmd: count, 127 | name: string, data: string) 128 | { 129 | add_to_smb_filename_tag(c, name); 130 | } 131 | 132 | event smb_get_dfs_referral(c: connection, max_referral_level: count, file_name: string) 133 | { 134 | add_to_smb_filename_tag(c, file_name); 135 | } 136 | 137 | event smb_com_tree_connect_andx(c: connection, path: string, service: string) 138 | { 139 | local basic = sub(path, /.*\\/, "\\"); 140 | if ( /\$$/ in basic ) 141 | add_to_smb_filename_tag(c, basic); 142 | } 143 | 144 | event delete_smb_tag(c: connection) 145 | { 146 | delete smb_filename_tag[c$id]; 147 | } 148 | 149 | event connection_state_remove(c: connection) 150 | { 151 | if ( c$id in smb_filename_tag ) 152 | { 153 | if ( log_smb_tags ) 154 | print log, fmt("conn %s start %.6f SMB [%s]", 155 | conn_id_string(c$id), 156 | c$start_time, 157 | smb_filename_tag[c$id]); 158 | event delete_smb_tag(c); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/time-machine/tm-gap.bro: -------------------------------------------------------------------------------- 1 | # $Id: tm-gap.bro,v 1.1.2.1 2006/01/05 22:38:37 sommer Exp $ 2 | # 3 | # When we see a content gap, we request the same connection from the TM. 4 | # If we get it from there completely, fine. If not, we check whether the 5 | # gap is at the same place as before, which would indicate that the packet 6 | # was indeed missing on the link. 7 | 8 | @load conn-id 9 | @load time-machine 10 | 11 | module TimeMachineGap; 12 | 13 | export { 14 | # If true, we assume a BPF filter that includes *all* data packets. 15 | const seeing_all_packets = F &redef; 16 | 17 | # Exclude these ports. 18 | const ignore_ports = { 80/tcp, 22/tcp, 443/tcp }; 19 | 20 | redef enum Notice += { 21 | # A connection has at least one gap that matches a gap 22 | # on the link. 23 | ContentGapTmAndLink, 24 | 25 | # A connection that had a gap on the link has been fully 26 | # received from the TM. 27 | ContentGapSolved, 28 | }; 29 | } 30 | 31 | type gap : record { 32 | is_orig: bool; 33 | seq: count; 34 | length: count; 35 | }; 36 | 37 | # Remembers the first gap per connection. 38 | # (FIXME: Would it make sense to remember all gaps?) 39 | global conns: table[conn_id] of gap; 40 | 41 | global f = open_log_file("gap"); 42 | 43 | event content_gap(c: connection, is_orig: bool, seq: count, length: count) 44 | { 45 | if ( ! is_external_connection(c) ) 46 | { 47 | if ( c$id in conns ) 48 | # We already requested the conn. 49 | return; 50 | 51 | if ( c$id$resp_p in ignore_ports ) 52 | return; 53 | 54 | # It only makes sense to request the connection if we are 55 | # not just analyzing TCP control packets for it. There's 56 | # no perfect way to determine whether we do so but, as a 57 | # heuristic, we assume that we are supposed to see data 58 | # packets if: 59 | # 60 | # (1) the service port is well-known for one of our analyzers 61 | # (because then the analyzer script is loaded which extends 62 | # the capture filter accordingly; or 63 | # (2) the user explicitly tells us they are using a filter that 64 | # includes all packets (e.g., DPD); or 65 | # (3) (special case) it's an HTTP reply, but we only 66 | # load http-request. 67 | 68 | if ( ! seeing_all_packets ) 69 | { 70 | if ( c$id$resp_p !in dpd_analyzer_ports ) 71 | return; 72 | 73 | if ( c$id$resp_p in dpd_analyzer_ports && ! is_orig && 74 | ANALYZER_HTTP in dpd_analyzer_ports[c$id$resp_p]) 75 | { 76 | @ifdef ( process_HTTP_replies ) 77 | if ( ! process_HTTP_replies ) 78 | @endif 79 | return; 80 | } 81 | } 82 | 83 | local g: gap = [$is_orig=is_orig, $seq=seq, $length=length]; 84 | conns[c$id] = g; 85 | 86 | # Should be in TM's memory. 87 | TimeMachine::request_connection(c, T, "tm-gap"); 88 | 89 | print f, "ask", id_string(c$id); 90 | } 91 | 92 | else 93 | { # a gap in a connection from the TM 94 | if ( c$id !in conns ) 95 | # Will be reported as ContentGap by weird.bro. 96 | return; 97 | 98 | local h = conns[c$id]; 99 | 100 | if ( h$is_orig == is_orig && h$seq == seq && h$length == length ) 101 | { 102 | NOTICE([$note=ContentGapTmAndLink, $conn=c, 103 | $msg=fmt("%s same content gap on link and from time-machine (%s %d/%d)", 104 | id_string(c$id), 105 | is_orig ? ">" : "<", seq, length)]); 106 | } 107 | 108 | delete conns[c$id]; 109 | } 110 | } 111 | 112 | event connection_external(c: connection, tag: string) 113 | { 114 | if ( c$id in conns ) 115 | print f, "got", id_string(c$id); 116 | } 117 | 118 | event connection_state_remove(c: connection) 119 | { 120 | if ( c$id in conns && is_external_connection(c) ) 121 | { # It's still in the table, so we got it completely. Yippie! 122 | NOTICE([$note=ContentGapSolved, $conn=c, 123 | $msg=fmt("%s content gap(s) solved by time-machine", 124 | id_string(c$id))]); 125 | delete conns[c$id]; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/worm.bro: -------------------------------------------------------------------------------- 1 | # $Id: worm.bro 4758 2007-08-10 06:49:23Z vern $ 2 | 3 | @load notice 4 | @load site 5 | 6 | # signatures.bro needs this. 7 | global is_worm_infectee: function(ip: addr) : bool; 8 | 9 | @load signatures 10 | 11 | redef enum Notice += { 12 | LocalWorm, # worm seen in local host 13 | RemoteWorm, # worm seen in remote host 14 | }; 15 | 16 | # redef capture_filters += { ["worm"] = "tcp dst port 80" }; 17 | 18 | const worm_log = open_log_file("worm") &redef; 19 | 20 | # Maps types of worms to URI patterns. 21 | const worm_types: table[string] of pattern = { 22 | ["Code Red 1"] = /\.id[aq]\?.*NNNNNNNNNNNNN/, 23 | ["Code Red 2"] = /\.id[aq]\?.*XXXXXXXXXXXXX/, 24 | ["Nimda"] = /\/scripts\/root\.exe\?\/c\+tftp/ | 25 | /\/MSADC\/root.exe\?\/c\+dir/ | 26 | /cool\.dll.*httpodbc\.dll/, # 29Oct01 Nimda variant 27 | } &redef; 28 | 29 | # Maps signatures to worm types. 30 | const worm_sigs: table[string] of string = { 31 | ["slammer"] = "Slammer", 32 | ["nimda"] = "Nimda", 33 | ["bagle-bc"] = "Bagle.bc" 34 | }; 35 | 36 | # We handle these ourselves. 37 | redef signature_actions += { 38 | ["codered1"] = SIG_IGNORE, 39 | ["codered2"] = SIG_IGNORE, 40 | ["slammer"] = SIG_IGNORE, 41 | ["nimda"] = SIG_IGNORE, 42 | ["bagle-bc"] = SIG_IGNORE 43 | }; 44 | 45 | # Indexed by infectee. 46 | global worm_list: table[addr] of count &default=0 &read_expire = 2 days; 47 | 48 | # Indexed by infectee and type of worm. 49 | global worm_type_list: table[addr, string] of count 50 | &default=0 &read_expire = 2 days; 51 | 52 | # Invoked each time a new infectee (or a new type of worm for an existing 53 | # infectee) is seen. For the first instance of any type for a new infectee, 54 | # two events will be generated, one with worm_type of "first instance", 55 | # and another with the particular worm type. 56 | global worm_infectee_seen: event(c: connection, is_local: bool, worm_type: string); 57 | 58 | # Invoked whenever connection c has included a URI of worm type "worm_type". 59 | event worm_instance(c: connection, worm_type: string) 60 | { 61 | local id = c$id; 62 | local src = id$orig_h; 63 | local is_local = is_local_addr(src); 64 | 65 | if ( ++worm_list[src] == 1 ) 66 | event worm_infectee_seen(c, is_local, "first instance"); 67 | 68 | if ( ++worm_type_list[src, worm_type] == 1 ) 69 | event worm_infectee_seen(c, is_local, worm_type); 70 | } 71 | 72 | event worm_infectee_seen(c: connection, is_local: bool, worm_type: string) 73 | { 74 | if ( worm_type == "first instance" ) 75 | return; # just do the reporting for the specific type 76 | 77 | local infectee = c$id$orig_h; 78 | local where = is_local ? "local" : "remote"; 79 | local msg = fmt("%s %s worm source: %s", where, worm_type, infectee); 80 | 81 | if ( is_local ) 82 | NOTICE([$note=LocalWorm, $conn=c, $src=infectee, 83 | $msg=msg, $sub=worm_type]); 84 | else 85 | NOTICE([$note=RemoteWorm, $conn=c, $src=infectee, 86 | $msg=msg, $sub=worm_type]); 87 | 88 | print worm_log, fmt("%.6f %s", network_time(), msg); 89 | } 90 | 91 | event http_request(c: connection, method: string, 92 | original_URI: string, unescaped_URI: string, version: string) 93 | { 94 | # It's a pity to do this as a loop. Better would be if Bro could 95 | # search the patterns as one large RE and note which matched. 96 | 97 | for ( wt in worm_types ) 98 | if ( worm_types[wt] in unescaped_URI ) 99 | event worm_instance(c, wt); 100 | } 101 | 102 | event signature_match(state: signature_state, msg: string, data: string) 103 | { 104 | if ( state$id in worm_sigs ) 105 | event worm_instance(state$conn, worm_sigs[state$id]); 106 | } 107 | 108 | # Ignore "weird" events, we get some due to the capture_filter above that 109 | # only captures the client side of an HTTP session. 110 | event conn_weird(name: string, c: connection) 111 | { 112 | } 113 | 114 | function is_worm_infectee(ip: addr): bool 115 | { 116 | return ip in worm_list; 117 | } 118 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/sigs/dpd.sig: -------------------------------------------------------------------------------- 1 | # ALS signatures for protocol detection. 2 | 3 | signature dpd_ftp_client { 4 | ip-proto == tcp 5 | payload /(|.*[\n\r]) *[uU][sS][eE][rR] / 6 | tcp-state originator 7 | } 8 | 9 | # Match for server greeting (220, 120) and for login or passwd 10 | # required (230, 331). 11 | signature dpd_ftp_server { 12 | ip-proto == tcp 13 | payload /[\n\r ]*(120|220)[^0-9].*[\n\r] *(230|331)[^0-9]/ 14 | tcp-state responder 15 | requires-reverse-signature dpd_ftp_client 16 | enable "ftp" 17 | } 18 | 19 | signature dpd_http_client { 20 | ip-proto == tcp 21 | payload /^[[:space:]]*(GET|HEAD|POST)[[:space:]]*/ 22 | tcp-state originator 23 | } 24 | 25 | signature dpd_http_server { 26 | ip-proto == tcp 27 | payload /^HTTP\/[0-9]/ 28 | tcp-state responder 29 | requires-reverse-signature dpd_http_client 30 | enable "http" 31 | } 32 | 33 | signature dpd_bittorrenttracker_client { 34 | ip-proto == tcp 35 | payload /^.*\/announce\?.*info_hash/ 36 | tcp-state originator 37 | } 38 | 39 | signature dpd_bittorrenttracker_server { 40 | ip-proto == tcp 41 | payload /^HTTP\/[0-9]/ 42 | tcp-state responder 43 | requires-reverse-signature dpd_bittorrenttracker_client 44 | enable "bittorrenttracker" 45 | } 46 | 47 | signature dpd_bittorrent_peer1 { 48 | ip-proto == tcp 49 | payload /^\x13BitTorrent protocol/ 50 | tcp-state originator 51 | } 52 | 53 | signature dpd_bittorrent_peer2 { 54 | ip-proto == tcp 55 | payload /^\x13BitTorrent protocol/ 56 | tcp-state responder 57 | requires-reverse-signature dpd_bittorrent_peer1 58 | enable "bittorrent" 59 | } 60 | 61 | signature irc_client1 { 62 | ip-proto == tcp 63 | payload /(|.*[\r\n]) *[Uu][Ss][Ee][Rr] +.+[\n\r]+ *[Nn][Ii][Cc][Kk] +.*[\r\n]/ 64 | requires-reverse-signature irc_server_reply 65 | tcp-state originator 66 | enable "irc" 67 | } 68 | 69 | signature irc_client2 { 70 | ip-proto == tcp 71 | payload /(|.*[\r\n]) *[Nn][Ii][Cc][Kk] +.+[\r\n]+ *[Uu][Ss][Ee][Rr] +.+[\r\n]/ 72 | requires-reverse-signature irc_server_reply 73 | tcp-state originator 74 | enable "irc" 75 | } 76 | 77 | signature irc_server_reply { 78 | ip-proto == tcp 79 | payload /^(|.*[\n\r])(:[^ \n\r]+ )?[0-9][0-9][0-9] / 80 | tcp-state responder 81 | } 82 | 83 | signature irc_sig3 { 84 | ip-proto == tcp 85 | payload /(.*\x0a)*(\x20)*[Ss][Ee][Rr][Vv][Ee][Rr](\x20)+.+\x0a/ 86 | } 87 | 88 | signature irc_sig4 { 89 | ip-proto == tcp 90 | payload /(.*\x0a)*(\x20)*[Ss][Ee][Rr][Vv][Ee][Rr](\x20)+.+\x0a/ 91 | requires-reverse-signature irc_sig3 92 | enable "irc" 93 | } 94 | 95 | signature dpd_smtp_client { 96 | ip-proto == tcp 97 | payload /(|.*[\n\r])[[:space:]]*([hH][eE][lL][oO]|[eE][hH][lL][oO])/ 98 | requires-reverse-signature dpd_smtp_server 99 | enable "smtp" 100 | tcp-state originator 101 | } 102 | 103 | signature dpd_smtp_server { 104 | ip-proto == tcp 105 | payload /^[[:space:]]*220[[:space:]-]/ 106 | tcp-state responder 107 | } 108 | 109 | signature dpd_ssh_client { 110 | ip-proto == tcp 111 | payload /^[sS][sS][hH]-/ 112 | requires-reverse-signature dpd_ssh_server 113 | enable "ssh" 114 | tcp-state originator 115 | } 116 | 117 | signature dpd_ssh_server { 118 | ip-proto == tcp 119 | payload /^[sS][sS][hH]-/ 120 | tcp-state responder 121 | } 122 | 123 | signature dpd_pop3_server { 124 | ip-proto == tcp 125 | payload /^\+OK/ 126 | requires-reverse-signature dpd_pop3_client 127 | enable "pop3" 128 | tcp-state responder 129 | } 130 | 131 | signature dpd_pop3_client { 132 | ip-proto == tcp 133 | payload /(|.*[\r\n])[[:space:]]*([uU][sS][eE][rR][[:space:]]|[aA][pP][oO][pP][[:space:]]|[cC][aA][pP][aA]|[aA][uU][tT][hH])/ 134 | tcp-state originator 135 | } 136 | 137 | signature dpd_ssl_server { 138 | ip-proto == tcp 139 | # Server hello. 140 | payload /^(\x16\x03[\x00\x01\x02]..\x02...\x03[\x00\x01\x02]|...?\x04..\x00\x02).*/ 141 | requires-reverse-signature dpd_ssl_client 142 | enable "ssl" 143 | tcp-state responder 144 | } 145 | 146 | signature dpd_ssl_client { 147 | ip-proto == tcp 148 | # Client hello. 149 | payload /^(\x16\x03[\x00\x01\x02]..\x01...\x03[\x00\x01\x02]|...?\x01[\x00\x01\x02][\x02\x03]).*/ 150 | tcp-state originator 151 | } 152 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/firewall.bro: -------------------------------------------------------------------------------- 1 | # $Id: firewall.bro 4758 2007-08-10 06:49:23Z vern $ 2 | # 3 | # Firewall-like rules. 4 | 5 | @load notice 6 | @load conn 7 | @load ftp 8 | 9 | module Firewall; 10 | 11 | export { 12 | type action: enum { ALLOW, DENY }; 13 | type cmp: enum { EQ, NE }; 14 | 15 | type rule: record { 16 | label: string &default = ""; 17 | orig: subnet &default = 0.0.0.0/0; 18 | orig_set: set[addr] &optional; 19 | orig_cmp: cmp &default = EQ; 20 | orig_p: port &default = 0/tcp; 21 | orig_p_cmp: cmp &default = EQ; 22 | resp: subnet &default = 0.0.0.0/0; 23 | resp_set: set[addr] &optional; 24 | resp_cmp: cmp &default = EQ; 25 | resp_p: port &default = 0/tcp; 26 | resp_p_cmp: cmp &default = EQ; 27 | prot: transport_proto &default = unknown_transport; 28 | prot_cmp: cmp &default = EQ; 29 | state: string &default = ""; 30 | state_cmp: cmp &default = EQ; 31 | is_ftp: bool &default = F; 32 | 33 | action: action &default = ALLOW; 34 | }; 35 | 36 | redef enum Notice += { 37 | DenyRuleMatched 38 | }; 39 | 40 | global begin: function(c: connection); 41 | global match_rule: function(c: connection, r: rule); 42 | } 43 | 44 | const log_file = open_log_file("firewall") &redef; 45 | 46 | global stop_matching = F; 47 | 48 | function do_match(c: connection, r: rule): bool 49 | { 50 | if ( r$orig_cmp == EQ ) 51 | { 52 | if ( r?$orig_set ) 53 | { 54 | if ( c$id$orig_h !in r$orig_set && c$id$orig_h !in r$orig ) 55 | return F; 56 | } 57 | else 58 | { 59 | if ( c$id$orig_h !in r$orig ) 60 | return F; 61 | } 62 | } 63 | else 64 | { 65 | if ( r?$orig_set ) 66 | { 67 | if ( c$id$orig_h in r$orig_set || c$id$orig_h in r$orig ) 68 | return F; 69 | } 70 | else 71 | { 72 | if ( c$id$orig_h in r$orig ) 73 | return F; 74 | } 75 | } 76 | 77 | if ( r$resp_cmp == EQ ) 78 | { 79 | if ( r?$resp_set ) 80 | { 81 | if ( c$id$resp_h !in r$resp_set && c$id$resp_h !in r$resp ) 82 | return F; 83 | } 84 | else 85 | { 86 | if ( c$id$resp_h !in r$resp ) 87 | return F; 88 | } 89 | } 90 | else 91 | { 92 | if ( r?$resp_set ) 93 | { 94 | if ( c$id$resp_h in r$resp_set || c$id$resp_h in r$resp ) 95 | return F; 96 | } 97 | else 98 | { 99 | if ( c$id$resp_h in r$resp ) 100 | return F; 101 | } 102 | } 103 | 104 | if ( r$orig_p != 0/tcp ) 105 | { 106 | if ( r$orig_p_cmp == EQ ) 107 | { 108 | if ( c$id$orig_p != r$orig_p ) 109 | return F; 110 | } 111 | else 112 | if ( c$id$orig_p == r$orig_p ) 113 | return F; 114 | } 115 | 116 | if ( r$resp_p != 0/tcp ) 117 | { 118 | if ( r$resp_p_cmp == EQ ) 119 | { 120 | if ( c$id$resp_p != r$resp_p ) 121 | return F; 122 | } 123 | else 124 | if ( c$id$resp_p == r$resp_p ) 125 | return F; 126 | } 127 | 128 | if ( r$state != "" ) 129 | { 130 | local state = conn_state(c, get_port_transport_proto(c$id$orig_p)); 131 | if ( r$state_cmp == EQ ) 132 | { 133 | if ( state != r$state ) 134 | return F; 135 | } 136 | else 137 | if ( state == r$state ) 138 | return F; 139 | } 140 | 141 | if ( r$prot != unknown_transport ) 142 | { 143 | local proto = get_port_transport_proto(c$id$orig_p); 144 | if ( r$prot_cmp == EQ ) 145 | { 146 | if ( proto != r$prot ) 147 | return F; 148 | } 149 | else 150 | if ( proto == r$prot ) 151 | return F; 152 | } 153 | 154 | if ( r$is_ftp && ! is_ftp_data_conn(c) ) 155 | return F; 156 | 157 | return T; 158 | } 159 | 160 | 161 | function report_violation(c: connection, r:rule) 162 | { 163 | local trans = get_port_transport_proto(c$id$orig_p); 164 | local state = conn_state(c, trans); 165 | 166 | NOTICE([$note=DenyRuleMatched, 167 | $msg=fmt("%s %s", 168 | id_string(c$id), trans), $conn=c, $sub=r$label]); 169 | append_addl(c, fmt("<%s>", r$label)); 170 | record_connection(log_file, c); 171 | } 172 | 173 | function begin(c: connection) 174 | { 175 | stop_matching = F; 176 | } 177 | 178 | function match_rule(c: connection, r: rule) 179 | { 180 | if ( stop_matching ) 181 | return; 182 | 183 | if ( do_match(c, r) ) 184 | { 185 | stop_matching = T; 186 | 187 | if ( r$action == DENY ) 188 | report_violation(c, r); 189 | } 190 | } 191 | 192 | event bro_init() 193 | { 194 | set_buf(log_file, F); 195 | } 196 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/http-anon-utils.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | @load anon 4 | 5 | global http_anon_log = open_log_file("http-anon") &redef; 6 | 7 | const URI_proto_pat = /^ *([a-zA-Z]+)\:\/\// ; 8 | const known_URI_proto_pat = /^ *(http|https|ftp|ssh)\:\/\// ; 9 | 10 | const host_pat = / *^([\-0-9a-zA-Z]+\.)+([\_\-0-9a-zA-Z])*/ ; 11 | const port_pat = /^ *(\:[0-9]+\.)/ ; 12 | 13 | const query_pat = /\?/ ; 14 | 15 | function anonymize_http_URI(URI: string): string 16 | { 17 | URI = to_lower(URI); 18 | 19 | # Strip off protocol. 20 | local proto = ""; 21 | if ( URI_proto_pat in URI ) 22 | { 23 | local proto_part = split(URI, /\:\/\//); 24 | 25 | # Check if we know the protocol. If not, flag it so we 26 | # can update our protocol database. 27 | 28 | if ( known_URI_proto_pat !in URI ) 29 | { 30 | print http_anon_log, 31 | fmt("*** protocol %s unknown ", proto_part[1]); 32 | 33 | proto_part[1] = 34 | string_cat(" (bro: unknown) ", 35 | anonymize_arg("proto", proto_part[1])); 36 | } 37 | 38 | proto = string_cat(proto_part[1],"://"); 39 | URI = proto_part[2]; 40 | } 41 | 42 | # Strip off domain. 43 | local host = ""; 44 | if ( host_pat in URI ) 45 | { 46 | local base_parts = 47 | split_all(URI, / *^([\-\_0-9a-z]+\.)+[\-\_0-9a-z]*/); 48 | 49 | if ( |base_parts| < 2 ) 50 | { 51 | print http_anon_log, 52 | fmt (" XXXXXXXXXXXXXXXXXXXXXX BASE %s", URI); 53 | return " XXXX processing error XXXX"; 54 | } 55 | 56 | if ( |base_parts| == 2 ) 57 | URI = ""; 58 | 59 | else if ( |base_parts| == 3) 60 | URI = base_parts[3]; 61 | 62 | else if ( |base_parts| > 3) 63 | { 64 | local patch_me = ""; 65 | local hack = base_parts[2]; 66 | 67 | local i = 1; 68 | for ( part in base_parts ) 69 | { 70 | if ( i != 2 ) 71 | patch_me = string_cat(patch_me, 72 | base_parts[i]); 73 | i += 1; 74 | } 75 | 76 | URI = patch_me; 77 | } 78 | 79 | if ( host == simple_filename ) 80 | host = anonymize_path(host); 81 | else 82 | host = anonymize_host(base_parts[2]); 83 | } 84 | 85 | # Strip off port (if it exists). 86 | local pport = ""; 87 | if ( port_pat in URI ) 88 | { 89 | print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "; 90 | print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "; 91 | print "XXXXX anon.bro doing nothing with port XXXXXXXXXXX "; 92 | print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "; 93 | print "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "; 94 | } 95 | 96 | # Handle query (if exists). 97 | local tail = ""; 98 | if ( URI == "/" ) 99 | { 100 | # -- pass 101 | } 102 | 103 | else if ( query_pat in URI ) 104 | { 105 | local query_part = split(URI, /\?/); 106 | 107 | tail = fmt("%s?%s", 108 | anonymize_path(query_part[1]), 109 | anonymize_path(query_part[2])); 110 | } 111 | 112 | else 113 | tail = anonymize_path(URI); 114 | 115 | tail = string_cat("/", tail); 116 | 117 | return fmt("%s%s%s%s", proto, host, pport, tail); 118 | } 119 | 120 | 121 | const a_href_pat = /.*\< *a *href.*\>.*/ ; 122 | #/.*\< *a *href *= *\"[[:print:]]+\" *\>.*/; 123 | 124 | # Doesn't get everything ... but works for most. 125 | const a_href_split = 126 | /\< *a *href *= *(\\)?(\"|\')?([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+(\\)?(\"|\')?/ ; 127 | 128 | # Elegant ... yeah ... really .. :-/ 129 | const file_split = 130 | /(\"|\')([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+(\"|\')/ ; 131 | const file_strip_split = /([0-9a-z\/._!\[\]():*;~&|$\\=+\-?%@])+/ ; 132 | 133 | function http_doc_link_list(abstract: string): string 134 | { 135 | abstract = to_lower(abstract); 136 | 137 | if ( abstract == "" ) 138 | return abstract; 139 | 140 | local concat_key = ""; 141 | local href_parts = split_all(abstract, a_href_split); 142 | 143 | for ( part in href_parts ) 144 | { 145 | if ( href_parts[part] == a_href_split ) 146 | { 147 | local file_parts = 148 | split_all(href_parts[part], file_split); 149 | for ( a_part in file_parts ) 150 | { 151 | if ( file_parts[a_part] == file_split ) 152 | { 153 | local file_strip_parts = 154 | split_all(file_parts[a_part], 155 | file_strip_split); 156 | concat_key = fmt("%s %s", concat_key, 157 | anonymize_http_URI(file_strip_parts[2])); 158 | } 159 | } 160 | } 161 | } 162 | 163 | return concat_key; 164 | } 165 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/detect-protocols-http.bro: -------------------------------------------------------------------------------- 1 | # $Id: detect-protocols-http.bro,v 1.1.4.2 2006/05/31 00:16:21 sommer Exp $ 2 | # 3 | # Identifies protocols that use HTTP. 4 | 5 | @load detect-protocols 6 | 7 | module DetectProtocolHTTP; 8 | 9 | export { 10 | # Defines characteristics of a protocol. All attributes must match 11 | # to trigger the detection. We match patterns against lower-case 12 | # versions of the data. 13 | type protocol : record { 14 | url: pattern &optional; 15 | client_header: pattern &optional; 16 | client_header_content: pattern &optional; 17 | server_header: pattern &optional; 18 | server_header_content: pattern &optional; 19 | }; 20 | 21 | const protocols: table[string] of protocol = { 22 | ["Kazaa"] = [$url=/^\/\.hash=.*/, $server_header=/^x-kazaa.*/], 23 | ["Gnutella"] = [$url=/^\/(uri-res|gnutella).*/, 24 | $server_header=/^x-gnutella-.*/], 25 | ["Gnutella_"] = [$url=/^\/(uri-res|gnutella).*/, 26 | $server_header=/^x-(content-urn|features).*/], 27 | ["Gnutella__"] = [$url=/^\/(uri-res|gnutella).*/, 28 | $server_header=/^content-type/, 29 | $server_header_content=/.*x-gnutella.*/], 30 | ["BitTorrent"] = [$url=/^.*\/(scrape|announce)\?.*info_hash.*/], 31 | ["SOAP"] = [$client_header=/^([:print:]+-)?(soapaction|methodname|messagetype).*/], 32 | ["Squid"] = [$server_header=/^x-squid.*/], 33 | } &redef; 34 | } 35 | 36 | # Bit masks. 37 | const url_found = 1; 38 | const client_header_found = 2; 39 | const server_header_found = 2; 40 | 41 | type index : record { 42 | id: conn_id; 43 | pid: string; 44 | }; 45 | 46 | # Maps to characteristics found so far. 47 | # FIXME: An integer would suffice for the bit-field 48 | # if we had bit-operations ... 49 | global conns: table[index] of set[count] &read_expire = 1hrs; 50 | 51 | function check_match(c: connection, pid: string, mask: set[count]) 52 | { 53 | conns[[$id=c$id, $pid=pid]] = mask; 54 | 55 | local p = protocols[pid]; 56 | 57 | if ( p?$url && url_found !in mask ) 58 | return; 59 | 60 | if ( p?$client_header && client_header_found !in mask ) 61 | return; 62 | 63 | if ( p?$server_header && server_header_found !in mask ) 64 | return; 65 | 66 | # All found. 67 | 68 | ProtocolDetector::found_protocol(c, ANALYZER_HTTP, pid); 69 | } 70 | 71 | event http_request(c: connection, method: string, original_URI: string, 72 | unescaped_URI: string, version: string) 73 | { 74 | for ( pid in protocols ) 75 | { 76 | local p = protocols[pid]; 77 | 78 | if ( ! p?$url ) 79 | next; 80 | 81 | local mask: set[count]; 82 | local idx = [$id=c$id, $pid=pid]; 83 | if ( idx in conns ) 84 | mask = conns[idx]; 85 | 86 | if ( url_found in mask ) 87 | # Already found a match. 88 | next; 89 | 90 | # FIXME: There are people putting NULs into the URLs 91 | # (BitTorrent), which to_lower() does not like. Not sure 92 | # what the right fix is, though. 93 | unescaped_URI = subst_string(unescaped_URI, "\x00", ""); 94 | 95 | if ( to_lower(unescaped_URI) == p$url ) 96 | { 97 | add mask[url_found]; 98 | check_match(c, pid, mask); 99 | } 100 | } 101 | } 102 | 103 | event http_header(c: connection, is_orig: bool, name: string, value: string) 104 | { 105 | if ( name == /[sS][eE][rR][vV][eE][rR]/ ) 106 | { 107 | # Try to extract the server software. 108 | local s = split1(strip(value), /[[:space:]\/]/); 109 | if ( s[1] == /[-a-zA-Z0-9_]+/ ) 110 | ProtocolDetector::found_protocol(c, ANALYZER_HTTP, s[1]); 111 | } 112 | 113 | for ( pid in protocols ) 114 | { 115 | local p = protocols[pid]; 116 | 117 | local mask: set[count]; 118 | local idx = [$id=c$id, $pid=pid]; 119 | if ( idx in conns ) 120 | mask = conns[idx]; 121 | 122 | if ( p?$client_header && is_orig ) 123 | { 124 | if ( client_header_found in mask ) 125 | return; 126 | 127 | if ( to_lower(name) == p$client_header ) 128 | { 129 | if ( p?$client_header_content ) 130 | if ( to_lower(value) != 131 | p$client_header_content ) 132 | return; 133 | 134 | add mask[client_header_found]; 135 | check_match(c, pid, mask); 136 | } 137 | } 138 | 139 | if ( p?$server_header && ! is_orig ) 140 | { 141 | if ( server_header_found in mask ) 142 | return; 143 | 144 | if ( to_lower(name) == p$server_header ) 145 | { 146 | if ( p?$server_header_content ) 147 | if ( to_lower(value) != 148 | p$server_header_content ) 149 | return; 150 | 151 | add mask[server_header_found]; 152 | check_match(c, pid, mask); 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/mime-pop.bro: -------------------------------------------------------------------------------- 1 | # $Id: mime-pop.bro 4758 2007-08-10 06:49:23Z vern $ 2 | # 3 | # A stripped-down version of mime.bro adapted to work on POP3 events. 4 | # 5 | # FIXME: What's the best way to merge mime.bro and mime-pop3.bro? 6 | 7 | @load pop3 8 | 9 | module MIME_POP3; 10 | 11 | const mime_log = open_log_file("mime-pop") &redef; 12 | 13 | type mime_session_info: record { 14 | id: count; 15 | connection_id: conn_id; 16 | level: count; 17 | data_offset: count; 18 | }; 19 | 20 | global mime_session_id = 0; 21 | global mime_sessions: table[conn_id] of mime_session_info; 22 | 23 | function mime_session_string(session: mime_session_info): string 24 | { 25 | return fmt("#%s %s +%d", prefixed_id(session$id), 26 | id_string(session$connection_id), session$level); 27 | } 28 | 29 | function mime_log_warning(what: string) 30 | { 31 | print mime_log, fmt("%.6f warning: %s", network_time(), what); 32 | } 33 | 34 | function mime_log_msg(session: mime_session_info, where: string, what: string) 35 | { 36 | print mime_log, fmt("%.6f %s: [%s] %s", 37 | network_time(), 38 | mime_session_string(session), 39 | where, 40 | what); 41 | } 42 | 43 | function new_mime_session(c: connection) 44 | { 45 | local id = c$id; 46 | local session_id = ++mime_session_id; 47 | local info: mime_session_info; 48 | 49 | info$id = session_id; 50 | info$connection_id = id; 51 | info$level = 0; 52 | info$data_offset = 0; 53 | 54 | mime_sessions[id] = info; 55 | mime_log_msg(info, "start", ""); 56 | } 57 | 58 | function get_mime_session(c: connection, new_session_ok: bool): mime_session_info 59 | { 60 | local id = c$id; 61 | 62 | if ( id !in mime_sessions ) 63 | { 64 | if ( ! new_session_ok ) 65 | mime_log_warning(fmt("begin_entity missing for new MIME session %s", id_string(id))); 66 | 67 | new_mime_session(c); 68 | } 69 | 70 | return mime_sessions[id]; 71 | } 72 | 73 | function end_mime_session(session: mime_session_info) 74 | { 75 | mime_log_msg(session, "finish", ""); 76 | delete mime_sessions[session$connection_id]; 77 | } 78 | 79 | event connection_state_remove(c: connection) 80 | { 81 | if ( c$id$resp_p != 110/tcp ) 82 | return; 83 | 84 | local id = c$id; 85 | 86 | if ( id in mime_sessions ) 87 | { 88 | mime_log_msg(mime_sessions[id], "state remove", ""); 89 | delete mime_sessions[id]; 90 | } 91 | } 92 | 93 | function do_mime_begin_entity(c: connection) 94 | { 95 | local session = get_mime_session(c, T); 96 | 97 | ++session$level; 98 | session$data_offset = 0; 99 | mime_log_msg(session, "begin entity", ""); 100 | } 101 | 102 | event mime_begin_entity(c: connection) 103 | { 104 | if ( c$id$resp_p != 110/tcp ) 105 | return; 106 | 107 | do_mime_begin_entity(c); 108 | } 109 | 110 | function do_mime_end_entity(c: connection) 111 | { 112 | local session = get_mime_session(c, T); 113 | 114 | mime_log_msg(session, "end entity", ""); 115 | 116 | if ( session$level > 0 ) 117 | { 118 | --session$level; 119 | if ( session$level == 0 ) 120 | end_mime_session(session); 121 | } 122 | else 123 | mime_log_warning(fmt("unmatched end_entity for MIME session %s", 124 | mime_session_string(session))); 125 | } 126 | 127 | event mime_end_entity(c: connection) 128 | { 129 | if ( c$id$resp_p != 110/tcp ) 130 | return; 131 | 132 | do_mime_end_entity(c); 133 | } 134 | 135 | event mime_next_entity(c: connection) 136 | { 137 | if ( c$id$resp_p != 110/tcp ) 138 | return; 139 | 140 | do_mime_end_entity(c); 141 | do_mime_begin_entity(c); 142 | } 143 | 144 | event mime_all_headers(c: connection, hlist: mime_header_list) 145 | { 146 | if ( c$id$resp_p != 110/tcp ) 147 | return; 148 | 149 | local session = get_mime_session(c, T); 150 | local i = 0; 151 | 152 | for ( i in hlist ) 153 | { 154 | local h = hlist[i]; 155 | mime_log_msg(session, "header", 156 | fmt("%s: \"%s\"", h$name, h$value)); 157 | } 158 | } 159 | 160 | event mime_segment_data(c: connection, length: count, data: string) 161 | { 162 | if ( c$id$resp_p != 110/tcp ) 163 | return; 164 | 165 | local session = get_mime_session(c, T); 166 | 167 | if ( session$data_offset < 256 ) 168 | mime_log_msg(session, "data", fmt("%d: %s", length, data)); 169 | 170 | session$data_offset = session$data_offset + length; 171 | } 172 | 173 | event mime_event(c: connection, event_type: string, detail: string) 174 | { 175 | if ( c$id$resp_p != 110/tcp ) 176 | return; 177 | 178 | local session = get_mime_session(c, T); 179 | mime_log_msg(session, "event", fmt("%s: %s", event_type, detail)); 180 | } 181 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/pop3.bro: -------------------------------------------------------------------------------- 1 | # $Id: pop3.bro 4758 2007-08-10 06:49:23Z vern $ 2 | # 3 | # Analyzer for Post Office Protocol, version 3. 4 | # 5 | # If you want to decode the mail itself, also load mime-pop.bro. 6 | 7 | @load login 8 | 9 | module POP3; 10 | 11 | export { 12 | # Report if source triggers more ERR messages. 13 | const error_threshold: count = 3 &redef; 14 | # Don't log these commands. 15 | const ignore_commands: set[string] = { "STAT" } &redef; 16 | } 17 | 18 | redef capture_filters += { ["pop3"] = "port 110" }; 19 | 20 | global pop3_ports = { 110/tcp } &redef; 21 | redef dpd_config += { [ANALYZER_POP3] = [$ports = pop3_ports] }; 22 | 23 | const log_file = open_log_file("pop3") &redef; 24 | 25 | type pop3_session_info: record { 26 | id: count; # Unique session ID. 27 | quit_sent: bool; # Client issued a QUIT. 28 | last_command: string; # Last command of client. 29 | }; 30 | 31 | 32 | global pop_log: function(conn: pop3_session_info, 33 | command: string, message: string); 34 | global get_connection: function(id: conn_id): pop3_session_info; 35 | 36 | 37 | global pop_connections: 38 | table[conn_id] of pop3_session_info &read_expire = 60 mins; 39 | global pop_connection_weirds: 40 | table[addr] of count &default=0 &read_expire = 60 mins; 41 | global pop_session_id = 0; 42 | 43 | 44 | event pop3_request(c: connection, is_orig: bool, command: string, arg: string) 45 | { 46 | local conn = get_connection(c$id); 47 | 48 | pop_log(conn, command, fmt("%.6f #%s > %s %s", 49 | network_time(), prefixed_id(conn$id), command, arg)); 50 | 51 | conn$last_command = command; 52 | 53 | if ( command == "QUIT" ) 54 | conn$quit_sent = T; 55 | } 56 | 57 | event pop3_reply(c: connection, is_orig: bool, cmd: string, msg: string) 58 | { 59 | local conn = get_connection(c$id); 60 | 61 | pop_log(conn, cmd, 62 | fmt("%.6f #%s < %s %s", network_time(), prefixed_id(conn$id), cmd, msg)); 63 | 64 | if ( cmd == "OK" ) 65 | { 66 | if ( conn$quit_sent ) 67 | delete pop_connections[c$id]; 68 | } 69 | 70 | else if ( cmd == "ERR" ) 71 | { 72 | ++pop_connection_weirds[c$id$orig_h]; 73 | if ( pop_connection_weirds[c$id$orig_h] > error_threshold ) 74 | print log_file, fmt("%.6f #%s %s/%d > %s/%d WARNING: error count exceeds threshold", 75 | network_time(), prefixed_id(conn$id), 76 | c$id$orig_h, c$id$orig_p, 77 | c$id$resp_h, c$id$resp_p); 78 | } 79 | } 80 | 81 | event pop3_login_success(c: connection, is_orig: bool, 82 | user: string, password: string) 83 | { 84 | local conn = get_connection(c$id); 85 | 86 | local pw = byte_len(password) != 0 ? password : ""; 87 | 88 | print log_file, fmt("%.6f #%s > login successful: user %s password: %s", 89 | network_time(), prefixed_id(conn$id), user, pw); 90 | 91 | event login_success(c, user, "", password, ""); 92 | } 93 | 94 | event pop3_login_failure(c: connection, is_orig: bool, 95 | user: string, password: string) 96 | { 97 | local conn = get_connection(c$id); 98 | 99 | print log_file, fmt("%.6f #%s > login failed: user %s password: %s", 100 | network_time(), prefixed_id(conn$id), user, password); 101 | 102 | event login_failure(c, user, "", password, ""); 103 | } 104 | 105 | # event pop3_data(c: connection, is_orig: bool, data: string) 106 | # { 107 | # # We could instantiate partial connections here if we wished, 108 | # # but at considerable cost in terms of event counts. 109 | # local conn = get_connection(c$id); 110 | # } 111 | 112 | event pop3_unexpected(c: connection, is_orig: bool, msg: string, detail: string) 113 | { 114 | local conn = get_connection(c$id); 115 | print log_file, fmt("%.6f #%s unexpected cmd: %s detail: %s", 116 | network_time(), prefixed_id(conn$id), 117 | msg, detail); 118 | } 119 | 120 | event pop3_terminate(c: connection, is_orig: bool, msg: string) 121 | { 122 | delete pop_connections[c$id]; 123 | } 124 | 125 | 126 | function pop_log(conn: pop3_session_info, command: string, message: string) 127 | { 128 | if ( command !in ignore_commands ) 129 | { 130 | if ( (command == "OK" || command == "ERR") && 131 | conn$last_command in ignore_commands ) 132 | ; 133 | else 134 | print log_file, message; 135 | } 136 | } 137 | 138 | function get_connection(id: conn_id): pop3_session_info 139 | { 140 | if ( id in pop_connections ) 141 | return pop_connections[id]; 142 | 143 | local conn: pop3_session_info; 144 | 145 | conn$id = ++pop_session_id; 146 | conn$quit_sent = F; 147 | conn$last_command = "INIT"; 148 | pop_connections[id] = conn; 149 | 150 | print log_file, fmt("%.6f #%s %s/%d > %s/%d: new connection", 151 | network_time(), prefixed_id(conn$id), 152 | id$orig_h, id$orig_p, id$resp_h, id$resp_p); 153 | 154 | return conn; 155 | } 156 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/hot.bro: -------------------------------------------------------------------------------- 1 | # $Id: hot.bro 7057 2010-07-19 23:22:19Z vern $ 2 | 3 | @load site 4 | @load port-name 5 | @load notice 6 | @load terminate-connection 7 | 8 | module Hot; 9 | 10 | export { 11 | # True if it should be considered a spoofing attack if a connection has 12 | # the same local net for source and destination. 13 | const same_local_net_is_spoof = F &redef; 14 | 15 | const allow_spoof_services = { 16 | 110/tcp, # pop-3 17 | 139/tcp, # netbios-ssn 18 | } &redef; 19 | 20 | # Indexed by source address and destination address. 21 | const allow_pairs: set[addr, addr] &redef; 22 | 23 | const hot_srcs: table[addr] of string = { 24 | # [ph33r.the.eleet.com] = "kidz", 25 | } &redef; 26 | 27 | const hot_dsts: table[addr] of string = { 28 | [206.101.197.226] = "ILOVEYOU worm destination", 29 | } &redef; 30 | 31 | const allow_services = { 32 | ssh, http, gopher, ident, smtp, 20/tcp, 33 | 53/udp, # DNS queries 34 | 123/udp, # NTP 35 | } &redef; 36 | 37 | const allow_services_to: set[addr, port] &redef; 38 | const allow_services_from: set[addr, port] &redef; 39 | const allow_service_pairs: set[addr, addr, port] &redef; 40 | 41 | const flag_successful_service: table[port] of string = { 42 | [[31337/tcp]] = "popular backdoors", 43 | } &redef; 44 | 45 | const flag_successful_inbound_service: table[port] of string = { 46 | [1524/tcp] = "popular backdoor, but with false hits outbound", 47 | } &redef; 48 | 49 | const terminate_successful_inbound_service: table[port] of string &redef; 50 | 51 | const flag_rejected_service: table[port] of string &redef; 52 | 53 | # Different values to hand to check_hot() at different stages in 54 | # a connection's lifetime. 55 | const CONN_ATTEMPTED = 1; 56 | const CONN_ESTABLISHED = 2; 57 | const APPL_ESTABLISHED = 3; 58 | const CONN_FINISHED = 4; 59 | const CONN_REJECTED = 5; 60 | const CONN_TIMEOUT = 6; 61 | const CONN_REUSED = 7; 62 | 63 | global check_hot: function(c: connection, state: count): bool; 64 | global check_spoof: function(c: connection): bool; 65 | } 66 | 67 | # An internal function used by check_hot. 68 | function do_hot_check(c: connection, a: addr, t: table[addr] of string) 69 | { 70 | if ( a in t ) 71 | { 72 | ++c$hot; 73 | local hot_msg = fmt("<%s>", t[a]); 74 | append_addl(c, hot_msg); 75 | } 76 | } 77 | 78 | function check_spoof(c: connection): bool 79 | { 80 | local orig = c$id$orig_h; 81 | local resp = c$id$resp_h; 82 | local service = c$id$resp_p; 83 | 84 | if ( is_local_addr(orig) && is_local_addr(resp) && 85 | service !in allow_spoof_services ) 86 | { 87 | if ( c$id$orig_p == service && orig == resp ) 88 | event conn_weird("Land_attack", c); 89 | 90 | if ( same_local_net_is_spoof ) 91 | ++c$hot; 92 | } 93 | 94 | return c$hot != 0; 95 | } 96 | 97 | function check_hot(c: connection, state: count): bool 98 | { 99 | local id = c$id; 100 | local service = id$resp_p; 101 | 102 | if ( service in allow_services || "ftp-data" in c$service ) 103 | return F; 104 | 105 | if ( state == CONN_ATTEMPTED ) 106 | check_spoof(c); 107 | 108 | else if ( state == CONN_REJECTED ) 109 | { 110 | check_spoof(c); 111 | 112 | if ( service in flag_rejected_service ) 113 | ++c$hot; 114 | } 115 | 116 | else if ( state == CONN_ESTABLISHED ) 117 | { 118 | check_spoof(c); 119 | 120 | local inbound = is_local_addr(id$resp_h); 121 | 122 | if ( (service in flag_successful_service || 123 | (inbound && 124 | service in flag_successful_inbound_service)) && 125 | ([id$resp_h, id$resp_p] !in allow_services_to || 126 | [id$orig_h, id$resp_p] !in allow_services_from) ) 127 | { 128 | if ( inbound && 129 | service in terminate_successful_inbound_service ) 130 | TerminateConnection::terminate_connection(c); 131 | 132 | ++c$hot; 133 | if ( service in flag_successful_service ) 134 | append_addl(c, flag_successful_service[service]); 135 | else 136 | append_addl(c, flag_successful_inbound_service[service]); 137 | } 138 | } 139 | 140 | else if ( state == APPL_ESTABLISHED || 141 | ((state == CONN_FINISHED || state == CONN_TIMEOUT || 142 | state == CONN_REUSED) && 143 | service != telnet && c$orig$size > 0 && c$resp$size > 0) ) 144 | { 145 | # Connection established and has a non-trivial size. 146 | local orig = c$id$orig_h; 147 | local resp = c$id$resp_h; 148 | 149 | if ( [resp, service] in allow_services_to || 150 | [orig, service] in allow_services_from || 151 | [orig, resp, service] in allow_service_pairs || 152 | [orig, resp] in allow_pairs ) 153 | return F; 154 | 155 | do_hot_check(c, resp, hot_srcs); 156 | do_hot_check(c, resp, hot_dsts); 157 | } 158 | 159 | return c$hot != 0; 160 | } 161 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/http-rps-summary.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | @load http 4 | @load app-summary 5 | 6 | redef capture_filters = { 7 | ["http"] = "tcp port 80 or tcp port 8080 or tcp port 8000 or tcp port 8888 or tcp port 3128", 8 | }; 9 | 10 | module HTTP_req_per_session; 11 | 12 | export { 13 | global log = open_log_file("http-rps-summary") &redef; 14 | const http_session_idle_timeout = 1 sec &redef; 15 | } 16 | 17 | type http_session: record { 18 | # standard stuff 19 | connection_id: conn_id; # of the first conn 20 | conn_start: time; 21 | func: string; 22 | start: time; 23 | num_req: count; 24 | req_size: count; 25 | num_resp: count; 26 | resp_size: count; 27 | 28 | # for timeout 29 | unfinished_req: count; 30 | unfinished_resp: count; 31 | last_time: time; 32 | }; 33 | 34 | global expire_http_session: function( 35 | tbl: table[addr] of http_session, index: addr): interval; 36 | 37 | global http_ssn_table: table[addr] of http_session 38 | &read_expire = http_session_idle_timeout 39 | &expire_func = expire_http_session; 40 | 41 | function new_http_session(c: connection): http_session 42 | { 43 | local t = [ 44 | $connection_id = c$id, 45 | $conn_start = c$start_time, 46 | $func = "unknown", 47 | $start = network_time(), 48 | $num_req = 0, $req_size = 0, 49 | $num_resp = 0, $resp_size = 0, 50 | $unfinished_req = 0, $unfinished_resp = 0, 51 | $last_time = network_time()]; 52 | 53 | return t; 54 | } 55 | 56 | function lookup_http_session(c: connection, is_orig: bool): http_session 57 | { 58 | local id = c$id; 59 | local index = id$orig_h; 60 | 61 | if ( index !in http_ssn_table ) 62 | { 63 | if ( ! is_orig ) 64 | print fmt("%.6f HTTP session not found for a resposne", 65 | network_time(), conn_id_string(id)); 66 | 67 | http_ssn_table[index] = new_http_session(c); 68 | } 69 | 70 | return http_ssn_table[index]; 71 | } 72 | 73 | function end_http_session(t: http_session) 74 | { 75 | print_app_summary(log, t$connection_id, t$conn_start, t$func, t$start, 76 | t$num_req, t$req_size, 77 | t$num_resp, t$resp_size, 78 | fmt("duration %.6f", t$last_time - t$start)); 79 | } 80 | 81 | function check_expiration(t: http_session): bool 82 | { 83 | print fmt("%.6f check expiration http_session %s: %.6f %d,%d %d,%d", 84 | network_time(), conn_id_string(t$connection_id), 85 | t$last_time, 86 | t$num_req, t$num_resp, 87 | t$unfinished_req, t$unfinished_resp); 88 | 89 | if ( network_time() - t$last_time < http_session_idle_timeout 90 | || ( t$unfinished_req + t$unfinished_resp > 0 && 91 | network_time() - t$last_time < 15 min && 92 | ! done_with_network ) ) 93 | { 94 | print fmt("do not expire"); 95 | return F; 96 | } 97 | 98 | end_http_session(t); 99 | return T; 100 | } 101 | 102 | function expire_http_session(tbl: table[addr] of http_session, 103 | index: addr): interval 104 | { 105 | local t = tbl[index]; 106 | if ( ! check_expiration(t) ) 107 | { 108 | print fmt("... no, wait one more second: %d, %d", 109 | t$unfinished_req, t$unfinished_resp); 110 | return 1 sec; 111 | } 112 | return 0 sec; 113 | } 114 | 115 | event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) 116 | { 117 | local t = lookup_http_session(c, T); 118 | if ( check_expiration(t) ) 119 | { 120 | delete http_ssn_table[c$id$orig_h]; 121 | t = lookup_http_session(c, T); 122 | } 123 | t$func = method; 124 | ++t$num_req; 125 | ++t$unfinished_req; 126 | t$last_time = network_time(); 127 | } 128 | 129 | event http_reply(c: connection, version: string, code: count, reason: string) 130 | { 131 | # print fmt("http reply"); 132 | local t = lookup_http_session(c, F); 133 | ++t$num_resp; 134 | ++t$unfinished_resp; 135 | t$last_time = network_time(); 136 | } 137 | 138 | function http_request_done(c: connection, stat: http_message_stat) 139 | { 140 | # print fmt("http request done"); 141 | local t = lookup_http_session(c, T); 142 | t$req_size = t$req_size + stat$body_length; 143 | if ( t$unfinished_req > 0 ) 144 | --t$unfinished_req; 145 | t$last_time = network_time(); 146 | } 147 | 148 | function http_reply_done(c: connection, stat: http_message_stat) 149 | { 150 | local t = lookup_http_session(c, F); 151 | t$resp_size = t$resp_size + stat$body_length; 152 | if ( t$unfinished_resp > 0 ) 153 | --t$unfinished_resp; 154 | t$last_time = network_time(); 155 | } 156 | 157 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 158 | { 159 | if ( is_orig ) 160 | http_request_done(c, stat); 161 | else 162 | http_reply_done(c, stat); 163 | } 164 | 165 | event bro_done() 166 | { 167 | for ( index in http_ssn_table ) 168 | { 169 | end_http_session(http_ssn_table[index]); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/software.bro: -------------------------------------------------------------------------------- 1 | # $Id: software.bro 4907 2007-09-23 23:44:07Z vern $ 2 | # 3 | # Keeps track of the software running on hosts. 4 | 5 | @load site 6 | @load weird 7 | 8 | # Operational use of software.bro on a busy network can result in huge 9 | # data files. By default creating a log file is turned off. 10 | global log_software = F &redef; 11 | 12 | # If true, then logging is confined to just software versions of local hosts. 13 | global only_report_local = T &redef; 14 | 15 | global software_file = open_log_file("software"); 16 | 17 | # Invoked whenever we discover new software for a host (but not for a new 18 | # version of previously found software). 19 | global software_new: event(c: connection, host: addr, s: software, 20 | descr: string); 21 | 22 | # Invoked whenever we discover a new version of previously discovered 23 | # software. 24 | global software_version_change: event(c: connection, host: addr, s: software, 25 | old_version: software_version, descr: string); 26 | 27 | # Index is the name of the software. 28 | type software_set: table[string] of software; 29 | 30 | # You may or may not want to define a timeout for this. 31 | global software_table: table[addr] of software_set; 32 | 33 | # Some software can be installed twice on the same server 34 | # with different major numbers. 35 | global software_ident_by_major: set[string] = { 36 | "PHP", 37 | "WebSTAR", 38 | } &redef; 39 | 40 | # Compare two versions. 41 | # Returns -1 for v1 < v2, 0 for v1 == v2, 1 for v1 > v2. 42 | # If the numerical version numbers match, the addl string 43 | # is compared lexicographically. 44 | function software_cmp_version(v1: software_version, v2: software_version): int 45 | { 46 | if ( v1$major < v2$major ) 47 | return -1; 48 | if ( v1$major > v2$major ) 49 | return 1; 50 | 51 | if ( v1$minor < v2$minor ) 52 | return -1; 53 | if ( v1$minor > v2$minor ) 54 | return 1; 55 | 56 | if ( v1$minor2 < v2$minor2 ) 57 | return -1; 58 | if ( v1$minor2 > v2$minor2 ) 59 | return 1; 60 | 61 | return strcmp(v1$addl, v2$addl); 62 | } 63 | 64 | # Convert a version into a string "a.b.c-x". 65 | function software_fmt_version(v: software_version): string 66 | { 67 | return fmt("%s%s%s%s", 68 | v$major >= 0 ? fmt("%d", v$major) : "", 69 | v$minor >= 0 ? fmt(".%d", v$minor) : "", 70 | v$minor2 >= 0 ? fmt(".%d", v$minor2) : "", 71 | v$addl != "" ? fmt("-%s", v$addl) : ""); 72 | } 73 | 74 | # Convert a software into a string "name a.b.cx". 75 | function software_fmt(s: software): string 76 | { 77 | return fmt("%s %s", s$name, software_fmt_version(s$version)); 78 | } 79 | 80 | # Insert a mapping into the table 81 | # Overides old entries for the same software and generates events if needed. 82 | ### FIXME: Do we need a software_unregister() as well? 83 | function software_register(c: connection, host: addr, s: software, 84 | descr: string) 85 | { 86 | # Host already known? 87 | if ( host !in software_table ) 88 | software_table[host] = set(); 89 | 90 | # If a software can be installed more than once on a host 91 | # (with a different major version), we identify it by "-" 92 | if ( s$name in software_ident_by_major && s$version$major >= 0 ) 93 | s$name = fmt("%s-%d", s$name, s$version$major); 94 | 95 | local soft_set = software_table[host]; 96 | 97 | # Software already registered for this host? 98 | if ( s$name in soft_set ) 99 | { 100 | # Is it a different version? 101 | local old = soft_set[s$name]; 102 | if ( software_cmp_version(old$version, s$version) != 0 ) 103 | event software_version_change(c, host, s, old$version, 104 | descr); 105 | } 106 | else 107 | event software_new(c, host, s, descr); 108 | 109 | soft_set[s$name] = s; 110 | } 111 | 112 | event software_version_found(c: connection, host: addr, s: software, 113 | descr: string) 114 | { 115 | if ( ! only_report_local || is_local_addr(host) ) 116 | software_register(c, host, s, descr); 117 | } 118 | 119 | function software_endpoint_name(c: connection, host: addr): string 120 | { 121 | return fmt("%s %s", host, 122 | host == c$id$orig_h ? "client" : "server"); 123 | } 124 | 125 | event software_parse_error(c: connection, host: addr, descr: string) 126 | { 127 | if ( ! only_report_local || is_local_addr(host) ) 128 | { 129 | # Here we need a little hack, since software_file is 130 | # not always there. 131 | local msg = fmt("%.6f %s: can't parse '%s'", network_time(), 132 | software_endpoint_name(c, host), descr); 133 | 134 | if ( log_software ) 135 | print software_file, msg; 136 | else 137 | print Weird::weird_file, msg; 138 | } 139 | } 140 | 141 | event software_new(c: connection, host: addr, s: software, descr: string) 142 | { 143 | if ( log_software ) 144 | { 145 | print software_file, fmt("%.6f %s uses %s (%s)", network_time(), 146 | software_endpoint_name(c, host), 147 | software_fmt(s), descr); 148 | } 149 | } 150 | 151 | event software_version_change(c: connection, host: addr, s: software, 152 | old_version: software_version, descr: string) 153 | { 154 | if ( log_software ) 155 | { 156 | local msg = fmt("%.6f %s switched from %s to %s (%s)", 157 | network_time(), software_endpoint_name(c, host), 158 | software_fmt_version(old_version), 159 | software_fmt(s), descr); 160 | 161 | print software_file, msg; 162 | } 163 | } 164 | 165 | event software_unparsed_version_found(c: connection, host: addr, str: string) 166 | { 167 | if ( log_software ) 168 | { 169 | print software_file, fmt("%.6f %s: [%s]", network_time(), 170 | software_endpoint_name(c, host), str); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/anon.bro: -------------------------------------------------------------------------------- 1 | # $Id: anon.bro 6889 2009-08-21 16:45:17Z vern $ 2 | 3 | redef anonymize_ip_addr = T; 4 | 5 | const orig_addr_anonymization = RANDOM_MD5 &redef; 6 | const resp_addr_anonymization = RANDOM_MD5 &redef; 7 | const other_addr_anonymization = SEQUENTIALLY_NUMBERED &redef; 8 | 9 | const preserve_orig_addr: set[addr] = {} &redef; 10 | const preserve_resp_addr: set[addr] = {} &redef; 11 | const preserve_other_addr: set[addr] = { 12 | 0.0.0.0, 13 | } &redef; 14 | 15 | const preserved_subnet: set[subnet] = { 16 | # 192.150.186/23, 17 | } &redef; 18 | 19 | const preserved_net: set[net] = { 20 | # 192.150.186, 192.150.187, 21 | } &redef; 22 | 23 | global anon_log = open_log_file("anon") &redef; 24 | 25 | global anonymized_args: table[string] of string; 26 | 27 | global ip_anon_mapping: set[addr, addr]; 28 | 29 | event bro_init() 30 | { 31 | for ( n in preserved_net ) 32 | preserve_net(n); 33 | } 34 | 35 | function anonymize_address(a: addr, id: conn_id): addr 36 | { 37 | if ( a == id$orig_h ) 38 | return anonymize_addr(a, ORIG_ADDR); 39 | else if ( a == id$resp_h ) 40 | return anonymize_addr(a, RESP_ADDR); 41 | else 42 | return anonymize_addr(a, OTHER_ADDR); 43 | } 44 | 45 | event anonymization_mapping(orig: addr, mapped: addr) 46 | { 47 | if ( [orig, mapped] !in ip_anon_mapping ) 48 | { 49 | add ip_anon_mapping[orig, mapped]; 50 | print anon_log, fmt("%s -> %s", orig, mapped); 51 | } 52 | } 53 | 54 | function string_anonymized(from: string, to: string, seed: count) 55 | { 56 | print anon_log, fmt("\"%s\" %d=> \"%s\"", from, seed, to); 57 | } 58 | 59 | global num_string_id: count = 0 &redef; 60 | global anonymized_strings: table[string] of record { 61 | s: string; 62 | c: count; 63 | } &redef; 64 | 65 | # Hopefully, the total number of strings to anonymize is much less than 66 | # 36^unique_string_length. 67 | const unique_string_length = 8 &redef; 68 | # const anonymized_string_pattern = /U[0-9a-f]+U/; 69 | global unique_string_set: set[string]; 70 | 71 | event bro_init() 72 | { 73 | for ( s in anonymized_strings ) 74 | add unique_string_set[anonymized_strings[s]$s]; 75 | } 76 | 77 | function unique_string(s: string, seed: count): string 78 | { 79 | local t = cat("U", sub_bytes(md5_hmac(seed, s), 80 | 1, unique_string_length), "U"); 81 | if ( t in unique_string_set ) 82 | return unique_string(s, seed+1); 83 | 84 | anonymized_strings[s] = [$s = t, $c = 1]; 85 | add unique_string_set[t]; 86 | string_anonymized(s, t, seed); 87 | 88 | return t; 89 | } 90 | 91 | function anonymize_string(from: string): string 92 | { 93 | if ( from in anonymized_strings ) 94 | { 95 | ++anonymized_strings[from]$c; 96 | return anonymized_strings[from]$s; 97 | } 98 | 99 | local t = unique_string(from, 0); 100 | return t; 101 | } 102 | 103 | function anonymize_arg(typ: string, arg: string): string 104 | { 105 | if ( arg == "" ) 106 | return ""; # an empty argument is safe 107 | 108 | local arg_seed = string_cat(typ, arg); 109 | 110 | if ( arg_seed in anonymized_args ) 111 | return anonymized_args[arg_seed]; 112 | 113 | local a = anonymize_string(arg_seed); 114 | anonymized_args[arg_seed] = a; 115 | 116 | print anon_log, fmt("anonymize_arg: (%s) {%s} -> %s ", 117 | typ, to_string_literal(arg), to_string_literal(a)); 118 | return a; 119 | } 120 | 121 | 122 | # Does not contain ? and ends with an allowed suffix. 123 | const path_to_file_pat = 124 | /\/[^?]+\.(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)/ 125 | ; 126 | 127 | # Acceptable domain names. 128 | const kosher_dom_pat = 129 | /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/ 130 | ; 131 | 132 | # Simple filename pattern. 133 | const simple_filename = 134 | /[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)/ 135 | ; 136 | 137 | function anonymize_path(path: string): string 138 | { 139 | local hashed_path = ""; 140 | 141 | if ( to_lower(path) != path_to_file_pat ) 142 | { 143 | hashed_path = anonymize_arg("path", path); 144 | return hashed_path; 145 | } 146 | 147 | local file_parts = split(path, /\./); 148 | 149 | local i = 1; 150 | for ( part in file_parts ) 151 | { 152 | # This looks broken to me - VP. 153 | hashed_path = fmt("%s.%s", hashed_path, file_parts[i]); 154 | if ( ++i == length(file_parts) ) 155 | break; 156 | } 157 | 158 | return fmt("%s.%s", anonymize_arg("path", hashed_path), file_parts[i]); 159 | } 160 | 161 | function anonymize_host(host: string): string 162 | { 163 | local hashed_host = ""; 164 | local host_parts = split(host, /\./); 165 | 166 | local i = 1; 167 | for ( hosty in host_parts ) 168 | { 169 | if ( i == length(host_parts) ) 170 | break; 171 | 172 | # Check against "kosher" tld list. 173 | hashed_host = fmt("%s%s.", hashed_host, 174 | anonymize_arg("host", host_parts[i])); 175 | 176 | ++i; 177 | } 178 | 179 | if ( host_parts[i] == kosher_dom_pat ) 180 | return string_cat(hashed_host, host_parts[i]); 181 | 182 | print anon_log, fmt("anonymize_host: non-kosher domain %s", host); 183 | return string_cat(hashed_host, anonymize_arg("host", host_parts[i])); 184 | } 185 | 186 | event bro_done() 187 | { 188 | for ( s in anonymized_strings ) 189 | { 190 | print anon_log, fmt("appearance: %d: \"%s\" => \"%s\"", 191 | anonymized_strings[s]$c, s, anonymized_strings[s]$s); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/bt-tracker.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | # 3 | # bt-tracker.bro - analysis of BitTorrent tracker traffic 4 | # ------------------------------------------------------------------------------ 5 | # This code contributed by Nadi Sarrar. 6 | 7 | @load dpd 8 | @load weird 9 | 10 | module BitTorrent; 11 | 12 | export { 13 | # Whether to log tracker URIs. 14 | global log_tracker_request_uri = F &redef; 15 | } 16 | 17 | redef capture_filters += { ["bittorrent"] = "tcp", }; 18 | 19 | global bt_tracker_log = open_log_file("bt-tracker") &redef; 20 | 21 | global bt_tracker_conns: table[conn_id] of count; 22 | global tracker_conn_count: count = 0; 23 | 24 | 25 | function bt_log_tag(id: conn_id, cid: count, tag: string, is_orig: bool): string 26 | { 27 | return fmt("%.6f T%d %s %s:%d %s %s:%d", 28 | network_time(), cid, tag, id$orig_h, id$orig_p, 29 | is_orig ? ">" : "<", id$resp_h, id$resp_p); 30 | } 31 | 32 | event bt_tracker_request(c: connection, uri: string, 33 | headers: bt_tracker_headers) 34 | { 35 | # Parse and validate URI. 36 | local pair = split1(uri, /\?/); 37 | local keys = split(pair[2], /&/); 38 | 39 | local info_hash = ""; 40 | local peer_ide = ""; 41 | local peer_port = 0/udp; 42 | local uploaded = -1; 43 | local downloaded = -1; 44 | local left = -1; 45 | local compact = T; 46 | local peer_event = "empty"; 47 | 48 | for ( idx in keys ) 49 | { 50 | local keyval = split1(keys[idx], /=/); 51 | if ( length(keyval) != 2 ) 52 | next; 53 | 54 | local key = to_lower(keyval[1]); 55 | local val = keyval[2]; 56 | 57 | if ( key == "info_hash" ) 58 | info_hash = unescape_URI(val); 59 | else if ( key == "peer_id" ) 60 | peer_ide = unescape_URI(val); 61 | else if ( key == "port" ) 62 | peer_port = to_port(to_count(val), tcp); 63 | else if ( key == "uploaded" ) 64 | uploaded = to_int(val); 65 | else if ( key == "downloaded" ) 66 | downloaded = to_int(val); 67 | else if ( key == "left" ) 68 | left = to_int(val); 69 | else if ( key == "compact" ) 70 | compact = (to_int(val) == 1); 71 | 72 | else if ( key == "event" ) 73 | { 74 | val = to_lower(val); 75 | if ( val == /started|stopped|completed/ ) 76 | peer_event = val; 77 | } 78 | } 79 | 80 | if ( info_hash == "" || peer_ide == "" || peer_port == 0/udp ) 81 | { # Does not look like BitTorrent. 82 | disable_analyzer(c$id, current_analyzer()); 83 | delete bt_tracker_conns[c$id]; 84 | return; 85 | } 86 | 87 | if ( peer_port != 0/tcp ) 88 | expect_connection(to_addr("0.0.0.0"), c$id$orig_h, 89 | peer_port, ANALYZER_BITTORRENT, 1 min); 90 | 91 | local id: count; 92 | if ( c$id in bt_tracker_conns ) 93 | id = bt_tracker_conns[c$id]; 94 | else 95 | { 96 | id = ++tracker_conn_count; 97 | bt_tracker_conns[c$id] = id; 98 | } 99 | 100 | print bt_tracker_log, 101 | fmt("%s [peer_id:%s info_hash:%s port:%s event:%s up:%d down:%d left:%d compact:%s]%s", 102 | bt_log_tag(c$id, id, "request", T), 103 | bytestring_to_hexstr(peer_ide), 104 | bytestring_to_hexstr(info_hash), 105 | peer_port, peer_event, 106 | uploaded, downloaded, left, 107 | compact ? "yes" : "no", 108 | log_tracker_request_uri ? fmt(" GET %s", uri) : ""); 109 | } 110 | 111 | function benc_status(benc: bittorrent_benc_dir, tag: string): string 112 | { 113 | if ( tag !in benc || ! benc[tag]?$i ) 114 | return ""; 115 | 116 | local fmt_tag = sub(tag, / /, "_"); 117 | return fmt("%s:%d", fmt_tag, benc[tag]$i); 118 | } 119 | 120 | event bt_tracker_response(c: connection, status: count, 121 | headers: bt_tracker_headers, 122 | peers: bittorrent_peer_set, 123 | benc: bittorrent_benc_dir) 124 | { 125 | if ( c$id !in bt_tracker_conns ) 126 | return; 127 | 128 | local id = bt_tracker_conns[c$id]; 129 | 130 | for ( peer in peers ) 131 | expect_connection(c$id$orig_h, peer$h, peer$p, 132 | ANALYZER_BITTORRENT, 1 min); 133 | 134 | if ( "failure reason" in benc ) 135 | { 136 | print bt_tracker_log, 137 | fmt("%s [failure_reason:\"%s\"]", 138 | bt_log_tag(c$id, id, "response", F), 139 | benc["failure reason"]?$s ? 140 | benc["failure reason"]$s : ""); 141 | return; 142 | } 143 | 144 | print bt_tracker_log, 145 | fmt("%s [%s%s%s%s%speers:%d]", 146 | bt_log_tag(c$id, id, "response", F), 147 | benc_status(benc, "warning message"), 148 | benc_status(benc, "complete"), 149 | benc_status(benc, "incomplete"), 150 | benc_status(benc, "interval"), 151 | benc_status(benc, "min interval"), 152 | length(peers)); 153 | } 154 | 155 | event bt_tracker_response_not_ok(c: connection, status: count, 156 | headers: bt_tracker_headers) 157 | { 158 | if ( c$id in bt_tracker_conns ) 159 | { 160 | local id = bt_tracker_conns[c$id]; 161 | print bt_tracker_log, 162 | fmt("%s [status:%d]", 163 | bt_log_tag(c$id, id, "response", F), status); 164 | } 165 | } 166 | 167 | event bt_tracker_weird(c: connection, is_orig: bool, msg: string) 168 | { 169 | local id = (c$id in bt_tracker_conns) ? bt_tracker_conns[c$id] : 0; 170 | print bt_tracker_log, 171 | fmt("%s [%s]", bt_log_tag(c$id, id, "", is_orig), msg); 172 | 173 | event conn_weird(msg, c); 174 | } 175 | 176 | event connection_state_remove(c: connection) 177 | { 178 | if ( c$id !in bt_tracker_conns ) 179 | return; 180 | 181 | local id = bt_tracker_conns[c$id]; 182 | delete bt_tracker_conns[c$id]; 183 | 184 | print bt_tracker_log, 185 | fmt("%s [duration:%.06f total:%d]", 186 | # Ideally the direction here wouldn't be T or F 187 | # but both, displayed as "<>". 188 | bt_log_tag(c$id, id, "", T), c$duration, 189 | c$orig$size + c$resp$size); 190 | } 191 | -------------------------------------------------------------------------------- /scripts/todo/possible/arp.bro: -------------------------------------------------------------------------------- 1 | # $Id: arp.bro 4909 2007-09-24 02:26:36Z vern $ 2 | 3 | @load notice 4 | 5 | module ARP; 6 | 7 | export { 8 | redef enum Notice += { 9 | ARPSourceMAC_Mismatch, # source MAC doesn't match mappings 10 | ARPAddlMAC_Mapping, # another MAC->addr seen beyond just one 11 | ARPUnsolicitedReply, # could be poisoning; or just gratuitous 12 | # ARPRequestProvidesTargetAddr, # request includes non-triv addr 13 | 14 | # MAC/addr pair seen in request/reply different from 15 | # that in the cache. 16 | ARPCacheInconsistency, 17 | 18 | # ARP reply gives different value than previously seen. 19 | ARPMappingChanged, 20 | }; 21 | 22 | const arp_log = open_log_file("arp") &redef; 23 | } 24 | 25 | redef capture_filters += { ["arp"] = "arp" }; 26 | 27 | # Abbreviations taken from RFC 826: 28 | # 29 | # SHA: source hardware address 30 | # SPA: source protocol address (i.e., IP address) 31 | # THA: target hardware address 32 | # TPA: target protocol address 33 | 34 | # ARP requests indexed on SHA/SPA/TPA (no THA, as it's what it's being 35 | # queried). 36 | global arp_requests: set[string, addr, addr] &create_expire = 1 min; 37 | 38 | # ARP responses we've seen: indexed by IP address, yielding MAC address. 39 | global ARP_cache: table[addr] of string; 40 | 41 | 42 | # Bad ARPs can occur when: 43 | # - type/size pairs are not OK for HW and L3 addresses (Ethernet=6, IP=4) 44 | # - opcode is neither request (1) nor reply (2) 45 | # - MAC src address != ARP sender MAC address 46 | event bad_arp(SPA: addr, SHA: string, TPA: addr, THA: string, 47 | explanation: string) 48 | { 49 | print arp_log, fmt("%.06f bad-arp %s(%s) ? %s(%s): %s", 50 | network_time(), SPA, SHA, TPA, THA, explanation); 51 | } 52 | 53 | 54 | # The first of these maps a MAC address to the last protocol address seen 55 | # for it. The second tracks every protocol address seen. 56 | global mac_addr_map: table[string] of addr; 57 | global mac_addr_associations: table[string] of set[addr]; 58 | 59 | # A somewhat general notion of broadcast MAC/IP addresses. 60 | const broadcast_mac_addrs = { "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff", }; 61 | const broadcast_addrs = { 0.0.0.0, 255.255.255.255, }; 62 | 63 | 64 | # Called to note that we've seen an association between a MAC address 65 | # and an IP address. Note that this is *not* an association advertised 66 | # in an ARP reply (those are tracked in ARP_cache), but instead the 67 | # pairing of hardware address + protocol address as expressed in 68 | # an ARP request or reply header. 69 | function mac_addr_association(mac_addr: string, a: addr) 70 | { 71 | # Ignore placeholders. 72 | if ( mac_addr in broadcast_mac_addrs || a in broadcast_addrs ) 73 | return; 74 | 75 | local is_addl = F; 76 | if ( mac_addr in mac_addr_associations ) 77 | is_addl = a !in mac_addr_associations[mac_addr]; 78 | else 79 | mac_addr_associations[mac_addr] = set(); 80 | 81 | print arp_log, fmt("%.06f association %s -> %s%s", network_time(), 82 | mac_addr, a, is_addl ? " " : ""); 83 | 84 | mac_addr_map[mac_addr] = a; 85 | add mac_addr_associations[mac_addr][a]; 86 | 87 | if ( a in ARP_cache && ARP_cache[a] != mac_addr ) 88 | NOTICE([$note=ARPCacheInconsistency, $src=a, 89 | $msg=fmt("mapping for %s to %s doesn't match cache of %s", 90 | mac_addr, a, ARP_cache[a])]); 91 | } 92 | 93 | # Returns the IP address associated with a MAC address, if we've seen one. 94 | # Otherwise just returns the MAC address. 95 | function addr_from_mac(mac_addr: string): string 96 | { 97 | return mac_addr in mac_addr_map ? 98 | fmt("%s", mac_addr_map[mac_addr]) : mac_addr; 99 | } 100 | 101 | event arp_request(mac_src: string, mac_dst: string, SPA: addr, SHA: string, 102 | TPA: addr, THA: string) 103 | { 104 | mac_addr_association(SHA, SPA); 105 | 106 | local msg = fmt("%s -> %s who-has %s", 107 | addr_from_mac(mac_src), addr_from_mac(mac_dst), TPA); 108 | 109 | local mismatch = SHA != mac_src; 110 | if ( mismatch ) 111 | NOTICE([$note=ARPSourceMAC_Mismatch, $src=SPA, $msg=msg]); 112 | 113 | # It turns out that some hosts fill in the THA field even though 114 | # that doesn't make sense. (The RFC specifically allows this, 115 | # however.) Perhaps there's an attack that can be launched 116 | # doing so, but it's hard to see what it might be, so for now 117 | # we don't bother notice'ing these. 118 | # if ( THA !in broadcast_addrs ) 119 | # NOTICE([$note=ARPRequestProvidesTargetAddr, $src=SPA, 120 | # $msg=fmt("%s: %s", msg, THA)]); 121 | 122 | print arp_log, fmt("%.06f %s%s", network_time(), msg, 123 | mismatch ? " " : ""); 124 | 125 | add arp_requests[SHA, SPA, TPA]; 126 | } 127 | 128 | event arp_reply(mac_src: string, mac_dst: string, SPA: addr, SHA: string, 129 | TPA: addr, THA: string) 130 | { 131 | mac_addr_association(SHA, SPA); 132 | mac_addr_association(THA, TPA); 133 | 134 | local msg = fmt("%s -> %s: %s is-at %s", 135 | addr_from_mac(mac_src), addr_from_mac(mac_dst), 136 | SPA, SHA); 137 | 138 | local unsolicited = [THA, TPA, SPA] !in arp_requests; 139 | delete arp_requests[THA, TPA, SPA]; 140 | if ( unsolicited ) 141 | NOTICE([$note=ARPUnsolicitedReply, $src=SPA, 142 | $msg=fmt("%s: request[%s, %s, %s]", msg, THA, TPA, SPA)]); 143 | 144 | local mismatch = SHA != mac_src; 145 | if ( mismatch ) 146 | NOTICE([$note=ARPSourceMAC_Mismatch, $src=SPA, $msg=msg]); 147 | 148 | local mapping_changed = SPA in ARP_cache && ARP_cache[SPA] != SHA; 149 | if ( mapping_changed ) 150 | NOTICE([$note=ARPMappingChanged, $src=SPA, 151 | $msg=fmt("%s: was %s", msg, ARP_cache[SPA])]); 152 | 153 | print arp_log, fmt("%.06f %s%s%s%s", network_time(), msg, 154 | unsolicited ? " " : "", 155 | mismatch ? " " : "", 156 | mapping_changed ? 157 | fmt(" ", ARP_cache[SPA]) : ""); 158 | 159 | ARP_cache[SPA] = SHA; 160 | } 161 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/trw-impl.bro: -------------------------------------------------------------------------------- 1 | # $Id: trw.bro 2911 2006-05-06 17:58:43Z vern $ 2 | 3 | @load notice 4 | @load port-name 5 | @load hot 6 | 7 | module TRW; 8 | 9 | export { 10 | redef enum Notice += { 11 | TRWAddressScan, # source flagged as scanner by TRW algorithm 12 | TRWScanSummary, # summary of scanning activities reported by TRW 13 | }; 14 | 15 | # Activate TRW if T. 16 | global use_TRW_algorithm = F &redef; 17 | 18 | # Tell TRW not to flag a friendly remote. 19 | global do_not_flag_friendly_remotes = T &redef; 20 | 21 | # Set of services for outbound connections that are possibly triggered 22 | # by incoming connections. 23 | const triggered_outbound_services = { ident, finger, 20/tcp, } &redef; 24 | 25 | # The following correspond to P_D and P_F in the TRW paper, i.e., the 26 | # desired detection and false positive probabilities. 27 | global target_detection_prob = 0.99 &redef; 28 | global target_false_positive_prob = 0.01 &redef; 29 | 30 | # Given a legitimate remote, the probability that its connection 31 | # attempt will succeed. 32 | global theta_zero = 0.8 &redef; 33 | 34 | # Given a scanner, the probability that its connection attempt 35 | # will succeed. 36 | global theta_one = 0.2 &redef; 37 | 38 | 39 | # These variables the user usually won't alter, except they 40 | # might want to adjust the expiration times, which is why 41 | # they're exported here. 42 | global scan_sources: set[addr] &write_expire = 1 hr; 43 | global benign_sources: set[addr] &write_expire = 1 hr; 44 | 45 | global failed_locals: set[addr, addr] &write_expire = 30 mins; 46 | global successful_locals: set[addr, addr] &write_expire = 30 mins; 47 | 48 | global lambda: table[addr] of double 49 | &default = 1.0 &write_expire = 30 mins; 50 | global num_scanned_locals: 51 | table[addr] of count &default = 0 &write_expire = 30 mins; 52 | 53 | # Function called to perform TRW analysis. 54 | global check_TRW_scan: function(c: connection, state: string, 55 | reverse: bool): bool; 56 | } 57 | 58 | # Set of remote hosts that have been successfully accessed by local hosts. 59 | global friendly_remotes: set[addr] &read_expire = 30 mins; 60 | 61 | # Set of local honeypot hosts - for internal use at LBL. 62 | global honeypot: set[addr]; 63 | 64 | # Approximate solutions for upper and lower thresholds. 65 | global eta_zero: double; # initialized when Bro starts 66 | global eta_one: double; 67 | 68 | event bro_init() 69 | { 70 | eta_zero = 71 | (1 - target_detection_prob) / (1 - target_false_positive_prob); 72 | eta_one = target_detection_prob / target_false_positive_prob; 73 | } 74 | 75 | 76 | event TRW_scan_summary(orig: addr) 77 | { 78 | NOTICE([$note=TRWScanSummary, $src=orig, 79 | $msg=fmt("%s scanned a total of %d hosts", 80 | orig, num_scanned_locals[orig])]); 81 | } 82 | 83 | function check_TRW_scan(c: connection, state: string, reverse: bool): bool 84 | { 85 | local id = c$id; 86 | 87 | local service = "ftp-data" in c$service ? 20/tcp 88 | : (reverse ? id$orig_p : id$resp_p); 89 | local orig = reverse ? id$resp_h : id$orig_h; 90 | local resp = reverse ? id$orig_h : id$resp_h; 91 | local outbound = is_local_addr(orig); 92 | 93 | # Mark a remote as friendly if it is successfully accessed by 94 | # a local with protocols other than triggered_outbound_services. 95 | # XXX There is an ambiguity to determine who initiated a 96 | # connection when the status is "OTH". 97 | if ( outbound ) 98 | { 99 | if ( resp !in scan_sources && 100 | service !in triggered_outbound_services && 101 | orig !in honeypot && state != "OTH" ) 102 | add friendly_remotes[resp]; 103 | 104 | return F; 105 | } 106 | 107 | if ( orig in scan_sources ) 108 | return T; 109 | 110 | if ( orig in benign_sources ) 111 | return F; 112 | 113 | if ( do_not_flag_friendly_remotes && orig in friendly_remotes ) 114 | return F; 115 | 116 | # Start TRW evaluation. 117 | local flag = +0; 118 | local resp_byte = reverse ? c$orig$size : c$resp$size; 119 | local established = T; 120 | 121 | if ( state == "S0" || state == "REJ" || state == "OTH" || 122 | (state == "RSTOS0" && resp_byte <= 0) ) 123 | established = F; 124 | 125 | if ( ! established || resp in honeypot ) 126 | { 127 | if ( [orig, resp] !in failed_locals ) 128 | { 129 | flag = 1; 130 | add failed_locals[orig, resp]; 131 | } 132 | } 133 | 134 | else if ( [orig, resp] !in successful_locals ) 135 | { 136 | flag = -1; 137 | add successful_locals[orig, resp]; 138 | } 139 | 140 | if ( flag == 0 ) 141 | return F; 142 | 143 | local ratio = 1.0; 144 | 145 | # Update the corresponding likelihood ratio of orig. 146 | if ( theta_zero <= 0 || theta_zero >= 1 || theta_one <= 0 || 147 | theta_one >= 1 || theta_one >= theta_zero ) 148 | { 149 | # Error: theta_zero should be between 0 and 1. 150 | alarm "bad theta_zero/theta_one in check_TRW_scan"; 151 | use_TRW_algorithm = F; 152 | return F; 153 | } 154 | 155 | if ( flag == 1 ) 156 | ratio = (1 - theta_one) / (1 - theta_zero); 157 | 158 | if ( flag == -1 ) 159 | ratio = theta_one / theta_zero; 160 | 161 | ++num_scanned_locals[orig]; 162 | 163 | lambda[orig] = lambda[orig] * ratio; 164 | local updated_lambda = lambda[orig]; 165 | 166 | if ( target_detection_prob <= 0 || 167 | target_detection_prob >= 1 || 168 | target_false_positive_prob <= 0 || 169 | target_false_positive_prob >= 1 ) 170 | { 171 | # Error: target probabilities should be between 0 and 1 172 | alarm "bad target probabilities in check_TRW_scan"; 173 | use_TRW_algorithm = F; 174 | return F; 175 | } 176 | 177 | if ( updated_lambda > eta_one ) 178 | { 179 | add scan_sources[orig]; 180 | NOTICE([$note=TRWAddressScan, $src=orig, 181 | $msg=fmt("%s scanned a total of %d hosts", 182 | orig, num_scanned_locals[orig])]); 183 | schedule 1 day { TRW_scan_summary(orig) }; 184 | return T; 185 | } 186 | 187 | if ( updated_lambda < eta_zero ) 188 | add benign_sources[orig]; 189 | 190 | return F; 191 | } 192 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/smtp-relay.bro: -------------------------------------------------------------------------------- 1 | # $Id: smtp-relay.bro 5911 2008-07-03 22:59:01Z vern $ 2 | # 3 | # Tracks email relaying. 4 | 5 | @load smtp 6 | @load mime 7 | 8 | module SMTP; 9 | 10 | redef process_smtp_relay = T; 11 | 12 | export { 13 | const relay_log = open_log_file("relay") &redef; 14 | } 15 | 16 | global print_smtp_relay: function(t: table[count] of smtp_session_info, 17 | idx: count): interval; 18 | 19 | global smtp_relay_table: table[count] of smtp_session_info 20 | &write_expire = 5 min &expire_func = print_smtp_relay; 21 | 22 | global smtp_session_by_recipient: table[string] of smtp_session_info 23 | &write_expire = 5 min; 24 | global smtp_session_by_message_id: table[string] of smtp_session_info 25 | &write_expire = 5 min; 26 | global smtp_session_by_content_hash: table[string] of smtp_session_info 27 | &write_expire = 5 min; 28 | 29 | 30 | function add_to_smtp_relay_table(session: smtp_session_info) 31 | { 32 | if ( session$id !in smtp_relay_table ) 33 | smtp_relay_table[session$id] = session; 34 | } 35 | 36 | function check_relay_1(session: smtp_session_info, rcpt: string) 37 | { 38 | if ( session$external_orig && rcpt != local_mail_addr ) 39 | { 40 | smtp_message(session, 41 | fmt("relaying(1) message (from %s, to %s) to address %s", 42 | session$connection_id$orig_h, 43 | session$connection_id$resp_h, 44 | rcpt)); 45 | 46 | if ( session$relay_1_rcpt != "" ) 47 | session$relay_1_rcpt = cat(session$relay_1_rcpt, ","); 48 | 49 | session$relay_1_rcpt = cat(session$relay_1_rcpt, rcpt); 50 | add_to_smtp_relay_table(session); 51 | } 52 | } 53 | 54 | function check_relay_2(session: smtp_session_info, rcpt: string) 55 | { 56 | if ( rcpt in smtp_session_by_recipient ) 57 | { 58 | local prev_session = smtp_session_by_recipient[rcpt]; 59 | 60 | # Should only check the first condition only (external 61 | # followed by internal) but let's include the second one 62 | # for testing purposes for now. 63 | if ( (prev_session$external_orig && ! session$external_orig) || 64 | (! prev_session$external_orig && session$external_orig) ) 65 | { 66 | smtp_message(session, 67 | fmt("relaying(2) message (seen during #%d) to address %s (%s -> %s, %s -> %s)", 68 | prev_session$id, rcpt, 69 | prev_session$connection_id$orig_h, 70 | prev_session$connection_id$resp_h, 71 | session$connection_id$orig_h, 72 | session$connection_id$resp_h)); 73 | 74 | session$relay_2_from = prev_session$id; 75 | ++prev_session$relay_2_to; 76 | 77 | add_to_smtp_relay_table(session); 78 | add_to_smtp_relay_table(prev_session); 79 | } 80 | } 81 | 82 | smtp_session_by_recipient[rcpt] = session; 83 | } 84 | 85 | function check_relay_3(session: MIME::mime_session_info, msg_id: string) 86 | { 87 | local smtp_session = session$smtp_session; 88 | 89 | if ( msg_id in smtp_session_by_message_id ) 90 | { 91 | local prev_smtp_session = smtp_session_by_message_id[msg_id]; 92 | 93 | smtp_message(smtp_session, 94 | fmt("relaying(3) message (seen during #%d) with id %s (%s -> %s, %s -> %s)", 95 | prev_smtp_session$id, msg_id, 96 | prev_smtp_session$connection_id$orig_h, 97 | prev_smtp_session$connection_id$resp_h, 98 | smtp_session$connection_id$orig_h, 99 | smtp_session$connection_id$resp_h)); 100 | 101 | smtp_session$relay_3_from = prev_smtp_session$id; 102 | ++prev_smtp_session$relay_3_to; 103 | 104 | add_to_smtp_relay_table(smtp_session); 105 | add_to_smtp_relay_table(prev_smtp_session); 106 | } 107 | else 108 | smtp_session_by_message_id[msg_id] = smtp_session; 109 | } 110 | 111 | function check_relay_4(session: MIME::mime_session_info, content_hash: string) 112 | { 113 | local smtp_session = session$smtp_session; 114 | smtp_session$content_hash = content_hash; 115 | 116 | if ( content_hash in smtp_session_by_content_hash ) 117 | { 118 | local prev_smtp_session = smtp_session_by_content_hash[content_hash]; 119 | smtp_message(smtp_session, 120 | fmt("relaying(4) message (seen during #%d) with hash %s (%s -> %s, %s -> %s)", 121 | prev_smtp_session$id, 122 | string_to_ascii_hex(content_hash), 123 | prev_smtp_session$connection_id$orig_h, 124 | prev_smtp_session$connection_id$resp_h, 125 | smtp_session$connection_id$orig_h, 126 | smtp_session$connection_id$resp_h)); 127 | 128 | smtp_session$relay_4_from = prev_smtp_session$id; 129 | ++prev_smtp_session$relay_4_to; 130 | 131 | add_to_smtp_relay_table(smtp_session); 132 | add_to_smtp_relay_table(prev_smtp_session); 133 | } 134 | else 135 | smtp_session_by_content_hash[content_hash] = smtp_session; 136 | } 137 | 138 | # event mime_all_data(c: connection, length: count, data: string) 139 | # { 140 | # local session = get_mime_session(c, T); 141 | # session$content_hash = md5_hash(data); 142 | # if ( process_smtp_relay ) 143 | # check_relay_4(session, session$content_hash); 144 | # # mime_log_msg(session, "all data", fmt("%s", data)); 145 | # } 146 | 147 | event mime_content_hash(c: connection, content_len: count, hash_value: string) 148 | { 149 | local session = MIME::get_session(c, T); 150 | session$content_hash = hash_value; 151 | if ( process_smtp_relay && content_len > 0 ) 152 | check_relay_4(session, session$content_hash); 153 | } 154 | 155 | function relay_flow(from: count, to: count): string 156 | { 157 | if ( from > 0 ) 158 | return fmt("<#%d", from); 159 | 160 | if ( to > 0 ) 161 | return fmt(">%d", to); 162 | 163 | return "-"; 164 | } 165 | 166 | function print_smtp_relay(t: table[count] of smtp_session_info, 167 | idx: count): interval 168 | { 169 | local session = t[idx]; 170 | 171 | print relay_log, fmt("#%d: %s", 172 | session$id, 173 | directed_id_string(session$connection_id, T)); 174 | 175 | print relay_log, fmt("#%d: RCPT: <%s>, Subject: %s", 176 | session$id, 177 | session$recipients, session$subject); 178 | 179 | print relay_log, fmt("#%d: detected: [%s %s %s %s] %s", 180 | session$id, 181 | session$relay_1_rcpt == "" ? "-" : "1", 182 | relay_flow(session$relay_2_from, session$relay_2_to), 183 | relay_flow(session$relay_3_from, session$relay_3_to), 184 | relay_flow(session$relay_4_from, session$relay_4_to), 185 | session$content_gap ? "(content gap)" : ""); 186 | 187 | print relay_log, fmt("#%d: relay 1: <%s>", 188 | session$id, 189 | session$relay_1_rcpt); 190 | 191 | return 0 sec; 192 | } 193 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/dns-common-summary.bro: -------------------------------------------------------------------------------- 1 | @load conn-util 2 | @load app-summary 3 | @load dns-info 4 | 5 | module DNS_common_summary; 6 | 7 | 8 | export { 9 | 10 | global dns_summary_log = open_log_file("dns-common-summary") &redef; 11 | 12 | const server_ports = { 13 | 53/udp, 53/tcp, 137/udp, 14 | } &redef; 15 | } 16 | 17 | redef capture_filters += { 18 | ["dns"] = "port 53", 19 | ["netbios-ns"] = "udp port 137", 20 | }; 21 | 22 | const dns_op_name = { 23 | [0] = "QUERY", 24 | [1] = "IQUERY", 25 | [2] = "STATUS", 26 | [5] = "NB_REGISTER", 27 | [6] = "NB_RELEASE", 28 | [7] = "NB_WACK", 29 | [8] = "NB_REFRESH", 30 | } &default = function(op: count): string 31 | { 32 | return fmt("op-%d", op); 33 | }; 34 | 35 | function dns_qtype(qtype: int, server_port: port): string 36 | { 37 | if ( qtype < 0 ) 38 | return "none"; 39 | 40 | if ( server_port == 137/udp ) 41 | { 42 | if ( qtype == 32 ) 43 | return "NB"; 44 | if ( qtype == 33 ) 45 | return "NBSTAT"; 46 | } 47 | 48 | return query_types[int_to_count(qtype)]; 49 | } 50 | 51 | function dns_rcode(rcode: int): string 52 | { 53 | return ( rcode < 0 ) ? "none" : 54 | base_error[int_to_count(rcode)]; 55 | } 56 | 57 | const netbios_host_type = { 58 | ["00"] = "workstation", 59 | ["03"] = "messenger", 60 | ["1b"] = "domain_master_browser", 61 | ["20"] = "server", 62 | ["1c"] = "domain_group", 63 | ["1d"] = "master_browser_group", 64 | ["1e"] = "group", 65 | } &default = function(t: string): string { return t; }; 66 | 67 | const dns_transaction_timeout = 30 sec &redef; 68 | 69 | type dns_transaction: record { 70 | connection_id: conn_id; 71 | conn_start: time; 72 | func: string; 73 | start: time; 74 | num_req: count; 75 | req_size: count; 76 | num_resp: count; 77 | resp_size: count; 78 | 79 | num_q: count; 80 | qtype: string; 81 | query: string; 82 | host_type: string; 83 | rcode: string; 84 | resp_time: time; # of the first resp 85 | }; 86 | 87 | # Use only the client addr and transaction id for index, because 88 | # Netbios/NS clients sometimes send to broadcast address 89 | type dns_trans_index: record { 90 | client: addr; 91 | client_port: port; 92 | id: count; 93 | server: addr; 94 | server_port: port; 95 | }; 96 | global dns_trans_table: table[dns_trans_index] of dns_transaction; 97 | 98 | function fmt_list(x: string): string 99 | { 100 | if ( strstr(x, ",") > 0 ) 101 | return cat("[", x, "]"); 102 | else 103 | return x; 104 | } 105 | 106 | event expire_DNS_transaction(ind: dns_trans_index) 107 | { 108 | if ( ind !in dns_trans_table ) 109 | return; 110 | 111 | local t = dns_trans_table[ind]; 112 | if ( ind$server_port in server_ports ) 113 | { 114 | print_app_summary(dns_summary_log, 115 | t$connection_id, 116 | t$conn_start, 117 | t$func, t$start, 118 | t$num_req, t$req_size, 119 | t$num_resp, t$resp_size, 120 | fmt("qtype %s return %s query '%s' host_type %s latency %.6f", 121 | fmt_list(t$qtype), fmt_list(t$rcode), 122 | fmt_list(gsub(t$query, / /, "_")), 123 | fmt_list(t$host_type), 124 | t$resp_time >= t$start ? t$resp_time - t$start : -1 sec)); 125 | } 126 | delete dns_trans_table[ind]; 127 | } 128 | 129 | function lookup_dns_transaction(c: connection, msg: dns_msg, is_orig: bool): dns_transaction 130 | { 131 | local id = c$id; 132 | local client: addr; 133 | local server: addr; 134 | local client_port: port; 135 | local server_port: port; 136 | 137 | if ( ( ! msg$QR && is_orig ) || ( msg$QR && ! is_orig ) ) 138 | { 139 | client = id$orig_h; 140 | client_port = id$orig_p; 141 | server = id$resp_h; 142 | server_port = id$resp_p; 143 | } 144 | else 145 | { 146 | client = id$resp_h; 147 | client_port = id$resp_p; 148 | server = id$orig_h; 149 | server_port = id$orig_p; 150 | } 151 | 152 | # print fmt("%.6f client %s server %s", network_time(), client, server); 153 | 154 | # Netbios queries are sometimes sent to broadcast addresses, 155 | # so we ignore the server part 156 | if ( server_port == 137/udp ) 157 | server = 0.0.0.0; 158 | 159 | local ind = [$client = client, $client_port = client_port, 160 | $id = msg$id, 161 | $server = server, $server_port = server_port]; 162 | 163 | if ( ind !in dns_trans_table ) 164 | { 165 | local t = [ 166 | $connection_id = id, 167 | $conn_start = c$start_time, 168 | $func = dns_op_name[msg$opcode], 169 | $start = network_time(), 170 | $num_req = 0, $req_size = 0, 171 | $num_resp = 0, $resp_size = 0, 172 | $num_q = 0, 173 | $qtype = "none", 174 | $query = "none", $host_type = "none", 175 | $rcode = "none", 176 | $resp_time = network_time() - 1 sec]; 177 | dns_trans_table[ind] = t; 178 | } 179 | 180 | schedule dns_transaction_timeout { 181 | expire_DNS_transaction(ind) 182 | }; 183 | 184 | return dns_trans_table[ind]; 185 | } 186 | 187 | event dns_message(c: connection, is_orig: bool, msg: dns_msg, len: count) 188 | { 189 | local t = lookup_dns_transaction(c, msg, is_orig); 190 | if ( ! msg$QR ) 191 | { 192 | ++t$num_req; 193 | t$req_size = t$req_size + len; 194 | } 195 | else 196 | { 197 | local rcode = dns_rcode(msg$rcode); 198 | if ( t$rcode == "none" ) 199 | t$rcode = rcode; 200 | else if ( t$rcode != rcode ) 201 | t$rcode = cat(t$rcode, ",", rcode); 202 | ++t$num_resp; 203 | t$resp_size = t$resp_size + len; 204 | if ( t$num_resp == 1 ) 205 | t$resp_time = network_time(); 206 | } 207 | } 208 | 209 | function append_query(t: dns_transaction, query: string, host_type: string, qtype: string) 210 | { 211 | ++t$num_q; 212 | if ( t$num_q == 1 ) 213 | { 214 | t$qtype = qtype; 215 | t$query = query; 216 | t$host_type = host_type; 217 | } 218 | else 219 | { 220 | if ( qtype != t$qtype ) 221 | t$qtype = cat(t$qtype, ",", qtype); 222 | if ( query != t$query ) 223 | t$query = cat(t$query, ",", query); 224 | if ( host_type != t$host_type ) 225 | t$host_type = cat(t$host_type, ",", host_type); 226 | } 227 | } 228 | 229 | event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) 230 | { 231 | local host_type = "n/a"; 232 | if ( c$id$resp_p == 137/udp ) 233 | { 234 | query = decode_netbios_name(query); 235 | local last_byte = sub_bytes(query, byte_len(query) - 2, 2); 236 | host_type = netbios_host_type[last_byte]; 237 | } 238 | 239 | # print log, fmt("conn %s start %.6f op %d qtype 0x%x name [%s]", 240 | # conn_id_string(c$id), network_time(), 241 | # msg$opcode, qtype, query); 242 | 243 | local t = lookup_dns_transaction(c, msg, T); 244 | append_query(t, query, host_type, dns_qtype(qtype, c$id$resp_p)); 245 | } 246 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/loaders/brolite.bro: -------------------------------------------------------------------------------- 1 | # Bro Lite base configuration file. 2 | 3 | # General policy - these scripts are more infrastructural than service 4 | # oriented, so in general avoid changing anything here. 5 | 6 | @load site # defines local and neighbor networks from static config 7 | @load tcp # initialize BPF filter for SYN/FIN/RST TCP packets 8 | @load weird # initialize generic mechanism for unusual events 9 | @load conn # access and record connection events 10 | @load hot # defines certain forms of sensitive access 11 | @load frag # process TCP fragments 12 | @load print-resources # on exit, print resource usage information 13 | 14 | # Scan detection policy. 15 | @load scan # generic scan detection mechanism 16 | @load trw # additional, more sensitive scan detection 17 | #@load drop # include if installation has ability to drop hostile remotes 18 | 19 | # Application level policy - these scripts operate on the specific service. 20 | @load http # general http analyzer, low level of detail 21 | @load http-request # detailed analysis of http requests 22 | @load http-reply # detailed analysis of http reply's 23 | 24 | # Track software versions; required for some signature matching. Also 25 | # can be used by http and ftp policies. 26 | @load software 27 | 28 | @load ftp # FTP analysis 29 | @load portmapper # record and analyze RPC portmapper requests 30 | @load tftp # identify and log TFTP sessions 31 | @load login # rlogin/telnet analyzer 32 | @load irc # IRC analyzer 33 | @load blaster # blaster worm detection 34 | @load stepping # "stepping stone" detection 35 | @load synflood # synflood attacks detection 36 | @load smtp # record and analyze email traffic - somewhat expensive 37 | 38 | @load notice-policy # tuning of notices to downgrade some alarms 39 | 40 | # off by default 41 | #@load icmp # icmp analysis 42 | 43 | # Tuning of memory consumption. 44 | @load inactivity # time out connections for certain services more quickly 45 | # @load print-globals # on exit, print the size of global script variables 46 | 47 | # Record system statistics to the notice file 48 | @load stats 49 | 50 | # udp analysis - potentially expensive, depending on a site's traffic profile 51 | #@load udp.all 52 | #@load remove-multicast 53 | 54 | # Prints the pcap filter and immediately exits. Not used during 55 | # normal operation. 56 | #@load print-filter 57 | 58 | ## End policy script loading. 59 | 60 | ## General configuration. 61 | 62 | @load rotate-logs 63 | redef log_rotate_base_time = "0:00"; 64 | redef log_rotate_interval = 24 hr; 65 | 66 | 67 | # Set additional policy prefixes. 68 | @prefixes += lite 69 | 70 | ## End basic configuration. 71 | 72 | 73 | ## Scan configuration. 74 | @ifdef ( Scan::analyze_all_services ) 75 | redef Scan::analyze_all_services = T; 76 | 77 | # The following turns off scan detection. 78 | #redef Scan::suppress_scan_checks = T; 79 | 80 | # Be a bit more aggressive than default (though the defaults 81 | # themselves should be fixed). 82 | redef Scan::report_outbound_peer_scan = { 100, 1000, }; 83 | 84 | # These services are skipped for scan detection due to excessive 85 | # background noise. 86 | redef Scan::skip_services += { 87 | http, # Avoid Code Red etc. overload 88 | 27374/tcp, # Massive scanning in Jan 2002 89 | 1214/tcp, # KaZaa scans 90 | 12345/tcp, # Massive scanning in Apr 2002 91 | 445/tcp, # Massive distributed scanning Oct 2002 92 | 135/tcp, # These days, NetBIOS scanning is endemic 93 | 137/udp, # NetBIOS 94 | 139/tcp, # NetBIOS 95 | 1025/tcp, 96 | 6129/tcp, # Dameware 97 | 3127/tcp, # MyDoom worms worms worms! 98 | 2745/tcp, # Bagel worm 99 | 1433/tcp, # Distributed scanning, April 2004 100 | 5000/tcp, # Distributed scanning, May 2004 101 | 5554/tcp, # More worm food, May 2004 102 | 9898/tcp, # Worms attacking worms. ugh - May 2004 103 | 3410/tcp, # More worm food, June 2004 104 | 3140/tcp, # Dyslexic worm food, June 2004 105 | 27347/tcp, # Can't kids type anymore? 106 | 1023/tcp, # Massive scanning, July 2004 107 | 17300/tcp, # Massive scanning, July 2004 108 | }; 109 | 110 | @endif 111 | 112 | @ifdef ( ICMP::detect_scans ) 113 | # Whether to detect ICMP scans. 114 | redef ICMP::detect_scans = F; 115 | redef ICMP::scan_threshold = 100; 116 | @endif 117 | 118 | @ifdef ( TRW::TRWAddressScan ) 119 | # remove logging TRW scan events 120 | redef notice_action_filters += { 121 | [TRW::TRWAddressScan] = ignore_notice, 122 | }; 123 | @endif 124 | 125 | # Note: default scan configuration is conservative in terms of memory use and 126 | # might miss slow scans. Consider uncommenting these based on your sites scan 127 | # traffic. 128 | #redef distinct_peers &create_expire = 30 mins; 129 | #redef distinct_ports &create_expire = 30 mins; 130 | #redef distinct_low_ports &create_expire= 30 mins; 131 | 132 | 133 | ## End scan configuration. 134 | 135 | ## additional IRC checks 136 | redef IRC::hot_words += /.*exe/ ; 137 | 138 | 139 | ## Dynamic Protocol Detection configuration 140 | # 141 | # This is off by default, as it requires a more powerful Bro host. 142 | # Uncomment next line to activate. 143 | # const use_dpd = T; 144 | 145 | @ifdef ( use_dpd ) 146 | @load dpd 147 | @load irc-bot 148 | @load dyn-disable 149 | @load detect-protocols 150 | @load detect-protocols-http 151 | @load proxy 152 | @load ssh 153 | 154 | # By default, DPD looks at all traffic except port 80. 155 | # For lightly loaded networks, comment out the restrict_filters line. 156 | # For heavily loaded networks, try adding addition ports (e.g., 25) to 157 | # the restrict filters. 158 | redef capture_filters += [ ["tcp"] = "tcp" ]; 159 | redef restrict_filters += [ ["not-http"] = "not (port 80)" ]; 160 | @endif 161 | 162 | @ifdef ( ProtocolDetector::ServerFound ) 163 | # Report servers on non-standard ports only for local addresses. 164 | redef notice_policy += { 165 | [$pred(a: notice_info) = 166 | { return a$note == ProtocolDetector::ServerFound && 167 | ! is_local_addr(a$src); }, 168 | $result = NOTICE_FILE, 169 | $priority = 1], 170 | 171 | # Report protocols on non-standard ports only for local addresses 172 | # (unless it's IRC). 173 | [$pred(a: notice_info) = 174 | { return a$note == ProtocolDetector::ProtocolFound && 175 | ! is_local_addr(a$dst) && 176 | a$sub != "IRC"; }, 177 | $result = NOTICE_FILE, 178 | $priority = 1], 179 | }; 180 | @endif 181 | 182 | # The following is used to transfer state between Bro's when one 183 | # takes over from another. 184 | # 185 | # NOTE: not implemented in the production version, so ignored for now. 186 | @ifdef ( remote_peers_clear ) 187 | redef remote_peers_clear += { 188 | [127.0.0.1, 55555/tcp] = [$hand_over = T], 189 | [127.0.0.1, 0/tcp] = [$hand_over = T] 190 | }; 191 | @endif 192 | 193 | # Use tagged log files for alarms and notices. 194 | redef use_tagging = T; 195 | 196 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/smb-summary.bro: -------------------------------------------------------------------------------- 1 | @load app-summary 2 | 3 | redef capture_filters += { 4 | ["netbios-dgm"] = "udp port 138", 5 | ["netbios-ssn"] = "tcp port 139", 6 | ["microsft-ds"] = "tcp port 445", 7 | }; 8 | 9 | module SMB_summary; 10 | 11 | global smb_log = open_log_file("smb-summary") &redef; 12 | global chris_log = open_log_file("chris-summary") &redef; 13 | 14 | #const smb_transaction_func = { 15 | # ["SMB_COM_TRANSACTION", 0x0 ] = "\\PIPE\\LANMAN\\", 16 | # ["SMB_COM_TRANSACTION", 0x1 ] = "\\MAILSLOT\\", 17 | # ["SMB_COM_TRANSACTION", 0x54] = "CallNamedPipe", 18 | # ["SMB_COM_TRANSACTION", 0x53] = "WaitNamedPipe", 19 | # ["SMB_COM_TRANSACTION", 0x26] = "TransactNmPipe", 20 | # 21 | # ["SMB_COM_TRANSACTION2", 0x0] = "TRANS2_OPEN2", 22 | # ["SMB_COM_TRANSACTION2", 0x1] = "TRANS2_FIND_FIRST2", 23 | # ["SMB_COM_TRANSACTION2", 0x2] = "TRANS2_FIND_NEXT2", 24 | # ["SMB_COM_TRANSACTION2", 0x3] = "TRANS2_QUERY_FS_INFORMATION", 25 | # ["SMB_COM_TRANSACTION2", 0x5] = "TRANS2_QUERY_PATH_INFORMATION", 26 | # ["SMB_COM_TRANSACTION2", 0x6] = "TRANS2_SET_PATH_INFORMATION", 27 | # ["SMB_COM_TRANSACTION2", 0x7] = "TRANS2_QUERY_FILE_INFORMATION", 28 | # ["SMB_COM_TRANSACTION2", 0x8] = "TRANS2_SET_FILE_INFORMATION", 29 | # ["SMB_COM_TRANSACTION2", 0x0d] = "TRANS2_CREATE_DIRECTORY", 30 | # ["SMB_COM_TRANSACTION2", 0x0e] = "TRANS2_SESSION_SETUP", 31 | # ["SMB_COM_TRANSACTION2", 0x10] = "TRANS2_GET_DFS_REFERRAL", 32 | #} &default = function(cmd: string, subcmd: count): string 33 | # { 34 | # return fmt("%s/%d", cmd, subcmd); 35 | # }; 36 | 37 | type smb_req_resp: record { 38 | connection_id: conn_id; 39 | conn_start: time; 40 | func: string; 41 | cmd: string; 42 | start: time; 43 | num_req: count; 44 | req_size: count; 45 | num_resp: count; 46 | resp_size: count; 47 | }; 48 | 49 | type smb_req_reply_group: record { 50 | trans: table[count] of smb_req_resp; 51 | first_req: count; 52 | last_req: count; 53 | }; 54 | 55 | global smb_trans_table: table[conn_id] of smb_req_reply_group; 56 | 57 | function lookup_smb_req_reply_group(id: conn_id, create: bool): smb_req_reply_group 58 | { 59 | if ( id !in smb_trans_table ) 60 | { 61 | if ( create ) 62 | { 63 | local trans: table[count] of smb_req_resp; 64 | smb_trans_table[id] = [ 65 | $trans = trans, $first_req = 1, $last_req = 0]; 66 | } 67 | else 68 | print fmt("SMB req_reply_group not found: %s", 69 | conn_id_string(id)); 70 | } 71 | 72 | return smb_trans_table[id]; 73 | } 74 | 75 | function new_smb_req_resp(c: connection, cmd: string): smb_req_resp 76 | { 77 | local id = c$id; 78 | local g = lookup_smb_req_reply_group(id, T); 79 | 80 | if( is_udp_port(id$orig_p) || is_udp_port(id$resp_p) ) 81 | print fmt("%.6f %s a new req_resp was triggered on a UDP connection!: %s", 82 | network_time(), conn_id_string(id), cmd); 83 | 84 | local t = [ 85 | $connection_id = id, $conn_start = c$start_time, 86 | $cmd = cmd, $func = cmd, 87 | $start = network_time(), 88 | $num_req = 0, $req_size = 0, 89 | $num_resp = 0, $resp_size = 0 90 | ]; 91 | 92 | ++g$last_req; 93 | g$trans[g$last_req] = t; 94 | 95 | return g$trans[g$last_req]; 96 | } 97 | 98 | function end_smb_req_resp(t: smb_req_resp) 99 | { 100 | print_app_summary(smb_log, t$connection_id, t$conn_start, 101 | t$func, t$start, 102 | t$num_req, t$req_size, 103 | t$num_resp, t$resp_size, 104 | fmt("cmd %s", t$cmd)); 105 | } 106 | 107 | function lookup_smb_req_resp(c: connection, is_orig: bool, cmd: string): smb_req_resp 108 | { 109 | local id = c$id; 110 | local g = lookup_smb_req_reply_group(id, T); 111 | 112 | if( is_udp_port(id$orig_p) || is_udp_port(id$resp_p) ) 113 | print fmt("%.6f %s a lookup was triggered on a UDP connection!: %s", 114 | network_time(), conn_id_string(id), cmd); 115 | 116 | if ( g$first_req > g$last_req ) 117 | { 118 | print fmt("%.6f %s request missing: %s", 119 | network_time(), conn_id_string(id), cmd); 120 | return new_smb_req_resp(c, cmd); 121 | } 122 | 123 | if ( is_orig ) 124 | { 125 | return g$trans[g$last_req]; 126 | } 127 | else if ( cmd == "(current)" ) 128 | { 129 | return g$trans[g$first_req]; 130 | } 131 | else 132 | { 133 | local t = g$trans[g$first_req]; 134 | if ( g$first_req < g$last_req ) 135 | { 136 | end_smb_req_resp(t); 137 | ++g$first_req; 138 | t = g$trans[g$first_req]; 139 | } 140 | if ( t$cmd != cmd ) 141 | { 142 | if ( g$first_req < g$last_req ) 143 | return lookup_smb_req_resp(c, is_orig, cmd); 144 | print fmt("%.6f %s SMB command-reply mismatch", 145 | network_time(), conn_id_string(id)); 146 | } 147 | return t; 148 | } 149 | } 150 | 151 | event smb_message(c: connection, hdr: smb_hdr, is_orig: bool, cmd: 152 | string, body_length: count, body : string) 153 | { 154 | print chris_log, fmt("%.6f %s %s", network_time(), conn_id_string(c$id), cmd); 155 | 156 | local t: smb_req_resp; 157 | 158 | if ( is_udp_port( c$id$orig_p ) || is_udp_port ( c$id$resp_p ) ) 159 | { 160 | # dont need to keep track of UDP smb commands 161 | print_app_summary(smb_log, c$id, network_time(), 162 | cmd, network_time(), 163 | 0, 0, 0, 0, 164 | fmt("cmd %s", cmd)); 165 | } 166 | else if ( is_orig ) 167 | { 168 | t = new_smb_req_resp(c, cmd); 169 | ++t$num_req; 170 | t$req_size = t$req_size + body_length; 171 | } 172 | else 173 | { 174 | t = lookup_smb_req_resp(c, is_orig, cmd); 175 | ++t$num_resp; 176 | t$resp_size = t$resp_size + body_length; 177 | } 178 | } 179 | 180 | event smb_error(c: connection, hdr: smb_hdr, cmd: count, cmd_str: string, data: string) 181 | { 182 | print chris_log, fmt("%.6f %s SMB_ERROR:%s", network_time(), conn_id_string(c$id), cmd_str); 183 | } 184 | 185 | event dce_rpc_bind(c: connection, uuid: string) 186 | { 187 | local id = c$id; 188 | if ( id !in smb_trans_table ) 189 | return; 190 | local t = lookup_smb_req_resp(c, T, "(current)"); 191 | t$func = "DCE_RPC_BIND"; 192 | } 193 | 194 | event dce_rpc_request(c: connection, opnum: count, stub: string) 195 | { 196 | local id = c$id; 197 | if ( id !in smb_trans_table ) 198 | return; 199 | local t = lookup_smb_req_resp(c, T, "(current)"); 200 | t$func = "DCE_RPC_CALL"; 201 | } 202 | 203 | event dce_rpc_response(c: connection, opnum: count, stub: string) 204 | { 205 | local id = c$id; 206 | if ( id !in smb_trans_table ) 207 | return; 208 | local t = lookup_smb_req_resp(c, F, "(current)"); 209 | t$func = "DCE_RPC_CALL"; 210 | } 211 | 212 | event smb_com_transaction(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool) 213 | { 214 | if ( is_orig && !is_udp_port( c$id$orig_p ) ) 215 | { 216 | local t = lookup_smb_req_resp(c, T, "(current)"); 217 | } 218 | } 219 | 220 | event smb_com_transaction2(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool) 221 | { 222 | if ( is_orig && !is_udp_port( c$id$orig_p ) ) 223 | { 224 | local t = lookup_smb_req_resp(c, T, "(current)"); 225 | } 226 | } 227 | 228 | function end_smb_req_reply_group(g: smb_req_reply_group, index: count) 229 | { 230 | if ( index > g$last_req ) 231 | return; 232 | 233 | if ( index >= g$first_req && index in g$trans ) 234 | end_smb_req_resp(g$trans[index]); 235 | 236 | if( index in g$trans ) 237 | { 238 | delete g$trans[index]; 239 | end_smb_req_reply_group(g, index + 1); 240 | } 241 | } 242 | 243 | event connection_state_remove(c: connection) 244 | { 245 | local id = c$id; 246 | if ( !is_udp_port( id$orig_p ) && id in smb_trans_table ) 247 | { 248 | local g = smb_trans_table[id]; 249 | end_smb_req_reply_group(g, 1); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/http-anon-server.bro: -------------------------------------------------------------------------------- 1 | # $Id:$ 2 | 3 | # Anonymize values in Server: headers. 4 | # 5 | # TODO: 6 | # 7 | # - Zedo and IBM web servers can have Apache mods -- the parsing should 8 | # be extended to support them 9 | # 10 | 11 | @load anon 12 | @load http-anon-utils 13 | 14 | # --------------------------------------------------------------------- 15 | # Apache (and friends) 16 | # - abandon all hope ye who enter here ..... 17 | # --------------------------------------------------------------------- 18 | 19 | const apache_server = 20 | /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)\)? *)*/; 21 | 22 | const apache_mod_pat = 23 | /mod_fastcgi\/(([0-9]+\.)*[0-9a-z]{1,4})/ 24 | | /openssl\/([0-9]+\.)*[0-9a-z]{1,4}(-beta[0-9]{0,2})?/ 25 | | /dav\/(([0-9]+\.)*[0-9a-z]{1,4})/ 26 | | /php-cgi\/(([0-9]+\.)*[0-9a-z]{1,4})/ 27 | | /ben-ssl\/(([0-9]+\.)*[0-9a-z]{1,4})/ 28 | | /embperl\/(([0-9]+\.)*[0-9a-z]{1,4})/ 29 | | /mod_ruby\/(([0-9]+\.)*[0-9a-z]{1,4})/ 30 | | /nexadesic\/(([0-9]+\.)*[0-9a-z]{1,4})/ 31 | | /postgresql\/(([0-9]+\.)*[0-9a-z]{1,4})/ 32 | | /mod_tsunami\/(([0-9]+\.)*[0-9a-z]{1,4})/ 33 | | /mod_auth_svn\/(([0-9]+\.)*[0-9a-z]{1,4})/ 34 | | /mod_auth_mda\/(([0-9]+\.)*[0-9a-z]{1,4})/ 35 | | /rus\/pl(([0-9]+\.)*[0-9]{1,4})/ 36 | | /authmysql\/(([0-9]+\.)*[0-9]{1,4})/ 37 | | /mod_auth_pgsql\/(([0-9]+\.)*[0-9]{1,4})/ 38 | | /mod_ssl\/(([0-9]+\.)*[0-9a-z]{1,4})/ 39 | | /php\/(([0-9]+\.)*[0-9a-z]{1,4})(-[0-9]+)?/ 40 | | /mod_perl\/(([0-9]+\.)*[0-9a-z]{1,4})(\_[0-9]+)?/ 41 | | /mod_macro\/(([0-9]+\.)*[0-9a-z]{1,4})/ 42 | | /mod_auth_pam\/(([0-9]+\.)*[0-9a-z]{1,4})/ 43 | | /mod_oas\/(([0-9]+\.)*[0-9a-z]{1,4})/ 44 | | /mod_cap\/(([0-9]+\.)*[0-9a-z]{1,4})/ 45 | | /powweb\/(([0-9]+\.)*[0-9a-z]{1,4})/ 46 | | /mod_gzip\/(([0-9]+\.)*[0-9a-z]{1,4})/ 47 | | /resin\/(([0-9]+\.)*[0-9a-z]{1,4})/ 48 | | /mod_jk\/(([0-9]+\.)*[0-9a-z]{1,4})/ 49 | | /python\/(([0-9]+\.)*[0-9a-z]{1,4})/ 50 | | /perl\/(v)?(([0-9]+\.)*[0-9a-z]{1,4})/ 51 | | /mod_python\/(([0-9]+\.)*[0-9a-z]{1,4})/ 52 | | /mod_log_bytes\/(([0-9]+\.)*[0-9a-z]{1,4})/ 53 | | /mod_auth_passthrough\/(([0-9]+\.)*[0-9a-z]{1,4})/ 54 | | /mod_bwlimited\/(([0-9]+\.)*[0-9a-z]{1,4})/ 55 | | /mod_throttle\/(([0-9]+\.)*[0-9a-z]{1,4})/ 56 | | /mod_webapp\/(([0-9]+\.)*[0-9a-z]{1,4})(-dev)?/ 57 | | /frontpage\/(([0-9]+\.)*[0-9a-z]{1,5})/ 58 | | /mod_pubcookie\/[0-9a-z]{2}\/[0-9]+\.[0-9]+\-[0-9]+/ 59 | | /(-)?coyote\/(([0-9]+\.)*[0-9a-z]{1,4})/ 60 | | /svn\/(([0-9]+\.)*[0-9a-z]{1,4})/ 61 | ; 62 | 63 | # Various Apache variants (e.g., stronghold). 64 | const apache_misc = 65 | /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)\)? *)*/; 66 | 67 | const apache_basic = /apache?(\/([0-9]+\.)*[0-9]+)?/; 68 | const apache_platforms = 69 | /(\(?(red hat( linux)?|cobalt|suse\/linux|linux\/suse|darwin|gentoo\/linux|debian gnu\/linux|win32|fedora|freebsd|red-hat\/linux|unix)\)? *)*/; 70 | 71 | # ibm_http_server/1.3.26.2, apache/1.3.26 (unix). 72 | const IBM_server = 73 | /ibm_http_server(\/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)?( *apache\/[0-9]+\.[0-9]+\.[0-9]+ \(unix\))?/; 74 | 75 | 76 | # --------------------------------------------------------------------- 77 | # Servers values for which we don't retain all values. 78 | # --------------------------------------------------------------------- 79 | 80 | const zope_server = 81 | /zope\/\(zope ([0-9]+\.)*[0-9]+-[a-z0-9]{1,2}\, python ([0-9]+\.)*[0-9]+\, linux[0-9]\)/; 82 | 83 | const thttp_server = /thttpd\/[0-9]+\.[0-9]+(beta[0-9]+)?/; 84 | const weblogic_server = /weblogic server [0-9]+\.[0-9]+/; 85 | const zedo_server = /zedo 3g(\/([0-9]+\.)*[0-9]+)?/; 86 | const jetty_server = /jetty\/[0-9]+\.[0-9]+/; 87 | 88 | # --------------------------------------------------------------------- 89 | # Misc Servers 90 | # --------------------------------------------------------------------- 91 | 92 | const misc_server = 93 | /dclk creative/ 94 | | /gws\/[0-9]+\.[0-9]+/ 95 | | /nfe\/[0-9]+\.[0-9]+/ 96 | | /gfe\/[0-9]+\.[0-9]+/ 97 | | /dclk-adsvr/ 98 | | /rsi/ 99 | | /swcd\/([0-9]+\.)*[0-9]+/ 100 | | /microsoft-iis\/[0-9]{1,2}\.[0-9]{1,2}/ 101 | | /cafe\/[0-9]+\.[0-9]+/ 102 | | /artblast\/([0-9]+\.)*[0-9]+/ 103 | | /aolserver\/([0-9]+\.)*[0-9]+/ 104 | | /resin\/([0-9]+\.)*s?[0-9]+/ 105 | | /netscape-enterprise\/([0-9]+\.)*[0-9a-z]{1,2}+ *(aol)?/ 106 | | /mapquest listener/ 107 | | /miixpc\/[0-9]+\.[0-9]+/ 108 | | /sun-one-web-server\/[0-9]+\.[0-9]+/ 109 | | /appledotmacserver/ 110 | | /cj\/[0-9]+\.[0-9]+/ 111 | | /jigsaw\/([0-9]+\.)*[0-9]+/ 112 | | /boa\/[0-9]+\.[0-9]+(\.[0-9]+(rc[0-9]+)?)?/ 113 | | /tux\/[0-9]+\.[0-9]+ *\(linux\)/ 114 | | /igfe/ 115 | | /trafficmarketplace-jforce\/([0-9]+\.)*[0-9]+/ 116 | | /lighttpd/ 117 | | /hitbox gateway ([0-9]+\.)*[0-9]+ [a-z][0-9]/ 118 | | /jbird\/[0-9]+\.[0-9a-z]{1,2}/ 119 | | /perlbal/ 120 | | /big-ip/ 121 | | /konichiwa\/[0-9]+\.[0-9]+/ 122 | | /footprint [0-9]+\.[0-9]+\/fpmc/ 123 | | /iii [0-9]+/ 124 | | /clickability web server\/([0-9]+\.)*[0-9]+ *\(unix\)/ 125 | | /accipiter-directserver\/([0-9]+\.)*[0-9]+ \(nt; pentium\)/ 126 | | /ibm-proxy-wte\/([0-9]+\.)*[0-9]+/ 127 | | /netscape-commerce\/[0-9]+\.[0-9]+/ 128 | | /nde/ 129 | ; 130 | 131 | function do_apache_server(server: string): string 132 | { 133 | local apache_parts = split_all(server, apache_server); 134 | if ( apache_parts[3] == "" ) 135 | return apache_parts[2]; 136 | 137 | local apache_return_string = apache_parts[2]; 138 | local mod_parts = split(apache_parts[3], / /); 139 | 140 | for ( part in mod_parts ) 141 | { 142 | if ( mod_parts[part] == apache_mod_pat ) 143 | { 144 | apache_return_string = 145 | string_cat(apache_return_string, 146 | " "); 147 | apache_return_string = 148 | string_cat(apache_return_string, 149 | mod_parts[part]); 150 | } 151 | else 152 | print http_anon_log, fmt("** unknown Apache mod: %s:%s", mod_parts[part], server); 153 | } 154 | 155 | return apache_return_string; 156 | } 157 | 158 | function check_server(server: string, server_pat: pattern): bool 159 | { 160 | return server_pat in server; 161 | } 162 | 163 | function do_server(server: string, server_pat: pattern): string 164 | { 165 | return split_all(server, server_pat)[2]; 166 | } 167 | 168 | function filter_in_http_server(server: string): string 169 | { 170 | # Vanilla Apache is a hard one and a special case. Let's get the 171 | # nastiness over first. 172 | 173 | if ( apache_server in server ) 174 | return do_apache_server(server); 175 | 176 | if ( check_server(server, apache_misc) ) 177 | return do_server(server, apache_misc); 178 | if ( check_server(server, IBM_server) ) 179 | return do_server(server, IBM_server); 180 | if ( check_server(server, zedo_server) ) 181 | return do_server(server, zedo_server); 182 | if ( check_server(server, zope_server) ) 183 | return do_server(server, zope_server); 184 | if ( check_server(server, jetty_server) ) 185 | return do_server(server, jetty_server); 186 | if ( check_server(server, thttp_server) ) 187 | return do_server(server, thttp_server); 188 | if ( check_server(server, weblogic_server) ) 189 | return do_server(server, weblogic_server); 190 | 191 | # Grab bag. 192 | if ( misc_server in server ) 193 | return server; 194 | 195 | # Best guess - unknown Apache variant of some sort. 196 | if ( apache_basic in server ) 197 | { 198 | print http_anon_log, 199 | fmt("** unknown Apache variant: %s", server); 200 | 201 | return fmt("(bro: unknown) %s %s", 202 | split_all(server, apache_basic)[2], 203 | split_all(server, apache_platforms)[2]); 204 | } 205 | 206 | print http_anon_log, fmt("** unknown server: %s", server); 207 | 208 | return fmt("(bro: unknown) %s", anonymize_arg("server", server)); 209 | } 210 | -------------------------------------------------------------------------------- /scripts/todo/needs_review/summaries/http-summary.bro: -------------------------------------------------------------------------------- 1 | @load http 2 | @load app-summary 3 | 4 | redef capture_filters = { 5 | ["http"] = "tcp port 80 or tcp port 8080 or tcp port 8000 or tcp port 8888 or tcp port 3128", 6 | ["ipp"] = "tcp port 631", 7 | }; 8 | 9 | module HTTP_summary; 10 | 11 | global log = open_log_file("http-summary") &redef; 12 | 13 | type http_transaction: record { 14 | # standard stuff 15 | connection_id: conn_id; 16 | conn_start: time; 17 | func: string; 18 | start: time; 19 | num_req: count; 20 | req_size: count; 21 | num_resp: count; 22 | resp_size: count; 23 | 24 | # for tracking the state 25 | req_done: bool; 26 | resp_done: bool; 27 | done: bool; 28 | 29 | # http-specific stuff 30 | code: count; 31 | req_content_type: string; 32 | resp_content_type: string; 33 | conditional_get: string; 34 | user_agent: string; 35 | cache_control: string; 36 | last_modified: string; 37 | etag: string; 38 | }; 39 | 40 | type http_trans_group: record { 41 | trans: table[count] of http_transaction; 42 | first_req: count; 43 | last_req: count; 44 | }; 45 | 46 | global http_trans_table: table[conn_id] of http_trans_group; 47 | 48 | function lookup_http_trans_group(id: conn_id, create: bool): http_trans_group 49 | { 50 | if ( id !in http_trans_table ) 51 | { 52 | if ( create ) 53 | { 54 | local trans: table[count] of http_transaction; 55 | http_trans_table[id] = [ 56 | $trans = trans, $first_req = 1, $last_req = 0]; 57 | } 58 | else 59 | print fmt("HTTP trans_group not found: %s", conn_id_string(id)); 60 | } 61 | 62 | return http_trans_table[id]; 63 | } 64 | 65 | function new_http_transaction(c: connection, func: string): http_transaction 66 | { 67 | # print fmt("new http trans: %.6f %s", network_time(), func); 68 | local g = lookup_http_trans_group(c$id, T); 69 | 70 | local t = [ 71 | $connection_id = c$id, 72 | $conn_start = c$start_time, 73 | $func = func, 74 | $start = network_time(), 75 | $num_req = 0, $req_size = 0, 76 | $num_resp = 0, $resp_size = 0, 77 | $req_done = F, $resp_done = F, $done = F, 78 | $code = 0, 79 | $req_content_type = "none", 80 | $resp_content_type = "none", 81 | $conditional_get = "no", 82 | $user_agent = "none", 83 | $cache_control = "none", 84 | $last_modified = "none", 85 | $etag = "none"]; 86 | 87 | ++g$last_req; 88 | g$trans[g$last_req] = t; 89 | 90 | return t; 91 | } 92 | 93 | function lookup_http_transaction(id: conn_id, is_orig: bool): http_transaction 94 | { 95 | local g = lookup_http_trans_group(id, F); 96 | local index = is_orig ? g$last_req : g$first_req; 97 | 98 | if ( index !in g$trans ) 99 | { 100 | print fmt("HTTP transaction not found: %s : %d-%d", 101 | conn_id_string(id), g$first_req, g$last_req); 102 | } 103 | 104 | return g$trans[index]; 105 | } 106 | 107 | function end_http_transaction(t: http_transaction) 108 | { 109 | if ( t$req_done && t$resp_done ) 110 | { 111 | if ( t$done ) 112 | return; 113 | t$done = T; 114 | print_app_summary(log, t$connection_id, t$conn_start, t$func, t$start, 115 | t$num_req, t$req_size, 116 | t$num_resp, t$resp_size, 117 | fmt("code %d content_type_^ %s content_type_v %s conditional_get %s user_agent %s cache_control %s last_modified %s etag %s", 118 | t$code, 119 | t$req_content_type, t$resp_content_type, 120 | t$conditional_get, 121 | subst_string(t$user_agent, " ", "_"), 122 | subst_string(t$cache_control, " ", ""), 123 | t$last_modified == "none" ? "none" : "present", 124 | t$etag == "none" ? "none" : "present" 125 | )); 126 | } 127 | } 128 | 129 | event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) 130 | { 131 | # print fmt("http request"); 132 | local t = new_http_transaction(c, method); 133 | ++t$num_req; 134 | t$req_done = F; 135 | } 136 | 137 | event http_reply(c: connection, version: string, code: count, reason: string) 138 | { 139 | # print fmt("http reply"); 140 | local id = c$id; 141 | local g = lookup_http_trans_group(id, T); 142 | local t: http_transaction; 143 | if ( g$first_req in g$trans ) 144 | t = g$trans[g$first_req]; 145 | else 146 | t = new_http_transaction(c, "none"); 147 | 148 | ++t$num_resp; 149 | t$code = code; 150 | t$resp_done = F; 151 | } 152 | 153 | function http_request_done(c: connection, stat: http_message_stat) 154 | { 155 | # print fmt("http request done"); 156 | local t = lookup_http_transaction(c$id, T); 157 | t$req_size = t$req_size + stat$body_length; 158 | t$req_done = T; 159 | end_http_transaction(t); 160 | } 161 | 162 | function http_reply_done(c: connection, stat: http_message_stat) 163 | { 164 | # print fmt("http reply done"); 165 | local t = lookup_http_transaction(c$id, F); 166 | t$resp_size = t$resp_size + stat$body_length; 167 | if ( t$code >= 200 ) 168 | { 169 | t$resp_done = T; 170 | end_http_transaction(t); 171 | local g = lookup_http_trans_group(t$connection_id, F); 172 | ++g$first_req; 173 | } 174 | } 175 | 176 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) 177 | { 178 | if ( is_orig ) 179 | http_request_done(c, stat); 180 | else 181 | http_reply_done(c, stat); 182 | } 183 | 184 | event http_content_type(c: connection, is_orig: bool, ty: string, subty: string) 185 | { 186 | local t = lookup_http_transaction(c$id, is_orig); 187 | local type_str = fmt("%s/%s", ty, subty); 188 | if ( is_orig ) 189 | t$req_content_type = type_str; 190 | else 191 | t$resp_content_type = type_str; 192 | } 193 | 194 | function http_conditional_get(c: connection, is_orig: bool, h: mime_header_rec) 195 | { 196 | local t = lookup_http_transaction(c$id, is_orig); 197 | t$conditional_get = h$name; 198 | } 199 | 200 | function http_user_agent(c: connection, is_orig: bool, h: mime_header_rec) 201 | { 202 | local t = lookup_http_transaction(c$id, is_orig); 203 | t$user_agent = h$value; 204 | } 205 | 206 | function http_cache_control(c: connection, is_orig: bool, h: mime_header_rec) 207 | { 208 | local t = lookup_http_transaction(c$id, is_orig); 209 | t$cache_control = h$value; 210 | } 211 | 212 | function http_last_modified(c: connection, is_orig: bool, h: mime_header_rec) 213 | { 214 | local t = lookup_http_transaction(c$id, is_orig); 215 | t$last_modified = h$value; 216 | } 217 | 218 | function http_etag(c: connection, is_orig: bool, h: mime_header_rec) 219 | { 220 | local t = lookup_http_transaction(c$id, is_orig); 221 | t$etag = h$value; 222 | } 223 | 224 | # type mime_header_rec: record { 225 | # name: string; 226 | # value: string; 227 | # }; 228 | # type mime_header_list: table[count] of mime_header_rec; 229 | 230 | const conditional_get_headers = { 231 | "IF-MODIFIED-SINCE", 232 | "IF-UNMODIFIED-SINCE", 233 | "IF-MATCH", 234 | "IF-NONE-MATCH", 235 | "IF-RANGE", 236 | }; 237 | 238 | event http_all_headers(c: connection, is_orig: bool, hlist: mime_header_list) 239 | { 240 | if ( ! is_orig ) 241 | return; 242 | 243 | for ( i in hlist ) 244 | { 245 | local h = hlist[i]; 246 | if ( h$name in conditional_get_headers ) 247 | http_conditional_get(c, is_orig, h); 248 | if ( h$name == "USER-AGENT" ) 249 | http_user_agent(c, is_orig, h); 250 | if ( h$name == "CACHE-CONTROL" ) 251 | http_cache_control(c, is_orig, h); 252 | if ( h$name == "LAST-MODIFIED" ) 253 | http_last_modified(c, is_orig, h); 254 | if ( h$name == "ETAG" ) 255 | http_etag(c, is_orig, h); 256 | } 257 | } 258 | 259 | function end_http_trans_group(g: http_trans_group, index: count) 260 | { 261 | if ( index !in g$trans ) 262 | return; 263 | local t = g$trans[index]; 264 | 265 | t$req_done = T; 266 | t$resp_done = T; 267 | end_http_transaction(t); 268 | 269 | delete g$trans[index]; 270 | end_http_trans_group(g, index + 1); 271 | } 272 | 273 | event connection_state_remove(c: connection) 274 | { 275 | local id = c$id; 276 | if ( id in http_trans_table ) 277 | { 278 | end_http_trans_group(http_trans_table[id], 1); 279 | delete http_trans_table[id]; 280 | } 281 | } 282 | --------------------------------------------------------------------------------