doc: describe transform_result and the absent options

Document transform_result and both forms of absent, the transforms
that can signal an error, how must_error and error_or treat bodies and
files inspected in parts, and the rule-load constraints. Note the
behavior changes in upgrade.rst: absent's mode string in the analyzer,
absent: or_else rejected after a transform that can fail, gunzip and
zlib_deflate keeping their input on failure, pcrexform's empty capture,
and from_base64's bytes handling.

Issue: 7313
Issue: 7114
pull/16505/head
Jeff Lucovsky 9 months ago
parent 47e5ae0f3c
commit e45e570f59

@ -337,26 +337,146 @@ These differences are also discussed in :doc:`differences-from-snort`.
A discussion of this difference can be found at
https://redmine.openinfosecfoundation.org/issues/8031
.. _rules-keyword-absent:
absent
------
The keyword ``absent`` checks that a sticky buffer does not exist.
It can be used without any argument to match only on absent buffer :
The keyword ``absent`` checks that a sticky buffer does not exist. It takes
either no argument (match only on absent buffer) or ``or_else`` (match on
absent buffer or fall through to subsequent keywords).
To match on the result of a transform (``must_succeed``, ``must_error``,
``error_or``), see :ref:`rules-keyword-transform-result`.
absent (no argument)
~~~~~~~~~~~~~~~~~~~~
Example of ``absent`` in a rule:
Match only when the buffer is absent (NULL). No other keyword is allowed
on the same buffer.
.. container:: example-rule
alert http any any -> any any (msg:"HTTP request without referer"; :example-rule-emphasis:`http.referer; absent;` sid:1; rev:1;)
absent: or_else
~~~~~~~~~~~~~~~
It can take an argument "or_else" to match on absent buffer or on what comes next such as negated content, for instance :
Match when the buffer is absent (NULL) OR when subsequent keywords match,
for example a negated content.
.. container:: example-rule
alert http any any -> any any (msg:"HTTP request without referer"; :example-rule-emphasis:`http.referer; absent: or_else;` content: !"abc"; sid:1; rev:1;)
alert http any any -> any any (msg:"HTTP request without referer"; \
:example-rule-emphasis:`http.referer; absent: or_else;` content: !"abc"; sid:1; rev:1;)
``or_else`` cannot be used on a buffer with a transform that can signal
an error (``from_base64``, ``pcrexform``, ``gunzip``, ``zlib_deflate``);
use ``transform_result: error_or`` instead, which also matches an absent
buffer.
For files (i.e. ``file.data``), absent means there are no files in the
transaction.
.. _rules-keyword-transform-result:
transform_result
----------------
``transform_result`` matches on the outcome of a preceding transform.
When a transform that can signal an error cannot complete, it sets an
error flag on the inspection buffer and leaves its input unchanged, so
later transforms and keywords see the data the failed transform received.
When the failing transform is the first on the buffer, that is the
original buffer content. ``transform_result`` reacts to that flag.
The keyword takes one of ``must_succeed``, ``must_error``, or ``error_or``
and requires a transform that can signal errors on the same buffer:
``from_base64``, ``pcrexform``, ``gunzip`` or ``zlib_deflate``. See
:ref:`transform-error-signaling`.
transform_result: must_succeed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Do not match when the transform signals an error, so the keywords after
it only see decoded or extracted data. This keeps a content match from
running against the input bytes after a failed ``from_base64``. Content
after ``must_succeed`` can be the fast pattern. It does not match when the
buffer is absent.
.. container:: example-rule
alert http any any -> any any (msg:"Detect malware only in decoded base64"; file.data; \
from_base64; :example-rule-emphasis:`transform_result: must_succeed;` \
content:"malware"; sid:1; rev:1;)
transform_result: must_error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Match only when the transform signals an error. No other keyword is
allowed on the same buffer, but other sticky buffers can follow to combine
error detection with further matching conditions. It does not match when
the buffer is absent.
.. container:: example-rule
alert http any any -> any any (msg:"Detect base64 decode failure"; file.data; \
from_base64; :example-rule-emphasis:`transform_result: must_error;` sid:1; rev:1;)
Example combining with a second sticky buffer::
alert http any any -> any any (msg:"base64 error and bad host"; \
http.uri; from_base64: mode strict; transform_result: must_error; \
http.host; content:"suspicious.example.com"; sid:1;)
transform_result: error_or
~~~~~~~~~~~~~~~~~~~~~~~~~~
Match when the transform signals an error, when the buffer is absent, or
when the keywords after it match. It takes the place of ``absent: or_else``
on a buffer with a transform that can signal an error.
.. container:: example-rule
For files (i.e ``file.data``), absent means there are no files in the transaction.
alert http any any -> any any (msg:"Detect base64 decode error or malicious content"; \
file.data; from_base64; :example-rule-emphasis:`transform_result: error_or;` \
content:"malicious"; sid:1; rev:1;)
Bodies and files inspected in parts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``file.data``, ``http.response_body`` and ``http.request_body`` inspect a
large body or file in windows and run the transforms on each window. A
window can cut the data where a transform cannot handle it: a strict
``from_base64`` decode fails on a window that does not start on a 4-byte
boundary. ``must_error`` and ``error_or`` therefore only react to an error
when the whole body or file is inspected at once. ``must_succeed`` still
keeps content from matching a window whose transform failed.
Rule-writing constraints
~~~~~~~~~~~~~~~~~~~~~~~~
The following constraints apply to both ``absent`` and
``transform_result`` and are enforced at rule-load time:
* The keyword must come first on its buffer, after the sticky buffer
keyword and any transforms. A transform placed after the keyword starts
a new buffer; with ``absent: or_else`` or ``transform_result`` the rule
then fails to load.
* Only one ``absent`` or ``transform_result`` is allowed per buffer, and
the two cannot be mixed on the same buffer.
* ``fast_pattern`` cannot be used on a buffer that carries ``absent``,
``transform_result: must_error`` or ``transform_result: error_or``.
* ``absent`` and ``transform_result`` do not work on frame buffers.
* ``transform_result`` requires a transform that can signal errors on the
same buffer.
* ``absent: or_else`` cannot be used on such a buffer; use
``transform_result: error_or`` instead.
* ``absent: or_else``, ``transform_result: error_or`` and
``transform_result: must_succeed`` need at least one other keyword after
them on the buffer.
* ``must_error`` cannot be combined with other keywords on the same
buffer, but other sticky buffers can follow.
bsize
-----

