diff options
Diffstat (limited to 'src/detect-bytemath.c')
-rw-r--r-- | src/detect-bytemath.c | 1079 |
1 files changed, 1079 insertions, 0 deletions
diff --git a/src/detect-bytemath.c b/src/detect-bytemath.c new file mode 100644 index 0000000..9064b06 --- /dev/null +++ b/src/detect-bytemath.c @@ -0,0 +1,1079 @@ +/* Copyright (C) 2020-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Jeff Lucovsky <jeff@lucovsky.org> + */ + +/* + * Refer to the Snort manual, section 3.5.34 for details. + */ + +#include "suricata-common.h" +#include "threads.h" +#include "decode.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-state.h" +#include "detect-engine-build.h" +#include "detect-content.h" +#include "detect-pcre.h" +#include "detect-byte.h" +#include "detect-bytemath.h" + +#include "app-layer-parser.h" +#include "app-layer-protos.h" +#include "rust-bindings.h" + +#include "flow.h" +#include "flow-var.h" +#include "flow-util.h" + +#include "util-byte.h" +#include "util-debug.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-spm.h" + +static int DetectByteMathSetup(DetectEngineCtx *, Signature *, const char *); +#ifdef UNITTESTS +static void DetectByteMathRegisterTests(void); +#endif +static void DetectByteMathFree(DetectEngineCtx *, void *); + +#define DETECT_BYTEMATH_ENDIAN_DEFAULT (uint8_t) BigEndian +#define DETECT_BYTEMATH_BASE_DEFAULT (uint8_t) BaseDec +/** + * \brief Registers the keyword handlers for the "byte_math" keyword. + */ +void DetectBytemathRegister(void) +{ + sigmatch_table[DETECT_BYTEMATH].name = "byte_math"; + sigmatch_table[DETECT_BYTEMATH].Match = NULL; + sigmatch_table[DETECT_BYTEMATH].Setup = DetectByteMathSetup; + sigmatch_table[DETECT_BYTEMATH].Free = DetectByteMathFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_BYTEMATH].RegisterTests = DetectByteMathRegisterTests; +#endif +} + +static inline bool DetectByteMathValidateNbytesOnly(const DetectByteMathData *data, int32_t nbytes) +{ + return nbytes >= 1 && + (((data->flags & DETECT_BYTEMATH_FLAG_STRING) && nbytes <= 10) || (nbytes <= 4)); +} + +int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *smd, + const Signature *s, const uint8_t *payload, uint16_t payload_len, uint8_t nbytes, + uint64_t rvalue, uint64_t *value, uint8_t endian) +{ + const DetectByteMathData *data = (DetectByteMathData *)smd->ctx; + if (payload_len == 0) { + return 0; + } + + if (!DetectByteMathValidateNbytesOnly(data, nbytes)) { + return 0; + } + + const uint8_t *ptr; + int32_t len; + uint64_t val; + int extbytes; + + /* Calculate the ptr value for the byte-math op and length remaining in + * the packet from that point. + */ + if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) { + SCLogDebug("relative, working with det_ctx->buffer_offset %" PRIu32 ", " + "data->offset %" PRIi32 "", + det_ctx->buffer_offset, data->offset); + + ptr = payload + det_ctx->buffer_offset; + len = payload_len - det_ctx->buffer_offset; + + ptr += data->offset; + len -= data->offset; + + /* No match if there is no relative base */ + if (len <= 0) { + return 0; + } + } else { + SCLogDebug("absolute, data->offset %" PRIi32 "", data->offset); + + ptr = payload + data->offset; + len = payload_len - data->offset; + } + + /* Validate that the to-be-extracted is within the packet */ + if (ptr < payload || nbytes > len) { + SCLogDebug("Data not within payload pkt=%p, ptr=%p, len=%" PRIu32 ", nbytes=%d", payload, + ptr, len, nbytes); + return 0; + } + + /* Extract the byte data */ + if (data->flags & DETECT_BYTEMATH_FLAG_STRING) { + extbytes = ByteExtractStringUint64(&val, data->base, nbytes, (const char *)ptr); + if (extbytes <= 0) { + if (val == 0) { + SCLogDebug("No Numeric value"); + return 0; + } else { + SCLogDebug("error extracting %d bytes of string data: %d", nbytes, extbytes); + return -1; + } + } + } else { + ByteMathEndian bme = endian; + int endianness = (bme == BigEndian) ? BYTE_BIG_ENDIAN : BYTE_LITTLE_ENDIAN; + extbytes = ByteExtractUint64(&val, endianness, nbytes, ptr); + if (extbytes != nbytes) { + SCLogDebug("error extracting %d bytes of numeric data: %d", nbytes, extbytes); + return 0; + } + } + + BUG_ON(extbytes > len); + + ptr += extbytes; + + switch (data->oper) { + case OperatorNone: + break; + case Addition: + val += rvalue; + break; + case Subtraction: + val -= rvalue; + break; + case Division: + if (rvalue == 0) { + SCLogDebug("avoiding division by zero"); + return 0; + } + val /= rvalue; + break; + case Multiplication: + val *= rvalue; + break; + case LeftShift: + if (rvalue < 64) { + val <<= rvalue; + } else { + val = 0; + } + break; + case RightShift: + val >>= rvalue; + break; + } + + det_ctx->buffer_offset = ptr - payload; + + if (data->flags & DETECT_BYTEMATH_FLAG_BITMASK) { + val &= data->bitmask_val; + if (val && data->bitmask_shift_count) { + val = val >> data->bitmask_shift_count; + } + } + + *value = val; + return 1; +} + +/** + * \internal + * \brief Used to parse byte_math arg. + * + * \param arg The argument to parse. + * \param rvalue May be NULL. When non-null, will contain the variable + * name of rvalue (iff rvalue is not a scalar value) + * + * \retval bmd On success an instance containing the parsed data. + * On failure, NULL. + */ +static DetectByteMathData *DetectByteMathParse( + DetectEngineCtx *de_ctx, const char *arg, char **nbytes, char **rvalue) +{ + DetectByteMathData *bmd; + if ((bmd = ScByteMathParse(arg)) == NULL) { + SCLogError("invalid bytemath values"); + return NULL; + } + + if (bmd->nbytes_str) { + if (nbytes == NULL) { + SCLogError("byte_math supplied with " + "var name for nbytes. \"nbytes\" argument supplied to " + "this function must be non-NULL"); + goto error; + } + *nbytes = SCStrdup(bmd->nbytes_str); + if (*nbytes == NULL) { + goto error; + } + } + + if (bmd->rvalue_str) { + if (rvalue == NULL) { + SCLogError("byte_math supplied with " + "var name for rvalue. \"rvalue\" argument supplied to " + "this function must be non-NULL"); + goto error; + } + *rvalue = SCStrdup(bmd->rvalue_str); + if (*rvalue == NULL) { + goto error; + } + } + + if (bmd->flags & DETECT_BYTEMATH_FLAG_BITMASK) { + if (bmd->bitmask_val) { + uint32_t bmask = bmd->bitmask_val; + while (!(bmask & 0x1)){ + bmask = bmask >> 1; + bmd->bitmask_shift_count++; + } + } + } + + return bmd; + + error: + if (bmd != NULL) + DetectByteMathFree(de_ctx, bmd); + return NULL; +} + +/** + * \brief The setup function for the byte_math keyword for a signature. + * + * \param de_ctx Pointer to the detection engine context. + * \param s Pointer to signature for the current Signature being parsed + * from the rules. + * \param arg Pointer to the string holding the keyword value. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) +{ + SigMatch *sm = NULL; + SigMatch *prev_pm = NULL; + DetectByteMathData *data; + char *rvalue = NULL; + char *nbytes = NULL; + int ret = -1; + + data = DetectByteMathParse(de_ctx, arg, &nbytes, &rvalue); + if (data == NULL) + goto error; + + int sm_list; + if (s->init_data->list != DETECT_SM_LIST_NOTSET) { + if (DetectBufferGetActiveList(de_ctx, s) == -1) + goto error; + + sm_list = s->init_data->list; + + if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) { + prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, -1); + if (!prev_pm) { + SCLogError("relative specified without " + "previous pattern match"); + goto error; + } + } + } else if (data->endian == EndianDCE) { + if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) { + prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, + DETECT_BYTETEST, DETECT_BYTEJUMP, + DETECT_BYTE_EXTRACT, + DETECT_BYTEMATH, + DETECT_ISDATAAT, -1); + if (prev_pm == NULL) { + sm_list = DETECT_SM_LIST_PMATCH; + } else { + sm_list = SigMatchListSMBelongsTo(s, prev_pm); + if (sm_list < 0) + goto error; + } + } else { + sm_list = DETECT_SM_LIST_PMATCH; + } + + if (DetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0) + goto error; + s->flags |= SIG_FLAG_APPLAYER; + + } else if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) { + prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, + DETECT_BYTETEST, DETECT_BYTEJUMP, + DETECT_BYTE_EXTRACT, DETECT_BYTEMATH, + DETECT_ISDATAAT, -1); + if (prev_pm == NULL) { + sm_list = DETECT_SM_LIST_PMATCH; + } else { + sm_list = SigMatchListSMBelongsTo(s, prev_pm); + if (sm_list < 0) + goto error; + if (sm_list != DETECT_SM_LIST_PMATCH) + s->flags |= SIG_FLAG_APPLAYER; + } + + } else { + sm_list = DETECT_SM_LIST_PMATCH; + } + + if (data->endian == EndianDCE) { + if (DetectSignatureSetAppProto(s, ALPROTO_DCERPC) != 0) + goto error; + + if ((data->flags & DETECT_BYTEMATH_FLAG_STRING) || (data->base == BaseDec) || + (data->base == BaseHex) || (data->base == BaseOct)) { + SCLogError("Invalid option. " + "A bytemath keyword with dce holds other invalid modifiers."); + goto error; + } + } + + if (nbytes != NULL) { + DetectByteIndexType index; + if (!DetectByteRetrieveSMVar(nbytes, s, &index)) { + SCLogError("unknown byte_ keyword var seen in byte_math - %s", nbytes); + goto error; + } + data->nbytes = index; + data->flags |= DETECT_BYTEMATH_FLAG_NBYTES_VAR; + SCFree(nbytes); + nbytes = NULL; + } + + if (rvalue != NULL) { + DetectByteIndexType index; + if (!DetectByteRetrieveSMVar(rvalue, s, &index)) { + SCLogError("unknown byte_ keyword var seen in byte_math - %s", rvalue); + goto error; + } + data->rvalue = index; + data->flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR; + SCFree(rvalue); + rvalue = NULL; + } + + SigMatch *prev_bmd_sm = DetectGetLastSMByListId(s, sm_list, + DETECT_BYTEMATH, -1); + if (prev_bmd_sm == NULL) { + data->local_id = 0; + } else { + data->local_id = ((DetectByteMathData *)prev_bmd_sm->ctx)->local_id + 1; + } + if (data->local_id > de_ctx->byte_extract_max_local_id) { + de_ctx->byte_extract_max_local_id = data->local_id; + } + + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + sm->type = DETECT_BYTEMATH; + sm->ctx = (void *)data; + SigMatchAppendSMToList(s, sm, sm_list); + + if (!(data->flags & DETECT_BYTEMATH_FLAG_RELATIVE)) + goto okay; + + if (prev_pm == NULL) + goto okay; + + if (prev_pm->type == DETECT_CONTENT) { + DetectContentData *cd = (DetectContentData *)prev_pm->ctx; + cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; + } else if (prev_pm->type == DETECT_PCRE) { + DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx; + pd->flags |= DETECT_PCRE_RELATIVE_NEXT; + } + + okay: + return 0; + + error: + if (rvalue) + SCFree(rvalue); + if (nbytes) + SCFree(nbytes); + DetectByteMathFree(de_ctx, data); + return ret; +} + +/** + * \brief Used to free instances of DetectByteMathractData. + * + * \param ptr Instance of DetectByteMathData to be freed. + */ +static void DetectByteMathFree(DetectEngineCtx *de_ctx, void *ptr) +{ + ScByteMathFree(ptr); +} + +/** + * \brief Lookup the SigMatch for a named byte_math variable. + * + * \param arg The name of the byte_math variable to lookup. + * \param s Pointer the signature to look in. + * + * \retval A pointer to the SigMatch if found, otherwise NULL. + */ +SigMatch *DetectByteMathRetrieveSMVar(const char *arg, const Signature *s) +{ + for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { + SigMatch *sm = s->init_data->buffers[x].head; + while (sm != NULL) { + if (sm->type == DETECT_BYTEMATH) { + const DetectByteMathData *bmd = (const DetectByteMathData *)sm->ctx; + if (strcmp(bmd->result, arg) == 0) { + SCLogDebug("Retrieved SM for \"%s\"", arg); + return sm; + } + } + sm = sm->next; + } + } + + for (int list = 0; list < DETECT_SM_LIST_MAX; list++) { + SigMatch *sm = s->init_data->smlists[list]; + while (sm != NULL) { + if (sm->type == DETECT_BYTEMATH) { + const DetectByteMathData *bmd = (const DetectByteMathData *)sm->ctx; + if (strcmp(bmd->result, arg) == 0) { + SCLogDebug("Retrieved SM for \"%s\"", arg); + return sm; + } + } + sm = sm->next; + } + } + + return NULL; +} + +/*************************************Unittests********************************/ +#ifdef UNITTESTS +#include "detect-engine-alert.h" + +static int DetectByteMathParseTest01(void) +{ + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue 10, result bar", + NULL, NULL); + FAIL_IF(bmd == NULL); + + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 10); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest02(void) +{ + /* bytes value invalid */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 257, offset 2, oper +, " + "rvalue 39, result bar", + NULL, NULL); + + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest03(void) +{ + /* bytes value invalid */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 11, offset 2, oper +, " + "rvalue 39, result bar", + NULL, NULL); + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest04(void) +{ + /* offset value invalid */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 70000, oper +," + " rvalue 39, result bar", + NULL, NULL); + + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest05(void) +{ + /* oper value invalid */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 11, offset 16, oper &," + "rvalue 39, result bar", + NULL, NULL); + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest06(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE; + char *rvalue = NULL; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 0, oper +," + "rvalue 248, result var, relative", + NULL, &rvalue); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 0); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 248); + FAIL_IF_NOT(strcmp(bmd->result, "var") == 0); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest07(void) +{ + char *rvalue = NULL; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue foo, result bar", + NULL, &rvalue); + FAIL_IF_NOT(rvalue); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(strcmp(rvalue, "foo") == 0); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + SCFree(rvalue); + + PASS; +} + +static int DetectByteMathParseTest08(void) +{ + /* ensure Parse checks the pointer value when rvalue is a var */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue foo, result bar", + NULL, NULL); + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest09(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue 39, result bar, relative", + NULL, NULL); + FAIL_IF(bmd == NULL); + + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest10(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_ENDIAN; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue 39, result bar, endian" + " big", + NULL, NULL); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == BigEndian); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest11(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_ENDIAN; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +, " + "rvalue 39, result bar, dce", + NULL, NULL); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == EndianDCE); + FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest12(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE | DETECT_BYTEMATH_FLAG_STRING; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue 39, result bar, " + "relative, string dec", + NULL, NULL); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == BigEndian); + FAIL_IF_NOT(bmd->base == BaseDec); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathParseTest13(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_STRING | + DETECT_BYTEMATH_FLAG_RELATIVE | + DETECT_BYTEMATH_FLAG_BITMASK; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +, " + "rvalue 39, result bar, " + "relative, string dec, bitmask " + "0x8f40", + NULL, NULL); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->bitmask_val == 0x8f40); + FAIL_IF_NOT(bmd->bitmask_shift_count == 6); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == BigEndian); + FAIL_IF_NOT(bmd->base == BaseDec); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + + +static int DetectByteMathParseTest14(void) +{ + /* incomplete */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +," + "rvalue foo", + NULL, NULL); + + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest15(void) +{ + + /* incomplete */ + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset 2, oper +, " + "result bar", + NULL, NULL); + + FAIL_IF_NOT(bmd == NULL); + + PASS; +} + +static int DetectByteMathParseTest16(void) +{ + uint8_t flags = DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_RELATIVE | + DETECT_BYTEMATH_FLAG_BITMASK; + + DetectByteMathData *bmd = DetectByteMathParse(NULL, + "bytes 4, offset -2, oper +, " + "rvalue 39, result bar, " + "relative, string dec, bitmask " + "0x8f40", + NULL, NULL); + + FAIL_IF(bmd == NULL); + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == -2); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->rvalue == 39); + FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0); + FAIL_IF_NOT(bmd->bitmask_val == 0x8f40); + FAIL_IF_NOT(bmd->bitmask_shift_count == 6); + FAIL_IF_NOT(bmd->flags == flags); + FAIL_IF_NOT(bmd->endian == BigEndian); + FAIL_IF_NOT(bmd->base == BaseDec); + + DetectByteMathFree(NULL, bmd); + + PASS; +} + +static int DetectByteMathPacket01(void) +{ + uint8_t buf[] = { 0x38, 0x35, 0x6d, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6d, 0x00, 0x01, 0x00 }; + Flow f; + void *dns_state = NULL; + Packet *p = NULL; + Signature *s = NULL; + ThreadVars tv; + DetectEngineThreadCtx *det_ctx = NULL; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + + p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP, + "192.168.1.5", "192.168.1.1", + 41424, 53); + FAIL_IF_NULL(p); + + FLOW_INITIALIZE(&f); + f.flags |= FLOW_IPV4; + f.proto = IPPROTO_UDP; + f.protomap = FlowGetProtoMapping(f.proto); + + p->flow = &f; + p->flags |= PKT_HAS_FLOW; + p->flowflags |= FLOW_PKT_TOSERVER; + f.alproto = ALPROTO_DNS; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->mpm_matcher = mpm_default_matcher; + de_ctx->flags |= DE_QUIET; + + /* + * byte_extract: Extract 1 byte from offset 0 --> 0x0038 + * byte_math: Extract 1 byte from offset 2 (0x35) + * Add 0x35 + 0x38 = 109 (0x6d) + * byte_test: Compare 2 bytes at offset 13 bytes from last + * match and compare with 0x6d + */ + s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;" + "byte_test: 2, =, var, 13;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:1;)"); + FAIL_IF_NULL(s); + + /* this rule should not alert */ + s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;" + "byte_test: 2, !=, var, 13;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:2;)"); + FAIL_IF_NULL(s); + + /* + * this rule should alert: + * compares offset 15 with var ... 1 (offset 15) < 0x6d (var) + */ + s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;" + "byte_test: 2, <, var, 15;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:3;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + FAIL_IF_NULL(det_ctx); + + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, + STREAM_TOSERVER, buf, sizeof(buf)); + FAIL_IF_NOT(r == 0); + + dns_state = f.alstate; + FAIL_IF_NULL(dns_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + /* ensure sids 1 & 3 alerted */ + FAIL_IF_NOT(PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 2)); + FAIL_IF_NOT(PacketAlertCheck(p, 3)); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + DetectEngineCtxFree(de_ctx); + + FLOW_DESTROY(&f); + UTHFreePacket(p); + + PASS; +} + +static int DetectByteMathPacket02(void) +{ + uint8_t buf[] = { 0x38, 0x35, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x00, 0x01, 0x00 }; + Flow f; + void *dns_state = NULL; + Packet *p = NULL; + Signature *s = NULL; + ThreadVars tv; + DetectEngineThreadCtx *det_ctx = NULL; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + + p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP, "192.168.1.5", "192.168.1.1", 41424, 53); + FAIL_IF_NULL(p); + + FLOW_INITIALIZE(&f); + f.flags |= FLOW_IPV4; + f.proto = IPPROTO_UDP; + f.protomap = FlowGetProtoMapping(f.proto); + + p->flow = &f; + p->flags |= PKT_HAS_FLOW; + p->flowflags |= FLOW_PKT_TOSERVER; + f.alproto = ALPROTO_DNS; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->mpm_matcher = mpm_default_matcher; + de_ctx->flags |= DE_QUIET; + + /* + * byte_extract: Extract 1 byte from offset 0 --> 0x38 + * byte_math: Extract 1 byte from offset -1 (0x38) + * Add 0x38 + 0x38 = 112 (0x70) + * byte_test: Compare 2 bytes at offset 13 bytes from last + * match and compare with 0x70 + */ + s = DetectEngineAppendSig(de_ctx, + "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset -1, oper +, rvalue extracted_val, result var, relative;" + "byte_test: 2, =, var, 13;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:1;)"); + FAIL_IF_NULL(s); + + /* this rule should not alert */ + s = DetectEngineAppendSig(de_ctx, + "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset -1, oper +, rvalue extracted_val, result var, relative;" + "byte_test: 2, !=, var, 13;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:2;)"); + FAIL_IF_NULL(s); + + /* + * this rule should alert: + * compares offset 15 with var ... 1 (offset 15) < 0x70 (var) + */ + s = DetectEngineAppendSig(de_ctx, + "alert udp any any -> any any " + "(byte_extract: 1, 0, extracted_val, relative;" + "byte_math: bytes 1, offset -1, oper +, rvalue extracted_val, result var, relative;" + "byte_test: 2, <, var, 15;" + "msg:\"Byte extract and byte math with byte test verification\";" + "sid:3;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + FAIL_IF_NULL(det_ctx); + + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf, sizeof(buf)); + FAIL_IF_NOT(r == 0); + + dns_state = f.alstate; + FAIL_IF_NULL(dns_state); + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + /* ensure sids 1 & 3 alerted */ + FAIL_IF_NOT(PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 2)); + FAIL_IF_NOT(PacketAlertCheck(p, 3)); + + AppLayerParserThreadCtxFree(alp_tctx); + DetectEngineThreadCtxDeinit(&tv, det_ctx); + DetectEngineCtxFree(de_ctx); + + FLOW_DESTROY(&f); + UTHFreePacket(p); + + PASS; +} + +static int DetectByteMathContext01(void) +{ + DetectEngineCtx *de_ctx = NULL; + Signature *s = NULL; + SigMatch *sm = NULL; + DetectContentData *cd = NULL; + DetectByteMathData *bmd = NULL; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF(de_ctx == NULL); + + de_ctx->flags |= DE_QUIET; + s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any " + "(msg:\"Testing bytemath_body\"; " + "content:\"|00 04 93 F3|\"; " + "content:\"|00 00 00 07|\"; distance:4; within:4;" + "byte_math:bytes 4, offset 0, oper +, rvalue " + "248, result var, relative; sid:1;)"); + + FAIL_IF(de_ctx->sig_list == NULL); + + FAIL_IF(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL); + + sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; + FAIL_IF(sm->type != DETECT_CONTENT); + cd = (DetectContentData *)sm->ctx; + FAIL_IF(cd->flags & DETECT_CONTENT_WITHIN); + FAIL_IF(cd->flags & DETECT_CONTENT_DISTANCE); + FAIL_IF(cd->content_len != 4); + + sm = sm->next; + FAIL_IF(sm->type != DETECT_CONTENT); + sm = sm->next; + FAIL_IF(sm->type != DETECT_BYTEMATH); + + FAIL_IF(sm->ctx == NULL); + + bmd = (DetectByteMathData *)sm->ctx; + FAIL_IF_NOT(bmd->nbytes == 4); + FAIL_IF_NOT(bmd->offset == 0); + FAIL_IF_NOT(bmd->rvalue == 248); + FAIL_IF_NOT(strcmp(bmd->result, "var") == 0); + FAIL_IF_NOT(bmd->flags == DETECT_BYTEMATH_FLAG_RELATIVE); + FAIL_IF_NOT(bmd->endian == BigEndian); + FAIL_IF_NOT(bmd->oper == Addition); + FAIL_IF_NOT(bmd->base == BaseDec); + + DetectEngineCtxFree(de_ctx); + + PASS; +} + +static void DetectByteMathRegisterTests(void) +{ + UtRegisterTest("DetectByteMathParseTest01", DetectByteMathParseTest01); + UtRegisterTest("DetectByteMathParseTest02", DetectByteMathParseTest02); + UtRegisterTest("DetectByteMathParseTest03", DetectByteMathParseTest03); + UtRegisterTest("DetectByteMathParseTest04", DetectByteMathParseTest04); + UtRegisterTest("DetectByteMathParseTest05", DetectByteMathParseTest05); + UtRegisterTest("DetectByteMathParseTest06", DetectByteMathParseTest06); + UtRegisterTest("DetectByteMathParseTest07", DetectByteMathParseTest07); + UtRegisterTest("DetectByteMathParseTest08", DetectByteMathParseTest08); + UtRegisterTest("DetectByteMathParseTest09", DetectByteMathParseTest09); + UtRegisterTest("DetectByteMathParseTest10", DetectByteMathParseTest10); + UtRegisterTest("DetectByteMathParseTest11", DetectByteMathParseTest11); + UtRegisterTest("DetectByteMathParseTest12", DetectByteMathParseTest12); + UtRegisterTest("DetectByteMathParseTest13", DetectByteMathParseTest13); + UtRegisterTest("DetectByteMathParseTest14", DetectByteMathParseTest14); + UtRegisterTest("DetectByteMathParseTest15", DetectByteMathParseTest15); + UtRegisterTest("DetectByteMathParseTest16", DetectByteMathParseTest16); + UtRegisterTest("DetectByteMathPacket01", DetectByteMathPacket01); + UtRegisterTest("DetectByteMathPacket02", DetectByteMathPacket02); + UtRegisterTest("DetectByteMathContext01", DetectByteMathContext01); +} +#endif /* UNITTESTS */ |