mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
357 lines
15 KiB
ReStructuredText
357 lines
15 KiB
ReStructuredText
|
|
.. _notice-framework:
|
|
|
|
Notice Framework
|
|
================
|
|
|
|
.. rst-class:: opening
|
|
|
|
One of the easiest ways to customize Bro is writing a local notice
|
|
policy. Bro can detect a large number of potentially interesting
|
|
situations, and the notice policy hook which of them the user wants to be
|
|
acted upon in some manner. In particular, the notice policy can specify
|
|
actions to be taken, such as sending an email or compiling regular
|
|
alarm emails. This page gives an introduction into writing such a notice
|
|
policy.
|
|
|
|
.. contents::
|
|
|
|
Overview
|
|
--------
|
|
|
|
Let's start with a little bit of background on Bro's philosophy on reporting
|
|
things. Bro ships with a large number of policy scripts which perform a wide
|
|
variety of analyses. Most of these scripts monitor for activity which might be
|
|
of interest for the user. However, none of these scripts determines the
|
|
importance of what it finds itself. Instead, the scripts only flag situations
|
|
as *potentially* interesting, leaving it to the local configuration to define
|
|
which of them are in fact actionable. This decoupling of detection and
|
|
reporting allows Bro to address the different needs that sites have.
|
|
Definitions of what constitutes an attack or even a compromise differ quite a
|
|
bit between environments, and activity deemed malicious at one site might be
|
|
fully acceptable at another.
|
|
|
|
Whenever one of Bro's analysis scripts sees something potentially
|
|
interesting it flags the situation by calling the :bro:see:`NOTICE`
|
|
function and giving it a single :bro:see:`Notice::Info` record. A Notice
|
|
has a :bro:see:`Notice::Type`, which reflects the kind of activity that
|
|
has been seen, and it is usually also augmented with further context
|
|
about the situation.
|
|
|
|
More information about raising notices can be found in the `Raising Notices`_
|
|
section.
|
|
|
|
Once a notice is raised, it can have any number of actions applied to it by
|
|
writing :bro:see:`Notice::policy` hooks which is described in the `Notice Policy`_
|
|
section below. Such actions can be to send a mail to the configured
|
|
address(es) or to simply ignore the notice. Currently, the following actions
|
|
are defined:
|
|
|
|
.. list-table::
|
|
:widths: 20 80
|
|
:header-rows: 1
|
|
|
|
* - Action
|
|
- Description
|
|
|
|
* - Notice::ACTION_LOG
|
|
- Write the notice to the :bro:see:`Notice::LOG` logging stream.
|
|
|
|
* - Notice::ACTION_ALARM
|
|
- Log into the :bro:see:`Notice::ALARM_LOG` stream which will rotate
|
|
hourly and email the contents to the email address or addresses
|
|
defined in the :bro:see:`Notice::mail_dest` variable.
|
|
|
|
* - Notice::ACTION_EMAIL
|
|
- Send the notice in an email to the email address or addresses given in
|
|
the :bro:see:`Notice::mail_dest` variable.
|
|
|
|
* - Notice::ACTION_PAGE
|
|
- Send an email to the email address or addresses given in the
|
|
:bro:see:`Notice::mail_page_dest` variable.
|
|
|
|
How these notice actions are applied to notices is discussed in the
|
|
`Notice Policy`_ and `Notice Policy Shortcuts`_ sections.
|
|
|
|
Processing Notices
|
|
------------------
|
|
|
|
Notice Policy
|
|
*************
|
|
|
|
The hook :bro:see:`Notice::policy` provides the mechanism for applying
|
|
actions and generally modifying the notice before it's sent onward to
|
|
the action plugins. Hooks can be thought of as multi-bodied functions
|
|
and using them looks very similar to handling events. The difference
|
|
is that they don't go through the event queue like events. Users can
|
|
alter notice processing by directly modifying fields in the
|
|
:bro:see:`Notice::Info` record given as the argument to the hook.
|
|
|
|
Here's a simple example which tells Bro to send an email for all notices of
|
|
type :bro:see:`SSH::Password_Guessing` if the guesser attempted to log in to
|
|
the server at 192.168.56.103:
|
|
|
|
.. btest-include:: ${DOC_ROOT}/frameworks/notice_ssh_guesser.bro
|
|
|
|
.. btest:: notice_ssh_guesser.bro
|
|
|
|
@TEST-EXEC: btest-rst-cmd bro -C -r ${TRACES}/ssh/sshguess.pcap ${DOC_ROOT}/frameworks/notice_ssh_guesser.bro
|
|
@TEST-EXEC: btest-rst-cmd cat notice.log
|
|
|
|
.. note::
|
|
|
|
Keep in mind that the semantics of the :bro:see:`SSH::Password_Guessing`
|
|
notice are such that it is only raised when Bro heuristically detects
|
|
a failed login.
|
|
|
|
Hooks can also have priorities applied to order their execution like events
|
|
with a default priority of 0. Greater values are executed first. Setting
|
|
a hook body to run before default hook bodies might look like this:
|
|
|
|
.. code:: bro
|
|
|
|
hook Notice::policy(n: Notice::Info) &priority=5
|
|
{
|
|
# Insert your code here.
|
|
}
|
|
|
|
Hooks can also abort later hook bodies with the ``break`` keyword. This
|
|
is primarily useful if one wants to completely preempt processing by
|
|
lower priority :bro:see:`Notice::policy` hooks.
|
|
|
|
Notice Policy Shortcuts
|
|
***********************
|
|
|
|
Although the notice framework provides a great deal of flexibility and
|
|
configurability there are many times that the full expressiveness isn't needed
|
|
and actually becomes a hindrance to achieving results. The framework provides
|
|
a default :bro:see:`Notice::policy` hook body as a way of giving users the
|
|
shortcuts to easily apply many common actions to notices.
|
|
|
|
These are implemented as sets and tables indexed with a
|
|
:bro:see:`Notice::Type` enum value. The following table shows and describes
|
|
all of the variables available for shortcut configuration of the notice
|
|
framework.
|
|
|
|
.. list-table::
|
|
:widths: 32 40
|
|
:header-rows: 1
|
|
|
|
* - Variable name
|
|
- Description
|
|
|
|
* - :bro:see:`Notice::ignored_types`
|
|
- Adding a :bro:see:`Notice::Type` to this set results in the notice
|
|
being ignored. It won't have any other action applied to it, not even
|
|
:bro:see:`Notice::ACTION_LOG`.
|
|
|
|
* - :bro:see:`Notice::emailed_types`
|
|
- Adding a :bro:see:`Notice::Type` to this set results in
|
|
:bro:see:`Notice::ACTION_EMAIL` being applied to the notices of
|
|
that type.
|
|
|
|
* - :bro:see:`Notice::alarmed_types`
|
|
- Adding a :bro:see:`Notice::Type` to this set results in
|
|
:bro:see:`Notice::ACTION_ALARM` being applied to the notices of
|
|
that type.
|
|
|
|
* - :bro:see:`Notice::not_suppressed_types`
|
|
- Adding a :bro:see:`Notice::Type` to this set results in that notice
|
|
no longer undergoing the normal notice suppression that would
|
|
take place. Be careful when using this in production it could
|
|
result in a dramatic increase in the number of notices being
|
|
processed.
|
|
|
|
* - :bro:see:`Notice::type_suppression_intervals`
|
|
- This is a table indexed on :bro:see:`Notice::Type` and yielding an
|
|
interval. It can be used as an easy way to extend the default
|
|
suppression interval for an entire :bro:see:`Notice::Type`
|
|
without having to create a whole :bro:see:`Notice::policy` entry
|
|
and setting the ``$suppress_for`` field.
|
|
|
|
Raising Notices
|
|
---------------
|
|
|
|
A script should raise a notice for any occurrence that a user may want
|
|
to be notified about or take action on. For example, whenever the base
|
|
SSH analysis scripts sees enough failed logins to a given host, it
|
|
raises a notice of the type :bro:see:`SSH::Password_Guessing`. The code
|
|
in the base SSH analysis script which raises the notice looks like this:
|
|
|
|
.. code:: bro
|
|
|
|
NOTICE([$note=Password_Guessing,
|
|
$msg=fmt("%s appears to be guessing SSH passwords (seen in %d connections).", key$host, r$num),
|
|
$src=key$host,
|
|
$identifier=cat(key$host)]);
|
|
|
|
:bro:see:`NOTICE` is a normal function in the global namespace which
|
|
wraps a function within the ``Notice`` namespace. It takes a single
|
|
argument of the :bro:see:`Notice::Info` record type. The most common
|
|
fields used when raising notices are described in the following table:
|
|
|
|
.. list-table::
|
|
:widths: 32 40
|
|
:header-rows: 1
|
|
|
|
* - Field name
|
|
- Description
|
|
|
|
* - ``$note``
|
|
- This field is required and is an enum value which represents the
|
|
notice type.
|
|
|
|
* - ``$msg``
|
|
- This is a human readable message which is meant to provide more
|
|
information about this particular instance of the notice type.
|
|
|
|
* - ``$sub``
|
|
- This is a sub-message meant for human readability but will
|
|
frequently also be used to contain data meant to be matched with the
|
|
``Notice::policy``.
|
|
|
|
* - ``$conn``
|
|
- If a connection record is available when the notice is being raised
|
|
and the notice represents some attribute of the connection, then the
|
|
connection record can be given here. Other fields such as ``$id`` and
|
|
``$src`` will automatically be populated from this value.
|
|
|
|
* - ``$id``
|
|
- If a conn_id record is available when the notice is being raised and
|
|
the notice represents some attribute of the connection, then the
|
|
connection can be given here. Other fields such as ``$src`` will
|
|
automatically be populated from this value.
|
|
|
|
* - ``$src``
|
|
- If the notice represents an attribute of a single host then it's
|
|
possible that only this field should be filled out to represent the
|
|
host that is being "noticed".
|
|
|
|
* - ``$n``
|
|
- This normally represents a number if the notice has to do with some
|
|
number. It's most frequently used for numeric tests in the
|
|
``Notice::policy`` for making policy decisions.
|
|
|
|
* - ``$identifier``
|
|
- This represents a unique identifier for this notice. This field is
|
|
described in more detail in the `Automated Suppression`_ section.
|
|
|
|
* - ``$suppress_for``
|
|
- This field can be set if there is a natural suppression interval for
|
|
the notice that may be different than the default value. The
|
|
value set to this field can also be modified by a user's
|
|
:bro:see:`Notice::policy` so the value is not set permanently
|
|
and unchangeably.
|
|
|
|
When writing Bro scripts which raise notices, some thought should be given to
|
|
what the notice represents and what data should be provided to give a consumer
|
|
of the notice the best information about the notice. If the notice is
|
|
representative of many connections and is an attribute of a host (e.g. a
|
|
scanning host) it probably makes most sense to fill out the ``$src`` field and
|
|
not give a connection or conn_id. If a notice is representative of a
|
|
connection attribute (e.g. an apparent SSH login) then it makes sense to fill
|
|
out either ``$conn`` or ``$id`` based on the data that is available when the
|
|
notice is raised. Using care when inserting data into a notice will make later
|
|
analysis easier when only the data to fully represent the occurrence that
|
|
raised the notice is available. If complete connection information is
|
|
available when an SSL server certificate is expiring, the logs will be very
|
|
confusing because the connection that the certificate was detected on is a
|
|
side topic to the fact that an expired certificate was detected. It's possible
|
|
in many cases that two or more separate notices may need to be generated. As
|
|
an example, one could be for the detection of the expired SSL certificate and
|
|
another could be for if the client decided to go ahead with the connection
|
|
neglecting the expired certificate.
|
|
|
|
Automated Suppression
|
|
---------------------
|
|
|
|
The notice framework supports suppression for notices if the author of the
|
|
script that is generating the notice has indicated to the notice framework how
|
|
to identify notices that are intrinsically the same. Identification of these
|
|
"intrinsically duplicate" notices is implemented with an optional field in
|
|
:bro:see:`Notice::Info` records named ``$identifier`` which is a simple string.
|
|
If the ``$identifier`` and ``$note`` fields are the same for two notices, the
|
|
notice framework actually considers them to be the same thing and can use that
|
|
information to suppress duplicates for a configurable period of time.
|
|
|
|
.. note::
|
|
|
|
If the ``$identifier`` is left out of a notice, no notice suppression
|
|
takes place due to the framework's inability to identify duplicates. This
|
|
could be completely legitimate usage if no notices could ever be
|
|
considered to be duplicates.
|
|
|
|
The ``$identifier`` field is typically comprised of several pieces of
|
|
data related to the notice that when combined represent a unique
|
|
instance of that notice. Here is an example of the script
|
|
:doc:`/scripts/policy/protocols/ssl/validate-certs.bro` raising a notice
|
|
for session negotiations where the certificate or certificate chain did
|
|
not validate successfully against the available certificate authority
|
|
certificates.
|
|
|
|
.. code:: bro
|
|
|
|
NOTICE([$note=SSL::Invalid_Server_Cert,
|
|
$msg=fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status),
|
|
$sub=c$ssl$subject,
|
|
$conn=c,
|
|
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$validation_status,c$ssl$cert_hash)]);
|
|
|
|
In the above example you can see that the ``$identifier`` field contains a
|
|
string that is built from the responder IP address and port, the validation
|
|
status message, and the MD5 sum of the server certificate. Those fields in
|
|
particular are chosen because different SSL certificates could be seen on any
|
|
port of a host, certificates could fail validation for different reasons, and
|
|
multiple server certificates could be used on that combination of IP address
|
|
and port with the ``server_name`` SSL extension (explaining the addition of
|
|
the MD5 sum of the certificate). The result is that if a certificate fails
|
|
validation and all four pieces of data match (IP address, port, validation
|
|
status, and certificate hash) that particular notice won't be raised again for
|
|
the default suppression period.
|
|
|
|
Setting the ``$identifier`` field is left to those raising notices because
|
|
it's assumed that the script author who is raising the notice understands the
|
|
full problem set and edge cases of the notice which may not be readily
|
|
apparent to users. If users don't want the suppression to take place or simply
|
|
want a different interval, they can set a notice's suppression
|
|
interval to ``0secs`` or delete the value from the ``$identifier`` field in
|
|
a :bro:see:`Notice::policy` hook.
|
|
|
|
|
|
Extending Notice Framework
|
|
--------------------------
|
|
|
|
There are a couple of mechanism currently for extending the notice framework
|
|
and adding new capability.
|
|
|
|
Extending Notice Emails
|
|
***********************
|
|
|
|
If there is extra information that you would like to add to emails, that is
|
|
possible to add by writing :bro:see:`Notice::policy` hooks.
|
|
|
|
There is a field in the :bro:see:`Notice::Info` record named
|
|
``$email_body_sections`` which will be included verbatim when email is being
|
|
sent. An example of including some information from an HTTP request is
|
|
included below.
|
|
|
|
.. code:: bro
|
|
|
|
hook Notice::policy(n: Notice::Info)
|
|
{
|
|
if ( n?$conn && n$conn?$http && n$conn$http?$host )
|
|
n$email_body_sections[|n$email_body_sections|] = fmt("HTTP host header: %s", n$conn$http$host);
|
|
}
|
|
|
|
|
|
Cluster Considerations
|
|
----------------------
|
|
|
|
As a user/developer of Bro, the main cluster concern with the notice framework
|
|
is understanding what runs where. When a notice is generated on a worker, the
|
|
worker checks to see if the notice should be suppressed based on information
|
|
locally maintained in the worker process. If it's not being
|
|
suppressed, the worker forwards the notice directly to the manager and does no more
|
|
local processing. The manager then runs the :bro:see:`Notice::policy` hook and
|
|
executes all of the actions determined to be run.
|
|
|