From 33e96c508768b6d4b7c519d1ed9925c23efe4d96 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 2 Dec 2015 07:12:02 -0600 Subject: [PATCH] doc: fast-pattern --- doc/sphinx/fast-pattern-explained.rst | 186 +++++++++++++++++++++++ doc/sphinx/fast-pattern.rst | 61 +++++++- doc/sphinx/fast-pattern/fast_pattern.png | Bin 0 -> 11808 bytes 3 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 doc/sphinx/fast-pattern-explained.rst create mode 100644 doc/sphinx/fast-pattern/fast_pattern.png diff --git a/doc/sphinx/fast-pattern-explained.rst b/doc/sphinx/fast-pattern-explained.rst new file mode 100644 index 0000000000..86654e5954 --- /dev/null +++ b/doc/sphinx/fast-pattern-explained.rst @@ -0,0 +1,186 @@ +Suricata Fast Pattern Determination Explained +============================================= + +If the 'fast_pattern' keyword is explicitly set in a rule, Suricata +will use that as the fast pattern match. The 'fast_pattern' keyword +can only be set once per rule. If 'fast_pattern' is not set, Suricata +automatically determines the content to use as the fast pattern match. + +The following explains the logic Suricata uses to automatically +determine the fast pattern match to use. + +Be aware that if there are positive (i.e. non-negated) content +matches, then negated content matches are ignored for fast pattern +determination. Otherwise, negated content matches are considered. + +Suricata 1.1.x - 1.4.x +---------------------- + +#. The longest (in terms of character/byte length) content match is + used as the fast pattern match. + +#. If multiple content matches qualify for the longest length, the one + with the highest character/byte diversity score ("Pattern + Strength") is used as the fast pattern match. See :ref:`Appendix C + ` for details on the algorithm + used to determine Pattern Strength. + +#. If multiple content matches qualify for the longest length and have + the same highest Pattern Strength, the buffer that has the *lower + "list_id"* is used as the fast pattern match. See :ref:`Appendix A + ` for the list_id of each + buffers/list. + +#. If multiple content matches qualify for the longest length and have + the same highest Pattern Strength, and have the same list_id + (i.e. are looking in the same buffer), then the one that comes + first (from left-to-right) in the rule is used as the fast pattern + match. + +It is worth noting that for content matches that have the same length +and Pattern Strength, regular 'content' matches take precedence over +matches that use the 'http_*' buffers. + +Suricata 2.0.x +-------------- + +#. Suricata first identifies all content matches that have the highest + "priority" that are used in the signature. The priority is based + off of the buffer being matched on and generally 'http_*' buffers + have a higher priority (lower number is higher priority). See + :ref:`Appendix B ` for details + on which buffers have what priority. +#. Within the content matches identified in step 1 (the highest + priority content matches), the longest (in terms of character/byte + length) content match is used as the fast pattern match. +#. If multiple content matches have the same highest priority and + qualify for the longest length, the one with the highest + character/byte diversity score ("Pattern Strength") is used as the + fast pattern match. See :ref:`Appendix C + ` for details on the algorithm + used to determine Pattern Strength. +#. If multiple content matches have the same highest priority, qualify + for the longest length, and the same highest Pattern Strength, the + buffer ("list_id") that was *registered last* is used as the fast + pattern match. See :ref:`Appendix B + ` for the registration order of + the different buffers/lists. +#. If multiple content matches have the same highest priority, qualify + for the longest length, the same highest Pattern Strength, and have + the same list_id (i.e. are looking in the same buffer), then the + one that comes first (from left-to-right) in the rule is used as + the fast pattern match. + +It is worth noting that for content matches that have the same +priority, length, and Pattern Strength, 'http_stat_msg', +'http_stat_code', and 'http_method' take precedence over regular +'content' matches. + +Appendices +---------- + +.. _fast-pattern-explained-appendix-a: + +Appendix A - Buffers, list_id values, and Registration Order for Suricata 1.3.4 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This should be pretty much the same for Suricata 1.1.x - 1.4.x. + +======= ============================== ======================== ================== +list_id Content Modifier Keyword Buffer Name Registration Order +======= ============================== ======================== ================== +1 (regular content match) DETECT_SM_LIST_PMATCH 1 (first) +2 http_uri DETECT_SM_LIST_UMATCH 2 +6 http_client_body DETECT_SM_LIST_HCBDMATCH 3 +7 http_server_body DETECT_SM_LIST_HSBDMATCH 4 +8 http_header DETECT_SM_LIST_HHDMATCH 5 +9 http_raw_header DETECT_SM_LIST_HRHDMATCH 6 +10 http_method DETECT_SM_LIST_HMDMATCH 7 +11 http_cookie DETECT_SM_LIST_HCDMATCH 8 +12 http_raw_uri DETECT_SM_LIST_HRUDMATCH 9 +13 http_stat_msg DETECT_SM_LIST_HSMDMATCH 10 +14 http_stat_code DETECT_SM_LIST_HSCDMATCH 11 +15 http_user_agent DETECT_SM_LIST_HUADMATCH 12 (last) +======= ============================== ======================== ================== + +Note: registration order doesn't matter when it comes to determining the fast pattern match for Suricata 1.3.4 but list_id value does. + +.. _fast-pattern-explained-appendix-b: + +Appendix B - Buffers, list_id values, Priorities, and Registration Order for Suricata 2.0.7 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This should be pretty much the same for Suricata 2.0.x. + +========================================== ================== ============================== ============================= ======= +Priority (lower number is higher priority) Registration Order Content Modifier Keyword Buffer Name list_id +========================================== ================== ============================== ============================= ======= +3 11 (regular content match) DETECT_SM_LIST_PMATCH 1 +3 12 http_method DETECT_SM_LIST_HMDMATCH 12 +3 13 http_stat_code DETECT_SM_LIST_HSCDMATCH 9 +3 14 http_stat_msg DETECT_SM_LIST_HSMDMATCH 8 +2 1 (first) http_client_body DETECT_SM_LIST_HCBDMATCH 4 +2 2 http_server_body DETECT_SM_LIST_HSBDMATCH 5 +2 3 http_header DETECT_SM_LIST_HHDMATCH 6 +2 4 http_raw_header DETECT_SM_LIST_HRHDMATCH 7 +2 5 http_uri DETECT_SM_LIST_UMATCH 2 +2 6 http_raw_uri DETECT_SM_LIST_HRUDMATCH 3 +2 7 http_host DETECT_SM_LIST_HHHDMATCH 10 +2 8 http_raw_host DETECT_SM_LIST_HRHHDMATCH 11 +2 9 http_cookie DETECT_SM_LIST_HCDMATCH 13 +2 10 http_user_agent DETECT_SM_LIST_HUADMATCH 14 +2 15 (last) dns_query DETECT_SM_LIST_DNSQUERY_MATCH 20 +========================================== ================== ============================== ============================= ======= + +Note: list_id value doesn't matter when it comes to determining the +fast pattern match for Suricata 2.0.7 but registration order does. + +.. _fast-pattern-explained-appendix-c: + +Appendix C - Pattern Strength Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +From detect-engine-mpm.c. Basically the Pattern Strength "score" +starts at zero and looks at each character/byte in the passed in byte +array from left to right. If the character/byte has not been seen +before in the array, it adds 3 to the score if it is an alpha +character; else it adds 4 to the score if it is a printable character, +0x00, 0x01, or 0xFF; else it adds 6 to the score. If the +character/byte has been seen before it adds 1 to the score. The final +score is returned. + +.. code-block:: c + + /** \brief Predict a strength value for patterns + * + * Patterns with high character diversity score higher. + * Alpha chars score not so high + * Other printable + a few common codes a little higher + * Everything else highest. + * Longer patterns score better than short patters. + * + * \param pat pattern + * \param patlen length of the patternn + * + * \retval s pattern score + */ + uint32_t PatternStrength(uint8_t *pat, uint16_t patlen) { + uint8_t a[256]; + memset(&a, 0 ,sizeof(a)); + uint32_t s = 0; + uint16_t u = 0; + for (u = 0; u < patlen; u++) { + if (a[pat[u]] == 0) { + if (isalpha(pat[u])) + s += 3; + else if (isprint(pat[u]) || pat[u] == 0x00 || pat[u] == 0x01 || pat[u] == 0xFF) + s += 4; + else + s += 6; + a[pat[u]] = 1; + } else { + s++; + } + } + return s; + } diff --git a/doc/sphinx/fast-pattern.rst b/doc/sphinx/fast-pattern.rst index 17be899879..c32b79844f 100644 --- a/doc/sphinx/fast-pattern.rst +++ b/doc/sphinx/fast-pattern.rst @@ -1,4 +1,63 @@ Fast Pattern ============ -Just a place holder now to demontrate linking. +.. toctree:: + + fast-pattern-explained + +Only one content of a signature will be used in the Multi Pattern +Matcher (MPM). If there are multiple contents, then Suricata uses the +'strongest' content. This means a combination of length, how varied a +content is, and what buffer it is looking in. Generally, the longer +and more varied the better. For full details on how Suricata +determines the fast pattern match, see :doc:`fast-pattern-explained`. + +Sometimes a signature writer concludes he wants Suricata to use +another content than it does by default. + +For instance:: + + User-agent: Mozilla/5.0 Badness; + + content:”User-Agent|3A|”; + content:”Badness”; distance:0; + +In this example you see the first content is longer and more varied +than the second one, so you know Suricata will use this content for +the MPM. Because 'User-Agent:' will be a match very often, and +'Badness' appears less often in network traffic, you can make Suricata +use the second content by using 'fast_pattern'. + +:: + + content:”User-Agent|3A|”; + content:”Badness”; distance:0; fast_pattern; + +The keyword fast_pattern modifies the content previous to it. + +.. image:: fast-pattern/fast_pattern.png + +Fast-pattern can also be combined with all previous mentioned +keywords, and all mentioned HTTP-modifiers. + +fast_pattern:only +----------------- + +Sometimes a signature contains only one content. In that case it is +not necessary Suricata will check it any further after a match has +been found in MPM. If there is only one content, the whole signature +matches. Suricata notices this automatically. In some signatures this +is still indicated with 'fast_pattern:only;'. Although Suricata does +not need fast_pattern:only, it does support it. + +Fast_pattern: 'chop' +-------------------- + +If you do not want the MPM to use the whole content, you can use +fast_pattern 'chop'. + +For example:: + + content: “aaaaaaaaabc”; fast_pattern:8,4; + +This way, MPM uses only the last four characters. diff --git a/doc/sphinx/fast-pattern/fast_pattern.png b/doc/sphinx/fast-pattern/fast_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..97163a50ab0ccce1ef7f13b5df6dddb1c52d7f43 GIT binary patch literal 11808 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