@ -167,6 +167,48 @@ on.
alert http any any -> any any (http_request_line; to_sha256; \
content:"\|54A9 7A8A B09C 1B81 3725 2214 51D3 F997 F015 9DD7 049E E5AD CED3 945A FC79 7401\|"; sid:1;)
.. _transform-error-signaling:
Error-Signaling Transforms
--------------------------
Some transforms can signal an error when they cannot complete their operation. The transform
then sets an error flag on the inspection buffer and leaves its input unchanged, so later
transforms and keywords see the data the failed transform received. When the failing
transform is the first on the buffer, that is the original buffer content.
The following transforms can signal errors. Each also signals one when it cannot get memory
for its result.
- :ref:`pcrexform <pcrexform>` signals an error when the regular expression does not match
or its capture group cannot be extracted. An empty buffer is left as it is, and a capture
of nothing leaves an empty buffer; neither is an error.
- :ref:`from_base64 <from_base64>` signals an error when a non-empty input is not decoded:
``offset`` is at or past the end of the buffer, ``bytes`` is larger than the input left
after ``offset``, or decoding produces nothing. Invalid bytes in the input are not an error
in ``rfc2045`` mode, which skips them, or in ``rfc4648`` mode when something decodes before
them; in ``strict`` mode they are.
- :ref:`gunzip <gunzip>` and :ref:`zlib_deflate <zlib_deflate>` signal an error when the
input does not decompress.
The ``transform_result`` keyword matches on the transform outcome:
- ``transform_result: error_or`` matches if the transform signals an error, if the buffer
is absent, or if the keywords after it match.
- ``transform_result: must_error`` matches only when the transform signals an error. No
other keyword is allowed on the same buffer. It does not match when the buffer is absent.
- ``transform_result: must_succeed`` does not match when the transform signals an error, so
the keywords after it only see decoded or extracted data. It does not match when the
buffer is absent.
On ``file.data``, ``http.response_body`` and ``http.request_body``, a large body or file is
inspected in windows, and ``must_error`` and ``error_or`` only react to an error when the
whole body or file is inspected at once.
See :ref:`rules-keyword-transform-result` for full documentation and examples of each option.
.. _pcrexform:
pcrexform
---------
@ -174,6 +216,9 @@ Takes the buffer, applies the required regular expression, and outputs the *firs
.. note:: this transform requires a mandatory option string containing a regular expression.
If the regular expression does not match the buffer content, the transform signals an error
and leaves its input unchanged. See :ref:`transform-error-signaling` for how to
detect or guard against transform failures using ``transform_result``.
This example alerts if ``http.request_line`` contains ``/dropper.php``:
@ -182,6 +227,20 @@ This example alerts if ``http.request_line`` contains ``/dropper.php``:
alert http any any -> any any (msg:"HTTP with pcrexform"; http.request_line; \
pcrexform:"[a-zA-Z]+\s+(.*)\s+HTTP"; content:"/dropper.php"; sid:1;)
This example relies on the input being kept when ``pcrexform`` fails. It reduces a name with
a subdomain to its last two labels (``www.example.com`` becomes ``example.com``); a name with
no subdomain does not match the expression and is kept as it is. Either way the buffer is
exactly ``example.com``::
alert dns any any -> any any (msg:"example.com or a subdomain of it"; \
dns.query; pcrexform:"\.([^\.]+\.[^\.]+)$"; content:"example.com"; \
startswith; endswith; sid:1;)
This example alerts if ``pcrexform`` fails to match (e.g., unexpected request line format)::
alert http any any -> any any (msg:"pcrexform match failure"; http.request_line; \
pcrexform:"[a-zA-Z]+\s+(.*)\s+HTTP"; transform_result: must_error; sid:2;)
url_decode
----------
@ -303,6 +362,13 @@ line breaks, and any non base64 alphabet.
Mode ``strict`` will fail if an invalid character is found in the encoded bytes.
.. note:: When a non-empty input is not decoded, ``from_base64`` signals an error and leaves
its input unchanged. That happens when ``offset`` is at or past the end
of the buffer, when ``bytes`` is larger than the input left after ``offset``, or when
decoding produces nothing (for example, ``strict`` mode rejecting a character outside the
base64 alphabet). See :ref:`transform-error-signaling` for how to detect or guard
against decode failures using ``transform_result``.
The following examples will alert when the buffer contents match (see the
last ``content`` value for the expected strings).
@ -431,6 +497,8 @@ the buffer.
return string.upper(sub), bytes
end
.. _gunzip:
gunzip
------
@ -444,6 +512,9 @@ If the decompressed data were to be larger than max-size,
the transform will decompress data up to max-size.
Value 0 is forbidden for max-size (there is no unlimited value).
If the input is not gzip data, the transform signals an error and leaves its input
unchanged. See :ref:`transform-error-signaling`.
This example alerts if ``http.uri`` contains base64-encoded gzipped value:
.. container:: example-rule
@ -454,6 +525,8 @@ This example alerts if ``http.uri`` contains base64-encoded gzipped value:
gunzip: max-size 1024; content:"This is compressed then base64-encoded"; startswith; endswith;
sid:2; rev:1;)
.. _zlib_deflate:
zlib_deflate
------------
@ -467,6 +540,9 @@ If the decompressed data were to be larger than max-size,
the transform will decompress data up to max-size.
Value 0 is forbidden for max-size (there is no unlimited value).
If the input is not zlib data, the transform signals an error and leaves its input
unchanged. See :ref:`transform-error-signaling`.
This example alerts if ``http.uri`` contains base64-encoded zlib-compressed value:
.. container:: example-rule

