]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/transform: Add from_base64 transform
authorJeff Lucovsky <jlucovsky@oisf.net>
Thu, 22 Feb 2024 15:41:37 +0000 (10:41 -0500)
committerVictor Julien <victor@inliniac.net>
Sat, 22 Jun 2024 13:54:36 +0000 (15:54 +0200)
Issue: 6487

Implement the from_base64 transform:
    [bytes value] [offset value] [mode strict|rfc4648|rfc2045]

    The value for bytes and offset may be a byte_ variable or an
    unsigned integer.

src/Makefile.am
src/detect-engine-register.c
src/detect-engine-register.h
src/detect-transform-base64.c [new file with mode: 0644]
src/detect-transform-base64.h [new file with mode: 0644]

index eb4e4e72053a029c57c7f95ff0fbcdf2defa8cc6..8b0d6294ad529793a22f932a74b2aa221735637e 100755 (executable)
@@ -331,6 +331,7 @@ noinst_HEADERS = \
        detect-tls-version.h \
        detect-tls-random.h \
        detect-tos.h \
+       detect-transform-base64.h \
        detect-transform-casechange.h \
        detect-transform-compress-whitespace.h \
        detect-transform-dotprefix.h \
@@ -931,6 +932,7 @@ libsuricata_c_a_SOURCES = \
        detect-tls-version.c \
        detect-tls-random.c \
        detect-tos.c \
+       detect-transform-base64.c \
        detect-transform-casechange.c \
        detect-transform-compress-whitespace.c \
        detect-transform-dotprefix.c \
index ada4278a5b0f0fb9bba1b77603a6b6b88eeed4e0..9c2e25f79107517a883e05e7b9a53c521f558d2f 100644 (file)
 #include "detect-transform-xor.h"
 #include "detect-transform-casechange.h"
 #include "detect-transform-header-lowercase.h"
+#include "detect-transform-base64.h"
 
 #include "util-rule-vars.h"
 
@@ -733,6 +734,7 @@ void SigTableSetup(void)
     DetectTransformToLowerRegister();
     DetectTransformToUpperRegister();
     DetectTransformHeaderLowercaseRegister();
+    DetectTransformFromBase64DecodeRegister();
 
     DetectFileHandlerRegister();
 
