├── README.md ├── extract_submit ├── extract_submit.bro └── test │ └── files.cap ├── process_metadata └── process_metadata.bro ├── share └── bro │ └── policy │ └── frameworks │ └── intel │ └── do_notice.bro └── statsd_test └── statsd_test.bro /README.md: -------------------------------------------------------------------------------- 1 | # bro-scripts 2 | Collection of bro scripts 3 | 4 | ## extract_submit 5 | Script to extract files of a specific mime_type and subject them to further processing 6 | 7 | ## process_metadata 8 | Script to extract and evaluate file metadata - for example to see if a recent compile time is present in an executable 9 | 10 | ## statsd_test 11 | Configuration to make use of JustinAzoff's bro-statsd-plugin 12 | 13 | ## share/bro/policy/frameworks/intel 14 | Customisations to the default intel scripts 15 | -------------------------------------------------------------------------------- /extract_submit/extract_submit.bro: -------------------------------------------------------------------------------- 1 | # 2 | # This script will undertale additional processing of files extracted 3 | # by the ANALYZER_EXTRACT module. 4 | # 5 | # Requires that the global ext_map contained in the 6 | # bro/share/bro/file-extraction/extract.bro 7 | # file includes relevant mime_types 8 | # 9 | # The cuckoo-submit.sh script will take an action on the extracted file: 10 | # - submitting it to cuckoo sandbox or other automated analysis platform 11 | # 12 | # Hat tip to https://github.com/hosom/bro-file-extraction 13 | # 14 | 15 | # needed to keep bro running while waiting for when to complete 16 | redef exit_only_after_terminate=T; 17 | 18 | # add cuckoo_id column to files.log file 19 | redef record Files::Info += { 20 | cuckoo_id: int &optional &log; 21 | }; 22 | 23 | global my_ext_map: table[string] of string = { 24 | ["application/msword"] = "doc", 25 | ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = "docx", 26 | ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = "xlsx", 27 | ["application/vnd.openxmlformats-officedocument.presentationml.presentation"] = "pptx", 28 | ["application/java-archive"] = "jar", 29 | ["application/x-java-applet"] = "jar", 30 | ["application/x-java-jnlp-file"] = "jnlp", 31 | ["application/x-dosexec"] = "exe", 32 | ["application/pdf"] = "pdf", 33 | ["application/zip"] = "zip", 34 | ["text/plain"] = "txt", 35 | ["image/jpeg"] = "jpg", 36 | ["image/png"] = "png", 37 | ["text/html"] = "html", 38 | } &default =""; 39 | 40 | const extraction_types: set[string] = { 41 | "application/msword", 42 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 43 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 44 | "application/vnd.openxmlformats-officedocument.presentationml.presentation", 45 | "application/x-dosexec", 46 | "application/pdf", 47 | "application/zip", 48 | "application/java-archive", 49 | "application/x-java-applet", 50 | "application/x-java-jnlp-file" 51 | }; 52 | 53 | export 54 | { 55 | const tool = fmt("/usr/local/bin/cuckoo-submit.sh"); 56 | redef enum Notice::Type += { 57 | ## Generated if file is extracted and analysed 58 | File::Cuckoo_Submission 59 | }; 60 | } 61 | 62 | function submit_cuckoo(f: fa_file): int 63 | { 64 | local command = Exec::Command($cmd=fmt("%s extract_files/%s",tool,f$info$extracted)); 65 | return when ( local result = Exec::run(command)){ 66 | local id: int = to_int(result$stdout[0]); 67 | return id; 68 | } 69 | } 70 | 71 | 72 | event file_sniff(f: fa_file, meta: fa_metadata) 73 | { 74 | if ( meta?$mime_type && meta$mime_type in extraction_types ) 75 | { 76 | local ext = ""; 77 | ext = my_ext_map[meta$mime_type]; 78 | local fname = fmt("%s-%s.%s", f$source, f$id, ext); 79 | Files::add_analyzer(f, Files::ANALYZER_EXTRACT, [$extract_filename=fname]); 80 | } 81 | } 82 | 83 | event file_state_remove( f: fa_file ) 84 | { 85 | if (f$info?$extracted) { 86 | when (local id = submit_cuckoo(f)){ 87 | f$info$cuckoo_id = id; 88 | print fmt("Cuckoo ID value set: %d", f$info$cuckoo_id); 89 | NOTICE([$note=File::Cuckoo_Submission,$msg=fmt("https://cuckoo/analysis/%s", id),$f=f]); 90 | } 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /extract_submit/test/files.cap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitunique/bro-scripts/a0e6e71b919dfb7fc8b1ec0593987ae3dfe4a306/extract_submit/test/files.cap -------------------------------------------------------------------------------- /process_metadata/process_metadata.bro: -------------------------------------------------------------------------------- 1 | # 2 | # Scripts to analyse file metadata that may be useful in identifying suspicious activity 3 | # Currently supports PE compile time 4 | # 5 | # Future versions should look at 6 | # - office document metadata 7 | # - PDF file attributes 8 | # 9 | 10 | event pe_file_header(f: fa_file, h: PE::FileHeader) 11 | { 12 | local delta_time: interval = 30 days ; 13 | if ( f$pe?$compile_ts ){ 14 | if ( network_time() - f$pe$compile_ts < delta_time ) { 15 | NOTICE([$note=RecentCompileTime, 16 | $msg=fmt("Recently compiled executable detected - file id: %s, compile time %s.",f$id,strftime("%Y-%m-%d %H:%M:%S",f$pe$compile_ts))]); 17 | 18 | } 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /share/bro/policy/frameworks/intel/do_notice.bro: -------------------------------------------------------------------------------- 1 | 2 | @load base/frameworks/intel 3 | @load base/frameworks/notice 4 | 5 | module Intel; 6 | 7 | export { 8 | redef enum Notice::Type += { 9 | ## Intel::Notice is a notice that happens when an intelligence 10 | ## indicator is denoted to be notice-worthy. 11 | Intel::Notice 12 | }; 13 | 14 | redef record Intel::MetaData += { 15 | ## A boolean value to allow the data itself to represent 16 | ## if the indicator that this metadata is attached to 17 | ## is notice worthy. 18 | do_notice: bool &default=F; 19 | 20 | ## Restrictions on when notices are created to only create 21 | ## them if the *do_notice* field is T and the notice was 22 | ## seen in the indicated location. 23 | if_in: Intel::Where &optional; 24 | }; 25 | } 26 | 27 | event Intel::match(s: Seen, items: set[Item]) 28 | { 29 | for ( item in items ) 30 | { 31 | if ( item$meta$do_notice && 32 | (! item$meta?$if_in || s$where == item$meta$if_in) ) 33 | { 34 | local n = Notice::Info($note=Intel::Notice, 35 | $msg=fmt("Intel hit on %s at %s from %s", s$indicator, s$where, item$meta$source), 36 | $sub=s$indicator); 37 | 38 | if ( s?$conn ) 39 | n$conn = s$conn; 40 | 41 | NOTICE(n); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /statsd_test/statsd_test.bro: -------------------------------------------------------------------------------- 1 | # 2 | # Script used to provide data to the statsd plugin 3 | # 4 | # See https://github.com/JustinAzoff/bro-statsd-plugin 5 | # 6 | # Works with https://github.com/kamon-io/docker-grafana-graphite 7 | # 8 | 9 | @load base/protocols/http 10 | @load base/protocols/ssh 11 | @load-plugin NCSA::Statsd 12 | 13 | event connection_established(c: connection) 14 | { 15 | statsd_increment("bro.connection.established", 1); 16 | } 17 | 18 | event connection_rejected(c: connection) 19 | { 20 | statsd_increment("bro.connection.rejected", 1); 21 | } 22 | 23 | event HTTP::log_http(rec: HTTP::Info) 24 | { 25 | local size = rec$response_body_len; 26 | 27 | statsd_increment("bro.http.request", 1); 28 | statsd_increment("bro.http.bytes", size); 29 | 30 | local s = fmt("bro.http.status_code.%d", rec$status_code); 31 | statsd_increment(s, 1); 32 | } 33 | 34 | event dns_message(c: connection, is_orig: bool, msg: dns_msg, len: count) 35 | { 36 | if ( c$dns$qtype_name=="A") 37 | { 38 | statsd_increment("bro.dns.query_type.a", 1); 39 | if ( msg$rcode==3 ) #rcode=3 for NXDOMAIN 40 | { 41 | statsd_increment("bro.dns.nxdomain", 1); 42 | } 43 | } 44 | 45 | else 46 | { 47 | local s = fmt("bro.dns.query_type.%s", c$dns$qtype_name); 48 | statsd_increment(s, 1); 49 | } 50 | 51 | } 52 | 53 | event ssh_auth_successful(c: connection, auth_method_none: bool) 54 | { 55 | if (c$id$resp_h == 192.168.1.77 && c$id$orig_h !in 192.168.1.0/24) 56 | { 57 | statsd_increment("bro.ssh.login.remote-to-local", 1); 58 | } 59 | if (c$id$resp_h in 192.168.1.0/24 && c$id$orig_h in 192.168.1.0/24) 60 | { 61 | statsd_increment("bro.ssh.login.local-to-local", 1); 62 | } 63 | else 64 | { 65 | statsd_increment("bro.ssh.login.local-to-remote", 1); 66 | } 67 | } 68 | 69 | event ssh_auth_failed(c: connection) 70 | { 71 | if (c$id$resp_h == 192.168.1.77 && c$id$orig_h !in 192.168.1.0/24) 72 | { 73 | statsd_increment("bro.ssh.login.failed.remote-to-local", 1); 74 | } 75 | if (c$id$resp_h in 192.168.1.0/24 && c$id$orig_h in 192.168.1.0/24) 76 | { 77 | statsd_increment("bro.ssh.login.failed.local-to-local", 1); 78 | } 79 | else 80 | { 81 | statsd_increment("bro.ssh.login.failed.local-to-remote", 1); 82 | } 83 | 84 | } 85 | --------------------------------------------------------------------------------