@ -156,6 +156,26 @@ Keyword Changes
See :doc:`rules/ssh-keywords` for the full state and failure
semantics.
- ``absent: or_else`` is now rejected on a buffer whose transform can signal
an error (``from_base64``, ``pcrexform``, ``gunzip``, ``zlib_deflate``),
and such a rule fails to load. Use ``transform_result: error_or`` instead:
it matches when the buffer is absent, when the transform fails, or when
the keywords after it match. See :ref:`rules-keyword-transform-result`.
- ``gunzip`` and ``zlib_deflate`` keep their input when it does not
decompress, and signal an error that ``transform_result`` can match on.
8.0 left an empty buffer, so ``bsize:0`` after a failed decompression no
longer matches; use ``transform_result: must_error`` instead.
- ``pcrexform`` leaves an empty buffer when its capture group matches
nothing. 8.0 could leave the original buffer in place in that case.
- ``from_base64`` decodes the input when ``bytes`` equals the input left after
``offset``; 8.0 left such a buffer undecoded. When ``offset`` is at or past
the end of the buffer, or ``bytes`` is larger than the input left after
``offset``, the buffer is still left undecoded and the transform now signals
an error that ``transform_result`` can match on.
Other Changes
~~~~~~~~~~~~~
- ``engine-analysis`` output has been changed for keywords that now use the generic integers framework
@ -165,6 +185,14 @@ Other Changes
- tcp.seq
- tcp.window
- Engine analysis (``rules.json``): the ``absent`` object emitted per keyword
changed from ``{"or_else": true|false}`` to ``{"mode": "only|or_else"}``.
The new ``transform_result`` keyword emits
``{"transform_result": {"mode": "error_or|must_error|must_succeed"}}``
under the same per-keyword object. Consumers reading the boolean ``or_else``
field should switch to the string ``mode`` field, and recognize both the
``absent`` and ``transform_result`` objects.
- ``dnp3`` has reduced the maximum number of open transactions from
500 down to 32, the maximum number of points per message from
unbounded to 16384, and the maximum number of objects per message

Loading…
Cancel
Save