├── .gitignore ├── misc ├── hassh │ ├── __load__.zeek │ ├── LICENSE.txt │ └── hassh.zeek ├── ja3 │ ├── __load__.zeek │ ├── intel_ja3.zeek │ ├── ja3s.zeek │ └── ja3.zeek ├── conn-add-worker.zeek └── conn-add-geoip.zeek ├── plugins ├── community_id.zeek ├── publish-community_id │ ├── dhcp.zeek │ ├── tunnel.zeek │ ├── ntp.zeek │ ├── modbus.zeek │ ├── radius.zeek │ ├── syslog.zeek │ ├── __load__.zeek │ ├── ssh.zeek │ ├── ftp.zeek │ ├── sip.zeek │ ├── rfb.zeek │ ├── irc.zeek │ ├── smtp.zeek │ ├── dce_rpc.zeek │ ├── dnp3.zeek │ ├── socks.zeek │ ├── http.zeek │ ├── dns.zeek │ ├── ssl.zeek │ ├── mysql.zeek │ ├── rdp.zeek │ ├── snmp.zeek │ ├── ntlm.zeek │ ├── connection.zeek │ ├── krb.zeek │ ├── smb.zeek │ └── README.md ├── afpacket.zeek └── kafka.zeek ├── frameworks ├── files │ ├── fsf-client │ │ ├── conf │ │ │ ├── __init__.py │ │ │ └── config.py │ │ └── fsf_client.py │ ├── extraction │ │ ├── __load__.zeek │ │ ├── plugins │ │ │ ├── extract-common-exploit-types.zeek │ │ │ ├── extract-executable-types.zeek │ │ │ ├── extract-all-files.zeek │ │ │ ├── extract-pe.zeek │ │ │ ├── extract-macho.zeek │ │ │ ├── extract-pdf.zeek │ │ │ ├── __load__.zeek │ │ │ ├── extract-elf.zeek │ │ │ ├── extract-java.zeek │ │ │ ├── extract-scripts.zeek │ │ │ ├── extract-ms-office.zeek │ │ │ ├── store-files-by-md5.zeek │ │ │ ├── store-files-by-sha1.zeek │ │ │ ├── extract-archive.zeek │ │ │ └── store-files-by-sha256.zeek │ │ ├── main.zeek │ │ ├── LICENSE │ │ ├── README.md │ │ └── file-extensions.zeek │ ├── extract2fsf.zeek │ └── unified2-integration.zeek ├── intel │ ├── intel-1.dat │ ├── intel.zeek │ └── __load__.zeek ├── logging │ ├── disable-ascii.zeek │ └── extension.zeek ├── compliance │ └── detect-insecure-protos.zeek └── notice │ └── scot-integration.zeek ├── __load__.zeek ├── protocols ├── pop3 │ ├── __load__.zeek │ └── main.zeek ├── smtp │ ├── extract_smtp_body.zeek │ └── smtp-url.zeek ├── http │ ├── cookie-log.zeek │ └── http-body-url-extraction.zeek ├── ssl │ ├── ssl-add-cert-hash.zeek │ ├── generate_zeek_dod_ca.sh │ └── new-certs.zeek └── dns │ └── known_domains.zeek ├── README.adoc ├── rock.zeek ├── skeleton.zeek ├── utils └── json.zeek └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .state 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /misc/hassh/__load__.zeek: -------------------------------------------------------------------------------- 1 | @load ./hassh 2 | -------------------------------------------------------------------------------- /plugins/community_id.zeek: -------------------------------------------------------------------------------- 1 | @load ./publish-community_id 2 | -------------------------------------------------------------------------------- /frameworks/files/fsf-client/conf/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['config'] 2 | -------------------------------------------------------------------------------- /frameworks/files/extraction/__load__.zeek: -------------------------------------------------------------------------------- 1 | @load ./main 2 | @load ./plugins 3 | -------------------------------------------------------------------------------- /misc/ja3/__load__.zeek: -------------------------------------------------------------------------------- 1 | @load ./ja3 2 | @load ./intel_ja3 3 | @load ./ja3s 4 | -------------------------------------------------------------------------------- /plugins/publish-community_id/dhcp.zeek: -------------------------------------------------------------------------------- 1 | export { 2 | redef record DHCP::Info += { 3 | cummunity_id: string &optional &log; 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-common-exploit-types.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | @load ./extract-java 4 | @load ./extract-pe 5 | @load ./extract-ms-office 6 | @load ./extract-pdf 7 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-executable-types.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | @load ./extract-pe 4 | @load ./extract-elf 5 | @load ./extract-macho 6 | @load ./extract-scripts 7 | 8 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-all-files.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=10 6 | { 7 | break; 8 | } 9 | -------------------------------------------------------------------------------- /frameworks/intel/intel-1.dat: -------------------------------------------------------------------------------- 1 | #fields indicator indicator_type meta.source meta.desc meta.url 2 | #1.2.3.4 Intel::ADDR source1 Sending phishing email http://source1.com/badhosts/1.2.3.4 3 | #a.b.com Intel::DOMAIN source2 Name used for data exfiltration - 4 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-pe.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 6 | { 7 | if ( meta$mime_type == "application/x-dosexec" ) 8 | break; 9 | } 10 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-macho.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 6 | { 7 | if ( meta$mime_type == "application/x-mach-o-executable" ) 8 | break; 9 | } 10 | -------------------------------------------------------------------------------- /plugins/publish-community_id/tunnel.zeek: -------------------------------------------------------------------------------- 1 | ### No suitable events for accessing connection and Tunnel::Info records 2 | 3 | @ifdef (Tunnel::Info) 4 | export { 5 | redef record Tunnel::Info += { 6 | community_id: string &optional &log; 7 | }; 8 | } 9 | @endif 10 | -------------------------------------------------------------------------------- /frameworks/intel/intel.zeek: -------------------------------------------------------------------------------- 1 | @load policy/frameworks/intel/seen 2 | @load policy/frameworks/intel/do_notice 3 | 4 | # See https://www.bro.org/sphinx-git/frameworks/intel.html 5 | # for info on how to customize 6 | 7 | redef Intel::read_files += { 8 | fmt("%s/intel-1.dat", @DIR), 9 | }; 10 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-pdf.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const pdf_types: set[string] = { "application/pdf" }; 6 | 7 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 8 | { 9 | if ( meta$mime_type in pdf_types ) 10 | break; 11 | } 12 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/__load__.zeek: -------------------------------------------------------------------------------- 1 | #@load ./extract-all-files 2 | @load ./extract-common-exploit-types 3 | @load ./extract-executable-types 4 | #@load ./extract-java 5 | #@load ./extract-ms-office 6 | #@load ./extract-pdf 7 | #@load ./extract-pe 8 | #@load ./store-files-by-md5 9 | #@load ./store-files-by-sha1 10 | #@load ./store-files-by-sha256 11 | -------------------------------------------------------------------------------- /plugins/publish-community_id/ntp.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (NTP::Info) 2 | export { 3 | redef record NTP::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event ntp_message(c: connection, is_orig: bool, msg: NTP::Message) 9 | { 10 | if ( ! c$ntp?$community_id && c?$community_id ) 11 | c$ntp$community_id = c$community_id; 12 | } 13 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/modbus.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (Modbus::Info) 2 | export { 3 | redef record Modbus::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool) 9 | { 10 | if ( ! c$modbus?$community_id && c?$community_id ) 11 | c$modbus$community_id = c$community_id; 12 | } 13 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/radius.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (RADIUS::Info) 2 | 3 | export { 4 | redef record RADIUS::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event radius_message(c: connection, result: RADIUS::Message) 10 | { 11 | if ( ! c$radius?$community_id && c?$community_id ) 12 | c$radius$community_id = c$community_id; 13 | } 14 | 15 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/syslog.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (Syslog::Info) 2 | export { 3 | redef record Syslog::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event syslog_message(c: connection, facility: count, severity: count, msg: string) 9 | { 10 | if ( !c$syslog?$community_id && c?$community_id ) 11 | c$syslog$community_id = c$community_id; 12 | } 13 | 14 | @endif 15 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-elf.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const linux_types: set[string] = { 6 | "application/x-object", 7 | "application/x-executable", 8 | "application/x-sharedlib", 9 | "application/x-coredump" 10 | }; 11 | 12 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 13 | { 14 | if ( meta$mime_type in linux_types ) 15 | break; 16 | } 17 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-java.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const java_types: set[string] = { 6 | "application/java-archive", 7 | "application/x-java-applet", 8 | "application/x-java-jnlp-file" 9 | }; 10 | 11 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 12 | { 13 | if ( meta$mime_type in java_types ) 14 | break; 15 | } 16 | -------------------------------------------------------------------------------- /plugins/publish-community_id/__load__.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (CommunityID::hash_conn) 2 | @load ./connection 3 | @load ./dce_rpc 4 | #@load ./dhcp 5 | @load ./dnp3 6 | @load ./dns 7 | @load ./ftp 8 | @load ./http 9 | @load ./irc 10 | @load ./krb 11 | @load ./modbus 12 | @load ./mysql 13 | @load ./ntlm 14 | @load ./ntp 15 | @load ./radius 16 | @load ./rdp 17 | @load ./rfb 18 | @load ./sip 19 | @load ./smb 20 | @load ./smtp 21 | @load ./snmp 22 | @load ./socks 23 | @load ./ssh 24 | @load ./ssl 25 | @load ./syslog 26 | @load ./tunnel 27 | @endif 28 | -------------------------------------------------------------------------------- /frameworks/files/fsf-client/conf/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Basic configuration attributes for scanner client. 4 | # 5 | 6 | # 'IP Address' is a list. It can contain one element, or more. 7 | # If you put multiple FSF servers in, the one your client chooses will 8 | # be done at random. A rudimentary way to distribute tasks. 9 | SERVER_CONFIG = { 'IP_ADDRESS' : ['127.0.0.1',], 10 | 'PORT' : 5800 } 11 | 12 | # Full path to debug file if run with --suppress-report 13 | CLIENT_CONFIG = { 'LOG_FILE' : '/tmp/client_dbg.log' } 14 | -------------------------------------------------------------------------------- /plugins/publish-community_id/ssh.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SSH::Info) 2 | export { 3 | redef record SSH::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event ssh_client_version(c: connection, version: string) 9 | { 10 | if ( ! c$ssh?$community_id && c?$community_id ) 11 | c$ssh$community_id = c$community_id; 12 | } 13 | 14 | event ssh_server_version(c: connection, version: string) 15 | { 16 | if ( ! c$ssh?$community_id && c?$community_id ) 17 | c$ssh$community_id = c$community_id; 18 | } 19 | 20 | @endif -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-scripts.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const script_types: set[string] = { 6 | "text/x-shellscript", 7 | "text/x-perl", 8 | "text/x-ruby", 9 | "text/x-python", 10 | "text/x-awk", 11 | "text/x-tcl", 12 | "text/x-lua", 13 | #"application/javascript", # Let's skip this one, but listing for completeness 14 | "text/x-php" 15 | }; 16 | 17 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 18 | { 19 | if ( meta$mime_type in script_types ) 20 | break; 21 | } 22 | -------------------------------------------------------------------------------- /plugins/publish-community_id/ftp.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (FTP::Info) 2 | 3 | export { 4 | redef record FTP::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event ftp_request(c: connection, command: string, arg: string) 10 | { 11 | if ( ! c$ftp?$community_id && c?$community_id ) 12 | c$ftp$community_id = c$community_id; 13 | } 14 | 15 | event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool) 16 | { 17 | if ( ! c$ftp?$community_id && c?$community_id ) 18 | c$ftp$community_id = c$community_id; 19 | } 20 | 21 | @endif -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-ms-office.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const office_types: set[string] = { "application/msword", 6 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 7 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 8 | "application/vnd.openxmlformats-officedocument.presentationml.presentation", 9 | }; 10 | 11 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 12 | { 13 | if ( meta$mime_type in office_types ) 14 | break; 15 | } 16 | -------------------------------------------------------------------------------- /plugins/publish-community_id/sip.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SIP::Info) 2 | export { 3 | redef record SIP::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event sip_reply(c: connection, version: string, code: count, reason: string) 9 | { 10 | if ( ! c$sip?$community_id && c?$community_id ) 11 | c$sip$community_id = c$community_id; 12 | } 13 | 14 | event sip_request(c: connection, method: string, original_URI: string, version: string) 15 | { 16 | if ( ! c$sip?$community_id && c?$community_id ) 17 | c$sip$community_id = c$community_id; 18 | } 19 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/rfb.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (RFB::Info) 2 | export { 3 | redef record RFB::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event rfb_client_version(c: connection, major_version: string, minor_version: string) 9 | { 10 | if ( ! c$rfb?$community_id && c?$community_id ) 11 | c$rfb$community_id = c$community_id; 12 | } 13 | 14 | event rfb_server_version(c: connection, major_version: string, minor_version: string) 15 | { 16 | if ( ! c$rfb?$community_id && c?$community_id ) 17 | c$rfb$community_id = c$community_id; 18 | } 19 | 20 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/irc.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (IRC::Info) 2 | 3 | export { 4 | redef record IRC::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | event irc_request(c: connection, is_orig: bool, prefix: string, command: string, arguments: string) 9 | { 10 | if ( ! c$irc?$community_id && c?$community_id ) 11 | c$irc$community_id = c$community_id; 12 | } 13 | 14 | event irc_reply(c: connection, is_orig: bool, prefix: string, code: count, params: string) 15 | { 16 | if ( ! c$irc?$community_id && c?$community_id ) 17 | c$irc$community_id = c$community_id; 18 | } 19 | 20 | @endif 21 | -------------------------------------------------------------------------------- /plugins/publish-community_id/smtp.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SMTP::Info) 2 | 3 | export { 4 | redef record SMTP::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, msg: string, cont_resp: bool) 10 | { 11 | if ( ! c$smtp?$community_id && c?$community_id ) 12 | c$smtp$community_id = c$community_id; 13 | } 14 | 15 | event smtp_request(c: connection, is_orig: bool, command: string, arg: string) 16 | { 17 | if ( ! c$smtp?$community_id && c?$community_id ) 18 | c$smtp$community_id = c$community_id; 19 | } 20 | @endif 21 | -------------------------------------------------------------------------------- /__load__.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | @load ./rock 16 | -------------------------------------------------------------------------------- /plugins/publish-community_id/dce_rpc.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (DCE_RPC::Info) 2 | export { 3 | redef record DCE_RPC::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event dce_rpc_request(c: connection, fid: count, ctx_id: count, opnum: count, stub_len: count) 9 | { 10 | if ( ! c$dce_rpc?$community_id && c?$community_id ) 11 | c$dce_rpc$community_id = c$community_id; 12 | } 13 | 14 | event dce_rpc_response(c: connection, fid: count, ctx_id: count, opnum: count, stub_len: count) 15 | { 16 | if ( ! c$dce_rpc?$community_id && c?$community_id ) 17 | c$dce_rpc$community_id = c$community_id; 18 | } 19 | 20 | @endif -------------------------------------------------------------------------------- /protocols/pop3/__load__.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | @load ./main 15 | -------------------------------------------------------------------------------- /plugins/publish-community_id/dnp3.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (DNP3::Info) 2 | 3 | export { 4 | redef record DNP3::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event dnp3_application_request_header(c: connection, is_orig: bool, application: count, fc: count) 10 | { 11 | if ( ! c$dnp3?$community_id && c?$community_id ) 12 | c$dnp3$community_id = c$community_id; 13 | } 14 | 15 | event dnp3_application_response_header(c: connection, is_orig: bool, application: count, fc: count, iin: count) 16 | { 17 | if ( ! c$dnp3?$community_id && c?$community_id ) 18 | c$dnp3$community_id = c$community_id; 19 | } 20 | 21 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/socks.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SOCKS::Info) 2 | 3 | export { 4 | redef record SOCKS::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event socks_reply(c: connection, version: count, reply: count, sa: SOCKS::Address, p: port) 10 | { 11 | if ( ! c$socks?$community_id && c?$community_id ) 12 | c$socks$community_id = c$community_id; 13 | } 14 | 15 | event socks_request(c: connection, version: count, request_type: count, sa: SOCKS::Address, p: port, user: string) 16 | { 17 | if ( ! c$socks?$community_id && c?$community_id ) 18 | c$socks$community_id = c$community_id; 19 | } 20 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/http.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (HTTP::Info) 2 | 3 | export { 4 | # Add the ID to the http record 5 | redef record HTTP::Info += { 6 | community_id: string &optional &log; 7 | }; 8 | } 9 | 10 | event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) 11 | { 12 | if (! c$http?$community_id && c?$community_id) 13 | c$http$community_id = c$community_id; 14 | } 15 | 16 | event http_reply(c: connection, version: string, code: count, reason: string) 17 | { 18 | if (! c$http?$community_id && c?$community_id) 19 | c$http$community_id = c$community_id; 20 | } 21 | 22 | @endif -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/store-files-by-md5.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | @load policy/frameworks/files/hash-all-files 3 | 4 | event file_state_remove(f: fa_file) 5 | { 6 | 7 | if ( !f$info?$extracted || !f$info?$md5 || FileExtraction::path == "" ) 8 | return; 9 | 10 | local orig = f$info$extracted; 11 | 12 | local split_orig = split_string(f$info$extracted, /\./); 13 | local extension = split_orig[|split_orig|-1]; 14 | 15 | local dest = fmt("%s%s-%s.%s", FileExtraction::path, f$source, f$info$md5, extension); 16 | 17 | local cmd = fmt("mv %s %s", orig, dest); 18 | when ( local result = Exec::run([$cmd=cmd]) ) 19 | { 20 | } 21 | f$info$extracted = dest; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/store-files-by-sha1.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | @load policy/frameworks/files/hash-all-files 3 | 4 | event file_state_remove(f: fa_file) 5 | { 6 | 7 | if ( !f$info?$extracted || !f$info?$sha1 || FileExtraction::path == "" ) 8 | return; 9 | 10 | local orig = f$info$extracted; 11 | 12 | local split_orig = split_string(f$info$extracted, /\./); 13 | local extension = split_orig[|split_orig|-1]; 14 | 15 | local dest = fmt("%s%s-%s.%s", FileExtraction::path, f$source, f$info$sha1, extension); 16 | 17 | local cmd = fmt("mv %s %s", orig, dest); 18 | when ( local result = Exec::run([$cmd=cmd]) ) 19 | { 20 | } 21 | f$info$extracted = dest; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /frameworks/intel/__load__.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017-2020, RockNSM Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | @load ./intel 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /plugins/publish-community_id/dns.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (DNS::Info) 2 | 3 | export { 4 | # Add the ID to the DNS record 5 | redef record DNS::Info += { 6 | community_id: string &optional &log; 7 | }; 8 | } 9 | 10 | event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) &priority=-5 11 | { 12 | # new_connection is not being triggered for UDP, so we need another plan 13 | if (! c$dns?$community_id && c?$community_id) 14 | c$dns$community_id = c$community_id; 15 | } 16 | 17 | event dns_end(c: connection, msg: dns_msg) 18 | { 19 | if (! c$dns?$community_id && c?$community_id) 20 | c$dns$community_id = c$community_id; 21 | } 22 | 23 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/ssl.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SSL::Info) 2 | export { 3 | redef record SSL::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec) 9 | { 10 | if ( c?$community_id ) 11 | c$ssl$community_id = c$community_id; 12 | } 13 | 14 | event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) 15 | { 16 | if ( ! c$ssl?$community_id && c?$community_id ) 17 | c$ssl$community_id = c$community_id; 18 | } 19 | 20 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/mysql.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (MySQL::Info) 2 | export { 3 | redef record MySQL::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event mysql_command_request(c: connection, command: count, arg: string) 9 | { 10 | if ( ! c$mysql?$community_id && c?$community_id ) 11 | c$mysql$community_id = c$community_id; 12 | } 13 | 14 | event mysql_error(c: connection, code: count, msg: string) 15 | { 16 | if ( ! c$mysql?$community_id && c?$community_id ) 17 | c$mysql$community_id = c$community_id; 18 | } 19 | 20 | event mysql_ok(c: connection, affected_rows: count) 21 | { 22 | if ( ! c$mysql?$community_id && c?$community_id ) 23 | c$mysql$community_id = c$community_id; 24 | } 25 | @endif 26 | -------------------------------------------------------------------------------- /plugins/publish-community_id/rdp.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (RDP::Info) 2 | export { 3 | redef record RDP::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event rdp_connect_request(c: connection, cookie: string) 9 | { 10 | if ( ! c$rdp?$community_id && c?$community_id ) 11 | c$rdp$community_id = c$community_id; 12 | } 13 | 14 | event rdp_negotiation_failure(c: connection, failure_code: count) 15 | { 16 | if ( ! c$rdp?$community_id && c?$community_id ) 17 | c$rdp$community_id = c$community_id; 18 | } 19 | 20 | event rdp_negotiation_response(c: connection, security_protocol: count) 21 | { 22 | if ( ! c$rdp?$community_id && c?$community_id ) 23 | c$rdp$community_id = c$community_id; 24 | } 25 | @endif 26 | 27 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/extract-archive.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | 3 | module FileExtraction; 4 | 5 | const archive_types: set[string] = { "application/x-rar-compressed", 6 | "application/x-bzip2", 7 | "application/gzip", 8 | "application/x-lzma", 9 | "application/x-lzip", 10 | "application/x-xz", 11 | "application/x-lzop", 12 | "application/x-compress", 13 | "application/x-7z-compressed", 14 | "application/x-ace-compressed", 15 | "application/vnd.ms-cab-compressed", 16 | "application/x-gtar", 17 | "application/zip", 18 | }; 19 | 20 | hook FileExtraction::extract(f: fa_file, meta: fa_metadata) &priority=5 21 | { 22 | if ( meta$mime_type in archive_types ) 23 | break; 24 | } 25 | -------------------------------------------------------------------------------- /misc/conn-add-worker.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | redef record Conn::Info += { 15 | peer_descr: string &default="unknown" &log; 16 | }; 17 | 18 | event connection_state_remove(c: connection){ 19 | c$conn$peer_descr = peer_description; 20 | } 21 | -------------------------------------------------------------------------------- /plugins/publish-community_id/snmp.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SNMP::Info) 2 | export { 3 | redef record SNMP::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event snmp_get_request(c: connection , is_orig: bool , header: SNMP::Header , pdu: SNMP::PDU) 9 | { 10 | if ( ! c$snmp?$community_id && c?$community_id ) 11 | c$snmp$community_id = c$community_id; 12 | } 13 | 14 | event snmp_set_request(c: connection , is_orig: bool , header: SNMP::Header , pdu: SNMP::PDU) 15 | { 16 | if ( ! c$snmp?$community_id && c?$community_id ) 17 | c$snmp$community_id = c$community_id; 18 | } 19 | 20 | event snmp_response(c: connection , is_orig: bool , header: SNMP::Header , pdu: SNMP::PDU) 21 | { 22 | if ( ! c$snmp?$community_id && c?$community_id ) 23 | c$snmp$community_id = c$community_id; 24 | } 25 | @endif -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = README 2 | 3 | == A collection of Bro scripts 4 | 5 | This is a collection of bro scripts that do some unique things. To use, 6 | go to your `site` directory and check it out to a named item. 7 | 8 | [source,bash] 9 | ---- 10 | cd /opt/bro/share/bro/site 11 | git clone https://github.com/mocyber/bro-scripts.git rock 12 | ---- 13 | 14 | Then, in your `local.bro` add the following at the bottom: 15 | 16 | [source,bro] 17 | ---- 18 | # Load the cyberdev scripts 19 | @load rock 20 | ---- 21 | 22 | This loads all the scripts defined in `__load__.bro`. If there's a script 23 | that you'd like to use that is not in there, you can load it directly: 24 | 25 | [source,bro] 26 | ---- 27 | # Load JSON util function 28 | @load rock/utils/json 29 | ---- 30 | 31 | == TODO 32 | 33 | - [ ] Cleanup scripts and add common header 34 | - [ ] Create skeleton script file as a starter 35 | -------------------------------------------------------------------------------- /frameworks/logging/disable-ascii.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Disable bro logging to filesystem for all logs 16 | event zeek_init() &priority=-5 17 | { 18 | for (stream_id in Log::active_streams) 19 | { 20 | Log::remove_filter(stream_id, "default"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /plugins/afpacket.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Workaround for AF_Packet plugin across multiple interfaces 16 | # See https://bro-tracker.atlassian.net/browse/BIT-1747 for more info 17 | redef AF_Packet::fanout_id = strcmp(getenv("fanout_id"),"") == 0 ? 0 : to_count(getenv("fanout_id")); 18 | -------------------------------------------------------------------------------- /frameworks/files/extraction/main.zeek: -------------------------------------------------------------------------------- 1 | @load ./file-extensions 2 | 3 | module FileExtraction; 4 | 5 | export { 6 | ## Path to store files 7 | const path: string = "" &redef; 8 | ## Hook to include files in extraction 9 | global extract: hook(f: fa_file, meta: fa_metadata); 10 | ## Hook to exclude files from extraction 11 | global ignore: hook(f: fa_file, meta: fa_metadata); 12 | } 13 | 14 | event file_sniff(f: fa_file, meta: fa_metadata) 15 | { 16 | if ( meta?$mime_type && !hook FileExtraction::extract(f, meta) ) 17 | { 18 | if ( !hook FileExtraction::ignore(f, meta) ) 19 | return; 20 | if ( meta$mime_type in mime_to_ext ) 21 | local fext = mime_to_ext[meta$mime_type]; 22 | else 23 | fext = split_string(meta$mime_type, /\//)[1]; 24 | 25 | local fname = fmt("%s%s-%s.%s", path, f$source, f$id, fext); 26 | Files::add_analyzer(f, Files::ANALYZER_EXTRACT, 27 | [$extract_filename=fname]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /frameworks/files/extraction/plugins/store-files-by-sha256.zeek: -------------------------------------------------------------------------------- 1 | @load ../__load__ 2 | @load policy/frameworks/files/hash-all-files 3 | 4 | event file_sniff(f: fa_file, meta: fa_metadata) 5 | { 6 | 7 | if ( meta?$mime_type && !hook FileExtraction::extract(f, meta) ) 8 | { 9 | 10 | if ( !hook FileExtraction::ignore(f, meta) ) 11 | return; 12 | 13 | Files::add_analyzer(f, Files::ANALYZER_SHA256); 14 | 15 | } 16 | 17 | } 18 | 19 | event file_state_remove(f: fa_file) 20 | { 21 | 22 | if ( !f$info?$extracted || !f$info?$sha256 || FileExtraction::path == "" ) 23 | return; 24 | 25 | local orig = f$info$extracted; 26 | 27 | local split_orig = split_string(f$info$extracted, /\./); 28 | local extension = split_orig[|split_orig|-1]; 29 | 30 | local dest = fmt("%s%s-%s.%s", FileExtraction::path, f$source, f$info$sha256, extension); 31 | 32 | local cmd = fmt("mv %s %s", orig, dest); 33 | when ( local result = Exec::run([$cmd=cmd]) ) 34 | { 35 | } 36 | f$info$extracted = dest; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /misc/ja3/intel_ja3.zeek: -------------------------------------------------------------------------------- 1 | # This Bro script adds JA3 to the Bro Intel Framework as Intel::JA3 2 | # 3 | # Author: John B. Althouse (jalthouse@salesforce.com) 4 | # 5 | # Copyright (c) 2017, salesforce.com, inc. 6 | # All rights reserved. 7 | # Licensed under the BSD 3-Clause license. 8 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 9 | 10 | module Intel; 11 | 12 | export { 13 | redef enum Intel::Type += { Intel::JA3 }; 14 | } 15 | 16 | export { 17 | redef enum Intel::Where += { SSL::IN_JA3 }; 18 | } 19 | 20 | @if ( Version::at_least("2.6") || ( Version::number == 20500 && Version::info$commit >= 944 ) ) 21 | event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec) 22 | @else 23 | event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) 24 | @endif 25 | { 26 | if ( c$ssl?$ja3 ) 27 | Intel::seen([$indicator=c$ssl$ja3, $indicator_type=Intel::JA3, $conn=c, $where=SSL::IN_JA3]); 28 | } 29 | -------------------------------------------------------------------------------- /protocols/smtp/extract_smtp_body.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # This policy extracts all SMTP bodies (from client side) seen in traffic. 15 | 16 | # NOTE: On a heavy SMTP segment, this will generate a lot of files! 17 | event protocol_confirmation (c: connection, atype: Analyzer::Tag, aid: count) 18 | { 19 | if ( atype == Analyzer::ANALYZER_SMTP ) 20 | { 21 | local body_file = generate_extraction_filename(Conn::extraction_prefix, c, "client.txt"); 22 | local body_f = open(body_file); 23 | set_contents_file(c$id, CONTENTS_ORIG, body_f); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plugins/publish-community_id/ntlm.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (NTLM::Info) 2 | export { 3 | redef record NTLM::Info += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | 8 | event ntlm_authenticate(c: connection, request: NTLM::Authenticate) 9 | { 10 | if ( ! c$ntlm?$community_id && c?$community_id ) 11 | c$ntlm$community_id = c$community_id; 12 | } 13 | 14 | event ntlm_challenge(c: connection, challenge: NTLM::Challenge) 15 | { 16 | if ( ! c$ntlm?$community_id && c?$community_id ) 17 | c$ntlm$community_id = c$community_id; 18 | } 19 | 20 | event ntlm_challenge(c: connection, challenge: NTLM::Challenge) 21 | { 22 | if ( ! c$ntlm?$community_id && c?$community_id ) 23 | c$ntlm$community_id = c$community_id; 24 | } 25 | 26 | event ntlm_challenge(c: connection, challenge: NTLM::Challenge) 27 | { 28 | if ( ! c$ntlm?$community_id && c?$community_id ) 29 | c$ntlm$community_id = c$community_id; 30 | } 31 | 32 | event ntlm_negotiate(c: connection, negotiate: NTLM::Negotiate) 33 | { 34 | if ( ! c$ntlm?$community_id && c?$community_id ) 35 | c$ntlm$community_id = c$community_id; 36 | } 37 | @endif -------------------------------------------------------------------------------- /plugins/publish-community_id/connection.zeek: -------------------------------------------------------------------------------- 1 | #! Module to set up community_id and add it to the connection 2 | #! record when a new connection is created. 3 | 4 | module CommunityID; 5 | 6 | export { 7 | # An unsigned 16-bit number to seed our hashing 8 | const seed: count = 0 &redef; 9 | 10 | # Whether to add a base64 pass over the hash digest. 11 | # Enabled by default, since it shortens the output. 12 | const do_base64: bool = T &redef; 13 | 14 | # Verbose debugging log output to the console. 15 | const verbose: bool = F &redef; 16 | 17 | # Add the ID string field to the connection record, for reuse 18 | # during its lifespan 19 | redef record connection += { 20 | community_id: string &optional; 21 | }; 22 | 23 | # Add the ID to the conn record 24 | redef record Conn::Info += { 25 | community_id: string &optional &log; 26 | }; 27 | } 28 | 29 | # Add the community_id to each newly tracked connection 30 | event new_connection(c: connection) &priority=-10 31 | { 32 | c$community_id = hash_conn(c); 33 | } 34 | 35 | # Add the ID to the conn log 36 | event connection_state_remove(c: connection) 37 | { 38 | if (c?$community_id) 39 | c$conn$community_id = c$community_id; 40 | } 41 | -------------------------------------------------------------------------------- /frameworks/files/extract2fsf.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # take extracted files and submit to FSF 16 | 17 | event file_state_remove(f: fa_file) 18 | { 19 | if ( f$info?$extracted ) 20 | { 21 | # invoke the FSF-CLIENT and add the source metadata of ROCK01 (sensorID), we're suppressing the returned report 22 | # becuase we don't need that 23 | local script_path = cat(@DIR, "/fsf-client/fsf_client.py"); 24 | local scan_cmd = fmt("python %s --suppress-report --archive none --source %s %s%s", script_path, ROCK::sensor_id, FileExtract::prefix, f$info$extracted); 25 | system(scan_cmd); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /plugins/publish-community_id/krb.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (KRB::Info) 2 | 3 | export { 4 | redef record KRB::Info += { 5 | community_id: string &optional &log; 6 | }; 7 | } 8 | 9 | event krb_ap_request(c: connection, ticket: KRB::Ticket, opts: KRB::AP_Options) 10 | { 11 | if ( ! c$krb?$community_id && c?$community_id ) 12 | c$krb$community_id = c$community_id; 13 | } 14 | 15 | event krb_ap_response(c: connection) 16 | { 17 | if ( ! c$krb?$community_id && c?$community_id ) 18 | c$krb$community_id = c$community_id; 19 | } 20 | 21 | event krb_as_request(c: connection, msg: KRB::KDC_Request) 22 | { 23 | if ( ! c$krb?$community_id && c?$community_id ) 24 | c$krb$community_id = c$community_id; 25 | } 26 | 27 | event krb_as_response(c: connection, msg: KRB::KDC_Response) 28 | { 29 | if ( ! c$krb?$community_id && c?$community_id ) 30 | c$krb$community_id = c$community_id; 31 | } 32 | 33 | event krb_tgs_request(c: connection, msg: KRB::KDC_Request) 34 | { 35 | if ( ! c$krb?$community_id && c?$community_id ) 36 | c$krb$community_id = c$community_id; 37 | } 38 | 39 | event krb_tgs_response(c: connection, msg: KRB::KDC_Response) 40 | { 41 | if ( ! c$krb?$community_id && c?$community_id ) 42 | c$krb$community_id = c$community_id; 43 | } 44 | 45 | @endif 46 | -------------------------------------------------------------------------------- /plugins/kafka.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | module Kafka; 15 | 16 | redef Kafka::topic_name = "zeek-raw"; 17 | redef Kafka::json_timestamps = JSON::TS_ISO8601; 18 | redef Kafka::tag_json = F; 19 | 20 | # Enable zeek (bro) logging to kafka for all logs 21 | event zeek_init() &priority=-5 22 | { 23 | for (stream_id in Log::active_streams) 24 | { 25 | if (|Kafka::logs_to_send| == 0 || stream_id in Kafka::logs_to_send) 26 | { 27 | local filter: Log::Filter = [ 28 | $name = fmt("kafka-%s", stream_id), 29 | $writer = Log::WRITER_KAFKAWRITER, 30 | $config = table(["stream_id"] = fmt("%s", stream_id)) 31 | ]; 32 | 33 | Log::add_filter(stream_id, filter); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /protocols/http/cookie-log.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | module Cookie; 15 | 16 | export { 17 | # The fully resolve name for this will be LocationExtract::LOG 18 | redef enum Log::ID += { LOG }; 19 | type Info: record { 20 | ts: time &log; 21 | uid: string &log; 22 | id: conn_id &log; 23 | cookie: string &log; 24 | cookie_unesc: string &log; 25 | }; 26 | } 27 | 28 | event zeek_init() &priority=5 { 29 | Log::create_stream(Cookie::LOG, [$columns=Info]); 30 | } 31 | 32 | event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=5 { 33 | if ( is_orig && name == "COOKIE") { 34 | local unesc_cookie = unescape_URI(value); 35 | local log_rec: Cookie::Info = [$ts=network_time(), $uid=c$uid, $id=c$id, $cookie=value, $cookie_unesc=unesc_cookie]; 36 | Log::write(Cookie::LOG, log_rec); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /misc/hassh/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Salesforce.com, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /frameworks/files/extraction/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of bro-file-extraction nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /frameworks/logging/extension.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ## Setup event extension to include sensor and probe name 16 | type Extension: record { 17 | ## The log stream that this log was written to. 18 | stream: string &log; 19 | ## The name of the system that wrote this log. This 20 | ## is defined in the const so that 21 | ## a system running lots of processes can give the 22 | ## same value for any process that writes a log. 23 | system: string &log; 24 | ## The name of the process that wrote the log. In 25 | ## clusters, this will typically be the name of the 26 | ## worker that wrote the log. 27 | proc: string &log; 28 | }; 29 | 30 | function add_log_extension(path: string): Extension 31 | { 32 | return Extension($stream = path, 33 | $system = ROCK::sensor_id, 34 | $proc = peer_description); 35 | } 36 | 37 | redef Log::default_ext_func = add_log_extension; 38 | redef Log::default_ext_prefix = "@"; 39 | redef Log::default_scope_sep = "_"; 40 | -------------------------------------------------------------------------------- /protocols/http/http-body-url-extraction.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This scans the body of HTTP-served html documents for urls that may be 16 | # loaded in the intel database 17 | # 18 | @load base/frameworks/intel 19 | @load base/protocols/http 20 | @load base/utils/urls 21 | 22 | export { 23 | redef enum Intel::Where += { 24 | HTTP::BODY, 25 | }; 26 | } 27 | 28 | event intel_http_body(f: fa_file, data: string) 29 | { 30 | if ( ! f?$conns ) 31 | return; 32 | 33 | if ( ! f?$info || ! f$info?$mime_type || f$info$mime_type != "text/html" ) 34 | return; 35 | 36 | for ( cid in f$conns ) 37 | { 38 | local c: connection = f$conns[cid]; 39 | local urls = find_all_urls_without_scheme(data); 40 | for ( url in urls ) 41 | { 42 | Intel::seen([$indicator=url, 43 | $indicator_type=Intel::URL, 44 | $conn=c, 45 | $where=HTTP::BODY]); 46 | } 47 | } 48 | } 49 | 50 | event file_sniff(f: fa_file, meta: fa_metadata ) 51 | { 52 | if ( f$is_orig == T ) 53 | return; 54 | 55 | if ( f$source == "HTTP" ) 56 | Files::add_analyzer(f, Files::ANALYZER_DATA_EVENT, [$stream_event=intel_http_body]); 57 | } 58 | -------------------------------------------------------------------------------- /frameworks/files/unified2-integration.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020, RockNSM Foundation 2 | # 3 | ## Licensed under the Apache License, Version 2.0 (the "License"); 4 | ## you may not use this file except in compliance with the License. 5 | ## You may obtain a copy of the License at 6 | ## 7 | ## http://www.apache.org/licenses/LICENSE-2.0 8 | ## 9 | ## Unless required by applicable law or agreed to in writing, software 10 | ## distributed under the License is distributed on an "AS IS" BASIS, 11 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ## See the License for the specific language governing permissions and 13 | ## limitations under the License. 14 | 15 | # This policy adds integration of Snort and Bro as implemented in ROCK 16 | # Namely, it populates the `unified2` bro log with alerts detected by Snort 17 | # It also will attempt to add connection info to the unified2 log 18 | 19 | @load base/files/unified2 20 | 21 | # ROCK-specific paths 22 | redef Unified2::classification_config = "/etc/snort/classification.config"; 23 | redef Unified2::gen_msg = "/etc/snort/gen-msg.map"; 24 | redef Unified2::sid_msg = "/etc/snort/sid-msg.map"; 25 | redef Unified2::watch_dir = "/data/snort/"; 26 | 27 | export { 28 | # Add connection ID to unified2 log 29 | redef record Unified2::Info += 30 | { 31 | uid: string &optional &log; 32 | }; 33 | } 34 | 35 | event log_unified2 ( rec: Unified2::Info ) 36 | { 37 | # The following fields are required in the Info record. No need to check 38 | local c = lookup_connection([ 39 | $orig_h=rec$id$src_ip, 40 | $orig_p=rec$id$src_p, 41 | $resp_h=rec$id$dst_ip, 42 | $resp_p=rec$id$dst_p 43 | ]); 44 | 45 | if ( c?$id ) 46 | { 47 | # Add conn info to event 48 | rec$uid = c$uid; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /protocols/ssl/ssl-add-cert-hash.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | module SSL; 16 | 17 | export { 18 | redef record SSL::Info += { 19 | orig_certificate_sha1: string &optional &log; 20 | resp_certificate_sha1: string &optional &log; 21 | }; 22 | } 23 | 24 | event file_hash(f: fa_file, kind: string, hash: string) { 25 | if (! ( kind == "sha1" && f?$source && f$source == "SSL" )) 26 | return; 27 | 28 | if ( |f$conns| != 1 ) 29 | return; 30 | 31 | if ( ! f?$info || ! f$info?$mime_type ) 32 | return; 33 | 34 | if ( ! ( f$info$mime_type == "application/x-x509-ca-cert" || f$info$mime_type == "application/x-x509-user-cert" 35 | || f$info$mime_type == "application/pkix-cert" ) ) 36 | return; 37 | 38 | local c: connection; 39 | 40 | for ( cid in f$conns ) 41 | { 42 | c = f$conns[cid]; 43 | 44 | if ( ! c?$ssl ) 45 | return; 46 | 47 | local chain: vector of string; 48 | 49 | if ( f$is_orig ) 50 | chain = c$ssl$client_cert_chain_fuids; 51 | else 52 | chain = c$ssl$cert_chain_fuids; 53 | 54 | if ( |chain| == 0 ) 55 | { 56 | Reporter::warning(fmt("Certificate not in chain? (fuid %s)", f$id)); 57 | return; 58 | } 59 | 60 | # Check if this is the host certificate, if so, log hash by direction 61 | if ( f$id == chain[0] ) { 62 | if ( f$is_orig ) 63 | c$ssl$orig_certificate_sha1 = hash; 64 | else 65 | c$ssl$resp_certificate_sha1 = hash; 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /plugins/publish-community_id/smb.zeek: -------------------------------------------------------------------------------- 1 | @ifdef (SMB::TreeInfo) 2 | export { 3 | redef record SMB::TreeInfo += { 4 | community_id: string &optional &log; 5 | }; 6 | } 7 | @endif 8 | 9 | @ifdef (SMB::FileInfo) 10 | export{ 11 | redef record SMB::FileInfo += { 12 | community_id: string &optional &log; 13 | }; 14 | } 15 | @endif 16 | 17 | 18 | @ifdef (SMB::CmdInfo) 19 | export { 20 | redef record SMB::CmdInfo += { 21 | community_id: string &optional &log; 22 | }; 23 | } 24 | @endif 25 | 26 | event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=-10 27 | { 28 | @ifdef (SMB::CmdInfo) 29 | if ( c$smb_state?$current_cmd && ! c$smb_state$current_cmd?$community_id && c?$community_id ) 30 | c$smb_state$current_cmd$community_id = c$community_id; 31 | @endif 32 | 33 | @ifdef (SMB::FileInfo) 34 | if ( c$smb_state?$current_file && ! c$smb_state$current_file?$community_id && c?$community_id ) 35 | c$smb_state$current_file$community_id = c$community_id; 36 | @endif 37 | 38 | @ifdef (SMB::TreeInfo) 39 | if ( c$smb_state?$current_tree && ! c$smb_state$current_tree?$community_id && c?$community_id ) 40 | c$smb_state$current_tree$community_id = c$community_id; 41 | @endif 42 | } 43 | 44 | event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=-10 45 | { 46 | @ifdef (SMB::CmdInfo) 47 | if ( c$smb_state?$current_cmd && ! c$smb_state$current_cmd?$community_id && c?$community_id ) 48 | c$smb_state$current_cmd$community_id = c$community_id; 49 | @endif 50 | 51 | @ifdef (SMB::FileInfo) 52 | if ( c$smb_state?$current_file && ! c$smb_state$current_file?$community_id && c?$community_id ) 53 | c$smb_state$current_file$community_id = c$community_id; 54 | @endif 55 | 56 | @ifdef (SMB::TreeInfo) 57 | if ( c$smb_state?$current_tree && ! c$smb_state$current_tree?$community_id && c?$community_id ) 58 | c$smb_state$current_tree$community_id = c$community_id; 59 | @endif 60 | } 61 | -------------------------------------------------------------------------------- /protocols/dns/known_domains.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: On a busy network, this may consume a lot of memory. Revisit 16 | # when Broker is efficient enough to handle this. 17 | 18 | module RockNSM; 19 | 20 | export { 21 | ## The known-hosts logging stream identifier. 22 | redef enum Log::ID += { UNIQDNS_LOG }; 23 | 24 | ## The record type which contains the column fields of the known-hosts log. 25 | type Info: record { 26 | ## The timestamp at which the host was detected. 27 | ts: time &log; 28 | ## The address that was detected originating or responding to a 29 | ## TCP connection. 30 | domain: string &log; 31 | }; 32 | 33 | ## The set of all known addresses to store for preventing duplicate 34 | ## logging of addresses. It can also be used from other scripts to 35 | ## inspect if an address has been seen in use. 36 | ## Maintain the list of known hosts for 24 hours so that the existence 37 | ## of each individual address is logged each day. 38 | global known_domains: set[string] &create_expire=1 day &synchronized &redef; 39 | 40 | ## An event that can be handled to access the :bro:type:`Known::HostsInfo` 41 | ## record as it is sent on to the logging framework. 42 | global log_known_domains: event(rec: Info); 43 | } 44 | 45 | event zeek_init() 46 | { 47 | Log::create_stream(RockNSM::UNIQDNS_LOG, [$columns=Info, $ev=log_known_domains, $path="known_domains"]); 48 | local f = Log::get_filter(RockNSM::UNIQDNS_LOG, "default"); 49 | #f$interv = 15 min; 50 | Log::add_filter(RockNSM::UNIQDNS_LOG, f); 51 | } 52 | 53 | event dns_query_reply(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) 54 | { 55 | if(!c?$dns) 56 | return; 57 | if(query !in known_domains) 58 | { 59 | add known_domains[query]; 60 | Log::write( RockNSM::UNIQDNS_LOG,[$ts=network_time(),$domain=query] ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /misc/conn-add-geoip.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! Add geo_location for the originator and responder of a connection 16 | ##! to the connection logs. 17 | 18 | module Conn; 19 | 20 | export 21 | { 22 | redef record Conn::Info += 23 | { 24 | orig_location: string &optional &log; 25 | resp_location: string &optional &log; 26 | orig_country_code: string &optional &log; 27 | resp_country_code: string &optional &log; 28 | orig_asn: count &log &optional; 29 | resp_asn: count &log &optional; 30 | }; 31 | } 32 | 33 | event connection_state_remove(c: connection) 34 | { 35 | local orig_loc = lookup_location(c$id$orig_h); 36 | if (orig_loc?$longitude && orig_loc?$latitude) 37 | c$conn$orig_location= cat(orig_loc$latitude,",",orig_loc$longitude); 38 | local orig_ccode = lookup_location(c$id$orig_h); 39 | if (orig_ccode?$country_code) 40 | c$conn$orig_country_code= cat(orig_ccode$country_code); 41 | c$conn$orig_asn= lookup_asn(c$id$orig_h); 42 | local resp_loc = lookup_location(c$id$resp_h); 43 | if (resp_loc?$longitude && resp_loc?$latitude) 44 | c$conn$resp_location= cat(resp_loc$latitude,",",resp_loc$longitude); 45 | local resp_ccode = lookup_location(c$id$resp_h); 46 | if (resp_ccode?$country_code) 47 | c$conn$resp_country_code= cat(resp_ccode$country_code); 48 | c$conn$resp_asn= lookup_asn(c$id$resp_h); 49 | } 50 | 51 | export 52 | { 53 | redef record Conn::Info += 54 | { 55 | orig_location: string &optional &log; 56 | resp_location: string &optional &log; 57 | }; 58 | } 59 | 60 | event connection_state_remove(c: connection) 61 | { 62 | local orig_loc = lookup_location(c$id$orig_h); 63 | if (orig_loc?$longitude && orig_loc?$latitude) 64 | c$conn$orig_location= cat(orig_loc$latitude,",",orig_loc$longitude); 65 | local resp_loc = lookup_location(c$id$resp_h); 66 | if (resp_loc?$longitude && resp_loc?$latitude) 67 | c$conn$resp_location= cat(resp_loc$latitude,",",resp_loc$longitude); 68 | } 69 | -------------------------------------------------------------------------------- /rock.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | module ROCK; 16 | 17 | export { 18 | const sensor_id = gethostname() &redef; 19 | } 20 | 21 | #=== Bro built-ins =================================== 22 | 23 | # Enable VLAN Logging 24 | @load policy/protocols/conn/vlan-logging 25 | 26 | # Log MAC addresses 27 | @load policy/protocols/conn/mac-logging 28 | 29 | # Log (All) Client and Server HTTP Headers 30 | @load policy/protocols/http/header-names 31 | 32 | #== ROCK specific scripts ============================ 33 | # Add empty Intel framework database 34 | @load ./frameworks/intel 35 | 36 | # Load integration with FSF 37 | @load ./frameworks/files/extract2fsf 38 | 39 | # Load file extraction 40 | @load ./frameworks/files/extraction 41 | redef FileExtract::prefix = "/data/zeek/logs/extract_files/"; 42 | redef FileExtract::default_limit = 1048576000; 43 | 44 | # Add sensor and log meta information to each log 45 | @load ./frameworks/logging/extension 46 | 47 | # Log all orig and resp cert hashes in ssl log 48 | @load ./protocols/ssl/ssl-add-cert-hash 49 | 50 | # Enable pop3 logging 51 | @load ./protocols/pop3 52 | 53 | # Notice on all recently created certs 54 | # @load ./protocols/ssl/new-certs 55 | 56 | # Generate log of all unique DNS queries with answers 57 | # @load ./protocols/dns/known_domains 58 | 59 | # Generate log of all URLs seen in an SMTP body 60 | # @load ./protocols/smtp/smtp-url 61 | 62 | # Generate log of local systems using unencrypted protocols 63 | # @load ./frameworks/compliance/detect-insecure-protos 64 | 65 | # Load DoD root certs for WCF and general purpose to allow 66 | # Zeek to validate them, which should clean up your notice log 67 | # @load ./protocols/ssl/dod-ca-list.zeek 68 | 69 | #== 3rd Party Scripts ================================= 70 | # Add Salesforce's JA3 SSL fingerprinting 71 | @load ./misc/ja3 72 | 73 | # Add Salesforce's HASSH SSH fingerprinting 74 | @load ./misc/hassh 75 | 76 | # Add community_id to all network logs 77 | @load ./plugins/community_id 78 | 79 | ### Sensor specific scripts ###################### 80 | 81 | # Configure AF_PACKET, if in use 82 | @load ./plugins/afpacket 83 | -------------------------------------------------------------------------------- /frameworks/files/extraction/README.md: -------------------------------------------------------------------------------- 1 | Bro Module for File Extraction 2 | ============================== 3 | 4 | This is a Bro script module for Bro (current master release only) that provides convenient extraction of files. 5 | 6 | Additionally, this script will generate file extensions for commonly encountered file types. 7 | 8 | Installation 9 | ------------ 10 | 11 | :: 12 | 13 | cd /share/bro/site/ 14 | git clone git://github.com/hosom/bro-file-extraction file-extraction 15 | echo "@load file-extraction" >> local.bro 16 | 17 | With the above installation, the module will not extract any files. In addition to the changes above, code must be written to hook FileExtraction::extract. For examples of this, look at the scripts in the plugins directory. 18 | 19 | In many cases, the desired functionality is for files commonly containing malware or exploits to be extracted. To do that, perform the actions below. 20 | 21 | :: 22 | 23 | echo "@load file-extraction/plugins/extract-common-exploit-types 24 | 25 | Additionally, to store files by sha1 hash, use the following: 26 | 27 | :: 28 | 29 | echo "@load file-extraction/plugins/store-files-by-sha1 30 | 31 | Configuration 32 | ------------- 33 | 34 | This set of scripts provides you with the ability to tune which files you are extracting and from where. 35 | 36 | Output 37 | ------------- 38 | 39 | Other than the extracted files, this module will generate no output. 40 | 41 | Plugins 42 | =============================== 43 | 44 | extract-all-files.bro 45 | ------------- 46 | 47 | Attaches the extract files analyzer to every file that has a mime_type detected. 48 | 49 | extract-java.bro 50 | ------------- 51 | 52 | Attaches the extract files analyzer to every JNLP and Java Archive file detected. 53 | 54 | extract-pe.bro 55 | ------------- 56 | 57 | Attaches the extract files analyzer to every PE file detected. 58 | 59 | extract-ms-office.bro 60 | ------------- 61 | 62 | Attaches the extract files analyzer to every ms office file detected. 63 | 64 | extract-pe.bro 65 | ------------- 66 | 67 | Attaches the extract files analyzer to every PDF file detected. 68 | 69 | extract-common-exploit-types.bro 70 | ------------- 71 | 72 | Loads the following plugins: 73 | - extract-java.bro 74 | - extract-pe.bro 75 | - extract-ms-office.bro 76 | - extract-pdf.bro 77 | 78 | store-files-by-md5.bro 79 | ------------- 80 | 81 | Uses file_state_remove to rename extracted files based on the md5 checksum whenever it is available. 82 | 83 | store-files-by-sha1.bro 84 | ------------- 85 | 86 | Uses file_state_remove to rename extracted files based on the sha1 checksum whenever it is available. 87 | 88 | store-files-by-sha256.bro 89 | ------------- 90 | 91 | Uses file_state_remove to rename extracted files based on the sha256 checksum whenever it is available. 92 | -------------------------------------------------------------------------------- /protocols/ssl/generate_zeek_dod_ca.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | set -o pipefail 4 | 5 | export CERT_URL='https://dl.dod.cyber.mil/wp-content/uploads/pki-pke/zip/certificates_pkcs7_v5-6_dod.zip' 6 | export WCF_URL='https://dl.dod.cyber.mil/wp-content/uploads/pki-pke/zip/unclass-certificates_pkcs7_v5-8_wcf.zip' 7 | 8 | # Download & Extract DoD root certificates 9 | /usr/bin/curl -s -LOJ ${CERT_URL} 10 | /usr/bin/curl -s -LOJ ${WCF_URL} 11 | 12 | /usr/bin/unzip -o "$(basename ${CERT_URL})" >/dev/null 13 | /usr/bin/unzip -o "$(basename ${WCF_URL})" >/dev/null 14 | 15 | cd "$(/usr/bin/zipinfo -1 "$(basename ${CERT_URL})" | /usr/bin/awk -F/ '{ print $1 }' | head -1)" 16 | 17 | cat << EOF 18 | # Don't edit! This file is automatically generated. 19 | # Generated at: $(date +%FT%T%z) 20 | # Generated from: ${CERT_URL}, ${WCF_URL} 21 | # 22 | # The original source files are published by the US Department of Defense at 23 | # the URLS listed above and are public-domain. 24 | 25 | @load base/protocols/ssl 26 | module SSL; 27 | redef root_certs += { 28 | EOF 29 | 30 | declare subj 31 | declare bytes 32 | 33 | # Convert pem.p7b certs to straight pem and import 34 | for item in *.pem.p7b; do 35 | TOPDIR="$(pwd)" 36 | TMPDIR="$(mktemp -d /tmp/"$(basename "${item}" .p7b)".XXXXXX)" || exit 1 37 | PEMNAME="$(basename "${item}" .p7b)" 38 | openssl pkcs7 -print_certs -in "${item}" -out "${TMPDIR}/${PEMNAME}" 39 | cd "${TMPDIR}" 40 | /usr/bin/split -p '^$' "${PEMNAME}" 41 | rm "$(ls x* | tail -1)" 42 | for cert in x??; do 43 | 44 | subj=$(openssl x509 -noout -subject -nameopt RFC2253 -in "${cert}" | sed 's/^subject= //') 45 | bytes=$(openssl x509 -outform DER -in "${cert}"| xxd -i | tr -d '\n' | sed 's/ 0x/\\x/g; s/[ ,]//g' | tr 'a-f' 'A-F') 46 | cat << EOF 47 | ["${subj}"] = "${bytes}", 48 | EOF 49 | done 50 | 51 | cd "${TOPDIR}" 52 | rm -rf "${TMPDIR}" 53 | done 54 | 55 | cd .. 56 | 57 | cd "$(/usr/bin/zipinfo -1 "$(basename ${WCF_URL})" | /usr/bin/awk -F/ '{ print $1 }' | head -1)" 58 | 59 | # Convert pem.p7b certs to straight pem and import 60 | for item in *.pem.p7b; do 61 | TOPDIR="$(pwd)" 62 | TMPDIR=$(mktemp -d /tmp/"$(basename "${item}" .p7b)".XXXXXX) || exit 1 63 | PEMNAME="$(basename "${item}" .p7b)" 64 | openssl pkcs7 -print_certs -in "${item}" -out "${TMPDIR}/${PEMNAME}" 65 | cd "${TMPDIR}" 66 | /usr/bin/split -p '^$' "${PEMNAME}" 67 | rm "$(ls x* | tail -1)" 68 | for cert in x??; do 69 | 70 | subj=$(openssl x509 -noout -subject -nameopt RFC2253 -in "${cert}" | sed 's/^subject= //') 71 | bytes=$(openssl x509 -outform DER -in "${cert}"| xxd -i | tr -d '\n' | sed 's/ 0x/\\x/g; s/[ ,]//g' | tr 'a-f' 'A-F') 72 | cat << EOF 73 | ["${subj}"] = "${bytes}", 74 | EOF 75 | done 76 | 77 | cd "${TOPDIR}" 78 | rm -rf "${TMPDIR}" 79 | done 80 | 81 | echo "};" 82 | -------------------------------------------------------------------------------- /skeleton.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! Short description of what this file is about 16 | # This file provides a rough sketch of a bro script 17 | 18 | # Load all of the other scripts this script depends on. Try to be careful to 19 | # not load more than necessary, but it's good practice to be sure that all 20 | # dependencies are loaded so that users only need to load this single script. 21 | @load base/frameworks/notice 22 | 23 | # Define your namespace where all of your locally defined functions and 24 | # variables will reside. 25 | module Skeleton; 26 | 27 | redef enum Notice::Action += { 28 | ACTION_LOG 29 | }; 30 | 31 | # The export section contains the external interface for customizing your 32 | # script and accessing useful internal state. Consts defined here should 33 | # be used for changing the behavior of the script and *MUST* have the &redef 34 | # attribute. Globals should be used for storing information which 35 | # is used by this script, but may be useful to another script at runtime. 36 | export { 37 | #============================# 38 | # Configuration variables # 39 | #============================# 40 | type Info: record { 41 | tags: set[string] &optional &log; 42 | sources: set[string] &optional &log; 43 | subject: string &log; 44 | data: Notice::Info &log; 45 | readgroups: set[string] &optional &log; 46 | modifygroups: set[string] &optional &log; 47 | }; 48 | 49 | # URL that notice will be POSTed to for alarm 50 | # This should be configured without the scheme (i.e. no https://) 51 | const alarm_api = "localhost/skeleton/api/v1/" &redef; 52 | 53 | # Username for authentication to SCOT 54 | const username = "admin" &redef; 55 | 56 | # Password for authentication to SCOT 57 | const passwd = "" &redef; 58 | 59 | # Add notice types from Notice::Type enum to exclude from being sent. 60 | # By default ACTION_LOG will send all notices as alarms. 61 | const exclude_notice_types = set() &redef; 62 | 63 | # This is a set of strings used to tag the source for the alarm in SCOT 64 | # This defaults to "bro" but might also be useful to use a unique sensor name 65 | const alarm_source = set("bro") &redef; 66 | } 67 | 68 | event zeek_init() 69 | { 70 | # Add any initalization logic here 71 | } 72 | 73 | # Add events, hooks, and other logic here 74 | -------------------------------------------------------------------------------- /protocols/ssl/new-certs.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! Generate notices when X.509 certificates over SSL/TLS are expired or 16 | ##! going to expire soon based on the date and time values stored within the 17 | ##! certificate. 18 | 19 | @load base/protocols/ssl 20 | @load base/files/x509 21 | @load base/frameworks/notice 22 | @load base/utils/directions-and-hosts 23 | 24 | module SSL; 25 | 26 | export { 27 | redef enum Notice::Type += { 28 | ## Indicates that a certificate's NotValidBefore date is within 29 | # the last `notify_when_cert_created_within` time. 30 | Certificate_Recently_Created 31 | }; 32 | 33 | #const ignore_certificate_list = // 34 | 35 | ## The category of hosts you would like to be notified about which have 36 | ## certificates that are recently created. By default, these 37 | ## notices will be suppressed by the notice framework for 1 day after 38 | ## a particular certificate has had a notice generated. 39 | ## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS 40 | const notify_certs_creation = LOCAL_HOSTS &redef; 41 | 42 | ## The time window after a certificate is created that you would like 43 | ## to start receiving :bro:enum:`SSL::Certificate_Recently_Created` notices. 44 | const notify_when_cert_created_within = 30days &redef; 45 | } 46 | 47 | event ssl_established(c: connection) &priority=3 48 | { 49 | # If there are no certificates or we are not interested in the server, just return. 50 | if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || 51 | ! addr_matches_host(c$id$resp_h, notify_certs_creation) || 52 | ! c$ssl$cert_chain[0]?$x509 || ! c$ssl$cert_chain[0]?$sha1 ) 53 | return; 54 | 55 | # TODO: If the certificate is in the whitelist, just return 56 | #if 57 | 58 | local fuid = c$ssl$cert_chain_fuids[0]; 59 | local cert = c$ssl$cert_chain[0]$x509$certificate; 60 | local hash = c$ssl$cert_chain[0]$sha1; 61 | 62 | if ( cert$not_valid_before + notify_when_cert_created_within > network_time() ) 63 | NOTICE([$note=Certificate_Recently_Created, 64 | $conn=c, $suppress_for=1day, 65 | $msg=fmt("Certificate %s was created for server %s at %T", cert$subject, c$ssl$server_name, cert$not_valid_before), 66 | $identifier=cat(c$id$resp_h, c$id$resp_p, hash), 67 | $fuid=fuid]); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /misc/ja3/ja3s.zeek: -------------------------------------------------------------------------------- 1 | # This Bro script appends JA3S (JA3 Server) to ssl.log 2 | # Version 1.0 (August 2018) 3 | # This builds a fingerprint for the SSL Server Hello packet based on SSL/TLS version, cipher picked, and extensions used. 4 | # Designed to be used in conjunction with JA3 to fingerprint SSL communication between clients and servers. 5 | # 6 | # Authors: John B. Althouse (jalthouse@salesforce.com) Jeff Atkinson (jatkinson@salesforce.com) 7 | # Copyright (c) 2018, salesforce.com, inc. 8 | # All rights reserved. 9 | # Licensed under the BSD 3-Clause license. 10 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 11 | # 12 | 13 | 14 | 15 | module JA3_Server; 16 | 17 | export { 18 | redef enum Log::ID += { LOG }; 19 | } 20 | 21 | type JA3Sstorage: record { 22 | server_version: count &default=0 &log; 23 | server_cipher: count &default=0 &log; 24 | server_extensions: string &default="" &log; 25 | }; 26 | 27 | redef record connection += { 28 | ja3sfp: JA3Sstorage &optional; 29 | }; 30 | 31 | redef record SSL::Info += { 32 | ja3s: string &optional &log; 33 | # LOG FIELD VALUES # 34 | # ja3s_version: string &optional &log; 35 | # ja3s_cipher: string &optional &log; 36 | # ja3s_extensions: string &optional &log; 37 | }; 38 | 39 | 40 | const sep = "-"; 41 | event zeek_init() { 42 | Log::create_stream(JA3_Server::LOG,[$columns=JA3Sstorage, $path="ja3sfp"]); 43 | } 44 | 45 | event ssl_extension(c: connection, is_orig: bool, code: count, val: string) 46 | { 47 | if ( ! c?$ja3sfp ) 48 | c$ja3sfp=JA3Sstorage(); 49 | if ( is_orig == F ) { 50 | if ( c$ja3sfp$server_extensions == "" ) { 51 | c$ja3sfp$server_extensions = cat(code); 52 | } 53 | else { 54 | c$ja3sfp$server_extensions = string_cat(c$ja3sfp$server_extensions, sep,cat(code)); 55 | } 56 | } 57 | } 58 | 59 | @if ( Version::at_least("2.6") || ( Version::number == 20500 && Version::info$commit >= 944 ) ) 60 | event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=1 61 | @else 62 | event ssl_server_hello(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=1 63 | @endif 64 | { 65 | if ( !c?$ja3sfp ) 66 | c$ja3sfp=JA3Sstorage(); 67 | c$ja3sfp$server_version = version; 68 | c$ja3sfp$server_cipher = cipher; 69 | local sep2 = ","; 70 | local ja3s_string = string_cat(cat(c$ja3sfp$server_version),sep2,cat(c$ja3sfp$server_cipher),sep2,c$ja3sfp$server_extensions); 71 | local ja3sfp_1 = md5_hash(ja3s_string); 72 | c$ssl$ja3s = ja3sfp_1; 73 | 74 | # LOG FIELD VALUES # 75 | #c$ssl$ja3s_version = cat(c$ja3sfp$server_version); 76 | #c$ssl$ja3s_cipher = cat(c$ja3sfp$server_cipher); 77 | #c$ssl$ja3s_extensions = c$ja3sfp$server_extensions; 78 | # 79 | # FOR DEBUGGING # 80 | #print "JA3S: "+ja3sfp_1+" Fingerprint String: "+ja3s_string; 81 | 82 | } 83 | -------------------------------------------------------------------------------- /plugins/publish-community_id/README.md: -------------------------------------------------------------------------------- 1 | # publish-community_id 2 | Ever want to associate Suricata alerts with *all* of the related Zeek logs? Or spot a suspicious DNS request in the Zeek dns log and quickly want to find out if there were any alerts related to that particular DNS transaction? The community_id developed by Corelight and the Suricata Project team, can help you do just that. 3 | 4 | This capability currently exists in both tools, as a configurable option in Suricata and as an add-on package for Zeek. The current Zeek community_id package contains a Zeek script that adds the community_id value to Zeek's conn log, and conn log only. This means, if you have a community_id from another tool (say... Suricata) and you want to find all of the Zeek logs related to the ID, you first search by the community_id, then grab the Zeek UID for that connection, and perform another search for logs containing the Zeek UID. This process can be automated, but it wouldn't it be more efficient if the community_id where everywhere? That way, one query (or grep) can return all the logs related to a given connection. 5 | 6 | Unfortunately, Zeek currently doesn't provide a way to add a new field to a log stream based on the contents of the underlying connection record. This missing functionality is detailed in this [issue](https://github.com/corelight/zeek-community-id/issues/3). 7 | 8 | 9 | So, in leiue of a better solution, while we ponder modifying Zeek's source... we dug deep and wrote all of the Zeek script code needed to safely publish the community_id across all of Zeek's logs. Its not perfect, but better than nothing... The publish-community_id package can be used instead of the Zeek script contained in the community_id package and anywhere you expect to see a Zeek UID, you'll also see a community_id. You can now associate all of your favorite Zeek events with related Suricata alerts, and vice versa, without all the grep and re-greping. 10 | 11 | So you know this wasn't done all willy nilly, here are some things we tried to accomplish: 12 | 13 | * be mindful of event name and signature changes across Zeek versions 14 | * make minimal calls to hash_conn() - only calculate the community_id once per connection 15 | * minimize memory footprint - store the ID value once and reuse it as needed for other app-layer logs 16 | * deal with asymmetry and loss - accommodate split transactions, request with no reply and reply with no request 17 | * modular and fault tolerant - things won't blow up if you disable a protocol analyzer 18 | 19 | **Install instructions** 20 | 1) Install Zeek 21 | 2) Install the community_id Zeek package 22 | 3) Clone the publish-community_id repo to a directory of you're choosing 23 | 4) Add a @load directive to your local.bro script that loads the publish_community-id module (via its new directory path) 24 | 25 | Note:: If you use publish_community-id you don't need to load the Zeek script from the community_id package. publish_community-id contains a fully compatible replacement for it, so you don't need both. 26 | 27 | 28 | Happy hunting! 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /frameworks/files/extraction/file-extensions.zeek: -------------------------------------------------------------------------------- 1 | module FileExtraction; 2 | 3 | export { 4 | ## Map file extensions to file mime_type 5 | const mime_to_ext: table[string] of string = { 6 | ["application/x-dosexec"] = "exe", 7 | ["application/msword"] = "doc", 8 | ["application/x-dmg"] = "dmg", 9 | ["application/x-gzip"] = "gz", 10 | ["application/x-rar"] = "rar", 11 | ["application/x-tar"] = "tar", 12 | ["application/x-xar"] = "pkg", 13 | ["application/x-rpm"] = "rpm", 14 | ["application/x-stuffit"] = "sif", 15 | ["application/x-archive"] = "", 16 | ["application/x-arc"] = "arc", 17 | ["application/x-eet"] = "eet", 18 | ["application/x-zoo"] = "zoo", 19 | ["application/x-lz4"] = "lz4", 20 | ["application/x-lrzip"] = "lrz", 21 | ["application/x-lzh"] = "lzh", 22 | ["application/warc"] = "warc", 23 | ["application/x-7z-compressed"] ="7z", 24 | ["application/x-xz"] = "xz", 25 | ["application/x-lha"] = "lha", 26 | ["application/x-arj"] = "arj", 27 | ["application/x-cpio"] = "cpio", 28 | ["application/x-compress"] = "", 29 | ["application/x-lzma"] = "", 30 | ["application/zip"] = "zip", 31 | ["application/vnd.ms-cab-compressed"] = "cab", 32 | ["application/pdf"] = "pdf", 33 | ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = "docx", 34 | ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = "xlsx", 35 | ["application/vnd.openxmlformats-officedocument.presentationml.presentation"] ="pptx", 36 | ["application/font-woff"] = "woff", 37 | ["application/x-font-ttf"] = "ttf", 38 | ["application/vnd.ms-fontobject"] = "eot", 39 | ["application/x-font-sfn"] = "", 40 | ["application/vnd.ms-opentype"] = "otf", 41 | ["application/x-mif"] = "mif", 42 | ["application/vnd.font-fontforge-sfd"] = "sfd", 43 | ["audio/mpeg"] = "mp3", 44 | ["audo/m4a"] = "mp4", 45 | ["image/tiff"] = "tiff", 46 | ["image/gif"] = "gif", 47 | ["image/jpeg"] = "jpg", 48 | ["image/x-ms-bmp"] = "bmp", 49 | ["image/x-icon"] = "ico", 50 | ["image/x-cursor"] = "cur", 51 | ["image/vnd.adobe.photoshop"] = "pnd", 52 | ["image/png"] = "png", 53 | ["text/html"] = "html", 54 | ["text/plain"] = "txt", 55 | ["text/json"] = "json", 56 | ["text/rtf"] = "rtf", 57 | ["application/xml"] = "xml", 58 | ["text/rss"] = "rss", 59 | ["application/java-archive"] = "jar", 60 | ["application/x-java-applet"] = "jar", 61 | ["application/x-shockwave-flash"] = "swf", 62 | ["application/pkcs7-signature"] = "p7", 63 | ["application/x-pem"] = "pem", 64 | ["application/x-java-jnlp-file"] = "jnlp", 65 | ["application/vnd.tcpdump.pcap"] = "pcap", 66 | ["text/x-shellscript"] = "sh", 67 | ["text/x-perl"] = "pl", 68 | ["text/x-ruby"] = "rb", 69 | ["text/x-python"] = "py", 70 | ["text/x-awk"] = "awk", 71 | ["text/x-lua"] ="lua", 72 | ["application/javascript"] = "js", 73 | ["text/x-php"] = "php", 74 | ["application/x-executable"] = "", 75 | ["application/x-coredump"] = "core", 76 | ["video/x-flv"] = "flv", 77 | ["video/x-fli"] = "fli", 78 | ["video/x-flc"] = "flc", 79 | ["video/mj2"] = "mj2", 80 | ["video/x-mng"] = "mng", 81 | ["video/x-jng"] = "jng", 82 | ["video/mpeg"] = "mpg", 83 | ["video/mpv"] = "mpv", 84 | ["video/h264"] = "264", 85 | ["video/webm"] = "webm", 86 | ["video/matroska"] = "mkv", 87 | ["vidoe/x-sgi-movie"] = "sgi", 88 | ["video/quicktime"] = "qt", 89 | ["video/mp4"] = "mp4", 90 | ["video/3gpp"] = "3gp", 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /frameworks/compliance/detect-insecure-protos.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020, RockNSM Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # filename: detect-insecure-protos.bro 16 | # 17 | # This policy provides a framework to detect non-compliant configurations that 18 | # provide services using insecure network protocols. You may whitelist specific 19 | # services and change which hosts are alerted on (defaults to local hosts). 20 | @load base/utils/directions-and-hosts 21 | @load base/utils/strings 22 | @load base/protocols/ftp 23 | @load base/protocols/http 24 | @load base/protocols/irc 25 | @load base/protocols/radius 26 | @load base/protocols/pop3 27 | 28 | module Compliance; 29 | 30 | export { 31 | #============================# 32 | # Notice Types # 33 | #============================# 34 | redef enum Notice::Type += { 35 | Compliance::NonCompliant_Protocol, 36 | }; 37 | 38 | #============================# 39 | # Configuration variables # 40 | #============================# 41 | # This is a set of protocol analyzers that are labeled as insecure. 42 | # you can add to this list or take away in other scripts as needed. 43 | const insecure_protocols: set[Analyzer::Tag] = { 44 | Analyzer::ANALYZER_FTP, 45 | Analyzer::ANALYZER_HTTP, 46 | Analyzer::ANALYZER_IRC, 47 | Analyzer::ANALYZER_RADIUS, 48 | # SMTP and SNMP are special cases and may need more refinement 49 | #Analyzer::ANALYZER_SMTP, 50 | #Analyzer::ANALYZER_SNMP, 51 | Analyzer::ANALYZER_POP3, 52 | } &redef; 53 | 54 | # This allows you to whitelist services by specific hosts 55 | # The event will check to see if the given protocol is being served 56 | # by a whitelisted host. If not, it will alert. 57 | const host_proto_exceptions: table[Analyzer::Tag] of set[addr] = { 58 | #[Analyzer::ANALYZER_HTTP] = set(127.0.0.1), 59 | } &redef; 60 | 61 | # Choices are LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS 62 | global alert_on_orig = ALL_HOSTS &redef; 63 | global alert_on_resp = LOCAL_HOSTS &redef; 64 | } 65 | 66 | event protocol_confirmation(c: connection, atype: Analyzer::Tag, aid: count) 67 | { 68 | # Check to see if this is the direction we care about 69 | if( ! addr_matches_host(c$id$orig_h, alert_on_orig) || 70 | ! addr_matches_host(c$id$resp_h, alert_on_resp) ) 71 | return; 72 | 73 | # Check to see if this is an insecure protocol 74 | if ( atype in insecure_protocols ) 75 | { 76 | ## Check to make sure this isn't a whitelisted host/service 77 | if ( atype !in host_proto_exceptions || 78 | c$id$resp_h !in host_proto_exceptions[atype] ) 79 | { 80 | local message = fmt("%s connected to %s using insecure protocol of %s", 81 | c$id$orig_h, 82 | c$id$resp_h, 83 | join_string_set(c$service, ",") ); 84 | # Generate notice 85 | NOTICE([$note=Compliance::NonCompliant_Protocol, 86 | $conn=c, 87 | $msg=message, 88 | $identifier=cat(c$id$orig_h,c$id$resp_h,c$service)]); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /utils/json.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | @load base/utils/strings 16 | 17 | module JSON; 18 | 19 | export { 20 | ## A function to convert arbitrary Bro data into a JSON string. 21 | ## 22 | ## v: The value to convert to JSON. Typically a record. 23 | ## 24 | ## only_loggable: If the v value is a record this will only cause 25 | ## fields with the &log attribute to be included in the JSON. 26 | ## 27 | ## returns: a JSON formatted string. 28 | global convert: function(v: any, only_loggable: bool &default=F): string; 29 | } 30 | 31 | function convert(v: any, only_loggable: bool &default=F): string 32 | { 33 | local tn = type_name(v); 34 | switch ( tn ) 35 | { 36 | case "type": 37 | return ""; 38 | 39 | case "string": 40 | return cat("\"", gsub(gsub(clean(v), /\\/, "\\\\"), /\"/, "\\\""), "\""); 41 | 42 | case "time": 43 | return fmt("\"%s.%06.0fZ\"", 44 | strftime("%Y-%m-%dT%H:%M:%S", v), 45 | (time_to_double(v) - floor(time_to_double(v))) * 1000000 46 | ); 47 | case "interval": 48 | return cat(interval_to_double(v)); 49 | 50 | case "bool": 51 | fallthrough; 52 | case "enum": 53 | fallthrough; 54 | case "addr": 55 | fallthrough; 56 | case "port": 57 | return cat("\"", v, "\""); 58 | 59 | case "int": 60 | fallthrough; 61 | case "count": 62 | fallthrough; 63 | case "double": 64 | fallthrough; 65 | 66 | return cat(v); 67 | 68 | default: 69 | break; 70 | } 71 | 72 | if ( /^record/ in tn ) 73 | { 74 | local rec_parts: string_vec = vector(); 75 | 76 | local ft = record_fields(v); 77 | for ( field in ft ) 78 | { 79 | local field_desc = ft[field]; 80 | if ( field_desc?$value && (!only_loggable || field_desc$log) ) 81 | { 82 | local onepart = cat("\"", field, "\": ", JSON::convert(field_desc$value, only_loggable)); 83 | rec_parts[|rec_parts|] = onepart; 84 | } 85 | } 86 | return cat("{", join_string_vec(rec_parts, ", "), "}"); 87 | } 88 | 89 | else if ( /^set/ in tn ) 90 | { 91 | local set_parts: string_vec = vector(); 92 | local sa: set[bool] = v; 93 | for ( sv in sa ) 94 | { 95 | set_parts[|set_parts|] = JSON::convert(sv, only_loggable); 96 | } 97 | return cat("[", join_string_vec(set_parts, ", "), "]"); 98 | } 99 | 100 | else if ( /^table/ in tn ) 101 | { 102 | local tab_parts: vector of string = vector(); 103 | local ta: table[bool] of any = v; 104 | for ( ti in ta ) 105 | { 106 | local ts = JSON::convert(ti); 107 | local if_quotes = (ts[0] == "\"") ? "" : "\""; 108 | tab_parts[|tab_parts|] = cat(if_quotes, ts, if_quotes, ": ", JSON::convert(ta[ti], only_loggable)); 109 | } 110 | return cat("{", join_string_vec(tab_parts, ", "), "}"); 111 | } 112 | 113 | else if ( /^vector/ in tn ) 114 | { 115 | local vec_parts: string_vec = vector(); 116 | local va: vector of any = v; 117 | for ( vi in va ) 118 | { 119 | vec_parts[|vec_parts|] = JSON::convert(va[vi], only_loggable); 120 | } 121 | return cat("[", join_string_vec(vec_parts, ", "), "]"); 122 | } 123 | 124 | return "\"\""; 125 | } 126 | -------------------------------------------------------------------------------- /frameworks/notice/scot-integration.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! Hooks to forward notices to [SCOT](https://github.com/sandialabs/scot) 16 | # This will forward all notices by default. Add notice types to 17 | # `SCOT::exclude_notice_types` that you wish to filter out. 18 | 19 | # NOTE: This is on hold until the upstream fixes a glitch in the matrix 20 | 21 | # Load all of the other scripts this script depends on. Try to be careful to 22 | # not load more than necessary, but it's good practice to be sure that all 23 | # dependencies are loaded so that users only need to load this single script. 24 | @load base/frameworks/notice 25 | @load base/utils/active-http 26 | 27 | # This is ROCK specific 28 | @load ../../utils/json 29 | 30 | redef exit_only_after_terminate = T; 31 | 32 | # Define your namespace where all of your locally defined functions and 33 | # variables will reside. 34 | module SCOT; 35 | 36 | redef enum Notice::Action += { 37 | ACTION_LOG 38 | }; 39 | 40 | # The export section contains the external interface for customizing your 41 | # script and accessing useful internal state. Consts defined here should 42 | # be used for changing the behavior of the script and *MUST* have the &redef 43 | # attribute. Globals should be used for storing information which 44 | # is used by this script, but may be useful to another script at runtime. 45 | export { 46 | #============================# 47 | # Configuration variables # 48 | #============================# 49 | type Info: record { 50 | tags: set[string] &optional &log; 51 | sources: set[string] &optional &log; 52 | subject: string &log; 53 | data: Notice::Info &log; 54 | readgroups: set[string] &optional &log; 55 | modifygroups: set[string] &optional &log; 56 | }; 57 | 58 | # URL that notice will be POSTed to for alarm 59 | # This should be configured without the scheme (i.e. no https://) 60 | const alarm_api = "localhost/scot/alertgroup" &redef; 61 | 62 | # Username for authentication to SCOT 63 | const username = "admin" &redef; 64 | 65 | # Password for authentication to SCOT 66 | const passwd = "" &redef; 67 | 68 | # Add notice types from Notice::Type enum to exclude from being sent. 69 | # By default ACTION_LOG will send all notices as alarms. 70 | const exclude_notice_types = set() &redef; 71 | 72 | # This is a set of strings used to tag the source for the alarm in SCOT 73 | # This defaults to "bro" but might also be useful to use a unique sensor name 74 | const alarm_source = set("bro") &redef; 75 | } 76 | 77 | hook Notice::notice(n: Notice::Info) 78 | { 79 | if ( SCOT::ACTION_LOG in n$actions ) 80 | { 81 | local data = Info( 82 | $sources = alarm_source, 83 | $subject = n$msg, 84 | $data=n 85 | ); 86 | 87 | local post_data = JSON::convert(data, $log_only=T); 88 | 89 | local r = ActiveHTTP::Request( 90 | $url=cat("https://", 91 | username, ":", passwd, 92 | "@", SCOT::alarm_api), 93 | $method="POST", 94 | $client_data=post_data, 95 | $max_time=60 secs, 96 | $addl_curl_args="-k" 97 | ); 98 | 99 | #print r; 100 | when( local resp = ActiveHTTP::request(r) ) 101 | { 102 | #print resp; 103 | } 104 | 105 | } 106 | } 107 | 108 | 109 | 110 | ############# 111 | 112 | 113 | redef enum Notice::Type += { 114 | ## The hash value of a file transferred over HTTP matched in the 115 | ## malware hash registry. 116 | SCOT::JSON_Alert 117 | }; 118 | 119 | hook Notice::policy(n: Notice::Info) 120 | { 121 | add n$actions[SCOT::ACTION_LOG]; 122 | } 123 | 124 | event zeek_init() 125 | { 126 | local message = fmt("bad things happened"); 127 | 128 | NOTICE([$ts=network_time(), 129 | $note=SCOT::JSON_Alert, 130 | $msg=message, 131 | $identifier=cat("127.0.0.1")]); 132 | } 133 | 134 | redef SCOT::alarm_api = "52.12.122.162/scot/alertgroup"; 135 | redef SCOT::passwd = "admin"; 136 | -------------------------------------------------------------------------------- /misc/ja3/ja3.zeek: -------------------------------------------------------------------------------- 1 | # This Bro script appends JA3 to ssl.log 2 | # Version 1.3 (June 2017) 3 | # 4 | # Authors: John B. Althouse (jalthouse@salesforce.com) & Jeff Atkinson (jatkinson@salesforce.com) 5 | # 6 | # Copyright (c) 2017, salesforce.com, inc. 7 | # All rights reserved. 8 | # Licensed under the BSD 3-Clause license. 9 | # For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause 10 | 11 | module JA3; 12 | 13 | export { 14 | redef enum Log::ID += { LOG }; 15 | } 16 | 17 | type TLSFPStorage: record { 18 | client_version: count &default=0 &log; 19 | client_ciphers: string &default="" &log; 20 | extensions: string &default="" &log; 21 | e_curves: string &default="" &log; 22 | ec_point_fmt: string &default="" &log; 23 | }; 24 | 25 | redef record connection += { 26 | tlsfp: TLSFPStorage &optional; 27 | }; 28 | 29 | redef record SSL::Info += { 30 | ja3: string &optional &log; 31 | # LOG FIELD VALUES ## 32 | # ja3_version: string &optional &log; 33 | # ja3_ciphers: string &optional &log; 34 | # ja3_extensions: string &optional &log; 35 | # ja3_ec: string &optional &log; 36 | # ja3_ec_fmt: string &optional &log; 37 | }; 38 | 39 | # Google. https://tools.ietf.org/html/draft-davidben-tls-grease-01 40 | const grease: set[int] = { 41 | 2570, 42 | 6682, 43 | 10794, 44 | 14906, 45 | 19018, 46 | 23130, 47 | 27242, 48 | 31354, 49 | 35466, 50 | 39578, 51 | 43690, 52 | 47802, 53 | 51914, 54 | 56026, 55 | 60138, 56 | 64250 57 | }; 58 | const sep = "-"; 59 | event zeek_init() { 60 | Log::create_stream(JA3::LOG,[$columns=TLSFPStorage, $path="tlsfp"]); 61 | } 62 | 63 | event ssl_extension(c: connection, is_orig: bool, code: count, val: string) 64 | { 65 | if ( ! c?$tlsfp ) 66 | c$tlsfp=TLSFPStorage(); 67 | if ( is_orig == T ) { 68 | if ( code in grease ) { 69 | next; 70 | } 71 | if ( c$tlsfp$extensions == "" ) { 72 | c$tlsfp$extensions = cat(code); 73 | } 74 | else { 75 | c$tlsfp$extensions = string_cat(c$tlsfp$extensions, sep,cat(code)); 76 | } 77 | } 78 | } 79 | 80 | event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec) 81 | { 82 | if ( !c?$tlsfp ) 83 | c$tlsfp=TLSFPStorage(); 84 | if ( is_orig == T ) { 85 | for ( i in point_formats ) { 86 | if ( point_formats[i] in grease ) { 87 | next; 88 | } 89 | if ( c$tlsfp$ec_point_fmt == "" ) { 90 | c$tlsfp$ec_point_fmt += cat(point_formats[i]); 91 | } 92 | else { 93 | c$tlsfp$ec_point_fmt += string_cat(sep,cat(point_formats[i])); 94 | } 95 | } 96 | } 97 | } 98 | 99 | event ssl_extension_elliptic_curves(c: connection, is_orig: bool, curves: index_vec) 100 | { 101 | if ( !c?$tlsfp ) 102 | c$tlsfp=TLSFPStorage(); 103 | if ( is_orig == T ) { 104 | for ( i in curves ) { 105 | if ( curves[i] in grease ) { 106 | next; 107 | } 108 | if ( c$tlsfp$e_curves == "" ) { 109 | c$tlsfp$e_curves += cat(curves[i]); 110 | } 111 | else { 112 | c$tlsfp$e_curves += string_cat(sep,cat(curves[i])); 113 | } 114 | } 115 | } 116 | } 117 | 118 | @if ( Version::at_least("2.6") || ( Version::number == 20500 && Version::info$commit >= 944 ) ) 119 | event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec) &priority=1 120 | @else 121 | event ssl_client_hello(c: connection, version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec) &priority=1 122 | @endif 123 | { 124 | if ( !c?$tlsfp ) 125 | c$tlsfp=TLSFPStorage(); 126 | c$tlsfp$client_version = version; 127 | for ( i in ciphers ) { 128 | if ( ciphers[i] in grease ) { 129 | next; 130 | } 131 | if ( c$tlsfp$client_ciphers == "" ) { 132 | c$tlsfp$client_ciphers += cat(ciphers[i]); 133 | } 134 | else { 135 | c$tlsfp$client_ciphers += string_cat(sep,cat(ciphers[i])); 136 | } 137 | } 138 | local sep2 = ","; 139 | local ja3_string = string_cat(cat(c$tlsfp$client_version),sep2,c$tlsfp$client_ciphers,sep2,c$tlsfp$extensions,sep2,c$tlsfp$e_curves,sep2,c$tlsfp$ec_point_fmt); 140 | local tlsfp_1 = md5_hash(ja3_string); 141 | c$ssl$ja3 = tlsfp_1; 142 | 143 | # LOG FIELD VALUES ## 144 | #c$ssl$ja3_version = cat(c$tlsfp$client_version); 145 | #c$ssl$ja3_ciphers = c$tlsfp$client_ciphers; 146 | #c$ssl$ja3_extensions = c$tlsfp$extensions; 147 | #c$ssl$ja3_ec = c$tlsfp$e_curves; 148 | #c$ssl$ja3_ec_fmt = c$tlsfp$ec_point_fmt; 149 | # 150 | # FOR DEBUGGING ## 151 | #print "JA3: "+tlsfp_1+" Fingerprint String: "+ja3_string; 152 | 153 | } 154 | -------------------------------------------------------------------------------- /protocols/pop3/main.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! Basic POP3 analyzer 16 | # From here: https://github.com/albert-magyar/bro/blob/topic/pop3/scripts/base/protocols/pop3/main.bro 17 | 18 | @load base/utils/numbers 19 | @load base/utils/files 20 | 21 | module POP3; 22 | 23 | export { 24 | redef enum Log::ID += { LOG }; 25 | 26 | ## Set to true to capture passwords from PASS command 27 | const default_capture_password = F &redef; 28 | 29 | type session_state: enum { AUTHORIZATION, TRANSACTION, UPDATE }; 30 | 31 | 32 | ## The most significant deviation in this script from the style of 33 | ## the HTTP and SMTP analyzers is that this record type is NOT used 34 | ## as a field of the connection struct. In those analyzers, any use 35 | ## of c$http/c$smtp in an event handler was preceded by a call to 36 | ## set_state that caused that field to hold the appropriate Info 37 | ## record instance. Since the persistence of the contents of that 38 | ## field were restricted to local scope, it has been replaced with 39 | ## local variables that hold the correct element of the pending queue. 40 | type CommandInfo: record { 41 | ts: time; 42 | command: string &optional; 43 | arg: string &optional; 44 | status: string ; 45 | msg: string &optional; 46 | has_client_activity: bool &default=F; 47 | }; 48 | 49 | type Info: record { 50 | ts: time &log; 51 | uid: string &log; 52 | id: conn_id &log; 53 | current_request: count &default=0; 54 | current_response: count &default=0; 55 | successful_commands: count &default=0 &log; 56 | failed_commands: count &default=0 &log; 57 | pending: table[count] of CommandInfo; 58 | username: string &optional &log; 59 | password: string &optional &log; 60 | state: session_state &default=AUTHORIZATION; 61 | }; 62 | 63 | ## Event that can be handled to access the POP3 record sent to the logging framework. 64 | global log_pop3: event(rec: Info); 65 | } 66 | 67 | # Add the POP3 state tracking fields to the connection record. 68 | redef record connection += { 69 | pop3: Info &optional; 70 | }; 71 | 72 | const ports = { 110/tcp }; 73 | redef likely_server_ports += { ports }; 74 | event zeek_init() &priority=5 { 75 | Log::create_stream(POP3::LOG, [$columns=Info, $ev=log_pop3]); 76 | Analyzer::register_for_ports(Analyzer::ANALYZER_POP3, ports); 77 | } 78 | 79 | 80 | function new_pop3_command(c: connection): CommandInfo { 81 | local tmp: CommandInfo; 82 | tmp$ts=network_time(); 83 | return tmp; 84 | } 85 | 86 | function new_pop3_session(c: connection): Info { 87 | local tmp: Info; 88 | tmp$ts=network_time(); 89 | tmp$uid=c$uid; 90 | tmp$id=c$id; 91 | return tmp; 92 | } 93 | 94 | function select_command(c: connection, is_request: bool): CommandInfo { 95 | if (!c?$pop3) { 96 | local s: Info; 97 | c$pop3 = s; 98 | } 99 | local current_command: count; 100 | current_command = (is_request) ? c$pop3$current_request : c$pop3$current_response; 101 | if (current_command !in c$pop3$pending) { 102 | c$pop3$pending[current_command] = new_pop3_command(c); 103 | } 104 | return c$pop3$pending[current_command]; 105 | } 106 | 107 | event pop3_request(c: connection, is_orig: bool, command: string, arg: string) &priority=5 { 108 | if (!c?$pop3) 109 | c$pop3 = new_pop3_session(c); 110 | local current_command: CommandInfo; 111 | current_command = select_command(c, is_orig); 112 | current_command$has_client_activity = T; 113 | current_command$command = command; 114 | current_command$arg = arg; 115 | ++c$pop3$current_request; 116 | } 117 | 118 | function process_command(c: connection, command: CommandInfo) { 119 | if (command?$command && command$status == "OK") { 120 | ++c$pop3$successful_commands; 121 | switch(command$command) { 122 | case "USER": 123 | c$pop3$username = command$arg; 124 | break; 125 | case "PASS": 126 | if (default_capture_password) 127 | c$pop3$password = command$arg; 128 | c$pop3$state = TRANSACTION; 129 | break; 130 | case "QUIT": 131 | c$pop3$state = UPDATE; 132 | break; 133 | } 134 | } else if (command?$command && command$status == "ERR") { 135 | ++c$pop3$failed_commands; 136 | } 137 | } 138 | 139 | event pop3_reply(c: connection, is_orig: bool, cmd: string, msg: string) &priority=5 { 140 | if (!c?$pop3) 141 | c$pop3 = new_pop3_session(c); 142 | local current_command: CommandInfo; 143 | current_command = select_command(c, is_orig); 144 | current_command$status = cmd; 145 | current_command$msg = msg; 146 | process_command(c, current_command); 147 | if (current_command$has_client_activity) 148 | ++c$pop3$current_response; 149 | } 150 | 151 | event connection_state_remove(c: connection) &priority=-5 { 152 | if (c?$pop3) { 153 | Log::write(POP3::LOG, c$pop3); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /protocols/smtp/smtp-url.zeek: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2020 RockNSM 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ##! A script for handling URLs in SMTP traffic. This script does 16 | ##! two things. It logs URLs discovered in SMTP traffic. It 17 | ##! also records them in a bloomfilter and looks for them to be 18 | ##! visited through HTTP requests. 19 | ##! 20 | ##! Authors: Aashish Sharma 21 | ##! Seth Hall 22 | ##! Derek Ditch 23 | 24 | 25 | @load base/utils/urls 26 | 27 | module SMTP_URL; 28 | 29 | export { 30 | redef enum Log::ID += { Links_LOG }; 31 | 32 | type Info: record { 33 | ## When the email was seen. 34 | ts: time &log; 35 | ## Unique ID for the connection. 36 | uid: string &log; 37 | ## Connection details. 38 | id: conn_id &log; 39 | ## Depth of the email into the SMTP exchange. 40 | trans_depth: count &log; 41 | ## The host field extracted from the discovered URL. 42 | host: string &log &optional; 43 | ## URL that was discovered. 44 | url: string &log &optional; 45 | }; 46 | 47 | redef enum Notice::Type += { 48 | ## A link discovered in an email appears to have been clicked. 49 | Link_in_Email_Clicked, 50 | 51 | ## An email was seen in email that matched the pattern in 52 | ## `SMTP_URL::suspicious_urls` 53 | Suspicious_URL, 54 | 55 | ## Certain file extensions in email links can be watched for 56 | ## with the pattern in `SMTP_URL::suspicious_file_extensions` 57 | Suspicious_File_Extension, 58 | 59 | ## URL with a dotted IP address seen in an email. 60 | Dotted_URL 61 | }; 62 | 63 | const suspicious_file_extensions = /\.([rR][aA][rR]|[eE][xX][eE]|[zZ][iI][pP])$/ &redef; 64 | const suspicious_urls = /googledocs?/ &redef; 65 | 66 | const ignore_file_types = /\.([gG][iI][fF]|[pP][nN][gG]|[jJ][pP][gG]|[xX][mM][lL]|[jJ][pP][eE]?[gG]|[cC][sS][sS])$/ &redef; 67 | 68 | ## The following 69 | const ignore_mail_originators: set[subnet] = { } &redef; 70 | const ignore_mailfroms = /bro@|alerts|reports/ &redef; 71 | const ignore_notification_emails = {"alerts@example.com", "notices@example.com"} &redef; 72 | const ignore_site_links = /http:\/\/.*\.example\.com\/|http:\/\/.*\.example\.net/ &redef; 73 | } 74 | 75 | # The bloomfilter that stores all of the links seen in email. 76 | global mail_links_bf: opaque of bloomfilter; 77 | 78 | redef record connection += { 79 | smtp_url: Info &optional; 80 | }; 81 | 82 | event zeek_init() &priority=5 83 | { 84 | # initialize the bloomfilter 85 | mail_links_bf = bloomfilter_basic_init(0.00000001, 10000000, "SMTP_URL"); 86 | 87 | Log::create_stream(Links_LOG, [$columns=Info]); 88 | } 89 | 90 | function extract_host(name: string): string 91 | { 92 | local split_on_slash = split_string(name, /\//); 93 | return split_on_slash[3]; 94 | } 95 | 96 | function log_smtp_urls(c: connection, url: string) 97 | { 98 | c$smtp_url = Info($ts = c$smtp$ts, 99 | $uid = c$uid, 100 | $id = c$id, 101 | $trans_depth = c$smtp$trans_depth, 102 | $host = extract_host(url), 103 | $url = url); 104 | 105 | Log::write(SMTP_URL::Links_LOG, c$smtp_url); 106 | } 107 | 108 | event SMTP_URL::email_data(f: fa_file, data: string) 109 | { 110 | # Grab the connection. 111 | local c: connection; 112 | for ( cid in f$conns ) 113 | { 114 | c = f$conns[cid]; 115 | break; 116 | } 117 | 118 | if( c$smtp?$mailfrom && ignore_mailfroms in c$smtp$mailfrom ) 119 | { 120 | return; 121 | } 122 | 123 | if ( c$smtp?$to ) 124 | { 125 | for ( to in c$smtp$to ) 126 | { 127 | if ( to in ignore_notification_emails ) 128 | return; 129 | } 130 | } 131 | 132 | local mail_info = Files::describe(f); 133 | local urls = find_all_urls(data); 134 | for ( link in urls ) 135 | { 136 | if ( ignore_file_types !in link ) 137 | { 138 | bloomfilter_add(mail_links_bf, link); 139 | log_smtp_urls(c, link); 140 | 141 | if ( suspicious_file_extensions in link ) 142 | { 143 | NOTICE([$note = Suspicious_File_Extension, 144 | $msg = fmt("Suspicious file extension embedded in URL %s from %s", link, c$id$orig_h), 145 | $sub = mail_info, 146 | $conn = c]); 147 | } 148 | 149 | if ( suspicious_urls in link ) 150 | { 151 | NOTICE([$note = Suspicious_URL, 152 | $msg = fmt("Suspicious text embedded in URL %s from %s", link, c$smtp$uid), 153 | $sub = mail_info, 154 | $conn = c]); 155 | } 156 | 157 | if ( /([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}.*/ in link ) 158 | { 159 | NOTICE([$note = Dotted_URL, 160 | $msg = fmt("Embedded IP address in URL %s from %s", link, c$id$orig_h), 161 | $sub = mail_info, 162 | $conn = c]); 163 | } 164 | 165 | } 166 | } 167 | } 168 | 169 | event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) 170 | { 171 | if ( f$source == "SMTP" && c?$smtp && 172 | c$id$orig_h !in ignore_mail_originators ) 173 | { 174 | Files::add_analyzer(f, Files::ANALYZER_DATA_EVENT, [$stream_event=SMTP_URL::email_data]); 175 | } 176 | } 177 | 178 | event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority=-3 179 | { 180 | local str = HTTP::build_url_http(c$http); 181 | if ( bloomfilter_lookup(SMTP_URL::mail_links_bf, str) > 0 && 182 | ignore_file_types !in str && 183 | ignore_site_links !in str) 184 | { 185 | NOTICE([$note=SMTP_URL::Link_in_Email_Clicked, 186 | $msg=fmt("URL %s ", str), 187 | $conn=c]); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /misc/hassh/hassh.zeek: -------------------------------------------------------------------------------- 1 | # HASSH # 2 | # SSH Key Initiation Exchange Fingerprinting # 3 | # # 4 | # Script Version: v1.0 # 5 | # Authors: Ben Reardon (breardon@salesforce.com, @benreardon) # 6 | # : Jeff Atkinson (jatkinson@salesforce.com) # 7 | # : John Althouse (jalthouse@salesforce.com) # 8 | # Description: This bro script appends hassh data to ssh.log # 9 | # by enumerating the SSH_MSG_KEXINIT packets sent # 10 | # as clear text between the client and server as part # 11 | # of the negotiation of an SSH connection. # 12 | # NOTE: bro currently ( <= v2.5.5) has a bug which reverses # 13 | # the Client/server flag, the logic in this script reverses # 14 | # this bug. Therefore once the bro bug is patched, the logic # 15 | # in this script also needs return to the proper form. # 16 | # # 17 | # Copyright (c) 2018, salesforce.com, inc. # 18 | # All rights reserved. # 19 | # SPDX-License-Identifier: BSD-3-Clause # 20 | # For full license text, see the LICENSE file in the repo root or # 21 | # https://opensource.org/licenses/BSD-3-Clause # 22 | 23 | 24 | module SSH; 25 | 26 | export { 27 | type HASSHStorage: record { 28 | hasshVersion:string &log &default="1.0"; # ANY change in hassh/hasshServer composition requires Version update 29 | hassh: string &log &optional &default=""; 30 | hasshServer: string &log &optional &default=""; 31 | 32 | # Client variables # 33 | ckex: string &log &optional &default=""; 34 | cshka: string &log &optional &default=""; 35 | ceacts: string &log &optional &default=""; 36 | cmacts: string &log &optional &default=""; 37 | ccacts: string &log &optional &default=""; 38 | #clcts: string &log &optional &default=""; 39 | hasshAlgorithms: string &log &optional &default=""; 40 | 41 | # Server variables # 42 | skex: string &log &optional &default=""; 43 | sshka: string &log &optional &default=""; 44 | seastc: string &log &optional &default=""; 45 | smastc: string &log &optional &default=""; 46 | scastc: string &log &optional &default=""; 47 | #slstc: string &log &optional &default=""; 48 | hasshServerAlgorithms: string &log &optional &default=""; 49 | }; 50 | } 51 | 52 | redef record connection += { 53 | hassh: HASSHStorage &optional; 54 | }; 55 | 56 | redef record SSH::Info += { 57 | hasshVersion: string &log &optional; 58 | hassh: string &log &optional; 59 | hasshServer: string &log &optional; 60 | 61 | # ===> Log Client variables <=== # 62 | # Comment out any fields that are not required to be logged in their raw form to ssh.log 63 | #ckex: string &log &optional; 64 | cshka: string &log &optional; 65 | #ceacts: string &log &optional; 66 | #cmacts: string &log &optional; 67 | #ccacts: string &log &optional; 68 | #clcts: string &log &optional; 69 | hasshAlgorithms: string &log &optional; 70 | 71 | # ===> Log Server variables <=== # 72 | # Comment out any fields that are not required to be logged in their raw form to ssh.log 73 | #skex: string &log &optional; 74 | sshka: string &log &optional; 75 | #seastc: string &log &optional; 76 | #smastc: string &log &optional; 77 | #scastc: string &log &optional; 78 | #slstc: string &log &optional; 79 | hasshServerAlgorithms: string &log &optional; 80 | }; 81 | 82 | 83 | # Build Client Application fingerprint # 84 | function get_hassh(c:connection, capabilities: SSH::Capabilities ) { 85 | c$hassh = HASSHStorage(); 86 | c$hassh$ckex = join_string_vec(capabilities$kex_algorithms,","); 87 | c$hassh$ceacts = join_string_vec(capabilities$encryption_algorithms$client_to_server,","); 88 | c$hassh$cmacts = join_string_vec(capabilities$mac_algorithms$client_to_server,","); 89 | c$hassh$ccacts = join_string_vec(capabilities$compression_algorithms$client_to_server,","); 90 | c$hassh$cshka = join_string_vec(capabilities$server_host_key_algorithms,","); # The Host key algorithm set may be useful information by itself but is not included in the hassh. 91 | #c$hassh$clcts = join_string_vec(capabilities$languages$client_to_server,","); # The Languages field may be useful information by itself but is not included in the hasshServer. 92 | c$hassh$hasshAlgorithms = string_cat(c$hassh$ckex,";",c$hassh$ceacts,";",c$hassh$cmacts,";",c$hassh$ccacts); # Contatenate the four selected lists of algorithms (Key,Enc,MAC,Compression) to build the Client hash 93 | c$hassh$hassh = md5_hash(c$hassh$hasshAlgorithms); 94 | } 95 | 96 | # Build Server Application fingerprint # 97 | function get_hasshServer(c:connection, capabilities: SSH::Capabilities ) { 98 | c$hassh = HASSHStorage(); 99 | c$hassh$skex = join_string_vec(capabilities$kex_algorithms,","); 100 | c$hassh$seastc = join_string_vec(capabilities$encryption_algorithms$server_to_client,","); 101 | c$hassh$smastc = join_string_vec(capabilities$mac_algorithms$server_to_client,","); 102 | c$hassh$scastc = join_string_vec(capabilities$compression_algorithms$server_to_client,","); 103 | c$hassh$sshka = join_string_vec(capabilities$server_host_key_algorithms,","); # The Host key algorithm set may be useful information by itself but is not included in the hasshServer. 104 | #c$hassh$slstc = join_string_vec(capabilities$languages$server_to_client,","); # The Languages field may be useful information by itself but is not included in the hasshServer. 105 | c$hassh$hasshServerAlgorithms = string_cat(c$hassh$skex,";",c$hassh$seastc,";",c$hassh$smastc,";",c$hassh$scastc); # Contatenate the four selected lists of algorithms (Key,Enc,Message,Compression) to build the Server hash 106 | c$hassh$hasshServer = md5_hash(c$hassh$hasshServerAlgorithms); 107 | } 108 | 109 | # Event # 110 | event ssh_capabilities(c: connection, cookie: string, capabilities: SSH::Capabilities) { 111 | if ( !c?$ssh ) {return;} 112 | c$hassh = HASSHStorage(); 113 | 114 | # bro currently has a bug which it reverses the Client/server flag. 115 | # The following "if" statements reverses this bug. Once the bro bug is patched, 116 | # this logic must return to the proper form. 117 | 118 | if ( capabilities$is_server == T ) { 119 | get_hassh(c, capabilities); 120 | c$ssh$hasshVersion = c$hassh$hasshVersion; 121 | c$ssh$hassh = c$hassh$hassh; 122 | 123 | # ===> Log Client variables <=== # 124 | # Comment out any fields that are not required to be logged in their raw form to ssh.log 125 | #c$ssh$ckex = c$hassh$ckex; 126 | c$ssh$cshka = c$hassh$cshka; 127 | #c$ssh$ceacts = c$hassh$ceacts; 128 | #c$ssh$cmacts = c$hassh$cmacts; 129 | #c$ssh$ccacts = c$hassh$ccacts; 130 | #c$ssh$clcts = c$hassh$clcts; 131 | c$ssh$hasshAlgorithms = c$hassh$hasshAlgorithms; 132 | } 133 | if ( capabilities$is_server == F ) { 134 | get_hasshServer(c, capabilities); 135 | c$ssh$hasshVersion = c$hassh$hasshVersion; 136 | c$ssh$hasshServer = c$hassh$hasshServer; 137 | 138 | # ===> Log Server variables <=== # 139 | # Comment out any fields that are not required to be logged in their raw form to ssh.log 140 | #c$ssh$skex = c$hassh$skex; 141 | c$ssh$sshka = c$hassh$sshka; 142 | #c$ssh$seastc = c$hassh$seastc; 143 | #c$ssh$smastc = c$hassh$smastc; 144 | #c$ssh$scastc = c$hassh$scastc; 145 | #c$ssh$slstc = c$hassh$clcts; 146 | c$ssh$hasshServerAlgorithms = c$hassh$hasshServerAlgorithms; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /frameworks/files/fsf-client/fsf_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # FSF Client for sending information and generating a report 4 | # 5 | # Jason Batchelor 6 | # Emerson Corporation 7 | # 02/09/2016 8 | ''' 9 | Copyright 2016 Emerson Electric Co. 10 | 11 | Licensed under the Apache License, Version 2.0 (the "License"); 12 | you may not use this file except in compliance with the License. 13 | You may obtain a copy of the License at 14 | 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | Unless required by applicable law or agreed to in writing, software 18 | distributed under the License is distributed on an "AS IS" BASIS, 19 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | See the License for the specific language governing permissions and 21 | limitations under the License. 22 | ''' 23 | 24 | import os 25 | import sys 26 | import socket 27 | import argparse 28 | import struct 29 | import json 30 | import time 31 | import hashlib 32 | import random 33 | from conf import config 34 | from datetime import datetime as dt 35 | 36 | class FSFClient: 37 | def __init__(self, fullpath, filename, delete, source, archive, suppress_report, full, file): 38 | 39 | self.fullpath = fullpath 40 | self.filename = filename 41 | self.delete = delete 42 | self.source = source 43 | self.archive = archive 44 | self.suppress_report = suppress_report 45 | self.full = full 46 | self.file = file 47 | # If multiple server candidates are given, we randomly choose one 48 | self.host = random.choice(config.SERVER_CONFIG['IP_ADDRESS']) 49 | self.port = config.SERVER_CONFIG['PORT'] 50 | self.logfile = config.CLIENT_CONFIG['LOG_FILE'] 51 | 52 | archive_options = ['none', 'file-on-alert', 'all-on-alert', 'all-the-files', 'all-the-things'] 53 | if args.archive not in archive_options: 54 | error = '%s Please specify a valid archive option: \'none\', \'file-on-alert\', \'all-on-alert\', \'all-the-files\' or \'all-the-things\'.\n' % dt.now() 55 | self.issue_error(error) 56 | sys.exit(1) 57 | 58 | # Send files to server for processing and await results 59 | def process_files(self): 60 | 61 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 62 | msg = '%sFSF_RPC%sFSF_RPC%sFSF_RPC%sFSF_RPC%sFSF_RPC%s' % (self.filename, self.source, self.archive, self.suppress_report, self.full, self.file) 63 | buffer = struct.pack('>I', len(msg)) + 'FSF_RPC' + msg 64 | 65 | try: 66 | sock.connect((self.host, self.port)) 67 | sock.sendall(buffer) 68 | except: 69 | e = sys.exc_info()[0] 70 | error = '%s There was a problem sending file %s to %s on port %s. Error: %s\n' % (dt.now(), self.filename, self.host, self.port, e) 71 | self.issue_error(error) 72 | 73 | finally: 74 | 75 | if self.delete: 76 | os.remove(self.fullpath) 77 | 78 | if not self.suppress_report: 79 | self.process_results(sock) 80 | 81 | sock.close() 82 | 83 | # Process the results sent back from the FSF server 84 | def process_results(self, sock): 85 | 86 | try: 87 | raw_msg_len = sock.recv(4) 88 | msg_len = struct.unpack('>I', raw_msg_len)[0] 89 | data = '' 90 | 91 | while len(data) < msg_len: 92 | recv_buff = sock.recv(msg_len - len(data)) 93 | data += recv_buff 94 | 95 | print data 96 | 97 | # Does the user want all sub objects? 98 | if self.full: 99 | # Generate dirname by calculating epoch time and hash of results 100 | dirname = 'fsf_dump_%s_%s' % (int(time.time()), hashlib.md5(data).hexdigest()) 101 | self.dump_subobjects(sock, dirname) 102 | 103 | except: 104 | e = sys.exc_info()[0] 105 | error = '%s There was a problem getting data for %s from %s on port %s. Error: %s' % (dt.now(), self.filename, self.host, self.port, e) 106 | self.issue_error(error) 107 | 108 | # Dump all subobjects returned by the scanner server 109 | def dump_subobjects(self, sock, dirname): 110 | 111 | sub_status = sock.recv(4) 112 | if sub_status == 'Null': 113 | print 'No subobjects were returned from scanner for %s.' % self.filename 114 | return 115 | 116 | os.mkdir(dirname) 117 | 118 | while self.full: 119 | raw_sub_count = sock.recv(4) 120 | sub_count = struct.unpack('>I', raw_sub_count)[0] 121 | raw_msg_len = sock.recv(4) 122 | msg_len = struct.unpack('>I', raw_msg_len)[0] 123 | data = '' 124 | 125 | while len(data) < msg_len: 126 | recv_buff = sock.recv(msg_len - len(data)) 127 | data += recv_buff 128 | 129 | fname = hashlib.md5(data).hexdigest() 130 | with open('%s/%s' % (dirname, fname), 'w') as f: 131 | f.write(data) 132 | f.close 133 | 134 | if sub_count == 0: 135 | self.full = False 136 | 137 | print 'Sub objects of %s successfully written to: %s' % (self.filename, dirname) 138 | 139 | # Either log to log file or print to stdout depending on flags used 140 | def issue_error(self, error): 141 | 142 | if self.suppress_report: 143 | with open(self.logfile, 'a') as f: 144 | f.write(error) 145 | f.close() 146 | else: 147 | print error 148 | 149 | if __name__ == '__main__': 150 | 151 | parser = argparse.ArgumentParser(prog='fsf_client', description='Uploads files to scanner server and returns the results to the user if desired. Results will always be written to a server side log file. Default options for each flag are designed to accommodate easy analyst interaction. Adjustments can be made to accommodate larger operations. Read the documentation for more details!') 152 | parser.add_argument('file', nargs='*', type=argparse.FileType('r'), help='Full path to file(s) to be processed.') 153 | parser.add_argument('--delete', default=False, action='store_true', help='Remove file from client after sending to the FSF server. Data can be archived later on server depending on selected options.') 154 | parser.add_argument('--source', nargs='?', type=str, default='Analyst', help='Specify the source of the input. Useful when scaling up to larger operations or supporting multiple input sources, such as; integrating with a sensor grid or other network defense solutions. Defaults to \'Analyst\' as submission source.') 155 | parser.add_argument('--archive', nargs='?', type=str, default='none', help='Specify the archive option to use. The most common option is \'none\' which will tell the server not to archive for this submission (default). \'file-on-alert\' will archive the file only if the alert flag is set. \'all-on-alert\' will archive the file and all sub objects if the alert flag is set. \'all-the-files\' will archive all the files sent to the scanner regardless of the alert flag. \'all-the-things\' will archive the file and all sub objects regardless of the alert flag.') 156 | parser.add_argument('--suppress-report', default=False, action='store_true', help='Don\'t return a JSON report back to the client and log client-side errors to the locally configured log directory. Choosing this will log scan results server-side only. Needed for automated scanning use cases when sending large amount of files for bulk collection. Set to false by default.') 157 | parser.add_argument('--full', default=False, action='store_true', help='Dump all sub objects of submitted file to current directory of the client. Format or directory name is \'fsf_dump_[epoch time]_[md5 hash of scan results]\'. Only supported when suppress-report option is false (default).') 158 | 159 | if len(sys.argv) == 1: 160 | parser.print_help() 161 | sys.exit(1) 162 | 163 | try: 164 | args = parser.parse_args() 165 | except IOError: 166 | e = sys.exc_info()[1] 167 | print 'The file provided could not be found. Error: %s' % e 168 | sys.exit(1) 169 | 170 | if len(args.file) == 0: 171 | print 'A file to scan needs to be provided!' 172 | 173 | for f in args.file: 174 | filename = os.path.basename(f.name) 175 | file = f.read() 176 | fsf = FSFClient(f.name, filename, args.delete, args.source, args.archive, args.suppress_report, args.full, file) 177 | fsf.process_files() 178 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------