Reference Manual: Signatures
From BroWiki
Contents |
Overview
In addition to the policy language, Bro provides another language which is specifically designed to define signatures. Signatures precisely describe how network traffic looks for certain, well-known attacks. As soon as a attack described by a signature is recognized, Bro may generate an event for this signature match which can then be analyzed by a policy script. To define signatures, Bro's language provides several powerful constructs like regular expressions @ and dependencies between multiple signatures.
Signatures are independent of Bro's policy scripts and, therefore, are put
into their own file(s). There two ways to specify which files contain
signatures: By using the -s flag when you invoke Bro, or by extending
the Bro variable signatures_files using the += operator.
If a signature file is given without a path, it is searched along
. The default extension of the file name is .sig
which Bro appends automatically.
Signature language
Each individual signature has the format
signature id { attribute-set } }
id is an unique label for the signature. There are two types of
attributes: conditions and actions. The conditions define
when the signature matches, while the actions declare what to do in the case of a match. Conditions can be further divided into
four types: header, content, dependency, and
context. We will discuss these in more detail in the following
subsections.
This is an example of a signature:
signature formmail-cve-1999-0172 {
ip-proto == tcp
dst-ip == 1.2.0.0/16
dst-port = 80
http /.*formmail.*\?.*recipient=[^&]*[;|]/
event "formmail shell command"
}
Conditions
Header conditions
Header conditions limit the applicability of the signature to a subset of traffic that contains matching packet headers. For TCP, this match is performed only for the first packet of a connection. For other protocols, it is done on each individual packet. There are pre-defined header conditions for some of the most used header fields:
- address-list
Destination address of IP packet (may include CIDR masks for specifying networks)
- integer-list
Destination port of TCP or UDP packet
- protocol-list
IP protocol; protocol may be tcp, udp, or icmp.
- address-list
Source address of IP packet (may include CIDR masks for specifying networks)
- integer-list
Source port of TCP or UDP packet
comp is one of ==, !=, <,
<=, >, >=. All lists are comma-separated values of
the given type which are sequentially compared against the corresponding
header field. If at least one of the comparisons evaluates to true, the whole
header condition matches (exception: if comp is !=, the header
condition only matches if all values differ). address is an
dotted IP address optionally followed by a CIDR/mask to define a subnet
instead of an individual address. protocol is either one of ip,
tcp, udp and icmp, or an integer.
In addition to this pre-defined short-cuts, a general header condition can be defined either as
header proto[offset:size] comp value-list}
or as
header proto[offset:size] & integer comp value-list}
This compares the value found at the given position of the packet header with
a list of values. offset defines the position of the value within
the header of the protocol defined by proto (which can ip, tcp,
udp oricmp. size is either 1, 2, or 4 and specifies the
value to have a size of this many bytes. If the optimal
& integer is given, the packet's value is first masked
with the integer before it is compared to the value-list. comp
is one of ==, !=, <,
<=, >, >=. value-list is a list of
comma-separated integers similar to those described above. The integers within
the list may be followed by an additional /mask where
mask is a value from 0 to 32. This corresponds to the CIDR notation
for netmasks and is translated into a corresponding bitmask which is applied
to the packet's value prior to the comparison (similar to the optional
& integer).
Putting all together, this is an example which is equivalent to
dst-ip == 1.2.3.4/16, 5.6.7.8/24:
header ip[16:4] == 1.2.3.4/16, 5.6.7.8/24
Content conditions
Content conditions are defined by regular expressions. We differentiate two
kinds of content conditions: first, the expression may be declared with the
payload statement, in which case it is matched against the raw
payload of a connection (for reassembled TCP streams) or of a each packet.
Alternatively, it may be prefixed with an analyzer-specific label, in which
case the expression is matched against the data as extracted by the
corresponding analyzer.
A payload condition has the form
payload /regular expression/
Currently, the following analyzer-specific content conditions are defined (note that
the corresponding analyzer has to be activated by loading its policy script):
-
http-request /regular expression/
The regular expression is matched against decoded URIs of the HTTP requests.
-
http-request-header /regular expression/
The regular expression is matched against client-side HTTP headers.
-
http-reply-header /regular expression/
The regular expression is matched against server-side HTTP headers.
-
ftp /regular expression/
The regular expression is matched against the command line input of FTP sessions.
-
finger /regular expression/
The regular expression is matched against the finger requests.
For example, http /(etc/(passwd|shadow)/ matches any URI
containing either etc/passwd or etc/shadow.
Dependency conditions
To define dependencies between different signatures, there are two conditions:
- requires-signature [! id]
Defines the current signature to match only if the signature given by id
matches for the same connection. Using `!' negates the
condition: The current signature only matches if id does not
match for the same connection (this decision is necessarily deferred until
the connection terminates).
- requires-reverse-signature [! id]
Similar to requires-signature, but id has to match for the
other direction of the same connections than the current signature.
This allows to model the notion of requests and replies.
Context conditions
Context conditions pass the match decision on to various other components of Bro. They are only evaluated if all other conditions have already matched. The following context conditions are defined:
-
eval policy_function
The given policy function is called and has to return a boolean
confirming the match. If F is returned, no signature
match is going to be triggered. The function has to be of type
function cond(state: signature_state, data: string): bool.
Here, content may contain the most recent content chunk
available at the time the signature was matched. If no such chunk is
available, content will be the empty string "".
signature_state is defined as follows:
type signature_state: record {
id: string; # ID of the signature
conn: connection; # Current connection
is_orig: bool; # True if current endpoint is originator
payload_size: count; # Payload size of the first pkt of curr. endpoint
};
-
ip-options
Not implemented currently.
-
payload-size comp_integer
Compares the integer to the size of the payload of a packet. For reassembled TCP streams, the integer is compared to the size of the first in-order payload chunk. Note that the latter is not well defined.
-
same-ip
Evaluates to true if the source address of the IP packets equals its destination address.
-
tcp-state state-list
Poses restrictions on the current TCP state of the connection.
state-list is a comma-separated list of established
(the three-way handshake has already been performed),
originator (the current data is send by the originator of the
connection), and responder (the current data is send by the
responder of the connection).
Actions
Actions define what to do if a signature matches. Currently, there is only one
action defined: event string raises a signature_match
event. The event handler has the following type:
event signature_match(state: signature_state, msg: string, data: string)
See \f{fig:signature-state} for a description of signature_state. The given string
is passed as msg, and data is the current part of the payload that
has eventually lead to the signature match (this may be empty for signatures without
content conditions).
snort2bro
The open-source IDS Snort provides an extensive library of signatures. The Python script {snort2bro} converts Snort's signature into Bro signatures. Due to different internal architectures of Bro and Snort, it is not always possible to keep the exact semantics of Snort's signatures, but most of the time it works very well.
To convert Snort signatures into Bro's format, snort2bro needs a
workable Snort configuration file (snort.cfg) which, in particular,
defines the variables used in the Snort signatures (usually things like
$EXTERNAL_NET or $HTTP_SERVERS). The conversion is
performed by calling snort2bro [-I dir] snort.cfg where the
directory optionally given by -I contains the files imported by
Snort's include statement. The converted signature set is written to
standard output and may be redirected to a file. This file can then be
evaluated by Bro using the -s flag or the signatures_files
variable.
Deficiency: snort2bro does not know about some of the newer Snort signature options and ignores them (but it gives a warning).