index f174fea0b6cd731bd37099cc9f931519821ab692..94f8de15de83f646a650f984e8b4efbf81ae9f4b 100644 (file)
@@ -331,6 +331,7 @@ enum DetectKeywordId {
     DETECT_TRANSFORM_TOLOWER,
     DETECT_TRANSFORM_TOUPPER,
     DETECT_TRANSFORM_HEADER_LOWERCASE,
+    DETECT_TRANSFORM_FROM_BASE64,
 
     DETECT_AL_IKE_EXCH_TYPE,
     DETECT_AL_IKE_SPI_INITIATOR,
diff --git a/src/detect-transform-base64.c b/src/detect-transform-base64.c
new file mode 100644 (file)
index 0000000..be29c0b
--- /dev/null
@@ -0,0 +1,381 @@
+/* Copyright (C) 2024 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 <jlucovsky@oisf.net>
+ *
+ * Implements the from_base64 transformation keyword
+ */
+
+#include "suricata-common.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-byte.h"
+
+#include "rust.h"
+
+#include "detect-transform-base64.h"
+
+#include "util-base64.h"
+#include "util-unittest.h"
+#include "util-print.h"
+
+static int DetectTransformFromBase64DecodeSetup(DetectEngineCtx *, Signature *, const char *);
+static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *, void *);
+#ifdef UNITTESTS
+static void DetectTransformFromBase64DecodeRegisterTests(void);
+#endif
+static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options);
+
+#define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT (uint8_t) Base64ModeRFC4648
+
+void DetectTransformFromBase64DecodeRegister(void)
+{
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].name = "from_base64";
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer";
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64";
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup;
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode;
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree;
+#ifdef UNITTESTS
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].RegisterTests =
+            DetectTransformFromBase64DecodeRegisterTests;
+#endif
+    sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].flags |= SIGMATCH_OPTIONAL_OPT;
+}
+
+static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+    SCTransformBase64Free(ptr);
+}
+
+static SCDetectTransformFromBase64Data *DetectTransformFromBase64DecodeParse(const char *str)
+{
+    SCDetectTransformFromBase64Data *tbd = SCTransformBase64Parse(str);
+    if (tbd == NULL) {
+        SCLogError("invalid transform_base64 values");
+    }
+    return tbd;
+}
+
+/**
+ *  \internal
+ *  \brief Base64 decode the input buffer
+ *  \param det_ctx detection engine ctx
+ *  \param s signature
+ *  \param opts_str transform options, if any
+ *  \retval 0 No decode
+ *  \retval >0 Decoded byte count
+ */
+static int DetectTransformFromBase64DecodeSetup(
+        DetectEngineCtx *de_ctx, Signature *s, const char *opts_str)
+{
+    int r = -1;
+
+    SCEnter();
+
+    SCDetectTransformFromBase64Data *b64d = DetectTransformFromBase64DecodeParse(opts_str);
+    if (b64d == NULL)
+        SCReturnInt(r);
+
+    if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR) {
+        SCLogError("offset value must be a value, not a variable name");
+        goto exit_path;
+    }
+
+    if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR) {
+        SCLogError("byte value must be a value, not a variable name");
+        goto exit_path;
+    }
+
+    r = DetectSignatureAddTransform(s, DETECT_TRANSFORM_FROM_BASE64, b64d);
+
+exit_path:
+    if (r != 0)
+        DetectTransformFromBase64DecodeFree(de_ctx, b64d);
+    SCReturnInt(r);
+}
+
+static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options)
+{
+    SCDetectTransformFromBase64Data *b64d = options;
+    const uint8_t *input = buffer->inspect;
+    const uint32_t input_len = buffer->inspect_len;
+    /*
+     * The output buffer is larger than needed. On average,
+     * a base64 encoded string is 75% of the encoded
+     * string.
+     */
+    uint8_t output[input_len];
+    uint32_t decode_length = input_len;
+
+    DetectBase64Mode mode = b64d->mode;
+    uint32_t offset = b64d->offset;
+    uint32_t nbytes = b64d->nbytes;
+
+    if (offset) {
+        if (offset > input_len) {
+            SCLogDebug("offset %d exceeds length %d; returning", offset, input_len);
+            return;
+        }
+        input += offset;
+        decode_length -= offset;
+    }
+
+    if (nbytes) {
+        if (nbytes > decode_length) {
+            SCLogDebug("byte count %d plus offset %d exceeds length %d; returning", nbytes, offset,
+                    input_len);
+            return;
+        }
+        decode_length = nbytes;
+    }
+
+    // PrintRawDataFp(stdout, input, input_len);
+    uint32_t decoded_length = 0;
+    uint32_t consumed = 0;
+    Base64Ecode code =
+            DecodeBase64(output, input_len, input, decode_length, &consumed, &decoded_length, mode);
+    if (code != BASE64_ECODE_ERR) {
+        // PrintRawDataFp(stdout, output, decoded_length);
+        if (decoded_length) {
+            InspectionBufferCopy(buffer, output, decoded_length);
+        }
+    }
+}
+
+#ifdef UNITTESTS
+/* Simple success case -- check buffer */
+static int DetectTransformFromBase64DecodeTest01(void)
+{
+    const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
+    uint32_t input_len = strlen((char *)input);
+    const char *result = "This is Suricata";
+    uint32_t result_len = strlen((char *)result);
+    SCDetectTransformFromBase64Data b64d = {
+        .nbytes = input_len,
+        .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT,
+    };
+
+    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 == result_len);
+    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* Simple success case with RFC2045 -- check buffer */
+static int DetectTransformFromBase64DecodeTest01a(void)
+{
+    const uint8_t *input = (const uint8_t *)"Zm 9v Ym Fy";
+    uint32_t input_len = strlen((char *)input);
+    const char *result = "foobar";
+    uint32_t result_len = strlen((char *)result);
+    SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 };
+
+    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 == result_len);
+    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* Decode failure case -- ensure no change to buffer */
+static int DetectTransformFromBase64DecodeTest02(void)
+{
+    const uint8_t *input = (const uint8_t *)"This is Suricata\n";
+    uint32_t input_len = strlen((char *)input);
+    SCDetectTransformFromBase64Data b64d = {
+        .nbytes = input_len,
+    };
+    InspectionBuffer buffer;
+    InspectionBuffer buffer_orig;
+    InspectionBufferInit(&buffer, input_len);
+    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
+    buffer_orig = buffer;
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    TransformFromBase64Decode(&buffer, &b64d);
+    FAIL_IF_NOT(buffer.inspect_offset == buffer_orig.inspect_offset);
+    FAIL_IF_NOT(buffer.inspect_len == buffer_orig.inspect_len);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* bytes > len so --> no transform */
+static int DetectTransformFromBase64DecodeTest03(void)
+{
+    const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
+    uint32_t input_len = strlen((char *)input);
+
+    SCDetectTransformFromBase64Data b64d = {
+        .nbytes = input_len + 1,
+    };
+
+    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(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* offset > len so --> no transform */
+static int DetectTransformFromBase64DecodeTest04(void)
+{
+    const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
+    uint32_t input_len = strlen((char *)input);
+
+    SCDetectTransformFromBase64Data b64d = {
+        .offset = input_len + 1,
+    };
+
+    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(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* partial transform */
+static int DetectTransformFromBase64DecodeTest05(void)
+{
+    const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
+    uint32_t input_len = strlen((char *)input);
+    const char *result = "This is S";
+    uint32_t result_len = strlen((char *)result);
+
+    SCDetectTransformFromBase64Data b64d = {
+        .nbytes = 12,
+        .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT,
+    };
+
+    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 == result_len);
+    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* transform from non-zero offset */
+static int DetectTransformFromBase64DecodeTest06(void)
+{
+    const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
+    uint32_t input_len = strlen((char *)input);
+    const char *result = "s is Suricata";
+    uint32_t result_len = strlen((char *)result);
+
+    SCDetectTransformFromBase64Data b64d = {
+        .offset = 4,
+        .mode = DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT,
+    };
+
+    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 == result_len);
+    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* partial decode */
+static int DetectTransformFromBase64DecodeTest07(void)
+{
+    /* Full string decodes to Hello World */
+    const uint8_t *input = (const uint8_t *)"SGVs bG8 gV29y bGQ=";
+    uint32_t input_len = strlen((char *)input);
+    const char *result = "Hello Wor";
+    uint32_t result_len = strlen((char *)result);
+
+    SCDetectTransformFromBase64Data b64d = { .nbytes = input_len - 4, /* NB: stop early */
+        .mode = Base64ModeRFC2045 };
+
+    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 == result_len);
+    FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
+    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
+    InspectionBufferFree(&buffer);
+    PASS;
+}
+
+/* input is not base64 encoded */
+static int DetectTransformFromBase64DecodeTest08(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 };
+
+    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);
+    UtRegisterTest(
+            "DetectTransformFromBase64DecodeTest01a", DetectTransformFromBase64DecodeTest01a);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest02", DetectTransformFromBase64DecodeTest02);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest03", DetectTransformFromBase64DecodeTest03);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest04", DetectTransformFromBase64DecodeTest04);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest05", DetectTransformFromBase64DecodeTest05);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07);
+    UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08);
+}
+#endif
diff --git a/src/detect-transform-base64.h b/src/detect-transform-base64.h
new file mode 100644 (file)
index 0000000..fc0847b
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright (C) 2024 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 <jlucovsky@oisf.net>
+ */
+
+#ifndef SURICATA_DETECT_TRANSFORM_BASE64_H
+#define SURICATA_DETECT_TRANSFORM_BASE64_H
+
+/* prototypes */
+void DetectTransformFromBase64DecodeRegister(void);
+
+#endif /* SURICATA_DETECT_TRANSFORM_BASE64_H */