]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/content: introduce startswith modifier
authorVictor Julien <victor@inliniac.net>
Sat, 16 Dec 2017 10:44:18 +0000 (11:44 +0100)
committerVictor Julien <victor@inliniac.net>
Fri, 19 Jan 2018 09:16:58 +0000 (10:16 +0100)
Add startswith modifier to simplify matching patterns at the start
of a buffer.

Instead of:
    content:"abc"; depth:3;
This enables:
    content:"abc"; startswith;

Especially with longer patterns this makes the intention of the rule
more clear and eases writing the rules.

Internally it's simply a shorthand for 'depth:<pattern len>;'.

Ticket https://redmine.openinfosecfoundation.org/issues/742

doc/userguide/rules/payload-keywords.rst
src/detect-content.h
src/detect-depth.c
src/detect-engine-register.h
src/detect-offset.c
src/tests/detect-engine-content-inspection.c

index e2e4d42a6b037cf6b75563883a27521da8f96689..1e7154025432dbae7ebb7cd7138cdafdcb7294f7 100644 (file)
@@ -126,6 +126,24 @@ Example:
 
 .. image:: payload-keywords/content4.png
 
+startswith
+----------
+
+The ``startswith`` keyword is similar to ``depth``. It takes no arguments
+and must follow a ``content`` keyword. It modifies the ``content`` to match
+exactly at the start of a buffer.
+
+Example::
+
+    content:"GET|20|"; startswith;
+
+``startswith`` is a short hand notation for::
+
+    content:"GET|20|"; depth:4; offset:0;
+
+``startswith`` cannot be mixed with ``depth``, ``offset``, ``within`` or
+``distance`` for the same pattern.
+
 offset
 ------
 
index 7335d279cc738a69946800eee19c1cce757ef09e..3bcf7c83bf5f9dea3c7ff1cd86f17b43205ca521 100644 (file)
@@ -56,6 +56,7 @@
 
 #define DETECT_CONTENT_WITHIN_NEXT      BIT_U32(17)
 #define DETECT_CONTENT_DISTANCE_NEXT    BIT_U32(18)
+#define DETECT_CONTENT_STARTS_WITH      BIT_U32(19)
 /** a relative match to this content is next, used in matching phase */
 #define DETECT_CONTENT_RELATIVE_NEXT    (DETECT_CONTENT_WITHIN_NEXT|DETECT_CONTENT_DISTANCE_NEXT)
 
index 3c8af604625d7ccd9d570956f583f96e95994415..0e7b4007a6e8b1c4b53c89c348173798a021c860 100644 (file)
@@ -42,6 +42,7 @@
 #include "util-debug.h"
 
 static int DetectDepthSetup (DetectEngineCtx *, Signature *, const char *);
+static int DetectStartsWithSetup (DetectEngineCtx *, Signature *, const char *);
 
 void DetectDepthRegister (void)
 {
@@ -52,6 +53,12 @@ void DetectDepthRegister (void)
     sigmatch_table[DETECT_DEPTH].Setup = DetectDepthSetup;
     sigmatch_table[DETECT_DEPTH].Free  = NULL;
     sigmatch_table[DETECT_DEPTH].RegisterTests = NULL;
+
+    sigmatch_table[DETECT_STARTS_WITH].name = "startswith";
+    sigmatch_table[DETECT_STARTS_WITH].desc = "pattern must be at the start of a buffer (same as 'depth:<pattern len>';)";
+    sigmatch_table[DETECT_STARTS_WITH].url = DOC_URL DOC_VERSION "/rules/payload-keywords.html#startswith";
+    sigmatch_table[DETECT_STARTS_WITH].Setup = DetectStartsWithSetup;
+    sigmatch_table[DETECT_STARTS_WITH].flags |= SIGMATCH_NOOPT;
 }
 
 static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, const char *depthstr)
@@ -128,3 +135,56 @@ static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, const char *
  end:
     return ret;
 }
+
+static int DetectStartsWithSetup (DetectEngineCtx *de_ctx, Signature *s, const char *unused)
+{
+    SigMatch *pm = NULL;
+    int ret = -1;
+
+    /* retrieve the sm to apply the depth against */
+    pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
+    if (pm == NULL) {
+        SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "startswith needs a "
+                   "preceding content option");
+        goto end;
+    }
+
+    /* verify other conditions. */
+    DetectContentData *cd = (DetectContentData *)pm->ctx;
+
+    if (cd->flags & DETECT_CONTENT_DEPTH) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple "
+                "depth/startswith settings for the same content");
+        goto end;
+    }
+    if ((cd->flags & DETECT_CONTENT_WITHIN) || (cd->flags & DETECT_CONTENT_DISTANCE)) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative "
+                   "keyword like within/distance with a absolute "
+                   "relative keyword like depth/offset for the same "
+                   "content.");
+        goto end;
+    }
+    if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+                   "negated keyword set along with a fast_pattern");
+        goto end;
+    }
+    if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative "
+                   "keyword set along with a fast_pattern:only;");
+        goto end;
+    }
+    if (cd->flags & DETECT_CONTENT_OFFSET) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't mix offset "
+                   "with startswith");
+        goto end;
+    }
+
+    cd->depth = cd->content_len;
+    cd->flags |= DETECT_CONTENT_DEPTH;
+    cd->flags |= DETECT_CONTENT_STARTS_WITH;
+
+    ret = 0;
+ end:
+    return ret;
+}
index 09f88cc4b1c93c3160341730cc5e6ca97d2a43be..4b6310d3cd0ab4966486eab9887ef57618892693 100644 (file)
@@ -60,6 +60,7 @@ enum {
     DETECT_URICONTENT,
     DETECT_PCRE,
     DETECT_DEPTH,
+    DETECT_STARTS_WITH,
     DETECT_DISTANCE,
     DETECT_WITHIN,
     DETECT_OFFSET,
index 31a835adf0fa282ff274927198060ba56b90a061..bed23a2d1ba261f58136b3eee9b0ea6ab1752b37 100644 (file)
@@ -69,6 +69,10 @@ int DetectOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *offset
     /* verify other conditions */
     DetectContentData *cd = (DetectContentData *)pm->ctx;
 
+    if (cd->flags & DETECT_CONTENT_STARTS_WITH) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use offset with startswith");
+        goto end;
+    }
     if (cd->flags & DETECT_CONTENT_OFFSET) {
         SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple offsets for the same content. ");
         goto end;
index bc14abe62af4ed239fe745acd4b33b78ce962f4e..214c5c3fa8cfc4203ab2330693b8d09e0ebd6c6a 100644 (file)
@@ -225,6 +225,16 @@ static int DetectEngineContentInspectionTest10(void) {
     TEST_FOOTER;
 }
 
+static int DetectEngineContentInspectionTest11(void) {
+    TEST_HEADER;
+    TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\";", true, 2);
+    TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0;", true, 2);
+    TEST_RUN("ab", 2, "content:\"ab\"; startswith;", true, 1);
+    TEST_RUN("ab", 2, "content:\"a\"; startswith;", true, 1);
+    TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1);
+    TEST_FOOTER;
+}
+
 void DetectEngineContentInspectionRegisterTests(void)
 {
     UtRegisterTest("DetectEngineContentInspectionTest01",
@@ -247,6 +257,8 @@ void DetectEngineContentInspectionRegisterTests(void)
                    DetectEngineContentInspectionTest09);
     UtRegisterTest("DetectEngineContentInspectionTest10",
                    DetectEngineContentInspectionTest10);
+    UtRegisterTest("DetectEngineContentInspectionTest11 startswith",
+                   DetectEngineContentInspectionTest11);
 }
 
 #undef TEST_HEADER