Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/userguide/rules/payload-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ byte_jump
---------

The ``byte_jump`` keyword allows for the ability to select a ``<num of bytes>`` from an ``<offset>`` and moves the detection pointer to that position. Content matches will then be based off the new position.
The ``bitmask`` value is applied to the extracted value before the multiplier. Additionally, the result of the bitmask operation is
right-shifted by the number of trailing zeroes in the bitmask value.

Format::

Expand Down
142 changes: 128 additions & 14 deletions src/detect-bytejump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -209,13 +211,27 @@ 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) {
if ((val % 4) != 0) {
val += 4 - (val % 4);
}
}

val += data->post_offset;
SCLogDebug("val: %" PRIi64 " post_offset: %" PRIi32, val, data->post_offset);

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/detect-bytejump.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,18 @@
#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 */
uint8_t base; /**< String value base (oct|dec|hex) */
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 */
Expand Down
Loading