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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 g^8YdAv~c|yCG5kYTSbE*AK)KU6g1^)