Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: transform/from_base64: Signal error condition (use with absent) #12337

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
27 changes: 26 additions & 1 deletion doc/userguide/rules/transforms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -209,12 +223,13 @@ The option values must be ``,`` separated and can appear in any order.

Format::

from_base64: [[bytes <value>] [, offset <offset_value> [, mode: strict|rfc4648|rfc2045]]]
from_base64: [[bytes <value>] [, offset <offset_value> [, 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.
Expand Down Expand Up @@ -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";
27 changes: 26 additions & 1 deletion rust/src/detect/transform_base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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,
);
}
}
13 changes: 9 additions & 4 deletions src/detect-engine-content-inspection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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");

Expand Down
2 changes: 1 addition & 1 deletion src/detect-isdataat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 27 additions & 1 deletion src/detect-transform-base64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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

Expand Down Expand Up @@ -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);
Expand All @@ -367,5 +392,6 @@ static void DetectTransformFromBase64DecodeRegisterTests(void)
UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06);
UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07);
UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08);
UtRegisterTest("DetectTransformFromBase64DecodeTest09", DetectTransformFromBase64DecodeTest09);
}
#endif
Loading