diff --git a/doc/userguide/rules/transforms.rst b/doc/userguide/rules/transforms.rst index e536757f29f7..b8e614388272 100644 --- a/doc/userguide/rules/transforms.rst +++ b/doc/userguide/rules/transforms.rst @@ -198,6 +198,20 @@ This transform is similar to the keyword ``base64_decode``: the buffer is decode the optional values for ``mode``, ``offset`` and ``bytes`` and is available for matching on the decoded data. +The optional value ``set_error`` can also be specified; this causes the output buffer to be +truncated when the input buffer cannot be decoded. This can be used in situations to determine +if the content is base64-decoded. The ``absent`` keyword is often combined with use of ``set_error``. +When ``set_error`` isn't specified (default), the input buffer will be unchanged if it cannot be +base64-decoded. + +This example will alert if the content cannot be base64 decoded:: + + alert tcp any any -> any any (msg:"from_base64: no-decode [mode rfc4648]"; flow:to_server,established; http.uri; content:"/?arg="; from_base64: set_error; absent; sid:1; rev:1;) + +This example will alert if the content cannot be base64 decoded or if the decoded content is `sisatest`:: + + alert http any any -> any any (msg:"from_base64: no-decode with or_else [mode rfc4648]"; http.uri; content:"/?arg=dGhpc2lzYXRlc3QK"; from_base64: offset 10; absent: or_else; content:"sisatest"; sid:2; rev:1;) + After this transform completes, the buffer will contain only bytes that could be bases64-decoded. If the decoding process encountered invalid bytes, those will not be included in the buffer. @@ -209,12 +223,13 @@ The option values must be ``,`` separated and can appear in any order. Format:: - from_base64: [[bytes ] [, offset [, mode: strict|rfc4648|rfc2045]]] + from_base64: [[bytes ] [, offset [, mode: strict|rfc4648|rfc2045] [, set_error]]] There are defaults for each of the options: - ``bytes`` defaults to the length of the input buffer - ``offset`` defaults to ``0`` and must be less than ``65536`` - ``mode`` defaults to ``rfc4648`` +- ``set_error`` defaults to off. Note that both ``bytes`` and ``offset`` may be variables from `byte_extract` and/or `byte_math` in later versions of Suricata. They are not supported yet. @@ -243,3 +258,13 @@ This example transforms `"Zm 9v Ym Fy"` to `"foobar"`:: content:"/?arg=Zm 9v Ym Fy"; from_base64: offset 6, mode rfc2045; \ content:"foobar"; + +This example uses ``set_error`` to test if the input is not-base64 encoded:: + + content:"Unencoded content"; from_base64: set_error; absent; + +This example uses ``set_error`` to test if the input is base64 encoded or matches +the specified ``content``:: + + content:"/?arg=Zm 9v Ym Fy"; from_base64: set_error; \ + absent: or_else; content: "foobar"; diff --git a/rust/src/detect/transform_base64.rs b/rust/src/detect/transform_base64.rs index d97fca2f2115..f5206c2acfe0 100644 --- a/rust/src/detect/transform_base64.rs +++ b/rust/src/detect/transform_base64.rs @@ -31,12 +31,13 @@ use std::str; pub const TRANSFORM_FROM_BASE64_MODE_DEFAULT: Base64Mode = Base64Mode::Base64ModeRFC4648; -const DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT: usize = 3; +const DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT: usize = 4; pub const DETECT_TRANSFORM_BASE64_FLAG_MODE: u8 = 0x01; pub const DETECT_TRANSFORM_BASE64_FLAG_NBYTES: u8 = 0x02; pub const DETECT_TRANSFORM_BASE64_FLAG_OFFSET: u8 = 0x04; pub const DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR: u8 = 0x08; pub const DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR: u8 = 0x10; +pub const DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR: u8 = 0x20; #[repr(C)] #[derive(Debug)] @@ -134,6 +135,14 @@ fn parse_transform_base64( transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_MODE; } + "set_error" => { + if 0 != (transform_base64.flags & DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR) { + return Err(make_error("set_error already set".to_string())); + } + + transform_base64.flags |= DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR; + } + "offset" => { if 0 != (transform_base64.flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET) { return Err(make_error("offset already set".to_string())); @@ -300,6 +309,9 @@ mod tests { assert!( parse_transform_base64("bytes 4, offset 70000, mode strict, mode rfc2045").is_err() ); + assert!( + parse_transform_base64("set_error, set_error").is_err() + ); } #[test] @@ -399,5 +411,18 @@ mod tests { | DETECT_TRANSFORM_BASE64_FLAG_OFFSET | DETECT_TRANSFORM_BASE64_FLAG_MODE, ); + valid_test( + "bytes var, offset 3933, mode rfc4648, set_error", + 0, + "var", + 3933, + "", + Base64Mode::Base64ModeRFC4648, + DETECT_TRANSFORM_BASE64_FLAG_NBYTES + | DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR + | DETECT_TRANSFORM_BASE64_FLAG_OFFSET + | DETECT_TRANSFORM_BASE64_FLAG_MODE + | DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR, + ); } } diff --git a/src/detect-engine-content-inspection.c b/src/detect-engine-content-inspection.c index e43e693b2151..8aafe654caa0 100644 --- a/src/detect-engine-content-inspection.c +++ b/src/detect-engine-content-inspection.c @@ -118,8 +118,13 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, SCReturnInt(-1); } + if (smd == NULL) { + KEYWORD_PROFILING_END(det_ctx, smd->type, 0); + SCReturnInt(0); + } + // we want the ability to match on bsize: 0 - if (smd == NULL || buffer == NULL) { + if (buffer == NULL && smd->type != DETECT_ABSENT) { KEYWORD_PROFILING_END(det_ctx, smd->type, 0); SCReturnInt(0); } @@ -384,11 +389,11 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, } else if (smd->type == DETECT_ABSENT) { const DetectAbsentData *id = (DetectAbsentData *)smd->ctx; - if (!id->or_else) { + if (id->or_else || buffer_len == 0) { // we match only on absent buffer - goto no_match; + goto match; } - goto match; + goto no_match; } else if (smd->type == DETECT_ISDATAAT) { SCLogDebug("inspecting isdataat"); diff --git a/src/detect-isdataat.c b/src/detect-isdataat.c index 6bc4e961c07a..dd114515bc8b 100644 --- a/src/detect-isdataat.c +++ b/src/detect-isdataat.c @@ -88,7 +88,7 @@ static int DetectAbsentSetup(DetectEngineCtx *de_ctx, Signature *s, const char * return -1; } if (s->init_data->curbuf == NULL || s->init_data->list != (int)s->init_data->curbuf->id) { - SCLogError("unspected buffer for absent keyword"); + SCLogError("no buffer for absent keyword"); return -1; } const DetectBufferType *b = DetectEngineBufferTypeGetById(de_ctx, s->init_data->list); diff --git a/src/detect-transform-base64.c b/src/detect-transform-base64.c index e0fbdeeb44d6..d943c0caf0e6 100644 --- a/src/detect-transform-base64.c +++ b/src/detect-transform-base64.c @@ -91,7 +91,8 @@ static int DetectTransformFromBase64DecodeSetup( SCEnter(); - SCDetectTransformFromBase64Data *b64d = DetectTransformFromBase64DecodeParse(opts_str); + SCDetectTransformFromBase64Data *b64d = + DetectTransformFromBase64DecodeParse(opts_str ? opts_str : ""); if (b64d == NULL) SCReturnInt(r); @@ -151,6 +152,8 @@ static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options) if (num_decoded > 0) { // PrintRawDataFp(stdout, output, b64data->decoded_len); InspectionBufferCopy(buffer, decoded, num_decoded); + } else if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR) { + InspectionBufferTruncate(buffer, 0); } } @@ -355,6 +358,28 @@ static int DetectTransformFromBase64DecodeTest08(void) InspectionBufferFree(&buffer); PASS; } + +/* input is not base64 encoded with set_error */ +static int DetectTransformFromBase64DecodeTest09(void) +{ + /* A portion of this string will be decoded */ + const uint8_t *input = (const uint8_t *)"This is not base64-encoded"; + uint32_t input_len = strlen((char *)input); + + SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, + .mode = Base64ModeRFC2045, + .flags = DETECT_TRANSFORM_BASE64_FLAG_SET_ERROR }; + + InspectionBuffer buffer; + InspectionBufferInit(&buffer, input_len); + InspectionBufferSetup(NULL, -1, &buffer, input, input_len); + // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + TransformFromBase64Decode(&buffer, &b64d); + FAIL_IF_NOT(buffer.inspect_len == 15); + // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len); + InspectionBufferFree(&buffer); + PASS; +} static void DetectTransformFromBase64DecodeRegisterTests(void) { UtRegisterTest("DetectTransformFromBase64DecodeTest01", DetectTransformFromBase64DecodeTest01); @@ -367,5 +392,6 @@ static void DetectTransformFromBase64DecodeRegisterTests(void) UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06); UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07); UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08); + UtRegisterTest("DetectTransformFromBase64DecodeTest09", DetectTransformFromBase64DecodeTest09); } #endif