mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 14:08:20 +00:00
Hopefully the last major script reorganization.
- policy/ renamed to scripts/ - By default BROPATH now contains: - scripts/ - scripts/policy - scripts/site - *Nearly* all tests pass. - All of scripts/base/ is loaded by main.cc - Can be disabled by setting $BRO_NO_BASE_SCRIPTS - Scripts in scripts/base/ don't use relative path loading to ease use of BRO_NO_BASE_SCRIPTS (to copy and paste that script). - The scripts in scripts/base/protocols/ only (or soon will only) do logging and state building. - The scripts in scripts/base/frameworks/ add functionality without causing any additional overhead. - All "detection" activity happens through scripts in scripts/policy/. - Communications framework modified temporarily to need an environment variable to actually enable (ENABLE_COMMUNICATION=1) - This is so the communications framework can be loaded as part of the base without causing trouble when it's not needed. - This will be removed once a resolution to ticket #540 is reached.
This commit is contained in:
parent
68171cf179
commit
597a4d6704
257 changed files with 1311 additions and 1225 deletions
3
scripts/base/protocols/conn/__load__.bro
Normal file
3
scripts/base/protocols/conn/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
|||
@load ./main
|
||||
@load ./contents
|
||||
@load ./inactivity
|
42
scripts/base/protocols/conn/contents.bro
Normal file
42
scripts/base/protocols/conn/contents.bro
Normal file
|
@ -0,0 +1,42 @@
|
|||
##! This script can be used to extract either the originator's data or the
|
||||
##! responders data or both. By default nothing is extracted, and in order
|
||||
##! to actually extract data the ``c$extract_orig`` and/or the
|
||||
##! ``c$extract_resp`` variable must be set to T. One way to achieve this
|
||||
##! would be to handle the connection_established event elsewhere and set the
|
||||
##! extract_orig and extract_resp options there. However, there may be trouble
|
||||
##! with the timing due the event queue delay.
|
||||
##! This script does not work well in a cluster context unless it has a
|
||||
##! remotely mounted disk to write the content files to.
|
||||
|
||||
module Conn;
|
||||
|
||||
export {
|
||||
## The prefix given to files as they are opened on disk.
|
||||
const extraction_prefix = "contents" &redef;
|
||||
|
||||
## If this variable is set to T, then all contents of all files will be
|
||||
## extracted.
|
||||
const default_extract = F &redef;
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
extract_orig: bool &default=default_extract;
|
||||
extract_resp: bool &default=default_extract;
|
||||
};
|
||||
|
||||
event connection_established(c: connection) &priority=-5
|
||||
{
|
||||
if ( c$extract_orig )
|
||||
{
|
||||
local orig_file = generate_extraction_filename(extraction_prefix, c, "orig.dat");
|
||||
local orig_f = open(orig_file);
|
||||
set_contents_file(c$id, CONTENTS_ORIG, orig_f);
|
||||
}
|
||||
|
||||
if ( c$extract_resp )
|
||||
{
|
||||
local resp_file = generate_extraction_filename(extraction_prefix, c, "resp.dat");
|
||||
local resp_f = open(resp_file);
|
||||
set_contents_file(c$id, CONTENTS_RESP, resp_f);
|
||||
}
|
||||
}
|
41
scripts/base/protocols/conn/inactivity.bro
Normal file
41
scripts/base/protocols/conn/inactivity.bro
Normal file
|
@ -0,0 +1,41 @@
|
|||
##! Adjust the inactivity timeouts for interactive services which could
|
||||
##! very possibly have long delays between packets.
|
||||
|
||||
module Conn;
|
||||
|
||||
export {
|
||||
## Define inactivty timeouts by the service detected being used over
|
||||
## the connection.
|
||||
const analyzer_inactivity_timeouts: table[AnalyzerTag] of interval = {
|
||||
# For interactive services, allow longer periods of inactivity.
|
||||
[[ANALYZER_SSH, ANALYZER_FTP]] = 1 hrs,
|
||||
} &redef;
|
||||
|
||||
## Define inactivity timeouts based on common protocol ports.
|
||||
const port_inactivity_timeouts: table[port] of interval = {
|
||||
[[21/tcp, 22/tcp, 23/tcp, 513/tcp]] = 1 hrs,
|
||||
} &redef;
|
||||
|
||||
}
|
||||
|
||||
event protocol_confirmation(c: connection, atype: count, aid: count)
|
||||
{
|
||||
if ( atype in analyzer_inactivity_timeouts )
|
||||
set_inactivity_timeout(c$id, analyzer_inactivity_timeouts[atype]);
|
||||
}
|
||||
|
||||
event connection_established(c: connection)
|
||||
{
|
||||
local service_port = c$id$resp_p;
|
||||
if ( c$orig$state == TCP_INACTIVE )
|
||||
{
|
||||
# We're seeing a half-established connection. Use the
|
||||
# service of the originator if it's well-known and the
|
||||
# responder isn't.
|
||||
if ( service_port !in likely_server_ports && c$id$orig_p in likely_server_ports )
|
||||
service_port = c$id$orig_p;
|
||||
}
|
||||
|
||||
if ( service_port in port_inactivity_timeouts )
|
||||
set_inactivity_timeout(c$id, port_inactivity_timeouts[service_port]);
|
||||
}
|
198
scripts/base/protocols/conn/main.bro
Normal file
198
scripts/base/protocols/conn/main.bro
Normal file
|
@ -0,0 +1,198 @@
|
|||
|
||||
module Conn;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { CONN };
|
||||
|
||||
type Info: record {
|
||||
## This is the time of the first packet.
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
proto: transport_proto &log;
|
||||
service: string &log &optional;
|
||||
duration: interval &log &optional;
|
||||
orig_bytes: count &log &optional;
|
||||
resp_bytes: count &log &optional;
|
||||
|
||||
## ========== ===============================================
|
||||
## conn_state Meaning
|
||||
## ========== ===============================================
|
||||
## S0 Connection attempt seen, no reply.
|
||||
## S1 Connection established, not terminated.
|
||||
## SF Normal establishment and termination. Note that this is the same symbol as for state S1. You can tell the two apart because for S1 there will not be any byte counts in the summary, while for SF there will be.
|
||||
## REJ Connection attempt rejected.
|
||||
## S2 Connection established and close attempt by originator seen (but no reply from responder).
|
||||
## S3 Connection established and close attempt by responder seen (but no reply from originator).
|
||||
## RSTO Connection established, originator aborted (sent a RST).
|
||||
## RSTR Established, responder aborted.
|
||||
## RSTOS0 Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder.
|
||||
## RSTRH Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator.
|
||||
## SH Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was "half" open).
|
||||
## SHR Responder sent a SYN ACK followed by a FIN, we never saw a SYN from the originator.
|
||||
## OTH No SYN seen, just midstream traffic (a "partial connection" that was not later closed).
|
||||
## ========== ===============================================
|
||||
conn_state: string &log &optional;
|
||||
|
||||
## If the connection is originated locally, this value will be T. If
|
||||
## it was originated remotely it will be F. In the case that the
|
||||
## :bro:id:`Site::local_nets` variable is undefined, this field will
|
||||
## be left empty at all times.
|
||||
local_orig: bool &log &optional;
|
||||
|
||||
## Indicates the number of bytes missed in content gaps which is
|
||||
## representative of packet loss. A value other than zero will
|
||||
## normally cause protocol analysis to fail but some analysis may
|
||||
## have been completed prior to the packet loss.
|
||||
missed_bytes: count &log &default=0;
|
||||
|
||||
## Records the state history of (TCP) connections as
|
||||
## a string of letters.
|
||||
##
|
||||
## ====== ====================================================
|
||||
## Letter Meaning
|
||||
## ====== ====================================================
|
||||
## s a SYN w/o the ACK bit set
|
||||
## h a SYN+ACK ("handshake")
|
||||
## a a pure ACK
|
||||
## d packet with payload ("data")
|
||||
## f packet with FIN bit set
|
||||
## r packet with RST bit set
|
||||
## c packet with a bad checksum
|
||||
## i inconsistent packet (e.g. SYN+RST bits both set)
|
||||
## ====== ====================================================
|
||||
##
|
||||
## If the letter is in upper case it means the event comes from the
|
||||
## originator and lower case then means the responder.
|
||||
## Also, there is compression. We only record one "d" in each direction,
|
||||
## for instance. I.e., we just record that data went in that direction.
|
||||
## This history is not meant to encode how much data that happened to be.
|
||||
history: string &log &optional;
|
||||
};
|
||||
|
||||
global log_conn: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
conn: Info &optional;
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(CONN, [$columns=Info, $ev=log_conn]);
|
||||
}
|
||||
|
||||
function conn_state(c: connection, trans: transport_proto): string
|
||||
{
|
||||
local os = c$orig$state;
|
||||
local rs = c$resp$state;
|
||||
|
||||
local o_inactive = os == TCP_INACTIVE || os == TCP_PARTIAL;
|
||||
local r_inactive = rs == TCP_INACTIVE || rs == TCP_PARTIAL;
|
||||
|
||||
if ( trans == tcp )
|
||||
{
|
||||
if ( rs == TCP_RESET )
|
||||
{
|
||||
if ( os == TCP_SYN_SENT || os == TCP_SYN_ACK_SENT ||
|
||||
(os == TCP_RESET &&
|
||||
c$orig$size == 0 && c$resp$size == 0) )
|
||||
return "REJ";
|
||||
else if ( o_inactive )
|
||||
return "RSTRH";
|
||||
else
|
||||
return "RSTR";
|
||||
}
|
||||
else if ( os == TCP_RESET )
|
||||
return r_inactive ? "RSTOS0" : "RSTO";
|
||||
else if ( rs == TCP_CLOSED && os == TCP_CLOSED )
|
||||
return "SF";
|
||||
else if ( os == TCP_CLOSED )
|
||||
return r_inactive ? "SH" : "S2";
|
||||
else if ( rs == TCP_CLOSED )
|
||||
return o_inactive ? "SHR" : "S3";
|
||||
else if ( os == TCP_SYN_SENT && rs == TCP_INACTIVE )
|
||||
return "S0";
|
||||
else if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED )
|
||||
return "S1";
|
||||
else
|
||||
return "OTH";
|
||||
}
|
||||
|
||||
else if ( trans == udp )
|
||||
{
|
||||
if ( os == UDP_ACTIVE )
|
||||
return rs == UDP_ACTIVE ? "SF" : "S0";
|
||||
else
|
||||
return rs == UDP_ACTIVE ? "SHR" : "OTH";
|
||||
}
|
||||
|
||||
else
|
||||
return "OTH";
|
||||
}
|
||||
|
||||
function determine_service(c: connection): string
|
||||
{
|
||||
local service = "";
|
||||
for ( s in c$service )
|
||||
{
|
||||
if ( sub_bytes(s, 0, 1) != "-" )
|
||||
service = service == "" ? s : cat(service, ",", s);
|
||||
}
|
||||
|
||||
return to_lower(service);
|
||||
}
|
||||
|
||||
function set_conn(c: connection, eoc: bool)
|
||||
{
|
||||
if ( ! c?$conn )
|
||||
{
|
||||
local id = c$id;
|
||||
local tmp: Info;
|
||||
tmp$ts=c$start_time;
|
||||
tmp$uid=c$uid;
|
||||
tmp$id=id;
|
||||
tmp$proto=get_port_transport_proto(id$resp_p);
|
||||
if( |Site::local_nets| > 0 )
|
||||
tmp$local_orig=Site::is_local_addr(id$orig_h);
|
||||
c$conn = tmp;
|
||||
}
|
||||
|
||||
if ( eoc )
|
||||
{
|
||||
if ( c$duration > 0secs )
|
||||
{
|
||||
c$conn$duration=c$duration;
|
||||
# TODO: these should optionally use Gregor's new
|
||||
# actual byte counting code if it's enabled.
|
||||
c$conn$orig_bytes=c$orig$size;
|
||||
c$conn$resp_bytes=c$resp$size;
|
||||
}
|
||||
local service = determine_service(c);
|
||||
if ( service != "" )
|
||||
c$conn$service=service;
|
||||
c$conn$conn_state=conn_state(c, get_port_transport_proto(c$id$resp_p));
|
||||
|
||||
if ( c$history != "" )
|
||||
c$conn$history=c$history;
|
||||
}
|
||||
}
|
||||
|
||||
event connection_established(c: connection) &priority=5
|
||||
{
|
||||
set_conn(c, F);
|
||||
}
|
||||
|
||||
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
|
||||
{
|
||||
set_conn(c, F);
|
||||
|
||||
c$conn$missed_bytes = c$conn$missed_bytes + length;
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
set_conn(c, T);
|
||||
Log::write(CONN, c$conn);
|
||||
}
|
||||
|
2
scripts/base/protocols/dns/__load__.bro
Normal file
2
scripts/base/protocols/dns/__load__.bro
Normal file
|
@ -0,0 +1,2 @@
|
|||
@load ./consts
|
||||
@load ./main
|
84
scripts/base/protocols/dns/consts.bro
Normal file
84
scripts/base/protocols/dns/consts.bro
Normal file
|
@ -0,0 +1,84 @@
|
|||
##! Types, errors, and fields for analyzing DNS data. A helper file
|
||||
##! for DNS analysis scripts.
|
||||
|
||||
module DNS;
|
||||
|
||||
export {
|
||||
const PTR = 12;
|
||||
const EDNS = 41;
|
||||
const ANY = 255;
|
||||
|
||||
## Mapping of DNS query type codes to human readable string representation.
|
||||
const query_types = {
|
||||
[1] = "A", [2] = "NS", [3] = "MD", [4] = "MF",
|
||||
[5] = "CNAME", [6] = "SOA", [7] = "MB", [8] = "MG",
|
||||
[9] = "MR", [10] = "NULL", [11] = "WKS", [PTR] = "PTR",
|
||||
[13] = "HINFO", [14] = "MINFO", [15] = "MX", [16] = "TXT",
|
||||
[17] = "RP", [18] = "AFSDB", [19] = "X25", [20] = "ISDN",
|
||||
[21] = "RT", [22] = "NSAP", [23] = "NSAP-PTR", [24] = "SIG",
|
||||
[25] = "KEY", [26] = "PX" , [27] = "GPOS", [28] = "AAAA",
|
||||
[29] = "LOC", [30] = "EID", [31] = "NIMLOC", [32] = "NB",
|
||||
[33] = "SRV", [34] = "ATMA", [35] = "NAPTR", [36] = "KX",
|
||||
[37] = "CERT", [38] = "A6", [39] = "DNAME", [40] = "SINK",
|
||||
[EDNS] = "EDNS", [42] = "APL", [43] = "DS", [44] = "SINK",
|
||||
[45] = "SSHFP", [46] = "RRSIG", [47] = "NSEC", [48] = "DNSKEY",
|
||||
[49] = "DHCID", [99] = "SPF", [100] = "DINFO", [101] = "UID",
|
||||
[102] = "GID", [103] = "UNSPEC", [249] = "TKEY", [250] = "TSIG",
|
||||
[251] = "IXFR", [252] = "AXFR", [253] = "MAILB", [254] = "MAILA",
|
||||
[32768] = "TA", [32769] = "DLV",
|
||||
[ANY] = "*",
|
||||
} &default = function(n: count): string { return fmt("query-%d", n); };
|
||||
|
||||
const code_types = {
|
||||
[0] = "X0",
|
||||
[1] = "Xfmt",
|
||||
[2] = "Xsrv",
|
||||
[3] = "Xnam",
|
||||
[4] = "Ximp",
|
||||
[5] = "X[",
|
||||
} &default="?";
|
||||
|
||||
## Errors used for non-TSIG/EDNS types.
|
||||
const base_errors = {
|
||||
[0] = "NOERROR", ##< No Error
|
||||
[1] = "FORMERR", ##< Format Error
|
||||
[2] = "SERVFAIL", ##< Server Failure
|
||||
[3] = "NXDOMAIN", ##< Non-Existent Domain
|
||||
[4] = "NOTIMP", ##< Not Implemented
|
||||
[5] = "REFUSED", ##< Query Refused
|
||||
[6] = "YXDOMAIN", ##< Name Exists when it should not
|
||||
[7] = "YXRRSET", ##< RR Set Exists when it should not
|
||||
[8] = "NXRRSet", ##< RR Set that should exist does not
|
||||
[9] = "NOTAUTH", ##< Server Not Authoritative for zone
|
||||
[10] = "NOTZONE", ##< Name not contained in zone
|
||||
[11] = "unassigned-11", ##< available for assignment
|
||||
[12] = "unassigned-12", ##< available for assignment
|
||||
[13] = "unassigned-13", ##< available for assignment
|
||||
[14] = "unassigned-14", ##< available for assignment
|
||||
[15] = "unassigned-15", ##< available for assignment
|
||||
[16] = "BADVERS", ##< for EDNS, collision w/ TSIG
|
||||
[17] = "BADKEY", ##< Key not recognized
|
||||
[18] = "BADTIME", ##< Signature out of time window
|
||||
[19] = "BADMODE", ##< Bad TKEY Mode
|
||||
[20] = "BADNAME", ##< Duplicate key name
|
||||
[21] = "BADALG", ##< Algorithm not supported
|
||||
[22] = "BADTRUNC", ##< draft-ietf-dnsext-tsig-sha-05.txt
|
||||
[3842] = "BADSIG", ##< 16 <= number collision with EDNS(16);
|
||||
##< this is a translation from TSIG(16)
|
||||
} &default = function(n: count): string { return fmt("rcode-%d", n); };
|
||||
|
||||
# This deciphers EDNS Z field values.
|
||||
const edns_zfield = {
|
||||
[0] = "NOVALUE", # regular entry
|
||||
[32768] = "DNS_SEC_OK", # accepts DNS Sec RRs
|
||||
} &default="?";
|
||||
|
||||
const classes = {
|
||||
[1] = "C_INTERNET",
|
||||
[2] = "C_CSNET",
|
||||
[3] = "C_CHAOS",
|
||||
[4] = "C_HESOD",
|
||||
[254] = "C_NONE",
|
||||
[255] = "C_ANY",
|
||||
} &default = function(n: count): string { return fmt("qclass-%d", n); };
|
||||
}
|
279
scripts/base/protocols/dns/main.bro
Normal file
279
scripts/base/protocols/dns/main.bro
Normal file
|
@ -0,0 +1,279 @@
|
|||
|
||||
module DNS;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { DNS };
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
proto: transport_proto &log;
|
||||
trans_id: count &log &optional;
|
||||
query: string &log &optional;
|
||||
qclass: count &log &optional;
|
||||
qclass_name: string &log &optional;
|
||||
qtype: count &log &optional;
|
||||
qtype_name: string &log &optional;
|
||||
rcode: count &log &optional;
|
||||
rcode_name: string &log &optional;
|
||||
QR: bool &log &default=F;
|
||||
AA: bool &log &default=F;
|
||||
TC: bool &log &default=F;
|
||||
RD: bool &log &default=F;
|
||||
RA: bool &log &default=F;
|
||||
Z: count &log &default=0;
|
||||
TTL: interval &log &optional;
|
||||
answers: set[string] &log &optional;
|
||||
|
||||
## This value indicates if this request/response pair is ready to be logged.
|
||||
ready: bool &default=F;
|
||||
total_answers: count &optional;
|
||||
total_replies: count &optional;
|
||||
};
|
||||
|
||||
type State: record {
|
||||
## Indexed by query id, returns Info record corresponding to
|
||||
## query/response which haven't completed yet.
|
||||
pending: table[count] of Info &optional;
|
||||
|
||||
## This is the list of DNS responses that have completed based on the
|
||||
## number of responses declared and the number received. The contents
|
||||
## of the set are transaction IDs.
|
||||
finished_answers: set[count] &optional;
|
||||
};
|
||||
|
||||
global log_dns: event(rec: Info);
|
||||
|
||||
## This is called by the specific dns_*_reply events with a "reply" which
|
||||
## may not represent the full data available from the resource record, but
|
||||
## it's generally considered a summarization of the response(s).
|
||||
# TODO: Weirdly enough, if I define this, the locally defined script layer
|
||||
# event won't trigger any of it's handlers.
|
||||
#global do_reply: event(c: connection, msg: dns_msg, ans: dns_answer, reply: string);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
dns: Info &optional;
|
||||
dns_state: State &optional;
|
||||
};
|
||||
|
||||
# DPD configuration.
|
||||
redef capture_filters += {
|
||||
["dns"] = "port 53",
|
||||
["mdns"] = "udp and port 5353",
|
||||
["llmns"] = "udp and port 5355",
|
||||
["netbios-ns"] = "udp port 137",
|
||||
};
|
||||
|
||||
global dns_ports = { 53/udp, 53/tcp, 137/udp, 5353/udp, 5355/udp } &redef;
|
||||
redef dpd_config += { [ANALYZER_DNS] = [$ports = dns_ports] };
|
||||
|
||||
global dns_udp_ports = { 53/udp, 137/udp, 5353/udp, 5355/udp } &redef;
|
||||
global dns_tcp_ports = { 53/tcp } &redef;
|
||||
redef dpd_config += { [ANALYZER_DNS_UDP_BINPAC] = [$ports = dns_udp_ports] };
|
||||
redef dpd_config += { [ANALYZER_DNS_TCP_BINPAC] = [$ports = dns_tcp_ports] };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(DNS, [$columns=Info, $ev=log_dns]);
|
||||
}
|
||||
|
||||
function new_session(c: connection, trans_id: count): Info
|
||||
{
|
||||
if ( ! c?$dns_state )
|
||||
{
|
||||
local state: State;
|
||||
state$pending=table();
|
||||
state$finished_answers=set();
|
||||
c$dns_state = state;
|
||||
}
|
||||
|
||||
local info: Info;
|
||||
info$ts = network_time();
|
||||
info$id = c$id;
|
||||
info$uid = c$uid;
|
||||
info$proto = get_conn_transport_proto(c$id);
|
||||
info$trans_id = trans_id;
|
||||
return info;
|
||||
}
|
||||
|
||||
function set_session(c: connection, msg: dns_msg, is_query: bool)
|
||||
{
|
||||
if ( ! c?$dns_state || msg$id !in c$dns_state$pending )
|
||||
c$dns_state$pending[msg$id] = new_session(c, msg$id);
|
||||
|
||||
c$dns = c$dns_state$pending[msg$id];
|
||||
|
||||
c$dns$rcode = msg$rcode;
|
||||
c$dns$rcode_name = base_errors[msg$rcode];
|
||||
|
||||
if ( ! is_query )
|
||||
{
|
||||
if ( ! c$dns?$total_answers )
|
||||
c$dns$total_answers = msg$num_answers;
|
||||
|
||||
if ( c$dns?$total_replies &&
|
||||
c$dns$total_replies != msg$num_answers + msg$num_addl + msg$num_auth )
|
||||
{
|
||||
event conn_weird("dns_changed_number_of_responses", c,
|
||||
fmt("The declared number of responses changed from %d to %d",
|
||||
c$dns$total_replies,
|
||||
msg$num_answers + msg$num_addl + msg$num_auth));
|
||||
}
|
||||
else
|
||||
{
|
||||
# Store the total number of responses expected from the first reply.
|
||||
c$dns$total_replies = msg$num_answers + msg$num_addl + msg$num_auth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=5
|
||||
{
|
||||
set_session(c, msg, F);
|
||||
|
||||
c$dns$AA = msg$AA;
|
||||
c$dns$RA = msg$RA;
|
||||
c$dns$TTL = ans$TTL;
|
||||
|
||||
if ( ans$answer_type == DNS_ANS )
|
||||
{
|
||||
if ( msg$id in c$dns_state$finished_answers )
|
||||
event conn_weird("dns_reply_seen_after_done", c, "");
|
||||
|
||||
if ( reply != "" )
|
||||
{
|
||||
if ( ! c$dns?$answers )
|
||||
c$dns$answers = set();
|
||||
add c$dns$answers[reply];
|
||||
}
|
||||
|
||||
if ( c$dns?$answers && |c$dns$answers| == c$dns$total_answers )
|
||||
{
|
||||
add c$dns_state$finished_answers[c$dns$trans_id];
|
||||
# Indicate this request/reply pair is ready to be logged.
|
||||
c$dns$ready = T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=-5
|
||||
{
|
||||
if ( c$dns$ready )
|
||||
{
|
||||
Log::write(DNS, c$dns);
|
||||
add c$dns_state$finished_answers[c$dns$trans_id];
|
||||
# This record is logged and no longer pending.
|
||||
delete c$dns_state$pending[c$dns$trans_id];
|
||||
}
|
||||
}
|
||||
|
||||
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) &priority=5
|
||||
{
|
||||
set_session(c, msg, T);
|
||||
|
||||
c$dns$RD = msg$RD;
|
||||
c$dns$TC = msg$TC;
|
||||
c$dns$qclass = qclass;
|
||||
c$dns$qclass_name = classes[qclass];
|
||||
c$dns$qtype = qtype;
|
||||
c$dns$qtype_name = query_types[qtype];
|
||||
|
||||
# Decode netbios name queries
|
||||
# Note: I'm ignoring the name type for now. Not sure if this should be
|
||||
# worked into the query/response in some fashion.
|
||||
if ( c$id$resp_p == 137/udp )
|
||||
query = decode_netbios_name(query);
|
||||
c$dns$query = query;
|
||||
|
||||
c$dns$Z = msg$Z;
|
||||
}
|
||||
|
||||
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, fmt("%s", a));
|
||||
}
|
||||
|
||||
event dns_TXT_reply(c: connection, msg: dns_msg, ans: dns_answer, str: string) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, str);
|
||||
}
|
||||
|
||||
event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr,
|
||||
astr: string) &priority=5
|
||||
{
|
||||
# TODO: What should we do with astr?
|
||||
event do_reply(c, msg, ans, fmt("%s", a));
|
||||
}
|
||||
|
||||
event dns_NS_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_CNAME_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_MX_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string,
|
||||
preference: count) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_PTR_reply(c: connection, msg: dns_msg, ans: dns_answer, name: string) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, name);
|
||||
}
|
||||
|
||||
event dns_SOA_reply(c: connection, msg: dns_msg, ans: dns_answer, soa: dns_soa) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, soa$mname);
|
||||
}
|
||||
|
||||
event dns_WKS_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, "");
|
||||
}
|
||||
|
||||
event dns_SRV_reply(c: connection, msg: dns_msg, ans: dns_answer) &priority=5
|
||||
{
|
||||
event do_reply(c, msg, ans, "");
|
||||
}
|
||||
|
||||
# TODO: figure out how to handle these
|
||||
#event dns_EDNS(c: connection, msg: dns_msg, ans: dns_answer)
|
||||
# {
|
||||
#
|
||||
# }
|
||||
#
|
||||
#event dns_EDNS_addl(c: connection, msg: dns_msg, ans: dns_edns_additional)
|
||||
# {
|
||||
#
|
||||
# }
|
||||
#
|
||||
#event dns_TSIG_addl(c: connection, msg: dns_msg, ans: dns_tsig_additional)
|
||||
# {
|
||||
#
|
||||
# }
|
||||
|
||||
|
||||
event dns_rejected(c: connection, msg: dns_msg,
|
||||
query: string, qtype: count, qclass: count) &priority=5
|
||||
{
|
||||
set_session(c, msg, F);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( ! c?$dns_state )
|
||||
return;
|
||||
|
||||
# If Bro is expiring state, we should go ahead and log all unlogged
|
||||
# request/response pairs now.
|
||||
for ( trans_id in c$dns_state$pending )
|
||||
Log::write(DNS, c$dns_state$pending[trans_id]);
|
||||
}
|
||||
|
3
scripts/base/protocols/ftp/__load__.bro
Normal file
3
scripts/base/protocols/ftp/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
|||
@load ./utils-commands
|
||||
@load ./main
|
||||
@load ./file-extract
|
65
scripts/base/protocols/ftp/file-extract.bro
Normal file
65
scripts/base/protocols/ftp/file-extract.bro
Normal file
|
@ -0,0 +1,65 @@
|
|||
##! File extraction for FTP.
|
||||
|
||||
module FTP;
|
||||
|
||||
export {
|
||||
## Pattern of file mime types to extract from FTP entity bodies.
|
||||
const extract_file_types = /NO_DEFAULT/ &redef;
|
||||
|
||||
## The on-disk prefix for files to be extracted from FTP-data transfers.
|
||||
const extraction_prefix = "ftp-item" &redef;
|
||||
}
|
||||
|
||||
redef record Info += {
|
||||
## The file handle for the file to be extracted
|
||||
extraction_file: file &log &optional;
|
||||
|
||||
extract_file: bool &default=F;
|
||||
num_extracted_files: count &default=0;
|
||||
};
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=3
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] !in ftp_data_expected )
|
||||
return;
|
||||
|
||||
local expected = ftp_data_expected[id$resp_h, id$resp_p];
|
||||
local s = expected$state;
|
||||
|
||||
if ( extract_file_types in s$mime_type )
|
||||
{
|
||||
s$extract_file = T;
|
||||
add s$tags["extracted_file"];
|
||||
++s$num_extracted_files;
|
||||
}
|
||||
}
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=-4
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] !in ftp_data_expected )
|
||||
return;
|
||||
|
||||
local expected = ftp_data_expected[id$resp_h, id$resp_p];
|
||||
local s = expected$state;
|
||||
|
||||
if ( s$extract_file )
|
||||
{
|
||||
local suffix = fmt("%d.dat", s$num_extracted_files);
|
||||
local fname = generate_extraction_filename(extraction_prefix, c, suffix);
|
||||
s$extraction_file = open(fname);
|
||||
if ( s$passive )
|
||||
set_contents_file(id, CONTENTS_RESP, s$extraction_file);
|
||||
else
|
||||
set_contents_file(id, CONTENTS_ORIG, s$extraction_file);
|
||||
}
|
||||
}
|
||||
|
||||
event log_ftp(rec: Info) &priority=-10
|
||||
{
|
||||
delete rec$extraction_file;
|
||||
delete rec$extract_file;
|
||||
}
|
336
scripts/base/protocols/ftp/main.bro
Normal file
336
scripts/base/protocols/ftp/main.bro
Normal file
|
@ -0,0 +1,336 @@
|
|||
##! The logging this script does is primarily focused on logging FTP commands
|
||||
##! along with metadata. For example, if files are transferred, the argument
|
||||
##! will take on the full path that the client is at along with the requested
|
||||
##! file name.
|
||||
##!
|
||||
##! TODO:
|
||||
##!
|
||||
##! * Handle encrypted sessions correctly (get an example?)
|
||||
|
||||
module FTP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { FTP };
|
||||
|
||||
## This setting changes if passwords used in FTP sessions are captured or not.
|
||||
const default_capture_password = F &redef;
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
user: string &log &default="<unknown>";
|
||||
password: string &log &optional;
|
||||
command: string &log &optional;
|
||||
arg: string &log &optional;
|
||||
|
||||
mime_type: string &log &optional;
|
||||
mime_desc: string &log &optional;
|
||||
file_size: count &log &optional;
|
||||
reply_code: count &log &optional;
|
||||
reply_msg: string &log &optional;
|
||||
tags: set[string] &log &default=set();
|
||||
|
||||
## By setting the CWD to '/.', we can indicate that unless something
|
||||
## more concrete is discovered that the existing but unknown
|
||||
## directory is ok to use.
|
||||
cwd: string &default="/.";
|
||||
cmdarg: CmdArg &optional;
|
||||
pending_commands: PendingCmds;
|
||||
|
||||
## This indicates if the session is in active or passive mode.
|
||||
passive: bool &default=F;
|
||||
|
||||
## This determines if the password will be captured for this request.
|
||||
capture_password: bool &default=default_capture_password;
|
||||
};
|
||||
|
||||
type ExpectedConn: record {
|
||||
host: addr;
|
||||
state: Info;
|
||||
};
|
||||
|
||||
## This record is to hold a parsed FTP reply code. For example, for the
|
||||
## 201 status code, the digits would be parsed as: x->2, y->0, z=>1.
|
||||
type ReplyCode: record {
|
||||
x: count;
|
||||
y: count;
|
||||
z: count;
|
||||
};
|
||||
|
||||
# TODO: add this back in some form. raise a notice again?
|
||||
#const excessive_filename_len = 250 &redef;
|
||||
#const excessive_filename_trunc_len = 32 &redef;
|
||||
|
||||
## These are user IDs that can be considered "anonymous".
|
||||
const guest_ids = { "anonymous", "ftp", "guest" } &redef;
|
||||
|
||||
## The list of commands that should have their command/response pairs logged.
|
||||
const logged_commands = {
|
||||
"APPE", "DELE", "RETR", "STOR", "STOU", "ACCT"
|
||||
} &redef;
|
||||
|
||||
## This function splits FTP reply codes into the three constituent
|
||||
global parse_ftp_reply_code: function(code: count): ReplyCode;
|
||||
|
||||
global log_ftp: event(rec: Info);
|
||||
}
|
||||
|
||||
# Add the state tracking information variable to the connection record
|
||||
redef record connection += {
|
||||
ftp: Info &optional;
|
||||
};
|
||||
|
||||
# Configure DPD
|
||||
const ports = { 21/tcp } &redef;
|
||||
redef capture_filters += { ["ftp"] = "port 21" };
|
||||
redef dpd_config += { [ANALYZER_FTP] = [$ports = ports] };
|
||||
|
||||
# Establish the variable for tracking expected connections.
|
||||
global ftp_data_expected: table[addr, port] of ExpectedConn &create_expire=5mins;
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(FTP, [$columns=Info, $ev=log_ftp]);
|
||||
}
|
||||
|
||||
## A set of commands where the argument can be expected to refer
|
||||
## to a file or directory.
|
||||
const file_cmds = {
|
||||
"APPE", "CWD", "DELE", "MKD", "RETR", "RMD", "RNFR", "RNTO",
|
||||
"STOR", "STOU", "REST", "SIZE", "MDTM",
|
||||
};
|
||||
|
||||
## Commands that either display or change the current working directory along
|
||||
## with the response codes to indicate a successful command.
|
||||
const directory_cmds = {
|
||||
["CWD", 250],
|
||||
["CDUP", 200], # typo in RFC?
|
||||
["CDUP", 250], # as found in traces
|
||||
["PWD", 257],
|
||||
["XPWD", 257],
|
||||
};
|
||||
|
||||
function parse_ftp_reply_code(code: count): ReplyCode
|
||||
{
|
||||
local a: ReplyCode;
|
||||
|
||||
a$z = code % 10;
|
||||
|
||||
code = code / 10;
|
||||
a$y = code % 10;
|
||||
|
||||
code = code / 10;
|
||||
a$x = code % 10;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
function set_ftp_session(c: connection)
|
||||
{
|
||||
if ( ! c?$ftp )
|
||||
{
|
||||
local s: Info;
|
||||
s$ts=network_time();
|
||||
s$uid=c$uid;
|
||||
s$id=c$id;
|
||||
c$ftp=s;
|
||||
|
||||
# Add a shim command so the server can respond with some init response.
|
||||
add_pending_cmd(c$ftp$pending_commands, "<init>", "");
|
||||
}
|
||||
}
|
||||
|
||||
function ftp_message(s: Info)
|
||||
{
|
||||
# If it either has a tag associated with it (something detected)
|
||||
# or it's a deliberately logged command.
|
||||
if ( |s$tags| > 0 || (s?$cmdarg && s$cmdarg$cmd in logged_commands) )
|
||||
{
|
||||
if ( s?$password && to_lower(s$user) !in guest_ids )
|
||||
s$password = "<hidden>";
|
||||
|
||||
local arg = s$cmdarg$arg;
|
||||
if ( s$cmdarg$cmd in file_cmds )
|
||||
arg = fmt("ftp://%s%s", s$id$resp_h, build_path_compressed(s$cwd, arg));
|
||||
|
||||
s$ts=s$cmdarg$ts;
|
||||
s$command=s$cmdarg$cmd;
|
||||
if ( arg == "" )
|
||||
delete s$arg;
|
||||
else
|
||||
s$arg=arg;
|
||||
|
||||
Log::write(FTP, s);
|
||||
}
|
||||
|
||||
# The MIME and file_size fields are specific to file transfer commands
|
||||
# and may not be used in all commands so they need reset to "blank"
|
||||
# values after logging.
|
||||
delete s$mime_type;
|
||||
delete s$mime_desc;
|
||||
delete s$file_size;
|
||||
# Tags are cleared everytime too.
|
||||
delete s$tags;
|
||||
}
|
||||
|
||||
event ftp_request(c: connection, command: string, arg: string) &priority=5
|
||||
{
|
||||
# Write out the previous command when a new command is seen.
|
||||
# The downside here is that commands definitely aren't logged until the
|
||||
# next command is issued or the control session ends. In practicality
|
||||
# this isn't an issue, but I suppose it could be a delay tactic for
|
||||
# attackers.
|
||||
if ( c?$ftp && c$ftp?$cmdarg && c$ftp?$reply_code )
|
||||
{
|
||||
remove_pending_cmd(c$ftp$pending_commands, c$ftp$cmdarg);
|
||||
ftp_message(c$ftp);
|
||||
}
|
||||
|
||||
local id = c$id;
|
||||
set_ftp_session(c);
|
||||
|
||||
# Queue up the new command and argument
|
||||
add_pending_cmd(c$ftp$pending_commands, command, arg);
|
||||
|
||||
if ( command == "USER" )
|
||||
c$ftp$user = arg;
|
||||
|
||||
else if ( command == "PASS" )
|
||||
c$ftp$password = arg;
|
||||
|
||||
else if ( command == "PORT" || command == "EPRT" )
|
||||
{
|
||||
local data = (command == "PORT") ?
|
||||
parse_ftp_port(arg) : parse_eftp_port(arg);
|
||||
|
||||
if ( data$valid )
|
||||
{
|
||||
c$ftp$passive=F;
|
||||
|
||||
local expected = [$host=id$resp_h, $state=copy(c$ftp)];
|
||||
ftp_data_expected[data$h, data$p] = expected;
|
||||
expect_connection(id$resp_h, data$h, data$p, ANALYZER_FILE, 5mins);
|
||||
}
|
||||
else
|
||||
{
|
||||
# TODO: raise a notice? does anyone care?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event ftp_reply(c: connection, code: count, msg: string, cont_resp: bool) &priority=5
|
||||
{
|
||||
# TODO: figure out what to do with continued FTP response (not used much)
|
||||
#if ( cont_resp ) return;
|
||||
|
||||
local id = c$id;
|
||||
set_ftp_session(c);
|
||||
|
||||
c$ftp$cmdarg = get_pending_cmd(c$ftp$pending_commands, code, msg);
|
||||
|
||||
c$ftp$reply_code = code;
|
||||
c$ftp$reply_msg = msg;
|
||||
|
||||
# TODO: do some sort of generic clear text login processing here.
|
||||
local response_xyz = parse_ftp_reply_code(code);
|
||||
#if ( response_xyz$x == 2 && # successful
|
||||
# session$cmdarg$cmd == "PASS" )
|
||||
# do_ftp_login(c, session);
|
||||
|
||||
if ( (code == 150 && c$ftp$cmdarg$cmd == "RETR") ||
|
||||
(code == 213 && c$ftp$cmdarg$cmd == "SIZE") )
|
||||
{
|
||||
# NOTE: This isn't exactly the right thing to do for SIZE since the size
|
||||
# on a different file could be checked, but the file size will
|
||||
# be overwritten by the server response to the RETR command
|
||||
# if that's given as well which would be more correct.
|
||||
c$ftp$file_size = extract_count(msg);
|
||||
}
|
||||
|
||||
# PASV and EPSV processing
|
||||
else if ( (code == 227 || code == 229) &&
|
||||
(c$ftp$cmdarg$cmd == "PASV" || c$ftp$cmdarg$cmd == "EPSV") )
|
||||
{
|
||||
local data = (code == 227) ? parse_ftp_pasv(msg) : parse_ftp_epsv(msg);
|
||||
|
||||
if ( data$valid )
|
||||
{
|
||||
c$ftp$passive=T;
|
||||
|
||||
if ( code == 229 && data$h == 0.0.0.0 )
|
||||
data$h = id$resp_h;
|
||||
|
||||
local expected = [$host=id$orig_h, $state=c$ftp];
|
||||
ftp_data_expected[data$h, data$p] = expected;
|
||||
expect_connection(id$orig_h, data$h, data$p, ANALYZER_FILE, 5mins);
|
||||
}
|
||||
else
|
||||
{
|
||||
# TODO: do something if there was a problem parsing the PASV message?
|
||||
}
|
||||
}
|
||||
|
||||
if ( [c$ftp$cmdarg$cmd, code] in directory_cmds )
|
||||
{
|
||||
if ( c$ftp$cmdarg$cmd == "CWD" )
|
||||
c$ftp$cwd = build_path(c$ftp$cwd, c$ftp$cmdarg$arg);
|
||||
|
||||
else if ( c$ftp$cmdarg$cmd == "CDUP" )
|
||||
c$ftp$cwd = cat(c$ftp$cwd, "/..");
|
||||
|
||||
else if ( c$ftp$cmdarg$cmd == "PWD" || c$ftp$cmdarg$cmd == "XPWD" )
|
||||
c$ftp$cwd = extract_path(msg);
|
||||
}
|
||||
|
||||
# In case there are multiple commands queued, go ahead and remove the
|
||||
# command here and log because we can't do the normal processing pipeline
|
||||
# to wait for a new command before logging the command/response pair.
|
||||
if ( |c$ftp$pending_commands| > 1 )
|
||||
{
|
||||
remove_pending_cmd(c$ftp$pending_commands, c$ftp$cmdarg);
|
||||
ftp_message(c$ftp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event expected_connection_seen(c: connection, a: count) &priority=10
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] in ftp_data_expected )
|
||||
add c$service["ftp-data"];
|
||||
}
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=5
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] in ftp_data_expected )
|
||||
{
|
||||
local expected = ftp_data_expected[id$resp_h, id$resp_p];
|
||||
local s = expected$state;
|
||||
s$mime_type = mime_type;
|
||||
s$mime_desc = descr;
|
||||
}
|
||||
}
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=-5
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] in ftp_data_expected )
|
||||
delete ftp_data_expected[id$resp_h, id$resp_p];
|
||||
}
|
||||
|
||||
# Use state remove event to cover connections terminated by RST.
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( ! c?$ftp ) return;
|
||||
|
||||
for ( ca in c$ftp$pending_commands )
|
||||
{
|
||||
c$ftp$cmdarg = c$ftp$pending_commands[ca];
|
||||
ftp_message(c$ftp);
|
||||
}
|
||||
}
|
134
scripts/base/protocols/ftp/utils-commands.bro
Normal file
134
scripts/base/protocols/ftp/utils-commands.bro
Normal file
|
@ -0,0 +1,134 @@
|
|||
module FTP;
|
||||
|
||||
export {
|
||||
type CmdArg: record {
|
||||
ts: time;
|
||||
cmd: string &default="<unknown>";
|
||||
arg: string &default="";
|
||||
seq: count &default=0;
|
||||
};
|
||||
|
||||
type PendingCmds: table[count] of CmdArg;
|
||||
|
||||
const cmd_reply_code: set[string, count] = {
|
||||
# According to RFC 959
|
||||
["<init>", [120, 220, 421]],
|
||||
["USER", [230, 331, 332, 421, 530, 500, 501]],
|
||||
["PASS", [230, 202, 332, 421, 530, 500, 501, 503]],
|
||||
["ACCT", [230, 202, 421, 530, 500, 501, 503]],
|
||||
["CWD", [250, 421, 500, 501, 502, 530, 550]],
|
||||
["CDUP", [200, 250, 421, 500, 501, 502, 530, 550]],
|
||||
["SMNT", [202, 250, 421, 500, 501, 502, 530, 550]],
|
||||
["REIN", [120, 220, 421, 500, 502]],
|
||||
["QUIT", [221, 500]],
|
||||
["PORT", [200, 421, 500, 501, 530]],
|
||||
["PASV", [227, 421, 500, 501, 502, 530]],
|
||||
["MODE", [200, 421, 500, 501, 502, 504, 530]],
|
||||
["TYPE", [200, 421, 500, 501, 504, 530]],
|
||||
["STRU", [200, 421, 500, 501, 504, 530]],
|
||||
["ALLO", [200, 202, 421, 500, 501, 504, 530]],
|
||||
["REST", [200, 350, 421, 500, 501, 502, 530]],
|
||||
["STOR", [110, 125, 150, 226, 250, 421, 425, 426, 451, 551, 552, 532, 450, 452, 553, 500, 501, 530, 550]],
|
||||
["STOU", [110, 125, 150, 226, 250, 421, 425, 426, 451, 551, 552, 532, 450, 452, 553, 500, 501, 530, 550]],
|
||||
["RETR", [110, 125, 150, 226, 250, 421, 425, 426, 451, 450, 500, 501, 530, 550]],
|
||||
["LIST", [125, 150, 226, 250, 421, 425, 426, 451, 450, 500, 501, 502, 530, 550]],
|
||||
["NLST", [125, 150, 226, 250, 421, 425, 426, 451, 450, 500, 501, 502, 530, 550]],
|
||||
["APPE", [125, 150, 226, 250, 421, 425, 426, 451, 551, 552, 532, 450, 550, 452, 553, 500, 501, 502, 530]],
|
||||
["RNFR", [350, 421, 450, 550, 500, 501, 502, 530]],
|
||||
["RNTO", [250, 421, 532, 553, 500, 501, 502, 503, 530]],
|
||||
["DELE", [250, 421, 450, 550, 500, 501, 502, 530]],
|
||||
["RMD", [250, 421, 500, 501, 502, 530, 550]],
|
||||
["MKD", [257, 421, 500, 501, 502, 530, 550]],
|
||||
["PWD", [257, 421, 500, 501, 502, 550]],
|
||||
["ABOR", [225, 226, 421, 500, 501, 502]],
|
||||
["SYST", [215, 421, 500, 501, 502, 530]],
|
||||
["STAT", [211, 212, 213, 421, 450, 500, 501, 502, 530]],
|
||||
["HELP", [200, 211, 214, 421, 500, 501, 502]],
|
||||
["SITE", [200, 202, 214, 500, 501, 502, 530]],
|
||||
["NOOP", [200, 421, 500]],
|
||||
|
||||
# Extensions
|
||||
["LPRT", [500, 501, 521]], # RFC1639
|
||||
["FEAT", [211, 500, 502]], # RFC2389
|
||||
["OPTS", [200, 451, 501]], # RFC2389
|
||||
["EPSV", [229, 500, 501]], # RFC2428
|
||||
["EPRT", [200, 500, 501, 522]], # RFC2428
|
||||
["SIZE", [213, 500, 501, 550]], # RFC3659
|
||||
["MDTM", [213, 500, 501, 550]], # RFC3659
|
||||
["MLST", [150, 226, 250, 500, 501, 550]], # RFC3659
|
||||
["MLSD", [150, 226, 250, 500, 501, 550]], # RFC3659
|
||||
|
||||
["CLNT", [200, 500]], # No RFC (indicate client software)
|
||||
["MACB", [200, 500, 550]], # No RFC (test for MacBinary support)
|
||||
|
||||
["<init>", 0], # unexpected command-reply pair
|
||||
["<missing>", 0], # unexpected command-reply pair
|
||||
["QUIT", 0], # unexpected command-reply pair
|
||||
} &redef;
|
||||
}
|
||||
|
||||
function add_pending_cmd(pc: PendingCmds, cmd: string, arg: string): CmdArg
|
||||
{
|
||||
local ca = [$cmd = cmd, $arg = arg, $seq=|pc|+1, $ts=network_time()];
|
||||
pc[ca$seq] = ca;
|
||||
|
||||
return ca;
|
||||
}
|
||||
|
||||
# Determine which is the best command to match with based on the
|
||||
# response code and message.
|
||||
function get_pending_cmd(pc: PendingCmds, reply_code: count, reply_msg: string): CmdArg
|
||||
{
|
||||
local best_match: CmdArg;
|
||||
local best_seq = 0;
|
||||
local best_score: int = -1;
|
||||
|
||||
for ( cmd_seq in pc )
|
||||
{
|
||||
local cmd = pc[cmd_seq];
|
||||
local score: int = 0;
|
||||
|
||||
# if the command is compatible with the reply code
|
||||
# code 500 (syntax error) is compatible with all commands
|
||||
if ( reply_code == 500 || [cmd$cmd, reply_code] in cmd_reply_code )
|
||||
score = score + 100;
|
||||
|
||||
# if the command or the command arg appears in the reply message
|
||||
if ( strstr(reply_msg, cmd$cmd) > 0 )
|
||||
score = score + 20;
|
||||
if ( strstr(reply_msg, cmd$arg) > 0 )
|
||||
score = score + 10;
|
||||
|
||||
if ( score > best_score ||
|
||||
( score == best_score && best_seq > cmd_seq ) ) # break tie with sequence number
|
||||
{
|
||||
best_score = score;
|
||||
best_seq = cmd_seq;
|
||||
best_match = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
#if ( [best_match$cmd, reply_code] !in cmd_reply_code )
|
||||
# {
|
||||
# # TODO: maybe do something when best match doesn't have an expected response code?
|
||||
# }
|
||||
return best_match;
|
||||
}
|
||||
|
||||
function remove_pending_cmd(pc: PendingCmds, ca: CmdArg): bool
|
||||
{
|
||||
if ( ca$seq in pc )
|
||||
{
|
||||
delete pc[ca$seq];
|
||||
return T;
|
||||
}
|
||||
else
|
||||
return F;
|
||||
}
|
||||
|
||||
function pop_pending_cmd(pc: PendingCmds, reply_code: count, reply_msg: string): CmdArg
|
||||
{
|
||||
local ca = get_pending_cmd(pc, reply_code, reply_msg);
|
||||
remove_pending_cmd(pc, ca);
|
||||
return ca;
|
||||
}
|
5
scripts/base/protocols/http/__load__.bro
Normal file
5
scripts/base/protocols/http/__load__.bro
Normal file
|
@ -0,0 +1,5 @@
|
|||
@load ./main
|
||||
@load ./utils
|
||||
@load ./file-ident
|
||||
@load ./file-hash
|
||||
@load ./file-extract
|
56
scripts/base/protocols/http/file-extract.bro
Normal file
56
scripts/base/protocols/http/file-extract.bro
Normal file
|
@ -0,0 +1,56 @@
|
|||
##! Extracts the items from HTTP traffic, one per file. At this time only
|
||||
##! the message body from the server can be extracted with this script.
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
## Pattern of file mime types to extract from HTTP entity bodies.
|
||||
const extract_file_types = /NO_DEFAULT/ &redef;
|
||||
|
||||
## The on-disk prefix for files to be extracted from HTTP entity bodies.
|
||||
const extraction_prefix = "http-item" &redef;
|
||||
|
||||
redef record Info += {
|
||||
## This field can be set per-connection to determine if the entity body
|
||||
## will be extracted. It must be set to T on or before the first
|
||||
## entity_body_data event.
|
||||
extracting_file: bool &default=F;
|
||||
|
||||
## This is the holder for the file handle as the file is being written
|
||||
## to disk.
|
||||
extraction_file: file &log &optional;
|
||||
};
|
||||
|
||||
redef record State += {
|
||||
entity_bodies: count &default=0;
|
||||
};
|
||||
}
|
||||
|
||||
event http_entity_data(c: connection, is_orig: bool, length: count, data: string) &priority=5
|
||||
{
|
||||
# Client body extraction is not currently supported in this script.
|
||||
if ( is_orig || ! c$http$first_chunk ) return;
|
||||
|
||||
if ( c$http$first_chunk )
|
||||
{
|
||||
if ( c$http?$mime_type &&
|
||||
extract_file_types in c$http$mime_type )
|
||||
{
|
||||
c$http$extracting_file = T;
|
||||
local suffix = fmt("%s_%d.dat", is_orig ? "orig" : "resp", ++c$http_state$entity_bodies);
|
||||
local fname = generate_extraction_filename(extraction_prefix, c, suffix);
|
||||
|
||||
c$http$extraction_file = open(fname);
|
||||
enable_raw_output(c$http$extraction_file);
|
||||
}
|
||||
}
|
||||
|
||||
if ( c$http$extracting_file )
|
||||
print c$http$extraction_file, data;
|
||||
}
|
||||
|
||||
event http_end_entity(c: connection, is_orig: bool)
|
||||
{
|
||||
if ( c$http$extracting_file )
|
||||
close(c$http$extraction_file);
|
||||
}
|
92
scripts/base/protocols/http/file-hash.bro
Normal file
92
scripts/base/protocols/http/file-hash.bro
Normal file
|
@ -0,0 +1,92 @@
|
|||
##! Calculate hashes for HTTP body transfers.
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that an MD5 sum was calculated for an HTTP response body.
|
||||
MD5,
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
## The MD5 sum for a file transferred over HTTP will be stored here.
|
||||
md5: string &log &optional;
|
||||
|
||||
## This value can be set per-transfer to determine per request
|
||||
## if a file should have an MD5 sum generated. It must be
|
||||
## set to T at the time of or before the first chunk of body data.
|
||||
calc_md5: bool &default=F;
|
||||
|
||||
## This boolean value indicates if an MD5 sum is currently being
|
||||
## calculated for the current file transfer.
|
||||
calculating_md5: bool &default=F;
|
||||
};
|
||||
|
||||
## Generate MD5 sums for these filetypes.
|
||||
const generate_md5 = /application\/x-dosexec/ # Windows and DOS executables
|
||||
| /application\/x-executable/ # *NIX executable binary
|
||||
&redef;
|
||||
}
|
||||
|
||||
## Initialize and calculate the hash.
|
||||
event http_entity_data(c: connection, is_orig: bool, length: count, data: string) &priority=5
|
||||
{
|
||||
if ( is_orig || ! c?$http ) return;
|
||||
|
||||
if ( c$http$first_chunk )
|
||||
{
|
||||
if ( c$http$calc_md5 ||
|
||||
(c$http?$mime_type && generate_md5 in c$http$mime_type) )
|
||||
{
|
||||
c$http$calculating_md5 = T;
|
||||
md5_hash_init(c$id);
|
||||
}
|
||||
}
|
||||
|
||||
if ( c$http$calculating_md5 )
|
||||
md5_hash_update(c$id, data);
|
||||
}
|
||||
|
||||
## In the event of a content gap during a file transfer, detect the state for
|
||||
## the MD5 sum calculation and stop calculating the MD5 since it would be
|
||||
## incorrect anyway.
|
||||
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
|
||||
{
|
||||
if ( is_orig || ! c?$http ) return;
|
||||
|
||||
set_state(c, F, is_orig);
|
||||
if ( c$http$calculating_md5 )
|
||||
{
|
||||
c$http$calculating_md5 = F;
|
||||
md5_hash_finish(c$id);
|
||||
}
|
||||
}
|
||||
|
||||
## When the file finishes downloading, finish the hash and generate a notice.
|
||||
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority=-3
|
||||
{
|
||||
if ( is_orig || ! c?$http ) return;
|
||||
|
||||
if ( c$http$calculating_md5 )
|
||||
{
|
||||
local url = build_url_http(c$http);
|
||||
c$http$calculating_md5 = F;
|
||||
c$http$md5 = md5_hash_finish(c$id);
|
||||
|
||||
NOTICE([$note=MD5, $msg=fmt("%s %s %s", c$id$orig_h, c$http$md5, url),
|
||||
$sub=c$http$md5, $conn=c, $URL=url]);
|
||||
}
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$http_state &&
|
||||
c$http_state$current_response in c$http_state$pending &&
|
||||
c$http_state$pending[c$http_state$current_response]$calculating_md5 )
|
||||
{
|
||||
# The MD5 sum isn't going to be saved anywhere since the entire
|
||||
# body wouldn't have been seen anyway and we'd just be giving an
|
||||
# incorrect MD5 sum.
|
||||
md5_hash_finish(c$id);
|
||||
}
|
||||
}
|
76
scripts/base/protocols/http/file-ident.bro
Normal file
76
scripts/base/protocols/http/file-ident.bro
Normal file
|
@ -0,0 +1,76 @@
|
|||
##! This script is involved in the identification of file types in HTTP
|
||||
##! response bodies.
|
||||
|
||||
# Add the magic number signatures to the core signature set.
|
||||
redef signature_files += "base/protocols/http/file-ident.sig";
|
||||
# Ignore the signatures used to match files
|
||||
redef Signatures::ignored_ids += /^matchfile-/;
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
# This notice is thrown when the file extension doesn't
|
||||
# seem to match the file contents.
|
||||
Incorrect_File_Type,
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
## This will record the mime_type identified.
|
||||
mime_type: string &log &optional;
|
||||
|
||||
## This indicates that no data of the current file transfer has been
|
||||
## seen yet. After the first :bro:id:`http_entity_data` event, it
|
||||
## will be set to T.
|
||||
first_chunk: bool &default=T;
|
||||
};
|
||||
|
||||
redef enum Tags += {
|
||||
IDENTIFIED_FILE
|
||||
};
|
||||
|
||||
# Create regexes that *should* in be in the urls for specifics mime types.
|
||||
# Notices are thrown if the pattern doesn't match the url for the file type.
|
||||
const mime_types_extensions: table[string] of pattern = {
|
||||
["application/x-dosexec"] = /\.([eE][xX][eE]|[dD][lL][lL])/,
|
||||
} &redef;
|
||||
}
|
||||
|
||||
event signature_match(state: signature_state, msg: string, data: string) &priority=5
|
||||
{
|
||||
# Only signatures matching file types are dealt with here.
|
||||
if ( /^matchfile-/ !in state$sig_id ) return;
|
||||
|
||||
local c = state$conn;
|
||||
set_state(c, F, F);
|
||||
|
||||
# Not much point in any of this if we don't know about the HTTP session.
|
||||
if ( ! c?$http ) return;
|
||||
|
||||
# Set the mime type that was detected.
|
||||
c$http$mime_type = msg;
|
||||
|
||||
if ( msg in mime_types_extensions &&
|
||||
c$http?$uri && mime_types_extensions[msg] !in c$http$uri )
|
||||
{
|
||||
local url = build_url_http(c$http);
|
||||
local message = fmt("%s %s %s", msg, c$http$method, url);
|
||||
NOTICE([$note=Incorrect_File_Type,
|
||||
$msg=message,
|
||||
$conn=c,
|
||||
$method=c$http$method,
|
||||
$URL=url]);
|
||||
}
|
||||
}
|
||||
|
||||
event http_entity_data(c: connection, is_orig: bool, length: count, data: string) &priority=5
|
||||
{
|
||||
if ( c$http$first_chunk && ! c$http?$mime_type )
|
||||
c$http$mime_type = split1(identify_data(data, T), /;/)[1];
|
||||
}
|
||||
|
||||
event http_entity_data(c: connection, is_orig: bool, length: count, data: string) &priority=-10
|
||||
{
|
||||
if ( c$http$first_chunk )
|
||||
c$http$first_chunk=F;
|
||||
}
|
144
scripts/base/protocols/http/file-ident.sig
Normal file
144
scripts/base/protocols/http/file-ident.sig
Normal file
|
@ -0,0 +1,144 @@
|
|||
# These signatures are used as a replacement for libmagic. The signature
|
||||
# name needs to start with "matchfile" and the "event" directive takes
|
||||
# the mime type of the file matched by the http-reply-body pattern.
|
||||
#
|
||||
# Signatures from: http://www.garykessler.net/library/file_sigs.html
|
||||
|
||||
signature matchfile-exe {
|
||||
http-reply-body /\x4D\x5A/
|
||||
event "application/x-dosexec"
|
||||
}
|
||||
|
||||
signature matchfile-elf {
|
||||
http-reply-body /\x7F\x45\x4C\x46/
|
||||
event "application/x-executable"
|
||||
}
|
||||
|
||||
signature matchfile-script {
|
||||
# This is meant to match the interpreter declaration at the top of many
|
||||
# interpreted scripts.
|
||||
http-reply-body /\#\![[:blank:]]?\//
|
||||
event "application/x-script"
|
||||
}
|
||||
|
||||
signature matchfile-wmv {
|
||||
http-reply-body /\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C/
|
||||
event "video/x-ms-wmv"
|
||||
}
|
||||
|
||||
signature matchfile-flv {
|
||||
http-reply-body /\x46\x4C\x56\x01/
|
||||
event "video/x-flv"
|
||||
}
|
||||
|
||||
signature matchfile-swf {
|
||||
http-reply-body /[\x46\x43]\x57\x53/
|
||||
event "application/x-shockwave-flash"
|
||||
}
|
||||
|
||||
signature matchfile-jar {
|
||||
http-reply-body /\x5F\x27\xA8\x89/
|
||||
event "application/java-archive"
|
||||
}
|
||||
|
||||
signature matchfile-class {
|
||||
http-reply-body /\xCA\xFE\xBA\xBE/
|
||||
event "application/java-byte-code"
|
||||
}
|
||||
|
||||
signature matchfile-msoffice-2007 {
|
||||
# MS Office 2007 XML documents
|
||||
http-reply-body /\x50\x4B\x03\x04\x14\x00\x06\x00/
|
||||
event "application/msoffice"
|
||||
}
|
||||
|
||||
signature matchfile-msoffice {
|
||||
# Older MS Office files
|
||||
http-reply-body /\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1/
|
||||
event "application/msoffice"
|
||||
}
|
||||
|
||||
signature matchfile-rtf {
|
||||
http-reply-body /\x7B\x5C\x72\x74\x66\x31/
|
||||
event "application/rtf"
|
||||
}
|
||||
|
||||
signature matchfile-lnk {
|
||||
http-reply-body /\x4C\x00\x00\x00\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x00\x00\x46/
|
||||
event "application/x-ms-shortcut"
|
||||
}
|
||||
|
||||
signature matchfile-torrent {
|
||||
http-reply-body /\x64\x38\x3A\x61\x6E\x6E\x6F\x75\x6E\x63\x65/
|
||||
event "application/x-bittorrent"
|
||||
}
|
||||
|
||||
signature matchfile-pdf {
|
||||
http-reply-body /\x25\x50\x44\x46/
|
||||
event "application/pdf"
|
||||
}
|
||||
|
||||
signature matchfile-html {
|
||||
http-reply-body /<[hH][tT][mM][lL]/
|
||||
event "text/html"
|
||||
}
|
||||
|
||||
signature matchfile-html2 {
|
||||
http-reply-body /<![dD][oO][cC][tT][yY][pP][eE][[:blank:]][hH][tT][mM][lL]/
|
||||
event "text/html"
|
||||
}
|
||||
|
||||
signature matchfile-xml {
|
||||
http-reply-body /<\??[xX][mM][lL]/
|
||||
event "text/xml"
|
||||
}
|
||||
|
||||
signature matchfile-gif {
|
||||
http-reply-body /\x47\x49\x46\x38[\x37\x39]\x61/
|
||||
event "image/gif"
|
||||
}
|
||||
|
||||
signature matchfile-jpg {
|
||||
http-reply-body /\xFF\xD8\xFF[\xDB\xE0\xE1\xE2\xE3\xE8]..[\x4A\x45\x53][\x46\x78\x50][\x49\x69][\x46\x66]/
|
||||
event "image/jpeg"
|
||||
}
|
||||
|
||||
signature matchfile-tiff {
|
||||
http-reply-body /\x4D\x4D\x00[\x2A\x2B]/
|
||||
event "image/tiff"
|
||||
}
|
||||
|
||||
signature matchfile-png {
|
||||
http-reply-body /\x89\x50\x4e\x47/
|
||||
event "image/png"
|
||||
}
|
||||
|
||||
signature matchfile-zip {
|
||||
http-reply-body /\x50\x4B\x03\x04/
|
||||
event "application/zip"
|
||||
}
|
||||
|
||||
signature matchfile-bzip {
|
||||
http-reply-body /\x42\x5A\x68/
|
||||
event "application/bzip2"
|
||||
}
|
||||
|
||||
signature matchfile-gzip {
|
||||
http-reply-body /\x1F\x8B\x08/
|
||||
event "application/x-gzip"
|
||||
}
|
||||
|
||||
signature matchfile-cab {
|
||||
http-reply-body /\x4D\x53\x43\x46/
|
||||
event "application/vnd.ms-cab-compressed"
|
||||
}
|
||||
|
||||
signature matchfile-rar {
|
||||
http-reply-body /\x52\x61\x72\x21\x1A\x07\x00/
|
||||
event "application/x-rar-compressed"
|
||||
}
|
||||
|
||||
signature matchfile-7z {
|
||||
http-reply-body /\x37\x7A\xBC\xAF\x27\x1C/
|
||||
event "application/x-7z-compressed"
|
||||
}
|
247
scripts/base/protocols/http/main.bro
Normal file
247
scripts/base/protocols/http/main.bro
Normal file
|
@ -0,0 +1,247 @@
|
|||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { HTTP };
|
||||
|
||||
## Indicate a type of attack or compromise in the record to be logged.
|
||||
type Tags: enum {
|
||||
EMPTY
|
||||
};
|
||||
|
||||
## This setting changes if passwords used in Basic-Auth are captured or not.
|
||||
const default_capture_password = F &redef;
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
## The verb used in the HTTP request (GET, POST, HEAD, etc.).
|
||||
method: string &log &optional;
|
||||
## The value of the HOST header.
|
||||
host: string &log &optional;
|
||||
## The URI used in the request.
|
||||
uri: string &log &optional;
|
||||
## The value of the "referer" header. The comment is deliberately
|
||||
## misspelled like the standard declares, but the name used here is
|
||||
## "referrer" spelled correctly.
|
||||
referrer: string &log &optional;
|
||||
## The value of the User-Agent header from the client.
|
||||
user_agent: string &log &optional;
|
||||
## The value of the Content-Length header from the client.
|
||||
request_content_length: count &log &optional;
|
||||
## The value of the Content-Length header from the server.
|
||||
response_content_length: count &log &optional;
|
||||
## The status code returned by the server.
|
||||
status_code: count &log &optional;
|
||||
## The status message returned by the server.
|
||||
status_msg: string &log &optional;
|
||||
## The filename given in the Content-Disposition header
|
||||
## sent by the server.
|
||||
filename: string &log &optional;
|
||||
## This is a set of indicators of various attributes discovered and
|
||||
## related to a particular request/response pair.
|
||||
tags: set[Tags] &log;
|
||||
|
||||
## The username if basic-auth is performed for the request.
|
||||
username: string &log &optional;
|
||||
## The password if basic-auth is performed for the request.
|
||||
password: string &log &optional;
|
||||
|
||||
## This determines if the password will be captured for this request.
|
||||
capture_password: bool &default=default_capture_password;
|
||||
|
||||
## All of the headers that may indicate if the request was proxied.
|
||||
proxied: set[string] &log &optional;
|
||||
};
|
||||
|
||||
type State: record {
|
||||
pending: table[count] of Info;
|
||||
current_response: count &default=0;
|
||||
current_request: count &default=0;
|
||||
};
|
||||
|
||||
## The list of HTTP headers typically used to indicate a proxied request.
|
||||
const proxy_headers: set[string] = {
|
||||
"FORWARDED",
|
||||
"X-FORWARDED-FOR",
|
||||
"X-FORWARDED-FROM",
|
||||
"CLIENT-IP",
|
||||
"VIA",
|
||||
"XROXY-CONNECTION",
|
||||
"PROXY-CONNECTION",
|
||||
} &redef;
|
||||
|
||||
global log_http: event(rec: Info);
|
||||
}
|
||||
|
||||
# Add the http state tracking fields to the connection record.
|
||||
redef record connection += {
|
||||
http: Info &optional;
|
||||
http_state: State &optional;
|
||||
};
|
||||
|
||||
# Initialize the HTTP logging stream.
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(HTTP, [$columns=Info, $ev=log_http]);
|
||||
}
|
||||
|
||||
# DPD configuration.
|
||||
const ports = {
|
||||
80/tcp, 81/tcp, 631/tcp, 1080/tcp, 3138/tcp,
|
||||
8000/tcp, 8080/tcp, 8888/tcp,
|
||||
};
|
||||
redef dpd_config += {
|
||||
[[ANALYZER_HTTP, ANALYZER_HTTP_BINPAC]] = [$ports = ports],
|
||||
};
|
||||
redef capture_filters += {
|
||||
["http"] = "tcp and port (80 or 81 or 631 or 1080 or 3138 or 8000 or 8080 or 8888)"
|
||||
};
|
||||
|
||||
function new_http_session(c: connection): Info
|
||||
{
|
||||
local tmp: Info;
|
||||
tmp$ts=network_time();
|
||||
tmp$uid=c$uid;
|
||||
tmp$id=c$id;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
function set_state(c: connection, request: bool, is_orig: bool)
|
||||
{
|
||||
if ( ! c?$http_state )
|
||||
{
|
||||
local s: State;
|
||||
c$http_state = s;
|
||||
}
|
||||
|
||||
# These deal with new requests and responses.
|
||||
if ( request || c$http_state$current_request !in c$http_state$pending )
|
||||
c$http_state$pending[c$http_state$current_request] = new_http_session(c);
|
||||
if ( ! is_orig && c$http_state$current_response !in c$http_state$pending )
|
||||
c$http_state$pending[c$http_state$current_response] = new_http_session(c);
|
||||
|
||||
if ( is_orig )
|
||||
c$http = c$http_state$pending[c$http_state$current_request];
|
||||
else
|
||||
c$http = c$http_state$pending[c$http_state$current_response];
|
||||
}
|
||||
|
||||
event http_request(c: connection, method: string, original_URI: string,
|
||||
unescaped_URI: string, version: string) &priority=5
|
||||
{
|
||||
if ( ! c?$http_state )
|
||||
{
|
||||
local s: State;
|
||||
c$http_state = s;
|
||||
}
|
||||
|
||||
++c$http_state$current_request;
|
||||
set_state(c, T, T);
|
||||
|
||||
c$http$method = method;
|
||||
c$http$uri = unescaped_URI;
|
||||
}
|
||||
|
||||
event http_reply(c: connection, version: string, code: count, reason: string) &priority=5
|
||||
{
|
||||
if ( ! c?$http_state )
|
||||
{
|
||||
local s: State;
|
||||
c$http_state = s;
|
||||
}
|
||||
|
||||
++c$http_state$current_response;
|
||||
set_state(c, F, F);
|
||||
|
||||
c$http$status_code = code;
|
||||
c$http$status_msg = reason;
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=5
|
||||
{
|
||||
set_state(c, F, is_orig);
|
||||
|
||||
if ( is_orig ) # client headers
|
||||
{
|
||||
if ( name == "REFERER" )
|
||||
c$http$referrer = value;
|
||||
|
||||
else if ( name == "HOST" )
|
||||
# The split is done to remove the occasional port value that shows up here.
|
||||
c$http$host = split1(value, /:/)[1];
|
||||
|
||||
else if ( name == "CONTENT-LENGTH" )
|
||||
c$http$request_content_length = extract_count(value);
|
||||
|
||||
else if ( name == "USER-AGENT" )
|
||||
c$http$user_agent = value;
|
||||
|
||||
else if ( name in proxy_headers )
|
||||
{
|
||||
if ( ! c$http?$proxied )
|
||||
c$http$proxied = set();
|
||||
add c$http$proxied[fmt("%s -> %s", name, value)];
|
||||
}
|
||||
|
||||
else if ( name == "AUTHORIZATION" )
|
||||
{
|
||||
if ( /^[bB][aA][sS][iI][cC] / in value )
|
||||
{
|
||||
local userpass = decode_base64(sub(value, /[bB][aA][sS][iI][cC][[:blank:]]/, ""));
|
||||
local up = split(userpass, /:/);
|
||||
if ( |up| >= 2 )
|
||||
{
|
||||
c$http$username = up[1];
|
||||
if ( c$http$capture_password )
|
||||
c$http$password = up[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
c$http$username = "<problem-decoding>";
|
||||
if ( c$http$capture_password )
|
||||
c$http$password = userpass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else # server headers
|
||||
{
|
||||
if ( name == "CONTENT-LENGTH" )
|
||||
c$http$response_content_length = extract_count(value);
|
||||
else if ( name == "CONTENT-DISPOSITION" &&
|
||||
/[fF][iI][lL][eE][nN][aA][mM][eE]/ in value )
|
||||
c$http$filename = sub(value, /^.*[fF][iI][lL][eE][nN][aA][mM][eE]=/, "");
|
||||
}
|
||||
}
|
||||
|
||||
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority = 5
|
||||
{
|
||||
set_state(c, F, is_orig);
|
||||
}
|
||||
|
||||
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat) &priority = -5
|
||||
{
|
||||
# The reply body is done so we're ready to log.
|
||||
if ( ! is_orig )
|
||||
{
|
||||
Log::write(HTTP, c$http);
|
||||
delete c$http_state$pending[c$http_state$current_response];
|
||||
}
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection)
|
||||
{
|
||||
# Flush all pending but incomplete request/response pairs.
|
||||
if ( c?$http_state )
|
||||
{
|
||||
for ( r in c$http_state$pending )
|
||||
{
|
||||
Log::write(HTTP, c$http_state$pending[r]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
94
scripts/base/protocols/http/partial-content.bro
Normal file
94
scripts/base/protocols/http/partial-content.bro
Normal file
|
@ -0,0 +1,94 @@
|
|||
##! This script makes it possible for the HTTP analysis scripts to analyze
|
||||
##! the apparent normal case of "206 Partial Content" responses.
|
||||
##!
|
||||
##! This script doesn't work yet and isn't loaded by default.
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
Partial_Content_Out_Of_Order,
|
||||
};
|
||||
|
||||
type Range: record {
|
||||
from: count;
|
||||
to: count;
|
||||
} &log;
|
||||
|
||||
redef record Info += {
|
||||
current_range: count &default=0;
|
||||
request_ranges: vector of Range &optional;
|
||||
response_range: Range &optional;
|
||||
};
|
||||
|
||||
## Index is client IP address, server IP address, and URL being requested. The
|
||||
## URL is tracked as part of the index in case multiple partial content segmented
|
||||
## files are being transferred simultaneously between the server and client.
|
||||
global partial_content_files: table[addr, addr, string] of Info &read_expire=5mins &redef;
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=2
|
||||
{
|
||||
local parts: table[count] of string;
|
||||
if ( is_orig && name == "RANGE" )
|
||||
{
|
||||
# Example --> Range: bytes=1-1,2336-4951
|
||||
parts = split(value, /[=]/);
|
||||
if ( 2 in parts )
|
||||
{
|
||||
local ranges = split(parts[2], /,/);
|
||||
for ( i in ranges )
|
||||
{
|
||||
if ( ! c$http?$request_ranges )
|
||||
c$http$request_ranges = vector();
|
||||
parts = split(ranges[i], /-/);
|
||||
local r: Range = [$from=extract_count(parts[1]), $to=extract_count(parts[2])];
|
||||
print r;
|
||||
c$http$request_ranges[|c$http$request_ranges|] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( ! is_orig && name == "CONTENT-RANGE" )
|
||||
{
|
||||
# Example --> Content-Range: bytes 2336-4951/489528
|
||||
parts = split(value, /[0-9]*/);
|
||||
|
||||
c$http$response_range = [$from=extract_count(parts[2]), $to=extract_count(parts[4])];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
event http_reply(c: connection, version: string, code: count, reason: string) &priority=5
|
||||
{
|
||||
if ( code != 206 || ! c$http?$request_ranges )
|
||||
return;
|
||||
|
||||
local url = build_url(c$http);
|
||||
if ( [c$id$orig_h, c$id$resp_h, url] !in partial_content_files )
|
||||
{
|
||||
partial_content_files[c$id$orig_h, c$id$resp_h, url] = copy(c$http);
|
||||
}
|
||||
}
|
||||
|
||||
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
|
||||
{
|
||||
if ( is_orig || c$http$status_code != 206 || ! c$http?$request_ranges )
|
||||
return;
|
||||
|
||||
local url = build_url(c$http);
|
||||
local http = partial_content_files[c$id$orig_h, c$id$resp_h, url];
|
||||
local range = http$request_ranges[http$current_range];
|
||||
|
||||
print http$current_range;
|
||||
if ( http$current_range == 0 &&
|
||||
c$http$response_range$from == 0 )
|
||||
{
|
||||
print "correct file beginning!";
|
||||
}
|
||||
}
|
||||
|
||||
event http_end_entity(c: connection, is_orig: bool)
|
||||
{
|
||||
print "end entity";
|
||||
++c$http$current_range;
|
||||
}
|
38
scripts/base/protocols/http/utils.bro
Normal file
38
scripts/base/protocols/http/utils.bro
Normal file
|
@ -0,0 +1,38 @@
|
|||
##! Utilities specific for HTTP processing.
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
global extract_keys: function(data: string, kv_splitter: pattern): string_vec;
|
||||
global build_url: function(rec: Info): string;
|
||||
global build_url_http: function(rec: Info): string;
|
||||
}
|
||||
|
||||
|
||||
function extract_keys(data: string, kv_splitter: pattern): string_vec
|
||||
{
|
||||
local key_vec: vector of string = vector();
|
||||
|
||||
local parts = split(data, kv_splitter);
|
||||
for ( part_index in parts )
|
||||
{
|
||||
local key_val = split1(parts[part_index], /=/);
|
||||
if ( 1 in key_val )
|
||||
key_vec[|key_vec|] = key_val[1];
|
||||
}
|
||||
return key_vec;
|
||||
}
|
||||
|
||||
function build_url(rec: Info): string
|
||||
{
|
||||
local uri = rec?$uri ? rec$uri : "/<missed_request>";
|
||||
local host = rec?$host ? rec$host : fmt("%s", rec$id$resp_h);
|
||||
if ( rec$id$resp_p != 80/tcp )
|
||||
host = fmt("%s:%s", host, rec$id$resp_p);
|
||||
return fmt("%s%s", host, uri);
|
||||
}
|
||||
|
||||
function build_url_http(rec: Info): string
|
||||
{
|
||||
return fmt("http://%s", build_url(rec));
|
||||
}
|
2
scripts/base/protocols/irc/__load__.bro
Normal file
2
scripts/base/protocols/irc/__load__.bro
Normal file
|
@ -0,0 +1,2 @@
|
|||
@load ./main
|
||||
@load ./dcc-send
|
109
scripts/base/protocols/irc/dcc-send.bro
Normal file
109
scripts/base/protocols/irc/dcc-send.bro
Normal file
|
@ -0,0 +1,109 @@
|
|||
##! File extraction and introspection for DCC transfers over IRC.
|
||||
##!
|
||||
##! There is a major problem with this script in the cluster context because
|
||||
##! we might see A send B a message that a DCC connection is to be expected,
|
||||
##! but that connection will actually be between B and C which could be
|
||||
##! analyzed on a different worker.
|
||||
##!
|
||||
##! Example line from IRC server indicating that the DCC SEND is about to start:
|
||||
##! PRIVMSG my_nick :^ADCC SEND whateverfile.zip 3640061780 1026 41709^A
|
||||
|
||||
module IRC;
|
||||
|
||||
export {
|
||||
redef enum Tag += { EXTRACTED_FILE };
|
||||
|
||||
## Pattern of file mime types to extract from IRC DCC file transfers.
|
||||
const extract_file_types = /NO_DEFAULT/ &redef;
|
||||
|
||||
## The on-disk prefix for files to be extracted from IRC DCC file transfers.
|
||||
const extraction_prefix = "irc-dcc-item" &redef;
|
||||
|
||||
redef record Info += {
|
||||
dcc_file_name: string &log &optional;
|
||||
dcc_file_size: count &log &optional;
|
||||
dcc_mime_type: string &log &optional;
|
||||
|
||||
## The file handle for the file to be extracted
|
||||
extraction_file: file &log &optional;
|
||||
|
||||
## A boolean to indicate if the current file transfer should be extraced.
|
||||
extract_file: bool &default=F;
|
||||
|
||||
## The count of the number of file that have been extracted during the session.
|
||||
num_extracted_files: count &default=0;
|
||||
};
|
||||
}
|
||||
|
||||
global dcc_expected_transfers: table[addr, port] of Info = table();
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=3
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] !in dcc_expected_transfers )
|
||||
return;
|
||||
|
||||
local irc = dcc_expected_transfers[id$resp_h, id$resp_p];
|
||||
|
||||
irc$dcc_mime_type = split1(mime_type, /;/)[1];
|
||||
|
||||
if ( extract_file_types == irc$dcc_mime_type )
|
||||
{
|
||||
irc$extract_file = T;
|
||||
add irc$tags[EXTRACTED_FILE];
|
||||
|
||||
local suffix = fmt("%d.dat", ++irc$num_extracted_files);
|
||||
local fname = generate_extraction_filename(extraction_prefix, c, suffix);
|
||||
irc$extraction_file = open(fname);
|
||||
}
|
||||
}
|
||||
|
||||
event file_transferred(c: connection, prefix: string, descr: string,
|
||||
mime_type: string) &priority=-4
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] !in dcc_expected_transfers )
|
||||
return;
|
||||
|
||||
local irc = dcc_expected_transfers[id$resp_h, id$resp_p];
|
||||
|
||||
local tmp = irc$command;
|
||||
irc$command = "DCC";
|
||||
Log::write(IRC, irc);
|
||||
irc$command = tmp;
|
||||
|
||||
if ( irc$extract_file && irc?$extraction_file )
|
||||
set_contents_file(id, CONTENTS_RESP, irc$extraction_file);
|
||||
|
||||
# Delete these values in case another DCC transfer
|
||||
# happens during the IRC session.
|
||||
delete irc$extract_file;
|
||||
delete irc$extraction_file;
|
||||
delete irc$dcc_file_name;
|
||||
delete irc$dcc_file_size;
|
||||
delete irc$dcc_mime_type;
|
||||
delete dcc_expected_transfers[id$resp_h, id$resp_p];
|
||||
}
|
||||
|
||||
event irc_dcc_message(c: connection, is_orig: bool,
|
||||
prefix: string, target: string,
|
||||
dcc_type: string, argument: string,
|
||||
address: addr, dest_port: count, size: count) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
if ( dcc_type != "SEND" )
|
||||
return;
|
||||
c$irc$dcc_file_name = argument;
|
||||
c$irc$dcc_file_size = size;
|
||||
local p = to_port(dest_port, tcp);
|
||||
expect_connection(to_addr("0.0.0.0"), address, p, ANALYZER_FILE, 5 min);
|
||||
dcc_expected_transfers[address, p] = c$irc;
|
||||
}
|
||||
|
||||
event expected_connection_seen(c: connection, a: count) &priority=10
|
||||
{
|
||||
local id = c$id;
|
||||
if ( [id$resp_h, id$resp_p] in dcc_expected_transfers )
|
||||
add c$service["irc-dcc-data"];
|
||||
}
|
124
scripts/base/protocols/irc/main.bro
Normal file
124
scripts/base/protocols/irc/main.bro
Normal file
|
@ -0,0 +1,124 @@
|
|||
##! This is the script that implements the core IRC analysis support. It only
|
||||
##! logs a very limited subset of the IRC protocol by default. The points
|
||||
##! that it logs at are NICK commands, USER commands, and JOIN commands. It
|
||||
##! log various bits of meta data as indicated in the :bro:type:`Info` record
|
||||
##! along with the command at the command arguments.
|
||||
|
||||
module IRC;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { IRC };
|
||||
|
||||
type Tag: enum {
|
||||
EMPTY
|
||||
};
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
nick: string &log &optional;
|
||||
user: string &log &optional;
|
||||
channels: set[string] &log &optional;
|
||||
|
||||
command: string &log &optional;
|
||||
value: string &log &optional;
|
||||
addl: string &log &optional;
|
||||
tags: set[Tag] &log;
|
||||
};
|
||||
|
||||
global irc_log: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
irc: Info &optional;
|
||||
};
|
||||
|
||||
# Some common IRC ports.
|
||||
redef capture_filters += { ["irc-6666"] = "port 6666" };
|
||||
redef capture_filters += { ["irc-6667"] = "port 6667" };
|
||||
|
||||
# DPD configuration.
|
||||
global irc_ports = { 6666/tcp, 6667/tcp } &redef;
|
||||
redef dpd_config += { [ANALYZER_IRC] = [$ports = irc_ports] };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(IRC, [$columns=Info, $ev=irc_log]);
|
||||
}
|
||||
|
||||
function new_session(c: connection): Info
|
||||
{
|
||||
local info: Info;
|
||||
info$ts = network_time();
|
||||
info$uid = c$uid;
|
||||
info$id = c$id;
|
||||
return info;
|
||||
}
|
||||
|
||||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$irc )
|
||||
c$irc = new_session(c);
|
||||
|
||||
c$irc$ts=network_time();
|
||||
}
|
||||
|
||||
event irc_nick_message(c: connection, is_orig: bool, who: string, newnick: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
if ( is_orig )
|
||||
{
|
||||
c$irc$command = "NICK";
|
||||
c$irc$value = newnick;
|
||||
}
|
||||
}
|
||||
|
||||
event irc_nick_message(c: connection, is_orig: bool, who: string, newnick: string) &priority=-5
|
||||
{
|
||||
if ( is_orig )
|
||||
{
|
||||
Log::write(IRC, c$irc);
|
||||
c$irc$nick = newnick;
|
||||
}
|
||||
}
|
||||
|
||||
event irc_user_message(c: connection, is_orig: bool, user: string, host: string, server: string, real_name: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
if ( is_orig )
|
||||
{
|
||||
c$irc$command = "USER";
|
||||
c$irc$value = user;
|
||||
c$irc$addl=fmt("%s %s %s", host, server, real_name);
|
||||
}
|
||||
}
|
||||
|
||||
event irc_user_message(c: connection, is_orig: bool, user: string, host: string, server: string, real_name: string) &priority=-5
|
||||
{
|
||||
if ( is_orig )
|
||||
{
|
||||
Log::write(IRC, c$irc);
|
||||
c$irc$user = user;
|
||||
}
|
||||
}
|
||||
|
||||
event irc_join_message(c: connection, is_orig: bool, info_list: irc_join_list) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
if ( is_orig )
|
||||
c$irc$command = "JOIN";
|
||||
}
|
||||
|
||||
event irc_join_message(c: connection, is_orig: bool, info_list: irc_join_list) &priority=-5
|
||||
{
|
||||
if ( is_orig )
|
||||
{
|
||||
for ( l in info_list )
|
||||
{
|
||||
c$irc$value = l$channel;
|
||||
c$irc$addl = (l$password != "" ? fmt(" with channel key: '%s'", l$password) : "");
|
||||
Log::write(IRC, c$irc);
|
||||
}
|
||||
}
|
||||
}
|
4
scripts/base/protocols/mime/__load__.bro
Normal file
4
scripts/base/protocols/mime/__load__.bro
Normal file
|
@ -0,0 +1,4 @@
|
|||
@load protocols/mime/base
|
||||
@load protocols/mime/file-ident
|
||||
@load protocols/mime/file-extract
|
||||
@load protocols/mime/file-hash
|
101
scripts/base/protocols/mime/base.bro
Normal file
101
scripts/base/protocols/mime/base.bro
Normal file
|
@ -0,0 +1,101 @@
|
|||
##! The mime script does analysis of MIME encoded messages seen in certain
|
||||
##! protocols (only SMTP and POP3 at the moment).
|
||||
|
||||
@load utils/strings
|
||||
|
||||
module MIME;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { MIME };
|
||||
|
||||
# Let's assume for now that nothing transferring files using
|
||||
# MIME attachments is multiplexing for simplicity's sake.
|
||||
# We can make the assumption that one connection == one file (at a time)
|
||||
|
||||
type Info: record {
|
||||
## This is the timestamp of when the MIME content transfer began.
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
## The application layer protocol over which the transfer was seen.
|
||||
app_protocol: string &log &optional;
|
||||
## The filename seen in the Content-Disposition header.
|
||||
filename: string &log &optional;
|
||||
## Track how many byte of the MIME encoded file have been seen.
|
||||
content_len: count &log &default=0;
|
||||
};
|
||||
|
||||
type State: record {
|
||||
## Track the number of MIME encoded files transferred during this session.
|
||||
level: count &default=0;
|
||||
};
|
||||
|
||||
global log_mime: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
mime: Info &optional;
|
||||
mime_state: State &optional;
|
||||
};
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Log::create_stream(MIME, [$columns=Info, $ev=log_mime]);
|
||||
}
|
||||
|
||||
function new_mime_session(c: connection): Info
|
||||
{
|
||||
local info: Info;
|
||||
|
||||
info$ts=network_time();
|
||||
info$uid=c$uid;
|
||||
info$id=c$id;
|
||||
return info;
|
||||
}
|
||||
|
||||
function set_session(c: connection, new_entity: bool)
|
||||
{
|
||||
if ( ! c?$mime_state )
|
||||
c$mime_state = [];
|
||||
|
||||
if ( ! c?$mime || new_entity )
|
||||
c$mime = new_mime_session(c);
|
||||
}
|
||||
|
||||
event mime_begin_entity(c: connection) &priority=10
|
||||
{
|
||||
set_session(c, T);
|
||||
|
||||
++c$mime_state$level;
|
||||
|
||||
if ( |c$service| > 0 )
|
||||
c$mime$app_protocol = join_string_set(c$service, ",");
|
||||
}
|
||||
|
||||
# This has priority -10 because other handlers need to know the current
|
||||
# content_len before it's updated by this handler.
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=-10
|
||||
{
|
||||
c$mime$content_len = c$mime$content_len + length;
|
||||
}
|
||||
|
||||
event mime_one_header(c: connection, h: mime_header_rec)
|
||||
{
|
||||
if ( h$name == "CONTENT-DISPOSITION" &&
|
||||
/[fF][iI][lL][eE][nN][aA][mM][eE]/ in h$value )
|
||||
c$mime$filename = sub(h$value, /^.*[fF][iI][lL][eE][nN][aA][mM][eE]=/, "");
|
||||
}
|
||||
|
||||
event mime_end_entity(c: connection) &priority=-5
|
||||
{
|
||||
# This check and the delete below are just to cope with a bug where
|
||||
# mime_end_entity can be generated multiple times for the same event.
|
||||
if ( ! c?$mime )
|
||||
return;
|
||||
|
||||
# Don't log anything if there wasn't any content.
|
||||
if ( c$mime$content_len > 0 )
|
||||
Log::write(MIME, c$mime);
|
||||
|
||||
delete c$mime;
|
||||
}
|
60
scripts/base/protocols/mime/file-extract.bro
Normal file
60
scripts/base/protocols/mime/file-extract.bro
Normal file
|
@ -0,0 +1,60 @@
|
|||
@load protocols/mime/file-ident
|
||||
@load utils/files
|
||||
|
||||
module MIME;
|
||||
|
||||
export {
|
||||
## Pattern of file mime types to extract from MIME bodies.
|
||||
const extract_file_types = /NO_DEFAULT/ &redef;
|
||||
|
||||
## The on-disk prefix for files to be extracted from MIME entity bodies.
|
||||
const extraction_prefix = "mime-item" &redef;
|
||||
|
||||
redef record Info += {
|
||||
## Optionally write the file to disk. Must be set prior to first
|
||||
## data chunk being seen in an event.
|
||||
extract_file: bool &default=F;
|
||||
|
||||
## Store the file handle here for the file currently being extracted.
|
||||
extraction_file: file &optional;
|
||||
|
||||
## Store a count of the number of files that have been transferred in
|
||||
## this conversation to create unique file names on disk.
|
||||
num_extracted_files: count &optional;
|
||||
};
|
||||
}
|
||||
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=5
|
||||
{
|
||||
if ( extract_file_types in c$mime$mime_type )
|
||||
c$mime$extract_file = T;
|
||||
}
|
||||
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=3
|
||||
{
|
||||
if ( c$mime$extract_file && c$mime$content_len == 0 )
|
||||
{
|
||||
local suffix = fmt("%d.dat", ++c$mime$num_extracted_files);
|
||||
local fname = generate_extraction_filename(extraction_prefix, c, suffix);
|
||||
c$mime$extraction_file = open(fname);
|
||||
enable_raw_output(c$mime$extraction_file);
|
||||
}
|
||||
}
|
||||
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=-5
|
||||
{
|
||||
if ( c$mime$extract_file && c$mime?$extraction_file )
|
||||
print c$mime$extraction_file, data;
|
||||
}
|
||||
|
||||
event mime_end_entity(c: connection) &priority=-3
|
||||
{
|
||||
# TODO: this check is only due to a bug in mime_end_entity that
|
||||
# causes the event to be generated twice for the same real event.
|
||||
if ( ! c?$mime )
|
||||
return;
|
||||
|
||||
if ( c$mime?$extraction_file )
|
||||
close(c$mime$extraction_file);
|
||||
}
|
||||
|
78
scripts/base/protocols/mime/file-hash.bro
Normal file
78
scripts/base/protocols/mime/file-hash.bro
Normal file
|
@ -0,0 +1,78 @@
|
|||
@load protocols/mime/file-ident
|
||||
|
||||
module MIME;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that an MD5 sum was calculated for a MIME message.
|
||||
MD5,
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
## The calculated MD5 sum for the MIME entity.
|
||||
md5: string &log &optional;
|
||||
|
||||
## Optionally calculate the file's MD5 sum. Must be set prior to the
|
||||
## first data chunk being see in an event.
|
||||
calc_md5: bool &default=F;
|
||||
|
||||
## This boolean value indicates if an MD5 sum is being calculated
|
||||
## for the current file transfer.
|
||||
calculating_md5: bool &default=F;
|
||||
};
|
||||
|
||||
## Generate MD5 sums for these filetypes.
|
||||
const generate_md5 = /application\/x-dosexec/ # Windows and DOS executables
|
||||
| /application\/x-executable/ # *NIX executable binary
|
||||
&redef;
|
||||
}
|
||||
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=-5
|
||||
{
|
||||
if ( ! c?$mime ) return;
|
||||
|
||||
if ( c$mime$content_len == 0 )
|
||||
{
|
||||
if ( generate_md5 in c$mime$mime_type )
|
||||
c$mime$calc_md5 = T;
|
||||
|
||||
if ( c$mime$calc_md5 )
|
||||
{
|
||||
c$mime$calculating_md5 = T;
|
||||
md5_hash_init(c$id);
|
||||
}
|
||||
}
|
||||
|
||||
if ( c$mime$calculating_md5 )
|
||||
md5_hash_update(c$id, data);
|
||||
}
|
||||
|
||||
## In the event of a content gap during the MIME transfer, detect the state for
|
||||
## the MD5 sum calculation and stop calculating the MD5 since it would be
|
||||
## incorrect anyway.
|
||||
event content_gap(c: connection, is_orig: bool, seq: count, length: count) &priority=5
|
||||
{
|
||||
if ( is_orig || ! c?$mime ) return;
|
||||
|
||||
if ( c$mime$calculating_md5 )
|
||||
{
|
||||
c$mime$calculating_md5 = F;
|
||||
md5_hash_finish(c$id);
|
||||
}
|
||||
}
|
||||
|
||||
event mime_end_entity(c: connection) &priority=-3
|
||||
{
|
||||
# TODO: this check is only due to a bug in mime_end_entity that
|
||||
# causes the event to be generated twice for the same real event.
|
||||
if ( ! c?$mime )
|
||||
return;
|
||||
|
||||
if ( c$mime$calculating_md5 )
|
||||
{
|
||||
c$mime$md5 = md5_hash_finish(c$id);
|
||||
|
||||
NOTICE([$note=MD5, $msg=fmt("Calculated a hash for a MIME entity from %s", c$id$orig_h),
|
||||
$sub=c$mime$md5, $conn=c]);
|
||||
}
|
||||
}
|
16
scripts/base/protocols/mime/file-ident.bro
Normal file
16
scripts/base/protocols/mime/file-ident.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load protocols/mime/base
|
||||
|
||||
module MIME;
|
||||
|
||||
export {
|
||||
redef record Info += {
|
||||
## Sniffed MIME type for the transfer.
|
||||
mime_type: string &log &optional;
|
||||
};
|
||||
}
|
||||
|
||||
event mime_segment_data(c: connection, length: count, data: string) &priority=7
|
||||
{
|
||||
if ( c$mime$content_len == 0 )
|
||||
c$mime$mime_type = split1(identify_data(data, T), /;/)[1];
|
||||
}
|
147
scripts/base/protocols/rpc/base.bro
Normal file
147
scripts/base/protocols/rpc/base.bro
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
#
|
||||
# Log RPC request and reply messages. Does not in itself start/activate
|
||||
# an analyzer. You need to load portmap and/or NFS for that
|
||||
#
|
||||
# TODO: maybe automatically load portmap, add a generic RPC analyzer and
|
||||
# use expect connection, so that we can see RPC request/replies for RPC
|
||||
# programs for which we don't have an analyzer.
|
||||
#
|
||||
|
||||
module RPC;
|
||||
|
||||
export {
|
||||
global log_file = open_log_file("rpc") &redef;
|
||||
# whether to match request to replies on the policy layer.
|
||||
# (will report on rexmit and missing requests or replies)
|
||||
global track_requests_replies = T &redef;
|
||||
}
|
||||
|
||||
|
||||
type rpc_call_state: enum {
|
||||
NONE,
|
||||
HAVE_CALL,
|
||||
HAVE_REPLY
|
||||
};
|
||||
|
||||
type rpc_call_info: record {
|
||||
state: rpc_call_state;
|
||||
calltime: time;
|
||||
cid: conn_id;
|
||||
};
|
||||
|
||||
function new_call(cid: conn_id): rpc_call_info
|
||||
{
|
||||
local ci: rpc_call_info;
|
||||
|
||||
ci$state = NONE;
|
||||
ci$calltime = network_time();
|
||||
ci$cid = cid;
|
||||
return ci;
|
||||
}
|
||||
|
||||
function rpc_expire_xid(t: table[count] of rpc_call_info, xid: count): interval
|
||||
{
|
||||
local ci = t[xid];
|
||||
if (ci$state != HAVE_REPLY)
|
||||
print log_file, fmt("%.6f %s %s note XID %d never recevied a reply",
|
||||
ci$calltime, id_string(ci$cid),
|
||||
get_port_transport_proto(ci$cid$orig_p), xid);
|
||||
return 0 sec;
|
||||
}
|
||||
|
||||
function new_xid_table(): table[count] of rpc_call_info
|
||||
{
|
||||
local inner: table[count] of rpc_call_info &write_expire=rpc_timeout &expire_func=rpc_expire_xid;
|
||||
return inner;
|
||||
}
|
||||
|
||||
|
||||
# Match requests to replies.
|
||||
# The analyzer does this indepently and might differ in timeouts and
|
||||
# handling of xid reuse.
|
||||
# FIXME: add timeouts. Note, we do clean up on connection_state_remove
|
||||
global rpc_calls: table[conn_id] of table[count] of rpc_call_info;
|
||||
# &write_expire = rpc_timeout &expire_func=expire_rpc_call;
|
||||
|
||||
|
||||
event rpc_dialogue(c: connection, prog: count, ver: count, proc: count, status: rpc_status, start_time: time, call_len: count, reply_len: count)
|
||||
{
|
||||
# TODO: We currently do nothing here.
|
||||
# using the rpc_call and rpc_reply events, is all we need.
|
||||
}
|
||||
|
||||
event rpc_call(c: connection, xid: count, prog: count, ver: count, proc: count, call_len: count)
|
||||
{
|
||||
if (track_requests_replies)
|
||||
{
|
||||
if (c$id !in rpc_calls)
|
||||
rpc_calls[c$id] = new_xid_table();
|
||||
if (xid !in rpc_calls[c$id])
|
||||
rpc_calls[c$id][xid] = new_call(c$id);
|
||||
local curstate = rpc_calls[c$id][xid]$state;
|
||||
|
||||
if (curstate == HAVE_CALL)
|
||||
print log_file, fmt("%.6f %s %s note XID %d call retransmitted",
|
||||
network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid);
|
||||
else if (curstate == HAVE_REPLY)
|
||||
print log_file, fmt("%.6f %s %s note XID %d call received after reply",
|
||||
network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid);
|
||||
rpc_calls[c$id][xid]$state = HAVE_CALL;
|
||||
}
|
||||
|
||||
print log_file, fmt("%.6f %s %s rpc_call %d %d %d %d %d",
|
||||
network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid, prog, ver, proc, call_len);
|
||||
}
|
||||
|
||||
event rpc_reply(c: connection, xid: count, status: rpc_status, reply_len: count)
|
||||
{
|
||||
if (track_requests_replies)
|
||||
{
|
||||
if (c$id !in rpc_calls)
|
||||
rpc_calls[c$id] = new_xid_table();
|
||||
if (xid !in rpc_calls[c$id])
|
||||
{
|
||||
rpc_calls[c$id][xid] = new_call(c$id);
|
||||
# XXX: what to do about calltime in rpc_call_info??
|
||||
}
|
||||
if (rpc_calls[c$id][xid]$state == NONE)
|
||||
print log_file, fmt("%.6f %s %s note XID %d reply but call is missing",
|
||||
network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid);
|
||||
else if (rpc_calls[c$id][xid]$state == HAVE_REPLY)
|
||||
print log_file, fmt("%.6f %s %s note XID %d reply retransmitted",
|
||||
network_time(), id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid);
|
||||
rpc_calls[c$id][xid]$state = HAVE_REPLY;
|
||||
}
|
||||
|
||||
print log_file, fmt("%.6f %s %s rpc_reply %d %s %d",
|
||||
network_time(), reverse_id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
xid, status, reply_len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function finish_calls(cid: conn_id)
|
||||
{
|
||||
for (xid in rpc_calls[cid])
|
||||
rpc_expire_xid(rpc_calls[cid], xid);
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection)
|
||||
{
|
||||
if (c$id !in rpc_calls)
|
||||
return;
|
||||
finish_calls(c$id);
|
||||
delete rpc_calls[c$id];
|
||||
}
|
||||
|
||||
event bro_done()
|
||||
{
|
||||
for (cid in rpc_calls)
|
||||
finish_calls(cid);
|
||||
}
|
1
scripts/base/protocols/smtp/__load__.bro
Normal file
1
scripts/base/protocols/smtp/__load__.bro
Normal file
|
@ -0,0 +1 @@
|
|||
@load ./main
|
367
scripts/base/protocols/smtp/main.bro
Normal file
367
scripts/base/protocols/smtp/main.bro
Normal file
|
@ -0,0 +1,367 @@
|
|||
|
||||
module SMTP;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SMTP };
|
||||
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that the server sent a reply mentioning an SMTP block list.
|
||||
BL_Error_Message,
|
||||
## Indicates the client's address is seen in the block list error message.
|
||||
BL_Blocked_Host,
|
||||
};
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
helo: string &log &optional;
|
||||
mailfrom: string &log &optional;
|
||||
rcptto: set[string] &log &optional;
|
||||
date: string &log &optional;
|
||||
from: string &log &optional;
|
||||
to: set[string] &log &optional;
|
||||
reply_to: string &log &optional;
|
||||
msg_id: string &log &optional;
|
||||
in_reply_to: string &log &optional;
|
||||
subject: string &log &optional;
|
||||
x_originating_ip: addr &log &optional;
|
||||
first_received: string &log &optional;
|
||||
second_received: string &log &optional;
|
||||
## The last message the server sent to the client.
|
||||
last_reply: string &log &optional;
|
||||
files: set[string] &log &optional;
|
||||
path: vector of addr &log &optional;
|
||||
user_agent: string &log &optional;
|
||||
|
||||
## Indicate if this session is currently transmitting SMTP message
|
||||
## envelope headers.
|
||||
in_headers: bool &default=F;
|
||||
## Indicate if the "Received: from" headers should still be processed.
|
||||
process_received_from: bool &default=T;
|
||||
## Maintain the current header for cases where there is header wrapping.
|
||||
current_header: string &default="";
|
||||
## Indicate when the message is logged and no longer applicable.
|
||||
done: bool &default=F;
|
||||
};
|
||||
|
||||
type State: record {
|
||||
helo: string &optional;
|
||||
## Count the number of individual messages transmitted during this
|
||||
## SMTP session. Note, this is not the number of recipients, but the
|
||||
## number of message bodies transferred.
|
||||
messages_transferred: count &default=0;
|
||||
|
||||
pending_messages: set[Info] &optional;
|
||||
};
|
||||
|
||||
## Direction to capture the full "Received from" path.
|
||||
## REMOTE_HOSTS - only capture the path until an internal host is found.
|
||||
## LOCAL_HOSTS - only capture the path until the external host is discovered.
|
||||
## ALL_HOSTS - always capture the entire path.
|
||||
## NO_HOSTS - never capture the path.
|
||||
const mail_path_capture = ALL_HOSTS &redef;
|
||||
|
||||
# This matches content in SMTP error messages that indicate some
|
||||
# block list doesn't like the connection/mail.
|
||||
const bl_error_messages =
|
||||
/spamhaus\.org\//
|
||||
| /sophos\.com\/security\//
|
||||
| /spamcop\.net\/bl/
|
||||
| /cbl\.abuseat\.org\//
|
||||
| /sorbs\.net\//
|
||||
| /bsn\.borderware\.com\//
|
||||
| /mail-abuse\.com\//
|
||||
| /b\.barracudacentral\.com\//
|
||||
| /psbl\.surriel\.com\//
|
||||
| /antispam\.imp\.ch\//
|
||||
| /dyndns\.com\/.*spam/
|
||||
| /rbl\.knology\.net\//
|
||||
| /intercept\.datapacket\.net\//
|
||||
| /uceprotect\.net\//
|
||||
| /hostkarma\.junkemailfilter\.com\// &redef;
|
||||
|
||||
global log_smtp: event(rec: Info);
|
||||
|
||||
## Configure the default ports for SMTP analysis.
|
||||
const ports = { 25/tcp, 587/tcp } &redef;
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
smtp: Info &optional;
|
||||
smtp_state: State &optional;
|
||||
};
|
||||
|
||||
# Configure DPD
|
||||
redef capture_filters += { ["smtp"] = "tcp port smtp or tcp port 587" };
|
||||
redef dpd_config += { [ANALYZER_SMTP] = [$ports = ports] };
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(SMTP, [$columns=SMTP::Info, $ev=log_smtp]);
|
||||
}
|
||||
|
||||
function find_address_in_smtp_header(header: string): string
|
||||
{
|
||||
local ips = find_ip_addresses(header);
|
||||
# If there are more than one IP address found, return the second.
|
||||
if ( |ips| > 1 )
|
||||
return ips[1];
|
||||
# Otherwise, return the first.
|
||||
else if ( |ips| > 0 )
|
||||
return ips[0];
|
||||
# Otherwise, there wasn't an IP address found.
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
function new_smtp_log(c: connection): Info
|
||||
{
|
||||
local l: Info;
|
||||
l$ts=network_time();
|
||||
l$uid=c$uid;
|
||||
l$id=c$id;
|
||||
if ( c?$smtp_state && c$smtp_state?$helo )
|
||||
l$helo = c$smtp_state$helo;
|
||||
|
||||
# The path will always end with the hosts involved in this connection.
|
||||
# The lower values in the vector are the end of the path.
|
||||
l$path = vector(c$id$resp_h, c$id$orig_h);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
function set_smtp_session(c: connection)
|
||||
{
|
||||
if ( ! c?$smtp_state )
|
||||
c$smtp_state = [];
|
||||
|
||||
if ( ! c?$smtp || c$smtp$done )
|
||||
{
|
||||
c$smtp = new_smtp_log(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function smtp_message(c: connection)
|
||||
{
|
||||
Log::write(SMTP, c$smtp);
|
||||
|
||||
c$smtp$done = T;
|
||||
# Track the number of messages seen in this session.
|
||||
++c$smtp_state$messages_transferred;
|
||||
}
|
||||
|
||||
event smtp_request(c: connection, is_orig: bool, command: string, arg: string) &priority=5
|
||||
{
|
||||
set_smtp_session(c);
|
||||
local upper_command = to_upper(command);
|
||||
|
||||
if ( upper_command == "HELO" || upper_command == "EHLO" )
|
||||
{
|
||||
c$smtp_state$helo = arg;
|
||||
c$smtp$helo = arg;
|
||||
}
|
||||
|
||||
else if ( upper_command == "RCPT" && /^[tT][oO]:/ in arg )
|
||||
{
|
||||
if ( ! c$smtp?$rcptto )
|
||||
c$smtp$rcptto = set();
|
||||
add c$smtp$rcptto[split1(arg, /:[[:blank:]]*/)[2]];
|
||||
}
|
||||
|
||||
else if ( upper_command == "MAIL" && /^[fF][rR][oO][mM]:/ in arg )
|
||||
{
|
||||
# In case this is not the first message in a session we want to
|
||||
# essentially write out a log, clear the session tracking, and begin
|
||||
# new session tracking.
|
||||
if ( c$smtp_state$messages_transferred > 0 )
|
||||
{
|
||||
smtp_message(c);
|
||||
set_smtp_session(c);
|
||||
}
|
||||
|
||||
local partially_done = split1(arg, /:[[:blank:]]*/)[2];
|
||||
c$smtp$mailfrom = split1(partially_done, /[[:blank:]]?/)[1];
|
||||
}
|
||||
|
||||
else if ( upper_command == "DATA" )
|
||||
{
|
||||
c$smtp$in_headers = T;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string,
|
||||
msg: string, cont_resp: bool) &priority=5
|
||||
{
|
||||
set_smtp_session(c);
|
||||
|
||||
# This continually overwrites, but we want the last reply,
|
||||
# so this actually works fine.
|
||||
if ( code != 421 && code >= 400 )
|
||||
{
|
||||
c$smtp$last_reply = fmt("%d %s", code, msg);
|
||||
|
||||
# Raise a notice when an SMTP error about a block list is discovered.
|
||||
if ( bl_error_messages in msg )
|
||||
{
|
||||
local note = BL_Error_Message;
|
||||
local message = fmt("%s received an error message mentioning an SMTP block list", c$id$orig_h);
|
||||
|
||||
# Determine if the originator's IP address is in the message.
|
||||
local ips = find_ip_addresses(msg);
|
||||
local text_ip = "";
|
||||
if ( |ips| > 0 && to_addr(ips[0]) == c$id$orig_h )
|
||||
{
|
||||
note = BL_Blocked_Host;
|
||||
message = fmt("%s is on an SMTP block list", c$id$orig_h);
|
||||
}
|
||||
|
||||
NOTICE([$note=note, $conn=c, $msg=message, $sub=msg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event smtp_data(c: connection, is_orig: bool, data: string) &priority=5
|
||||
{
|
||||
# Is there something we should be handling from the server?
|
||||
if ( ! is_orig ) return;
|
||||
|
||||
set_smtp_session(c);
|
||||
|
||||
if ( ! c$smtp$in_headers )
|
||||
{
|
||||
if ( /^[cC][oO][nN][tT][eE][nN][tT]-[dD][iI][sS].*[fF][iI][lL][eE][nN][aA][mM][eE]/ in data )
|
||||
{
|
||||
if ( ! c$smtp?$files )
|
||||
c$smtp$files = set();
|
||||
data = sub(data, /^.*[fF][iI][lL][eE][nN][aA][mM][eE]=/, "");
|
||||
add c$smtp$files[data];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( /^[[:blank:]]*$/ in data )
|
||||
c$smtp$in_headers = F;
|
||||
|
||||
# This is to reconstruct headers that tend to wrap around.
|
||||
if ( /^[[:blank:]]/ in data )
|
||||
{
|
||||
# Remove all but a single space at the beginning (this seems to follow
|
||||
# the most common behavior).
|
||||
data = sub(data, /^[[:blank:]]*/, " ");
|
||||
if ( c$smtp$current_header == "MESSAGE-ID" )
|
||||
c$smtp$msg_id += data;
|
||||
else if ( c$smtp$current_header == "RECEIVED" )
|
||||
c$smtp$first_received += data;
|
||||
else if ( c$smtp$current_header == "IN-REPLY-TO" )
|
||||
c$smtp$in_reply_to += data;
|
||||
else if ( c$smtp$current_header == "SUBJECCT" )
|
||||
c$smtp$subject += data;
|
||||
else if ( c$smtp$current_header == "FROM" )
|
||||
c$smtp$from += data;
|
||||
else if ( c$smtp$current_header == "REPLY-TO" )
|
||||
c$smtp$reply_to += data;
|
||||
else if ( c$smtp$current_header == "USER-AGENT" )
|
||||
c$smtp$user_agent += data;
|
||||
return;
|
||||
}
|
||||
# Once there isn't a line starting with a blank, we're not continuing a
|
||||
# header anymore.
|
||||
c$smtp$current_header = "";
|
||||
|
||||
local header_parts = split1(data, /:[[:blank:]]*/);
|
||||
# TODO: do something in this case? This would definitely be odd.
|
||||
# Header wrapping needs to be handled more elegantly. This will happen
|
||||
# if the header value is wrapped immediately after the header key.
|
||||
if ( |header_parts| != 2 )
|
||||
return;
|
||||
|
||||
local header_key = to_upper(header_parts[1]);
|
||||
c$smtp$current_header = header_key;
|
||||
|
||||
local header_val = header_parts[2];
|
||||
|
||||
if ( header_key == "MESSAGE-ID" )
|
||||
c$smtp$msg_id = header_val;
|
||||
|
||||
else if ( header_key == "RECEIVED" )
|
||||
{
|
||||
if ( c$smtp?$first_received )
|
||||
c$smtp$second_received = c$smtp$first_received;
|
||||
c$smtp$first_received = header_val;
|
||||
}
|
||||
|
||||
else if ( header_key == "IN-REPLY-TO" )
|
||||
c$smtp$in_reply_to = header_val;
|
||||
|
||||
else if ( header_key == "DATE" )
|
||||
c$smtp$date = header_val;
|
||||
|
||||
else if ( header_key == "FROM" )
|
||||
c$smtp$from = header_val;
|
||||
|
||||
else if ( header_key == "TO" )
|
||||
{
|
||||
if ( ! c$smtp?$to )
|
||||
c$smtp$to = set();
|
||||
add c$smtp$to[header_val];
|
||||
}
|
||||
|
||||
else if ( header_key == "REPLY-TO" )
|
||||
c$smtp$reply_to = header_val;
|
||||
|
||||
else if ( header_key == "SUBJECT" )
|
||||
c$smtp$subject = header_val;
|
||||
|
||||
else if ( header_key == "X-ORIGINATING-IP" )
|
||||
{
|
||||
local addresses = find_ip_addresses(header_val);
|
||||
if ( 1 in addresses )
|
||||
c$smtp$x_originating_ip = to_addr(addresses[1]);
|
||||
}
|
||||
|
||||
else if ( header_key == "X-MAILER" ||
|
||||
header_key == "USER-AGENT" ||
|
||||
header_key == "X-USER-AGENT" )
|
||||
{
|
||||
c$smtp$user_agent = header_val;
|
||||
# Explicitly set the current header here because there are several
|
||||
# headers bulked under this same key.
|
||||
c$smtp$current_header = "USER-AGENT";
|
||||
}
|
||||
}
|
||||
|
||||
# This event handler builds the "Received From" path by reading the
|
||||
# headers in the mail
|
||||
event smtp_data(c: connection, is_orig: bool, data: string) &priority=3
|
||||
{
|
||||
# If we've decided that we're done watching the received headers for
|
||||
# whatever reason, we're done. Could be due to only watching until
|
||||
# local addresses are seen in the received from headers.
|
||||
if ( c$smtp$current_header != "RECEIVED" ||
|
||||
! c$smtp$process_received_from )
|
||||
return;
|
||||
|
||||
local text_ip = find_address_in_smtp_header(data);
|
||||
if ( text_ip == "" )
|
||||
return;
|
||||
local ip = to_addr(text_ip);
|
||||
|
||||
if ( ! addr_matches_host(ip, mail_path_capture) &&
|
||||
! Site::is_private_addr(ip) )
|
||||
{
|
||||
c$smtp$process_received_from = F;
|
||||
}
|
||||
|
||||
if ( c$smtp$path[|c$smtp$path|-1] != ip )
|
||||
c$smtp$path[|c$smtp$path|] = ip;
|
||||
}
|
||||
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$smtp && ! c$smtp$done )
|
||||
smtp_message(c);
|
||||
}
|
1
scripts/base/protocols/ssh/__load__.bro
Normal file
1
scripts/base/protocols/ssh/__load__.bro
Normal file
|
@ -0,0 +1 @@
|
|||
@load ./main
|
248
scripts/base/protocols/ssh/main.bro
Normal file
248
scripts/base/protocols/ssh/main.bro
Normal file
|
@ -0,0 +1,248 @@
|
|||
|
||||
module SSH;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SSH };
|
||||
|
||||
redef enum Notice::Type += {
|
||||
Login,
|
||||
Password_Guessing,
|
||||
Login_By_Password_Guesser,
|
||||
Login_From_Interesting_Hostname,
|
||||
Bytecount_Inconsistency,
|
||||
};
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
status: string &log &optional;
|
||||
direction: string &log &optional;
|
||||
remote_location: geo_location &log &optional;
|
||||
client: string &log &optional;
|
||||
server: string &log &optional;
|
||||
resp_size: count &log &default=0;
|
||||
|
||||
## Indicate if the SSH session is done being watched.
|
||||
done: bool &default=F;
|
||||
};
|
||||
|
||||
const password_guesses_limit = 30 &redef;
|
||||
|
||||
# The size in bytes at which the SSH connection is presumed to be
|
||||
# successful.
|
||||
const authentication_data_size = 5500 &redef;
|
||||
|
||||
# The amount of time to remember presumed non-successful logins to build
|
||||
# model of a password guesser.
|
||||
const guessing_timeout = 30 mins &redef;
|
||||
|
||||
# The set of countries for which you'd like to throw notices upon successful login
|
||||
# requires Bro compiled with libGeoIP support
|
||||
const watched_countries: set[string] = {"RO"} &redef;
|
||||
|
||||
# Strange/bad host names to originate successful SSH logins
|
||||
const interesting_hostnames =
|
||||
/^d?ns[0-9]*\./ |
|
||||
/^smtp[0-9]*\./ |
|
||||
/^mail[0-9]*\./ |
|
||||
/^pop[0-9]*\./ |
|
||||
/^imap[0-9]*\./ |
|
||||
/^www[0-9]*\./ |
|
||||
/^ftp[0-9]*\./ &redef;
|
||||
|
||||
# This is a table with orig subnet as the key, and subnet as the value.
|
||||
const ignore_guessers: table[subnet] of subnet &redef;
|
||||
|
||||
# If true, we tell the event engine to not look at further data
|
||||
# packets after the initial SSH handshake. Helps with performance
|
||||
# (especially with large file transfers) but precludes some
|
||||
# kinds of analyses (e.g., tracking connection size).
|
||||
const skip_processing_after_detection = F &redef;
|
||||
|
||||
# Keeps count of how many rejections a host has had
|
||||
global password_rejections: table[addr] of TrackCount
|
||||
&write_expire=guessing_timeout
|
||||
&synchronized;
|
||||
|
||||
# Keeps track of hosts identified as guessing passwords
|
||||
# TODO: guessing_timeout doesn't work correctly here. If a user redefs
|
||||
# the variable, it won't take effect.
|
||||
global password_guessers: set[addr] &read_expire=guessing_timeout+1hr &synchronized;
|
||||
|
||||
global log_ssh: event(rec: Info);
|
||||
}
|
||||
|
||||
# Configure DPD and the packet filter
|
||||
redef capture_filters += { ["ssh"] = "tcp port 22" };
|
||||
redef dpd_config += { [ANALYZER_SSH] = [$ports = set(22/tcp)] };
|
||||
|
||||
redef record connection += {
|
||||
ssh: Info &optional;
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(SSH, [$columns=Info, $ev=log_ssh]);
|
||||
}
|
||||
|
||||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$ssh )
|
||||
{
|
||||
local info: Info;
|
||||
info$ts=network_time();
|
||||
info$uid=c$uid;
|
||||
info$id=c$id;
|
||||
c$ssh = info;
|
||||
}
|
||||
}
|
||||
|
||||
function check_ssh_connection(c: connection, done: bool)
|
||||
{
|
||||
# If done watching this connection, just return.
|
||||
if ( c$ssh$done )
|
||||
return;
|
||||
|
||||
# If this is still a live connection and the byte count has not
|
||||
# crossed the threshold, just return and let the resheduled check happen later.
|
||||
if ( !done && c$resp$size < authentication_data_size )
|
||||
return;
|
||||
|
||||
# Make sure the server has sent back more than 50 bytes to filter out
|
||||
# hosts that are just port scanning. Nothing is ever logged if the server
|
||||
# doesn't send back at least 50 bytes.
|
||||
if ( c$resp$size < 50 )
|
||||
return;
|
||||
|
||||
local status = "failure";
|
||||
local direction = Site::is_local_addr(c$id$orig_h) ? "to" : "from";
|
||||
local location: geo_location;
|
||||
location = (direction == "to") ? lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h);
|
||||
|
||||
if ( done && c$resp$size < authentication_data_size )
|
||||
{
|
||||
# presumed failure
|
||||
if ( c$id$orig_h !in password_rejections )
|
||||
password_rejections[c$id$orig_h] = new_track_count();
|
||||
|
||||
# Track the number of rejections
|
||||
if ( !(c$id$orig_h in ignore_guessers &&
|
||||
c$id$resp_h in ignore_guessers[c$id$orig_h]) )
|
||||
++password_rejections[c$id$orig_h]$n;
|
||||
|
||||
if ( default_check_threshold(password_rejections[c$id$orig_h]) )
|
||||
{
|
||||
add password_guessers[c$id$orig_h];
|
||||
NOTICE([$note=Password_Guessing,
|
||||
$conn=c,
|
||||
$msg=fmt("SSH password guessing by %s", c$id$orig_h),
|
||||
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n),
|
||||
$n=password_rejections[c$id$orig_h]$n]);
|
||||
}
|
||||
}
|
||||
# TODO: This is to work around a quasi-bug in Bro which occasionally
|
||||
# causes the byte count to be oversized.
|
||||
# Watch for Gregors work that adds an actual counter of bytes transferred.
|
||||
else if ( c$resp$size < 20000000 )
|
||||
{
|
||||
# presumed successful login
|
||||
status = "success";
|
||||
c$ssh$done = T;
|
||||
|
||||
if ( c$id$orig_h in password_rejections &&
|
||||
password_rejections[c$id$orig_h]$n > password_guesses_limit &&
|
||||
c$id$orig_h !in password_guessers )
|
||||
{
|
||||
add password_guessers[c$id$orig_h];
|
||||
NOTICE([$note=Login_By_Password_Guesser,
|
||||
$conn=c,
|
||||
$n=password_rejections[c$id$orig_h]$n,
|
||||
$msg=fmt("Successful SSH login by password guesser %s", c$id$orig_h),
|
||||
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n)]);
|
||||
}
|
||||
|
||||
local message = fmt("SSH login %s %s \"%s\" \"%s\" %f %f %s (triggered with %d bytes)",
|
||||
direction, location$country_code, location$region, location$city,
|
||||
location$latitude, location$longitude,
|
||||
id_string(c$id), c$resp$size);
|
||||
NOTICE([$note=Login,
|
||||
$conn=c,
|
||||
$msg=message,
|
||||
$sub=location$country_code]);
|
||||
|
||||
# Check to see if this login came from an interesting hostname
|
||||
when ( local hostname = lookup_addr(c$id$orig_h) )
|
||||
{
|
||||
if ( interesting_hostnames in hostname )
|
||||
{
|
||||
NOTICE([$note=Login_From_Interesting_Hostname,
|
||||
$conn=c,
|
||||
$msg=fmt("Strange login from %s", hostname),
|
||||
$sub=hostname]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( location$country_code in watched_countries )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if ( c$resp$size >= 200000000 )
|
||||
{
|
||||
NOTICE([$note=Bytecount_Inconsistency,
|
||||
$conn=c,
|
||||
$msg="During byte counting in SSH analysis, an overly large value was seen.",
|
||||
$sub=fmt("%d",c$resp$size)]);
|
||||
}
|
||||
|
||||
c$ssh$remote_location = location;
|
||||
c$ssh$status = status;
|
||||
c$ssh$direction = direction;
|
||||
c$ssh$resp_size = c$resp$size;
|
||||
|
||||
Log::write(SSH, c$ssh);
|
||||
|
||||
# Set the "done" flag to prevent the watching event from rescheduling
|
||||
# after detection is done.
|
||||
c$ssh$done;
|
||||
|
||||
# Stop watching this connection, we don't care about it anymore.
|
||||
if ( skip_processing_after_detection )
|
||||
{
|
||||
skip_further_processing(c$id);
|
||||
set_record_packets(c$id, F);
|
||||
}
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
if ( c?$ssh )
|
||||
check_ssh_connection(c, T);
|
||||
}
|
||||
|
||||
event ssh_watcher(c: connection)
|
||||
{
|
||||
local id = c$id;
|
||||
# don't go any further if this connection is gone already!
|
||||
if ( !connection_exists(id) )
|
||||
return;
|
||||
|
||||
check_ssh_connection(c, F);
|
||||
if ( ! c$ssh$done )
|
||||
schedule +15secs { ssh_watcher(c) };
|
||||
}
|
||||
|
||||
event ssh_server_version(c: connection, version: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
c$ssh$server = version;
|
||||
}
|
||||
|
||||
event ssh_client_version(c: connection, version: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
c$ssh$client = version;
|
||||
schedule +15secs { ssh_watcher(c) };
|
||||
}
|
3
scripts/base/protocols/ssl/__load__.bro
Normal file
3
scripts/base/protocols/ssl/__load__.bro
Normal file
|
@ -0,0 +1,3 @@
|
|||
@load ./consts
|
||||
@load ./main
|
||||
@load ./mozilla-ca-list
|
532
scripts/base/protocols/ssl/consts.bro
Normal file
532
scripts/base/protocols/ssl/consts.bro
Normal file
|
@ -0,0 +1,532 @@
|
|||
module SSL;
|
||||
|
||||
export {
|
||||
|
||||
const SSLv2 = 0x0002;
|
||||
const SSLv3 = 0x0300;
|
||||
const TLSv10 = 0x0301;
|
||||
const TLSv11 = 0x0302;
|
||||
const version_strings: table[count] of string = {
|
||||
[SSLv2] = "SSLv2",
|
||||
[SSLv3] = "SSLv3",
|
||||
[TLSv10] = "TLSv10",
|
||||
[TLSv11] = "TLSv11",
|
||||
} &default="UNKNOWN";
|
||||
|
||||
# http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xml
|
||||
const extensions: table[count] of string = {
|
||||
[0] = "server_name",
|
||||
[1] = "max_fragment_length",
|
||||
[2] = "client_certificate_url",
|
||||
[3] = "trusted_ca_keys",
|
||||
[4] = "truncated_hmac",
|
||||
[5] = "status_request",
|
||||
[6] = "user_mapping",
|
||||
[7] = "client_authz",
|
||||
[8] = "server_authz",
|
||||
[9] = "cert_type",
|
||||
[10] = "elliptic_curves",
|
||||
[11] = "ec_point_formats",
|
||||
[12] = "srp",
|
||||
[13] = "signature_algorithms",
|
||||
[14] = "use_srtp",
|
||||
[35] = "SessionTicket TLS",
|
||||
[65281] = "renegotiation_info"
|
||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||
|
||||
## SSLv2
|
||||
const SSLv20_CK_RC4_128_WITH_MD5 = 0x010080;
|
||||
const SSLv20_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080;
|
||||
const SSLv20_CK_RC2_128_CBC_WITH_MD5 = 0x030080;
|
||||
const SSLv20_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x040080;
|
||||
const SSLv20_CK_IDEA_128_CBC_WITH_MD5 = 0x050080;
|
||||
const SSLv20_CK_DES_64_CBC_WITH_MD5 = 0x060040;
|
||||
const SSLv20_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x0700C0;
|
||||
|
||||
## TLS
|
||||
const TLS_NULL_WITH_NULL_NULL = 0x0000;
|
||||
const TLS_RSA_WITH_NULL_MD5 = 0x0001;
|
||||
const TLS_RSA_WITH_NULL_SHA = 0x0002;
|
||||
const TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003;
|
||||
const TLS_RSA_WITH_RC4_128_MD5 = 0x0004;
|
||||
const TLS_RSA_WITH_RC4_128_SHA = 0x0005;
|
||||
const TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006;
|
||||
const TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007;
|
||||
const TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008;
|
||||
const TLS_RSA_WITH_DES_CBC_SHA = 0x0009;
|
||||
const TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A;
|
||||
const TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B;
|
||||
const TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C;
|
||||
const TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D;
|
||||
const TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E;
|
||||
const TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F;
|
||||
const TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010;
|
||||
const TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011;
|
||||
const TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012;
|
||||
const TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013;
|
||||
const TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014;
|
||||
const TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015;
|
||||
const TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016;
|
||||
const TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5 = 0x0017;
|
||||
const TLS_DH_ANON_WITH_RC4_128_MD5 = 0x0018;
|
||||
const TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA = 0x0019;
|
||||
const TLS_DH_ANON_WITH_DES_CBC_SHA = 0x001A;
|
||||
const TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B;
|
||||
const SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x001C;
|
||||
const SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x001D;
|
||||
const TLS_KRB5_WITH_DES_CBC_SHA = 0x001E;
|
||||
const TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F;
|
||||
const TLS_KRB5_WITH_RC4_128_SHA = 0x0020;
|
||||
const TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021;
|
||||
const TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022;
|
||||
const TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023;
|
||||
const TLS_KRB5_WITH_RC4_128_MD5 = 0x0024;
|
||||
const TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025;
|
||||
const TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026;
|
||||
const TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027;
|
||||
const TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028;
|
||||
const TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029;
|
||||
const TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A;
|
||||
const TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B;
|
||||
const TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F;
|
||||
const TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030;
|
||||
const TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031;
|
||||
const TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032;
|
||||
const TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033;
|
||||
const TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x0034;
|
||||
const TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035;
|
||||
const TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036;
|
||||
const TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037;
|
||||
const TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038;
|
||||
const TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039;
|
||||
const TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x003A;
|
||||
const TLS_RSA_WITH_NULL_SHA256 = 0x003B;
|
||||
const TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C;
|
||||
const TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D;
|
||||
const TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E;
|
||||
const TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F;
|
||||
const TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040;
|
||||
const TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041;
|
||||
const TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042;
|
||||
const TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043;
|
||||
const TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044;
|
||||
const TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045;
|
||||
const TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA = 0x0046;
|
||||
const TLS_RSA_EXPORT1024_WITH_RC4_56_MD5 = 0x0060;
|
||||
const TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 = 0x0061;
|
||||
const TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA = 0x0062;
|
||||
const TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA = 0x0063;
|
||||
const TLS_RSA_EXPORT1024_WITH_RC4_56_SHA = 0x0064;
|
||||
const TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA = 0x0065;
|
||||
const TLS_DHE_DSS_WITH_RC4_128_SHA = 0x0066;
|
||||
const TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067;
|
||||
const TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068;
|
||||
const TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069;
|
||||
const TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A;
|
||||
const TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B;
|
||||
const TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x006C;
|
||||
const TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D;
|
||||
const TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084;
|
||||
const TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085;
|
||||
const TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086;
|
||||
const TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087;
|
||||
const TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088;
|
||||
const TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA = 0x0089;
|
||||
const TLS_PSK_WITH_RC4_128_SHA = 0x008A;
|
||||
const TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B;
|
||||
const TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C;
|
||||
const TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D;
|
||||
const TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E;
|
||||
const TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F;
|
||||
const TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090;
|
||||
const TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091;
|
||||
const TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092;
|
||||
const TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093;
|
||||
const TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094;
|
||||
const TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095;
|
||||
const TLS_RSA_WITH_SEED_CBC_SHA = 0x0096;
|
||||
const TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097;
|
||||
const TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098;
|
||||
const TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099;
|
||||
const TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A;
|
||||
const TLS_DH_ANON_WITH_SEED_CBC_SHA = 0x009B;
|
||||
const TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C;
|
||||
const TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D;
|
||||
const TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E;
|
||||
const TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F;
|
||||
const TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0;
|
||||
const TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1;
|
||||
const TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2;
|
||||
const TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3;
|
||||
const TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4;
|
||||
const TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5;
|
||||
const TLS_DH_ANON_WITH_AES_128_GCM_SHA256 = 0x00A6;
|
||||
const TLS_DH_ANON_WITH_AES_256_GCM_SHA384 = 0x00A7;
|
||||
const TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8;
|
||||
const TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9;
|
||||
const TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA;
|
||||
const TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB;
|
||||
const TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC;
|
||||
const TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD;
|
||||
const TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE;
|
||||
const TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF;
|
||||
const TLS_PSK_WITH_NULL_SHA256 = 0x00B0;
|
||||
const TLS_PSK_WITH_NULL_SHA384 = 0x00B1;
|
||||
const TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2;
|
||||
const TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3;
|
||||
const TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4;
|
||||
const TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5;
|
||||
const TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6;
|
||||
const TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7;
|
||||
const TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8;
|
||||
const TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9;
|
||||
const TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA;
|
||||
const TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB;
|
||||
const TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC;
|
||||
const TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD;
|
||||
const TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE;
|
||||
const TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF;
|
||||
const TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0;
|
||||
const TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1;
|
||||
const TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2;
|
||||
const TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3;
|
||||
const TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4;
|
||||
const TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5;
|
||||
const TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001;
|
||||
const TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002;
|
||||
const TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005;
|
||||
const TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006;
|
||||
const TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007;
|
||||
const TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A;
|
||||
const TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B;
|
||||
const TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C;
|
||||
const TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D;
|
||||
const TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E;
|
||||
const TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F;
|
||||
const TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010;
|
||||
const TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011;
|
||||
const TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012;
|
||||
const TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013;
|
||||
const TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014;
|
||||
const TLS_ECDH_ANON_WITH_NULL_SHA = 0xC015;
|
||||
const TLS_ECDH_ANON_WITH_RC4_128_SHA = 0xC016;
|
||||
const TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA = 0xC017;
|
||||
const TLS_ECDH_ANON_WITH_AES_128_CBC_SHA = 0xC018;
|
||||
const TLS_ECDH_ANON_WITH_AES_256_CBC_SHA = 0xC019;
|
||||
const TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A;
|
||||
const TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B;
|
||||
const TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C;
|
||||
const TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D;
|
||||
const TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E;
|
||||
const TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F;
|
||||
const TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020;
|
||||
const TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021;
|
||||
const TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026;
|
||||
const TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027;
|
||||
const TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028;
|
||||
const TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029;
|
||||
const TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B;
|
||||
const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D;
|
||||
const TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E;
|
||||
const TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F;
|
||||
const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030;
|
||||
const TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031;
|
||||
const TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032;
|
||||
const TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033;
|
||||
const TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034;
|
||||
const TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035;
|
||||
const TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036;
|
||||
const TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037;
|
||||
const TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038;
|
||||
const TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039;
|
||||
const TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A;
|
||||
const TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B;
|
||||
const SSL_RSA_FIPS_WITH_DES_CBC_SHA = 0xFEFE;
|
||||
const SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xFEFF;
|
||||
const SSL_RSA_FIPS_WITH_DES_CBC_SHA_2 = 0xFFE1;
|
||||
const SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2 = 0xFFE0;
|
||||
const SSL_RSA_WITH_RC2_CBC_MD5 = 0xFF80;
|
||||
const SSL_RSA_WITH_IDEA_CBC_MD5 = 0xFF81;
|
||||
const SSL_RSA_WITH_DES_CBC_MD5 = 0xFF82;
|
||||
const SSL_RSA_WITH_3DES_EDE_CBC_MD5 = 0xFF83;
|
||||
const TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
|
||||
|
||||
# --- This is a table of all known cipher specs.
|
||||
# --- It can be used for detecting unknown ciphers and for
|
||||
# --- converting the cipher spec constants into a human readable format.
|
||||
|
||||
const cipher_desc: table[count] of string = {
|
||||
# --- sslv20 ---
|
||||
[SSLv20_CK_RC4_128_EXPORT40_WITH_MD5] =
|
||||
"SSLv20_CK_RC4_128_EXPORT40_WITH_MD5",
|
||||
[SSLv20_CK_RC4_128_WITH_MD5] = "SSLv20_CK_RC4_128_WITH_MD5",
|
||||
[SSLv20_CK_RC2_128_CBC_WITH_MD5] = "SSLv20_CK_RC2_128_CBC_WITH_MD5",
|
||||
[SSLv20_CK_RC2_128_CBC_EXPORT40_WITH_MD5] =
|
||||
"SSLv20_CK_RC2_128_CBC_EXPORT40_WITH_MD5",
|
||||
[SSLv20_CK_IDEA_128_CBC_WITH_MD5] = "SSLv20_CK_IDEA_128_CBC_WITH_MD5",
|
||||
[SSLv20_CK_DES_192_EDE3_CBC_WITH_MD5] =
|
||||
"SSLv20_CK_DES_192_EDE3_CBC_WITH_MD5",
|
||||
[SSLv20_CK_DES_64_CBC_WITH_MD5] = "SSLv20_CK_DES_64_CBC_WITH_MD5",
|
||||
|
||||
# --- TLS ---
|
||||
[TLS_NULL_WITH_NULL_NULL] = "TLS_NULL_WITH_NULL_NULL",
|
||||
[TLS_RSA_WITH_NULL_MD5] = "TLS_RSA_WITH_NULL_MD5",
|
||||
[TLS_RSA_WITH_NULL_SHA] = "TLS_RSA_WITH_NULL_SHA",
|
||||
[TLS_RSA_EXPORT_WITH_RC4_40_MD5] = "TLS_RSA_EXPORT_WITH_RC4_40_MD5",
|
||||
[TLS_RSA_WITH_RC4_128_MD5] = "TLS_RSA_WITH_RC4_128_MD5",
|
||||
[TLS_RSA_WITH_RC4_128_SHA] = "TLS_RSA_WITH_RC4_128_SHA",
|
||||
[TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5] = "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
|
||||
[TLS_RSA_WITH_IDEA_CBC_SHA] = "TLS_RSA_WITH_IDEA_CBC_SHA",
|
||||
[TLS_RSA_EXPORT_WITH_DES40_CBC_SHA] = "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_RSA_WITH_DES_CBC_SHA] = "TLS_RSA_WITH_DES_CBC_SHA",
|
||||
[TLS_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA] = "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_DES_CBC_SHA] = "TLS_DH_DSS_WITH_DES_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA] = "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA] = "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_DES_CBC_SHA] = "TLS_DH_RSA_WITH_DES_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA] = "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_DES_CBC_SHA] = "TLS_DHE_DSS_WITH_DES_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA] = "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA] = "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_DES_CBC_SHA] = "TLS_DHE_RSA_WITH_DES_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5] = "TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5",
|
||||
[TLS_DH_ANON_WITH_RC4_128_MD5] = "TLS_DH_ANON_WITH_RC4_128_MD5",
|
||||
[TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA] = "TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_DES_CBC_SHA] = "TLS_DH_ANON_WITH_DES_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA] = "TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA",
|
||||
[SSL_FORTEZZA_KEA_WITH_NULL_SHA] = "SSL_FORTEZZA_KEA_WITH_NULL_SHA",
|
||||
[SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA] = "SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA",
|
||||
[TLS_KRB5_WITH_DES_CBC_SHA] = "TLS_KRB5_WITH_DES_CBC_SHA",
|
||||
[TLS_KRB5_WITH_3DES_EDE_CBC_SHA] = "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_KRB5_WITH_RC4_128_SHA] = "TLS_KRB5_WITH_RC4_128_SHA",
|
||||
[TLS_KRB5_WITH_IDEA_CBC_SHA] = "TLS_KRB5_WITH_IDEA_CBC_SHA",
|
||||
[TLS_KRB5_WITH_DES_CBC_MD5] = "TLS_KRB5_WITH_DES_CBC_MD5",
|
||||
[TLS_KRB5_WITH_3DES_EDE_CBC_MD5] = "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
|
||||
[TLS_KRB5_WITH_RC4_128_MD5] = "TLS_KRB5_WITH_RC4_128_MD5",
|
||||
[TLS_KRB5_WITH_IDEA_CBC_MD5] = "TLS_KRB5_WITH_IDEA_CBC_MD5",
|
||||
[TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA] = "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
|
||||
[TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA] = "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
|
||||
[TLS_KRB5_EXPORT_WITH_RC4_40_SHA] = "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
|
||||
[TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5] = "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
|
||||
[TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5] = "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
|
||||
[TLS_KRB5_EXPORT_WITH_RC4_40_MD5] = "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
|
||||
[TLS_RSA_WITH_AES_128_CBC_SHA] = "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_AES_128_CBC_SHA] = "TLS_DH_DSS_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_AES_128_CBC_SHA] = "TLS_DH_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_AES_128_CBC_SHA] = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_AES_128_CBC_SHA] = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_AES_128_CBC_SHA] = "TLS_DH_ANON_WITH_AES_128_CBC_SHA",
|
||||
[TLS_RSA_WITH_AES_256_CBC_SHA] = "TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_AES_256_CBC_SHA] = "TLS_DH_DSS_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_AES_256_CBC_SHA] = "TLS_DH_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_AES_256_CBC_SHA] = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_AES_256_CBC_SHA] = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_AES_256_CBC_SHA] = "TLS_DH_ANON_WITH_AES_256_CBC_SHA",
|
||||
[TLS_RSA_WITH_NULL_SHA256] = "TLS_RSA_WITH_NULL_SHA256",
|
||||
[TLS_RSA_WITH_AES_128_CBC_SHA256] = "TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_RSA_WITH_AES_256_CBC_SHA256] = "TLS_RSA_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_DH_DSS_WITH_AES_128_CBC_SHA256] = "TLS_DH_DSS_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_DH_RSA_WITH_AES_128_CBC_SHA256] = "TLS_DH_RSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_DHE_DSS_WITH_AES_128_CBC_SHA256] = "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_RSA_WITH_CAMELLIA_128_CBC_SHA] = "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA] = "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA] = "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA] = "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA] = "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA] = "TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA",
|
||||
[TLS_RSA_EXPORT1024_WITH_RC4_56_MD5] = "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5",
|
||||
[TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5] = "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5",
|
||||
[TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA] = "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA",
|
||||
[TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA] = "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",
|
||||
[TLS_RSA_EXPORT1024_WITH_RC4_56_SHA] = "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA",
|
||||
[TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA] = "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",
|
||||
[TLS_DHE_DSS_WITH_RC4_128_SHA] = "TLS_DHE_DSS_WITH_RC4_128_SHA",
|
||||
[TLS_DHE_RSA_WITH_AES_128_CBC_SHA256] = "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_DH_DSS_WITH_AES_256_CBC_SHA256] = "TLS_DH_DSS_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_DH_RSA_WITH_AES_256_CBC_SHA256] = "TLS_DH_RSA_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_DHE_DSS_WITH_AES_256_CBC_SHA256] = "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_DHE_RSA_WITH_AES_256_CBC_SHA256] = "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_DH_ANON_WITH_AES_128_CBC_SHA256] = "TLS_DH_ANON_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_DH_ANON_WITH_AES_256_CBC_SHA256] = "TLS_DH_ANON_WITH_AES_256_CBC_SHA256",
|
||||
[TLS_RSA_WITH_CAMELLIA_256_CBC_SHA] = "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA] = "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA] = "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA] = "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA] = "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA] = "TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA",
|
||||
[TLS_PSK_WITH_RC4_128_SHA] = "TLS_PSK_WITH_RC4_128_SHA",
|
||||
[TLS_PSK_WITH_3DES_EDE_CBC_SHA] = "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_PSK_WITH_AES_128_CBC_SHA] = "TLS_PSK_WITH_AES_128_CBC_SHA",
|
||||
[TLS_PSK_WITH_AES_256_CBC_SHA] = "TLS_PSK_WITH_AES_256_CBC_SHA",
|
||||
[TLS_DHE_PSK_WITH_RC4_128_SHA] = "TLS_DHE_PSK_WITH_RC4_128_SHA",
|
||||
[TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA] = "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_DHE_PSK_WITH_AES_128_CBC_SHA] = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA",
|
||||
[TLS_DHE_PSK_WITH_AES_256_CBC_SHA] = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA",
|
||||
[TLS_RSA_PSK_WITH_RC4_128_SHA] = "TLS_RSA_PSK_WITH_RC4_128_SHA",
|
||||
[TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA] = "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_RSA_PSK_WITH_AES_128_CBC_SHA] = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA",
|
||||
[TLS_RSA_PSK_WITH_AES_256_CBC_SHA] = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA",
|
||||
[TLS_RSA_WITH_SEED_CBC_SHA] = "TLS_RSA_WITH_SEED_CBC_SHA",
|
||||
[TLS_DH_DSS_WITH_SEED_CBC_SHA] = "TLS_DH_DSS_WITH_SEED_CBC_SHA",
|
||||
[TLS_DH_RSA_WITH_SEED_CBC_SHA] = "TLS_DH_RSA_WITH_SEED_CBC_SHA",
|
||||
[TLS_DHE_DSS_WITH_SEED_CBC_SHA] = "TLS_DHE_DSS_WITH_SEED_CBC_SHA",
|
||||
[TLS_DHE_RSA_WITH_SEED_CBC_SHA] = "TLS_DHE_RSA_WITH_SEED_CBC_SHA",
|
||||
[TLS_DH_ANON_WITH_SEED_CBC_SHA] = "TLS_DH_ANON_WITH_SEED_CBC_SHA",
|
||||
[TLS_RSA_WITH_AES_128_GCM_SHA256] = "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_RSA_WITH_AES_256_GCM_SHA384] = "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DHE_RSA_WITH_AES_128_GCM_SHA256] = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DHE_RSA_WITH_AES_256_GCM_SHA384] = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DH_RSA_WITH_AES_128_GCM_SHA256] = "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DH_RSA_WITH_AES_256_GCM_SHA384] = "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DHE_DSS_WITH_AES_128_GCM_SHA256] = "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DHE_DSS_WITH_AES_256_GCM_SHA384] = "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DH_DSS_WITH_AES_128_GCM_SHA256] = "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DH_DSS_WITH_AES_256_GCM_SHA384] = "TLS_DH_DSS_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DH_ANON_WITH_AES_128_GCM_SHA256] = "TLS_DH_ANON_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DH_ANON_WITH_AES_256_GCM_SHA384] = "TLS_DH_ANON_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_PSK_WITH_AES_128_GCM_SHA256] = "TLS_PSK_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_PSK_WITH_AES_256_GCM_SHA384] = "TLS_PSK_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_DHE_PSK_WITH_AES_128_GCM_SHA256] = "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_DHE_PSK_WITH_AES_256_GCM_SHA384] = "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_RSA_PSK_WITH_AES_128_GCM_SHA256] = "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_RSA_PSK_WITH_AES_256_GCM_SHA384] = "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_PSK_WITH_AES_128_CBC_SHA256] = "TLS_PSK_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_PSK_WITH_AES_256_CBC_SHA384] = "TLS_PSK_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_PSK_WITH_NULL_SHA256] = "TLS_PSK_WITH_NULL_SHA256",
|
||||
[TLS_PSK_WITH_NULL_SHA384] = "TLS_PSK_WITH_NULL_SHA384",
|
||||
[TLS_DHE_PSK_WITH_AES_128_CBC_SHA256] = "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_DHE_PSK_WITH_AES_256_CBC_SHA384] = "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_DHE_PSK_WITH_NULL_SHA256] = "TLS_DHE_PSK_WITH_NULL_SHA256",
|
||||
[TLS_DHE_PSK_WITH_NULL_SHA384] = "TLS_DHE_PSK_WITH_NULL_SHA384",
|
||||
[TLS_RSA_PSK_WITH_AES_128_CBC_SHA256] = "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_RSA_PSK_WITH_AES_256_CBC_SHA384] = "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_RSA_PSK_WITH_NULL_SHA256] = "TLS_RSA_PSK_WITH_NULL_SHA256",
|
||||
[TLS_RSA_PSK_WITH_NULL_SHA384] = "TLS_RSA_PSK_WITH_NULL_SHA384",
|
||||
[TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256] = "TLS_DH_ANON_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
[TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256] = "TLS_DH_ANON_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
[TLS_ECDH_ECDSA_WITH_NULL_SHA] = "TLS_ECDH_ECDSA_WITH_NULL_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_RC4_128_SHA] = "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA] = "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA] = "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_NULL_SHA] = "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_RC4_128_SHA] = "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA] = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA] = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDH_RSA_WITH_NULL_SHA] = "TLS_ECDH_RSA_WITH_NULL_SHA",
|
||||
[TLS_ECDH_RSA_WITH_RC4_128_SHA] = "TLS_ECDH_RSA_WITH_RC4_128_SHA",
|
||||
[TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDH_RSA_WITH_AES_128_CBC_SHA] = "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDH_RSA_WITH_AES_256_CBC_SHA] = "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDHE_RSA_WITH_NULL_SHA] = "TLS_ECDHE_RSA_WITH_NULL_SHA",
|
||||
[TLS_ECDHE_RSA_WITH_RC4_128_SHA] = "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
[TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA] = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA] = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDH_ANON_WITH_NULL_SHA] = "TLS_ECDH_ANON_WITH_NULL_SHA",
|
||||
[TLS_ECDH_ANON_WITH_RC4_128_SHA] = "TLS_ECDH_ANON_WITH_RC4_128_SHA",
|
||||
[TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDH_ANON_WITH_AES_128_CBC_SHA] = "TLS_ECDH_ANON_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDH_ANON_WITH_AES_256_CBC_SHA] = "TLS_ECDH_ANON_WITH_AES_256_CBC_SHA",
|
||||
[TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA] = "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA] = "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA] = "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_SRP_SHA_WITH_AES_128_CBC_SHA] = "TLS_SRP_SHA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA] = "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA",
|
||||
[TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA] = "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA",
|
||||
[TLS_SRP_SHA_WITH_AES_256_CBC_SHA] = "TLS_SRP_SHA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA] = "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA",
|
||||
[TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA] = "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256] = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384] = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256] = "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384] = "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256] = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384] = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256] = "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384] = "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256] = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384] = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256] = "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384] = "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256] = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384] = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256] = "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
|
||||
[TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384] = "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
|
||||
[TLS_ECDHE_PSK_WITH_RC4_128_SHA] = "TLS_ECDHE_PSK_WITH_RC4_128_SHA",
|
||||
[TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA] = "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA",
|
||||
[TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA] = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
|
||||
[TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA] = "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
|
||||
[TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256] = "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",
|
||||
[TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384] = "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384",
|
||||
[TLS_ECDHE_PSK_WITH_NULL_SHA] = "TLS_ECDHE_PSK_WITH_NULL_SHA",
|
||||
[TLS_ECDHE_PSK_WITH_NULL_SHA256] = "TLS_ECDHE_PSK_WITH_NULL_SHA256",
|
||||
[TLS_ECDHE_PSK_WITH_NULL_SHA384] = "TLS_ECDHE_PSK_WITH_NULL_SHA384",
|
||||
[SSL_RSA_FIPS_WITH_DES_CBC_SHA] = "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
|
||||
[SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA] = "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
|
||||
[SSL_RSA_FIPS_WITH_DES_CBC_SHA_2] = "SSL_RSA_FIPS_WITH_DES_CBC_SHA_2",
|
||||
[SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2] = "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2",
|
||||
} &default="UNKNOWN";
|
||||
|
||||
const x509_errors: table[count] of string = {
|
||||
[0] = "X509_V_OK",
|
||||
[1] = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
|
||||
[2] = "X509_V_ERR_UNABLE_TO_GET_CRL",
|
||||
[3] = "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
|
||||
[4] = "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
|
||||
[5] = "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
|
||||
[6] = "X509_V_ERR_CERT_SIGNATURE_FAILURE",
|
||||
[7] = "X509_V_ERR_CRL_SIGNATURE_FAILURE",
|
||||
[8] = "X509_V_ERR_CERT_NOT_YET_VALID",
|
||||
[9] = "X509_V_ERR_CERT_HAS_EXPIRED",
|
||||
[10] = "X509_V_ERR_CRL_NOT_YET_VALID",
|
||||
[11] = "X509_V_ERR_CRL_HAS_EXPIRED",
|
||||
[12] = "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
|
||||
[13] = "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
|
||||
[14] = "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
|
||||
[15] = "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
|
||||
[16] = "X509_V_ERR_OUT_OF_MEM",
|
||||
[17] = "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
|
||||
[18] = "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
|
||||
[19] = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
||||
[20] = "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
||||
[21] = "X509_V_ERR_CERT_CHAIN_TOO_LONG",
|
||||
[22] = "X509_V_ERR_CERT_REVOKED",
|
||||
[23] = "X509_V_ERR_INVALID_CA",
|
||||
[24] = "X509_V_ERR_PATH_LENGTH_EXCEEDED",
|
||||
[25] = "X509_V_ERR_INVALID_PURPOSE",
|
||||
[26] = "X509_V_ERR_CERT_UNTRUSTED",
|
||||
[27] = "X509_V_ERR_CERT_REJECTED",
|
||||
[28] = "X509_V_ERR_SUBJECT_ISSUER_MISMATCH",
|
||||
[29] = "X509_V_ERR_AKID_SKID_MISMATCH",
|
||||
[30] = "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH",
|
||||
[31] = "X509_V_ERR_KEYUSAGE_NO_CERTSIGN",
|
||||
[32] = "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER",
|
||||
[33] = "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
|
||||
};
|
||||
|
||||
}
|
120
scripts/base/protocols/ssl/main.bro
Normal file
120
scripts/base/protocols/ssl/main.bro
Normal file
|
@ -0,0 +1,120 @@
|
|||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SSL };
|
||||
|
||||
redef enum Notice::Type += {
|
||||
Self_Signed_Cert
|
||||
};
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
version: string &log &optional;
|
||||
cipher: string &log &optional;
|
||||
server_name: string &log &optional;
|
||||
subject: string &log &optional;
|
||||
not_valid_before: time &log &optional;
|
||||
not_valid_after: time &log &optional;
|
||||
|
||||
cert: string &optional;
|
||||
cert_chain: vector of string &optional;
|
||||
};
|
||||
|
||||
## This is where the default root CA bundle is defined. By loading the
|
||||
## mozilla-ca-list.bro script it will be set to Mozilla's root CA list.
|
||||
const root_certs: table[string] of string = {} &redef;
|
||||
|
||||
global log_ssl: event(rec: Info);
|
||||
|
||||
const ports = {
|
||||
443/tcp, 563/tcp, 585/tcp, 614/tcp, 636/tcp,
|
||||
989/tcp, 990/tcp, 992/tcp, 993/tcp, 995/tcp, 5223/tcp
|
||||
} &redef;
|
||||
}
|
||||
|
||||
redef record connection += {
|
||||
ssl: Info &optional;
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(SSL, [$columns=Info, $ev=log_ssl]);
|
||||
}
|
||||
|
||||
redef capture_filters += {
|
||||
["ssl"] = "tcp port 443",
|
||||
["nntps"] = "tcp port 563",
|
||||
["imap4-ssl"] = "tcp port 585",
|
||||
["sshell"] = "tcp port 614",
|
||||
["ldaps"] = "tcp port 636",
|
||||
["ftps-data"] = "tcp port 989",
|
||||
["ftps"] = "tcp port 990",
|
||||
["telnets"] = "tcp port 992",
|
||||
["imaps"] = "tcp port 993",
|
||||
["ircs"] = "tcp port 994",
|
||||
["pop3s"] = "tcp port 995",
|
||||
["xmpps"] = "tcp port 5223",
|
||||
};
|
||||
|
||||
redef dpd_config += {
|
||||
[[ANALYZER_SSL]] = [$ports = ports]
|
||||
};
|
||||
|
||||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
c$ssl = [$ts=network_time(), $uid=c$uid, $id=c$id, $cert_chain=vector()];
|
||||
}
|
||||
|
||||
event ssl_client_hello(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
}
|
||||
|
||||
event ssl_server_hello(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
c$ssl$version = version_strings[version];
|
||||
c$ssl$cipher = cipher_desc[cipher];
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
if ( chain_idx == 0 )
|
||||
{
|
||||
# Save the primary cert.
|
||||
c$ssl$cert = der_cert;
|
||||
|
||||
# Also save other certificate information about the primary cert.
|
||||
c$ssl$subject = cert$subject;
|
||||
c$ssl$not_valid_before = cert$not_valid_before;
|
||||
c$ssl$not_valid_after = cert$not_valid_after;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Otherwise, add it to the cert validation chain.
|
||||
c$ssl$cert_chain[|c$ssl$cert_chain|] = der_cert;
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_extension(c: connection, code: count, val: string) &priority=5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
if ( extensions[code] == "server_name" )
|
||||
c$ssl$server_name = sub_bytes(val, 6, |val|);
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=-5
|
||||
{
|
||||
set_session(c);
|
||||
|
||||
Log::write(SSL, c$ssl);
|
||||
}
|
||||
|
140
scripts/base/protocols/ssl/mozilla-ca-list.bro
Normal file
140
scripts/base/protocols/ssl/mozilla-ca-list.bro
Normal file
File diff suppressed because one or more lines are too long
2
scripts/base/protocols/syslog/__load__.bro
Normal file
2
scripts/base/protocols/syslog/__load__.bro
Normal file
|
@ -0,0 +1,2 @@
|
|||
@load ./consts
|
||||
@load ./main
|
41
scripts/base/protocols/syslog/consts.bro
Normal file
41
scripts/base/protocols/syslog/consts.bro
Normal file
|
@ -0,0 +1,41 @@
|
|||
module Syslog;
|
||||
|
||||
export {
|
||||
const facility_codes: table[count] of string = {
|
||||
[0] = "KERN",
|
||||
[1] = "USER",
|
||||
[2] = "MAIL",
|
||||
[3] = "DAEMON",
|
||||
[4] = "AUTH",
|
||||
[5] = "SYSLOG",
|
||||
[6] = "LPR",
|
||||
[7] = "NEWS",
|
||||
[8] = "UUCP",
|
||||
[9] = "CRON",
|
||||
[10] = "AUTHPRIV",
|
||||
[11] = "FTP",
|
||||
[12] = "NTP",
|
||||
[13] = "AUDIT",
|
||||
[14] = "ALERT",
|
||||
[15] = "CLOCK",
|
||||
[16] = "LOCAL0",
|
||||
[17] = "LOCAL1",
|
||||
[18] = "LOCAL2",
|
||||
[19] = "LOCAL3",
|
||||
[20] = "LOCAL4",
|
||||
[21] = "LOCAL5",
|
||||
[22] = "LOCAL6",
|
||||
[23] = "LOCAL7",
|
||||
} &default=function(c: count): string { return fmt("?-%d", c); };
|
||||
|
||||
const severity_codes: table[count] of string = {
|
||||
[0] = "EMERG",
|
||||
[1] = "ALERT",
|
||||
[2] = "CRIT",
|
||||
[3] = "ERR",
|
||||
[4] = "WARNING",
|
||||
[5] = "NOTICE",
|
||||
[6] = "INFO",
|
||||
[7] = "DEBUG",
|
||||
} &default=function(c: count): string { return fmt("?-%d", c); };
|
||||
}
|
52
scripts/base/protocols/syslog/main.bro
Normal file
52
scripts/base/protocols/syslog/main.bro
Normal file
|
@ -0,0 +1,52 @@
|
|||
##! Core script support for logging syslog messages.
|
||||
|
||||
@load ./consts
|
||||
|
||||
module Syslog;
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SYSLOG };
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
uid: string &log;
|
||||
id: conn_id &log;
|
||||
proto: transport_proto &log;
|
||||
facility: string &log;
|
||||
severity: string &log;
|
||||
message: string &log;
|
||||
};
|
||||
|
||||
const ports = { 514/udp } &redef;
|
||||
}
|
||||
|
||||
redef capture_filters += { ["syslog"] = "port 514" };
|
||||
redef dpd_config += { [ANALYZER_SYSLOG_BINPAC] = [$ports = ports] };
|
||||
|
||||
redef record connection += {
|
||||
syslog: Info &optional;
|
||||
};
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(SYSLOG, [$columns=Info]);
|
||||
}
|
||||
|
||||
event syslog_message(c: connection, facility: count, severity: count, msg: string) &priority=5
|
||||
{
|
||||
local info: Info;
|
||||
info$ts=network_time();
|
||||
info$uid=c$uid;
|
||||
info$id=c$id;
|
||||
info$proto=get_port_transport_proto(c$id$resp_p);
|
||||
info$facility=facility_codes[facility];
|
||||
info$severity=severity_codes[severity];
|
||||
info$message=msg;
|
||||
|
||||
c$syslog = info;
|
||||
}
|
||||
|
||||
event syslog_message(c: connection, facility: count, severity: count, msg: string) &priority=-5
|
||||
{
|
||||
Log::write(SYSLOG, c$syslog);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue