From b872850f16e5012a07c709fa16a103770c0e1152 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Sat, 17 Jan 2026 08:27:28 -0500 Subject: [PATCH] detect/byte_jump: Support bitmask value Issue: 6693 Add bitmask support to byte_jump - Parse - Calculate shift count - Apply to value before applying multiplier - Order items in DetectBytejumpData to reduce holes. Snort: See https://github.com/chenkc/snort2.9/blob/master/snort-2.9.11.1/src/detection-plugins/sp_byte_jump.c#L780 --- src/detect-bytejump.c | 142 +++++++++++++++++++++++++++++++++++++----- src/detect-bytejump.h | 4 ++ 2 files changed, 132 insertions(+), 14 deletions(-) diff --git a/src/detect-bytejump.c b/src/detect-bytejump.c index 6b0363f098..66776008d1 100644 --- a/src/detect-bytejump.c +++ b/src/detect-bytejump.c @@ -47,18 +47,20 @@ /** * \brief Regex for parsing our options */ -#define PARSE_REGEX "^\\s*" \ - "([^\\s,]+\\s*,\\s*[^\\s,]+)" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "(?:\\s*,\\s*((?:multiplier|post_offset)\\s+[^\\s,]+|[^\\s,]+))?" \ - "\\s*$" +#define PARSE_REGEX \ + "^\\s*" \ + "([^\\s,]+\\s*,\\s*[^\\s,]+)" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "(?:\\s*,\\s*((?:multiplier|post_offset|bitmask)\\s+[^\\s,]+|[^\\s,]+))?" \ + "\\s*$" static DetectParseRegex parse_regex; @@ -209,6 +211,19 @@ bool DetectBytejumpDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, SCLogDebug("VAL: (%" PRIu64 " x %" PRIu32 ") + %" PRIi32 " + %" PRId32, val, data->multiplier, extbytes, data->post_offset); + /* Apply the bitmask value and bitmask shift count; note, the bitmask is applied + * before the multiplier per snort */ + if (data->flags & DETECT_BYTEJUMP_BITMASK) { + SCLogDebug("[before bitmask] val: %" PRIi64 " bitmask_value: %" PRIx32, val, + data->bitmask_value); + val &= data->bitmask_value; + if (val && data->bitmask_shift_count) { + val = val >> data->bitmask_shift_count; + } + SCLogDebug("[after bitmask] val: %" PRIi64 " bitmask_value: %" PRIx32, val, + data->bitmask_value); + } + /* Adjust the jump value based on flags */ val *= data->multiplier; if (flags & DETECT_BYTEJUMP_ALIGN) { @@ -216,6 +231,7 @@ bool DetectBytejumpDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, val += 4 - (val % 4); } } + val += data->post_offset; SCLogDebug("val: %" PRIi64 " post_offset: %" PRIi32, val, data->post_offset); @@ -263,7 +279,7 @@ static DetectBytejumpData *DetectBytejumpParse( DetectEngineCtx *de_ctx, const char *optstr, char **nbytes_str, char **offset) { DetectBytejumpData *data = NULL; - char args[10][64]; + char args[11][64]; int res = 0; size_t pcre2len; int numargs = 0; @@ -277,7 +293,7 @@ static DetectBytejumpData *DetectBytejumpParse( /* Execute the regex and populate args with captures. */ int ret = DetectParsePcreExec(&parse_regex, &match, optstr, 0, 0); - if (ret < 2 || ret > 10) { + if (ret < 2 || ret > 11) { SCLogError("parse error, ret %" PRId32 ", string \"%s\"", ret, optstr); goto error; } @@ -417,6 +433,17 @@ static DetectBytejumpData *DetectBytejumpParse( goto error; } SCLogDebug("post_offset: %s [%d]", optstr, data->post_offset); + } else if (strncasecmp("bitmask ", args[i], 8) == 0) { + data->flags |= DETECT_BYTEJUMP_BITMASK; + if (ByteExtractStringUint32(&data->bitmask_value, 0, 0, args[i] + strlen("bitmask ")) <= + 0) { + SCLogError("Malformed bitmask: %s", optstr); + goto error; + } + if (data->bitmask_value == 0) { + SCLogError("Invalid bitmask value: %d", data->bitmask_value); + goto error; + } } else if (strcasecmp("dce", args[i]) == 0) { data->flags |= DETECT_BYTEJUMP_DCE; } else { @@ -448,6 +475,17 @@ static DetectBytejumpData *DetectBytejumpParse( } } + data->bitmask_shift_count = 0; + if (data->flags & DETECT_BYTEJUMP_BITMASK) { + uint32_t bmask = data->bitmask_value; + while (!(bmask & 0x1)) { + bmask = bmask >> 1; + data->bitmask_shift_count++; + } + SCLogDebug("bitmask value 0x%x -> shift value %d", data->bitmask_value, + data->bitmask_shift_count); + } + pcre2_match_data_free(match); return data; @@ -1004,6 +1042,80 @@ static int DetectBytejumpTestParse14(void) PASS; } +static int DetectBytejumpTestParse15(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = + DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(msg:\"Testing bytejump_body\"; " + "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; " + "dce_stub_data; " + "content:\"one\"; distance:0; " + "byte_jump:4,0,align,multiplier 2, " + "post_offset -16,relative,bitmask 0x8f40,dce; sid:1;)"); + FAIL_IF_NULL(s); + SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id); + FAIL_IF_NULL(sm); + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NULL(sm->next); + sm = sm->next; + FAIL_IF_NOT(sm->type == DETECT_BYTEJUMP); + + DetectBytejumpData *bd = (DetectBytejumpData *)sm->ctx; + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_DCE); + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_RELATIVE); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_STRING); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_BIG); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_LITTLE); + + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_BITMASK); + FAIL_IF_NOT(bd->bitmask_value == 0x8f40); + FAIL_IF_NOT(bd->bitmask_shift_count == 6); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectBytejumpTestParse16(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = + DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(msg:\"Testing bytejump_body\"; " + "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; " + "dce_stub_data; " + "content:\"one\"; distance:0; " + "byte_jump:4,0,align,multiplier 2, " + "post_offset -16,relative,bitmask 5304,dce; sid:1;)"); + FAIL_IF_NULL(s); + SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id); + FAIL_IF_NULL(sm); + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NULL(sm->next); + sm = sm->next; + FAIL_IF_NOT(sm->type == DETECT_BYTEJUMP); + + DetectBytejumpData *bd = (DetectBytejumpData *)sm->ctx; + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_DCE); + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_RELATIVE); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_STRING); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_BIG); + FAIL_IF(bd->flags & DETECT_BYTEJUMP_LITTLE); + + FAIL_IF_NOT(bd->flags & DETECT_BYTEJUMP_BITMASK); + FAIL_IF_NOT(bd->bitmask_value == 5304); + FAIL_IF_NOT(bd->bitmask_shift_count == 3); + + DetectEngineCtxFree(de_ctx); + PASS; +} + /** * \test DetectByteJumpTestPacket01 is a test to check matches of * byte_jump and byte_jump relative works if the previous keyword is pcre @@ -1215,6 +1327,8 @@ static void DetectBytejumpRegisterTests(void) UtRegisterTest("DetectBytejumpTestParse12", DetectBytejumpTestParse12); UtRegisterTest("DetectBytejumpTestParse13", DetectBytejumpTestParse13); UtRegisterTest("DetectBytejumpTestParse14", DetectBytejumpTestParse14); + UtRegisterTest("DetectBytejumpTestParse15", DetectBytejumpTestParse15); + UtRegisterTest("DetectBytejumpTestParse16", DetectBytejumpTestParse16); UtRegisterTest("DetectByteJumpTestPacket01", DetectByteJumpTestPacket01); UtRegisterTest("DetectByteJumpTestPacket02", DetectByteJumpTestPacket02); diff --git a/src/detect-bytejump.h b/src/detect-bytejump.h index 85914c2a81..ed95d3a484 100644 --- a/src/detect-bytejump.h +++ b/src/detect-bytejump.h @@ -42,6 +42,7 @@ #define DETECT_BYTEJUMP_END BIT_U16(8) /**< "from_end" jump */ #define DETECT_BYTEJUMP_NBYTES_VAR BIT_U16(9) /**< nbytes string*/ #define DETECT_BYTEJUMP_OFFSET_VAR BIT_U16(10) /**< byte extract value enabled */ +#define DETECT_BYTEJUMP_BITMASK BIT_U16(11) /**< bitmask value */ typedef struct DetectBytejumpData_ { uint8_t nbytes; /**< Number of bytes to compare */ @@ -49,7 +50,10 @@ typedef struct DetectBytejumpData_ { uint16_t flags; /**< Flags (big|little|relative|string) */ int32_t offset; /**< Offset in payload to extract value */ int32_t post_offset; /**< Offset to adjust post-jump */ + uint32_t bitmask_value; /**< bitmask value */ + uint16_t multiplier; /**< Multiplier for nbytes (multiplier n)*/ + uint8_t bitmask_shift_count; /**< bitmask shift value*/ } DetectBytejumpData; /* prototypes */