You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-transform-base64.c

372 lines
14 KiB
C

/* 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-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;
uint32_t decode_length = input_len;
Base64Mode 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;
}
if (decode_length == 0) {
return;
}
uint32_t decoded_size = Base64DecodeBufferSize(decode_length);
uint8_t decoded[decoded_size];
uint32_t num_decoded = Base64Decode((const uint8_t *)input, decode_length, mode, decoded);
if (num_decoded > 0) {
// PrintRawDataFp(stdout, output, b64data->decoded_len);
InspectionBufferCopy(buffer, decoded, num_decoded);
}
}
#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, .mode = Base64ModeStrict };
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