mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 17:48:21 +00:00
Intelligence framework checkpoint
- Basic API seems to works, but tests aren't updated yet. - Several scripts are available in policy/frameworks/intel that call the "seen" function to provide data into the intel framework to be tested. - Intel::policy is not done yet and needs to be discussed to figure out what it needs to have. - Running the intel framework and having it do something finally is really cool!
This commit is contained in:
parent
71c5b49bdc
commit
a803cae92e
11 changed files with 198 additions and 109 deletions
|
@ -21,14 +21,14 @@ redef record Item += {
|
|||
};
|
||||
|
||||
# Primary intelligence distribution comes from manager.
|
||||
redef Cluster::manager2worker_events += /Intel::cluster_(new|updated)_item/;
|
||||
redef Cluster::manager2worker_events += /^Intel::cluster_.*/;
|
||||
# If a worker finds intelligence and adds it, it should share it back to the manager.
|
||||
redef Cluster::worker2manager_events += /Intel::(match_in_.*_no_items|cluster_(new|updated)_item)/;
|
||||
redef Cluster::worker2manager_events += /^Intel::(cluster_.*|match_no_items)/;
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event Intel::match_in_conn_no_items(c: connection, seen: Seen) &priority=5
|
||||
event Intel::match_no_items(s: Seen) &priority=5
|
||||
{
|
||||
event Intel::match_in_conn(c, seen, Intel::get_items(seen));
|
||||
event Intel::match(c, s, Intel::get_items(s));
|
||||
}
|
||||
@endif
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
|
||||
@load base/protocols/http
|
||||
@load base/frameworks/intel
|
||||
|
||||
module HTTP;
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
HTTP::IN_HEADER,
|
||||
HTTP::IN_REQUEST,
|
||||
HTTP::IN_HOST_HEADER,
|
||||
HTTP::IN_CONN_EST,
|
||||
HTTP::IN_DNS_REQUEST,
|
||||
};
|
||||
}
|
||||
|
||||
event connection_established(c: connection)
|
||||
{
|
||||
Intel::found_in_conn(c, [$host=c$id$orig_h, $where=IN_CONN_EST]);
|
||||
Intel::found_in_conn(c, [$host=c$id$resp_h, $where=IN_CONN_EST]);
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig && name == "USER-AGENT" )
|
||||
Intel::found_in_conn(c, [$str=value,
|
||||
$str_type=Intel::USER_AGENT,
|
||||
$where=IN_HEADER]);
|
||||
|
||||
if ( is_orig && name == "HOST" )
|
||||
Intel::found_in_conn(c, [$str=value,
|
||||
$str_type=Intel::DOMAIN,
|
||||
$where=IN_HOST_HEADER]);
|
||||
}
|
||||
|
||||
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
|
||||
{
|
||||
if ( c?$http )
|
||||
{
|
||||
if ( c$http?$user_agent )
|
||||
Intel::found_in_conn(c, [$str=c$http$user_agent,
|
||||
$str_type=Intel::USER_AGENT,
|
||||
$where=IN_HEADER]);
|
||||
|
||||
Intel::found_in_conn(c, [$str=HTTP::build_url(c$http),
|
||||
$str_type=Intel::URL,
|
||||
$where=IN_REQUEST]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
|
||||
{
|
||||
Intel::found_in_conn(c, [$str=query,
|
||||
$str_type=Intel::DOMAIN,
|
||||
$where=IN_DNS_REQUEST]);
|
||||
|
||||
}
|
||||
|
||||
event Intel::match_in_conn(c: connection, found: Intel::Found, items: set[Intel::Item])
|
||||
{
|
||||
print "matched one!";
|
||||
for ( i in items )
|
||||
{
|
||||
print " " + i$meta$desc;
|
||||
}
|
||||
}
|
|
@ -11,8 +11,7 @@ export {
|
|||
redef enum Log::ID += { LOG };
|
||||
|
||||
redef enum Notice::Type += {
|
||||
## This notice should be used in all detector scripts to indicate
|
||||
## an intelligence based detection.
|
||||
## Notice type to indicate an intelligence hit.
|
||||
Detection,
|
||||
};
|
||||
|
||||
|
@ -64,29 +63,42 @@ export {
|
|||
};
|
||||
|
||||
type Seen: record {
|
||||
host: addr &optional;
|
||||
str: string &optional;
|
||||
str_type: StrType &optional;
|
||||
host: addr &optional &log;
|
||||
str: string &optional &log;
|
||||
str_type: StrType &optional &log;
|
||||
|
||||
where: Where;
|
||||
where: Where &log;
|
||||
|
||||
conn: connection &optional;
|
||||
};
|
||||
|
||||
type Info: record {
|
||||
ts: time &log;
|
||||
|
||||
uid: string &log &optional;
|
||||
id: conn_id &log &optional;
|
||||
|
||||
seen: Seen &log;
|
||||
};
|
||||
|
||||
type PolicyItem: record {
|
||||
pred: function(seen: Seen, item: Item): bool &optional;
|
||||
pred: function(s: Seen, item: Item): bool &optional;
|
||||
|
||||
log_it: bool &default=T;
|
||||
};
|
||||
|
||||
## Intelligence data manipulation functions.
|
||||
global insert: function(item: Item);
|
||||
global delete_item: function(item: Item): bool;
|
||||
|
||||
## Function to declare discovery of a piece of data in order to check
|
||||
## it against known intelligence for matches.
|
||||
global seen_in_conn: function(c: connection, seen: Seen);
|
||||
global seen: function(s: Seen);
|
||||
|
||||
## Intelligence policy variable for handling matches.
|
||||
const policy: set[PolicyItem] = {} &redef;
|
||||
const policy: set[PolicyItem] = {
|
||||
# [$pred(s: Seen) = { return T; },
|
||||
# $action=Intel::ACTION_LOG]
|
||||
} &redef;
|
||||
|
||||
## API Events that indicate when various things happen internally within the
|
||||
## intelligence framework.
|
||||
|
@ -94,34 +106,40 @@ export {
|
|||
global updated_item: event(item: Item);
|
||||
}
|
||||
|
||||
## Event to represent a match happening in a connection. On clusters there
|
||||
## is no assurance as to where this event will be generated so don't
|
||||
## assume that arbitrary global state beyond the given data
|
||||
## will be available.
|
||||
global match_in_conn: event(c: connection, seen: Seen, items: set[Item]);
|
||||
# Event to represent a match happening in a connection. On clusters there
|
||||
# is no assurance as to where this event will be generated so don't
|
||||
# assume that arbitrary global state beyond the given data
|
||||
# will be available.
|
||||
global match: event(s: Seen, items: set[Item]);
|
||||
|
||||
# Internal handler for conn oriented matches with no metadata based on the have_full_data setting.
|
||||
global match_in_conn_no_items: event(c: connection, seen: Seen);
|
||||
global match_no_items: event(s: Seen);
|
||||
|
||||
## Optionally store metadata. This is used internally depending on
|
||||
## if this is a cluster deployment or not.
|
||||
# Optionally store metadata. This is used internally depending on
|
||||
# if this is a cluster deployment or not.
|
||||
const have_full_data = T &redef;
|
||||
|
||||
# The in memory data structure for holding intelligence.
|
||||
type DataStore: record {
|
||||
net_data: table[subnet] of set[MetaData];
|
||||
string_data: table[string, StrType] of set[MetaData];
|
||||
};
|
||||
global data_store: DataStore;
|
||||
|
||||
function find(seen: Seen): bool
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
if ( seen?$host &&
|
||||
seen$host in data_store$net_data )
|
||||
Log::create_stream(LOG, [$columns=Info]);
|
||||
}
|
||||
|
||||
function find(s: Seen): bool
|
||||
{
|
||||
if ( s?$host &&
|
||||
s$host in data_store$net_data )
|
||||
{
|
||||
return T;
|
||||
}
|
||||
else if ( seen?$str && seen?$str_type &&
|
||||
[seen$str, seen$str_type] in data_store$string_data )
|
||||
else if ( s?$str && s?$str_type &&
|
||||
[s$str, s$str_type] in data_store$string_data )
|
||||
{
|
||||
return T;
|
||||
}
|
||||
|
@ -131,7 +149,7 @@ function find(seen: Seen): bool
|
|||
}
|
||||
}
|
||||
|
||||
function get_items(seen: Seen): set[Item]
|
||||
function get_items(s: Seen): set[Item]
|
||||
{
|
||||
local item: Item;
|
||||
local return_data: set[Item] = set();
|
||||
|
@ -144,28 +162,28 @@ function get_items(seen: Seen): set[Item]
|
|||
return return_data;
|
||||
}
|
||||
|
||||
if ( seen?$host )
|
||||
if ( s?$host )
|
||||
{
|
||||
# See if the host is known about and it has meta values
|
||||
if ( seen$host in data_store$net_data )
|
||||
if ( s$host in data_store$net_data )
|
||||
{
|
||||
for ( m in data_store$net_data[seen$host] )
|
||||
for ( m in data_store$net_data[s$host] )
|
||||
{
|
||||
# TODO: the lookup should be finding all and not just most specific
|
||||
# and $host/$net should have the correct value.
|
||||
item = [$host=seen$host, $meta=m];
|
||||
item = [$host=s$host, $meta=m];
|
||||
add return_data[item];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( seen?$str && seen?$str_type )
|
||||
else if ( s?$str && s?$str_type )
|
||||
{
|
||||
# See if the string is known about and it has meta values
|
||||
if ( [seen$str, seen$str_type] in data_store$string_data )
|
||||
if ( [s$str, s$str_type] in data_store$string_data )
|
||||
{
|
||||
for ( m in data_store$string_data[seen$str, seen$str_type] )
|
||||
for ( m in data_store$string_data[s$str, s$str_type] )
|
||||
{
|
||||
item = [$str=seen$str, $str_type=seen$str_type, $meta=m];
|
||||
item = [$str=s$str, $str_type=s$str_type, $meta=m];
|
||||
add return_data[item];
|
||||
}
|
||||
}
|
||||
|
@ -174,18 +192,31 @@ function get_items(seen: Seen): set[Item]
|
|||
return return_data;
|
||||
}
|
||||
|
||||
function Intel::seen_in_conn(c: connection, seen: Seen)
|
||||
event Intel::match(s: Seen, items: set[Item])
|
||||
{
|
||||
if ( find(seen) )
|
||||
local info: Info = [$ts=network_time(), $seen=s];
|
||||
|
||||
if ( s?$conn )
|
||||
{
|
||||
info$uid = s$conn$uid;
|
||||
info$id = s$conn$id;
|
||||
}
|
||||
|
||||
Log::write(Intel::LOG, info);
|
||||
}
|
||||
|
||||
function Intel::seen(s: Seen)
|
||||
{
|
||||
if ( find(s) )
|
||||
{
|
||||
if ( have_full_data )
|
||||
{
|
||||
local items = get_items(seen);
|
||||
event Intel::match_in_conn(c, seen, items);
|
||||
local items = get_items(s);
|
||||
event Intel::match(s, items);
|
||||
}
|
||||
else
|
||||
{
|
||||
event Intel::match_in_conn_no_items(c, seen);
|
||||
event Intel::match_no_items(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
scripts/policy/frameworks/intel/__load__.bro
Normal file
6
scripts/policy/frameworks/intel/__load__.bro
Normal file
|
@ -0,0 +1,6 @@
|
|||
@load ./conn-established
|
||||
@load ./dns
|
||||
@load ./http-host-header
|
||||
@load ./http-url
|
||||
@load ./http-user-agent
|
||||
@load ./ssl
|
14
scripts/policy/frameworks/intel/conn-established.bro
Normal file
14
scripts/policy/frameworks/intel/conn-established.bro
Normal file
|
@ -0,0 +1,14 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
Conn::IN_ORIG,
|
||||
Conn::IN_RESP,
|
||||
};
|
||||
}
|
||||
|
||||
event connection_established(c: connection)
|
||||
{
|
||||
Intel::seen([$host=c$id$orig_h, $conn=c, $where=Conn::IN_ORIG]);
|
||||
Intel::seen([$host=c$id$resp_h, $conn=c, $where=Conn::IN_RESP]);
|
||||
}
|
16
scripts/policy/frameworks/intel/dns.bro
Normal file
16
scripts/policy/frameworks/intel/dns.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
DNS::IN_REQUEST,
|
||||
DNS::IN_RESPONSE,
|
||||
};
|
||||
}
|
||||
|
||||
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
|
||||
{
|
||||
Intel::seen([$str=query,
|
||||
$str_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
$where=DNS::IN_REQUEST]);
|
||||
}
|
16
scripts/policy/frameworks/intel/http-host-header.bro
Normal file
16
scripts/policy/frameworks/intel/http-host-header.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
HTTP::IN_HOST_HEADER,
|
||||
};
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig && name == "HOST" )
|
||||
Intel::seen([$str=value,
|
||||
$str_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_HOST_HEADER]);
|
||||
}
|
16
scripts/policy/frameworks/intel/http-url.bro
Normal file
16
scripts/policy/frameworks/intel/http-url.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
HTTP::IN_URL,
|
||||
};
|
||||
}
|
||||
|
||||
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
|
||||
{
|
||||
if ( is_orig && c?$http )
|
||||
Intel::seen([$str=HTTP::build_url(c$http),
|
||||
$str_type=Intel::URL,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_URL]);
|
||||
}
|
16
scripts/policy/frameworks/intel/http-user-agent.bro
Normal file
16
scripts/policy/frameworks/intel/http-user-agent.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
HTTP::IN_USER_AGENT_HEADER,
|
||||
};
|
||||
}
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig && name == "USER-AGENT" )
|
||||
Intel::seen([$str=value,
|
||||
$str_type=Intel::USER_AGENT,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_USER_AGENT_HEADER]);
|
||||
}
|
41
scripts/policy/frameworks/intel/ssl.bro
Normal file
41
scripts/policy/frameworks/intel/ssl.bro
Normal file
|
@ -0,0 +1,41 @@
|
|||
@load base/frameworks/intel
|
||||
|
||||
export {
|
||||
redef enum Intel::Where += {
|
||||
SSL::IN_SERVER_CERT,
|
||||
SSL::IN_CLIENT_CERT,
|
||||
SSL::IN_SERVER_NAME,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string)
|
||||
{
|
||||
if ( chain_idx == 0 )
|
||||
{
|
||||
if ( /emailAddress=/ in cert$subject )
|
||||
{
|
||||
local email = sub(cert$subject, /^.*emailAddress=/, "");
|
||||
email = sub(email, /,.*$/, "");
|
||||
Intel::seen([$str=email,
|
||||
$str_type=Intel::EMAIL,
|
||||
$conn=c,
|
||||
$where=(is_orig ? SSL::IN_CLIENT_CERT : SSL::IN_SERVER_CERT)]);
|
||||
}
|
||||
|
||||
Intel::seen([$str=sha1_hash(der_cert),
|
||||
$str_type=Intel::CERT_HASH,
|
||||
$conn=c,
|
||||
$where=(is_orig ? SSL::IN_CLIENT_CERT : SSL::IN_SERVER_CERT)]);
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_extension(c: connection, is_orig: bool, code: count, val: string)
|
||||
{
|
||||
if ( is_orig && SSL::extensions[code] == "server_name" &&
|
||||
c?$ssl && c$ssl?$server_name )
|
||||
Intel::seen([$str=c$ssl$server_name,
|
||||
$str_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
$where=SSL::IN_SERVER_NAME]);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue