suricata
detect-transform-base64.c
Go to the documentation of this file.
1 /* Copyright (C) 2024 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Jeff Lucovsky <jlucovsky@oisf.net>
22  *
23  * Implements the from_base64 transformation keyword
24  */
25 
26 #include "suricata-common.h"
27 
28 #include "detect.h"
29 #include "detect-parse.h"
30 #include "detect-engine.h"
31 #include "detect-byte.h"
32 
33 #include "rust.h"
34 
36 
37 #include "util-unittest.h"
38 #include "util-print.h"
39 
40 static int DetectTransformFromBase64DecodeSetup(DetectEngineCtx *, Signature *, const char *);
41 static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *, void *);
42 #ifdef UNITTESTS
43 #define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT (uint8_t) SCBase64ModeRFC4648
44 static void DetectTransformFromBase64DecodeRegisterTests(void);
45 #endif
46 static void TransformFromBase64Decode(
47  DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options);
48 
50 {
52  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer";
53  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64";
54  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup;
55  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode;
56  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree;
57 #ifdef UNITTESTS
59  DetectTransformFromBase64DecodeRegisterTests;
60 #endif
62 }
63 
64 static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *de_ctx, void *ptr)
65 {
66  SCTransformBase64Free(ptr);
67 }
68 
69 static SCDetectTransformFromBase64Data *DetectTransformFromBase64DecodeParse(const char *str)
70 {
71  SCDetectTransformFromBase64Data *tbd = SCTransformBase64Parse(str);
72  if (tbd == NULL) {
73  SCLogError("invalid transform_base64 values");
74  }
75  return tbd;
76 }
77 
78 /**
79  * \internal
80  * \brief Base64 decode the input buffer
81  * \param det_ctx detection engine ctx
82  * \param s signature
83  * \param opts_str transform options, if any
84  * \retval 0 No decode
85  * \retval >0 Decoded byte count
86  */
87 static int DetectTransformFromBase64DecodeSetup(
88  DetectEngineCtx *de_ctx, Signature *s, const char *opts_str)
89 {
90  int r = -1;
91 
92  SCEnter();
93 
94  SCDetectTransformFromBase64Data *b64d = DetectTransformFromBase64DecodeParse(opts_str);
95  if (b64d == NULL)
96  SCReturnInt(r);
97 
98  if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR) {
99  SCLogError("offset value must be a value, not a variable name");
100  goto exit_path;
101  }
102 
103  if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR) {
104  SCLogError("byte value must be a value, not a variable name");
105  goto exit_path;
106  }
107 
109 
110 exit_path:
111  if (r != 0)
112  DetectTransformFromBase64DecodeFree(de_ctx, b64d);
113  SCReturnInt(r);
114 }
115 
116 static void TransformFromBase64Decode(
117  DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options)
118 {
119  SCDetectTransformFromBase64Data *b64d = options;
120  const uint8_t *input = buffer->inspect;
121  const uint32_t input_len = buffer->inspect_len;
122  uint32_t decode_length = input_len;
123 
124  SCBase64Mode mode = b64d->mode;
125  uint32_t offset = b64d->offset;
126  uint32_t nbytes = b64d->nbytes;
127 
128  if (offset) {
129  if (offset > input_len) {
130  SCLogDebug("offset %d exceeds length %d; returning", offset, input_len);
131  return;
132  }
133  input += offset;
134  decode_length -= offset;
135  }
136 
137  if (nbytes) {
138  if (nbytes > decode_length) {
139  SCLogDebug("byte count %d plus offset %d exceeds length %d; returning", nbytes, offset,
140  input_len);
141  return;
142  }
143  decode_length = nbytes;
144  }
145  if (decode_length == 0) {
146  return;
147  }
148 
149  uint32_t decoded_size = SCBase64DecodeBufferSize(decode_length);
150  uint8_t decoded[decoded_size];
151  uint32_t num_decoded = SCBase64Decode((const uint8_t *)input, decode_length, mode, decoded);
152  if (num_decoded > 0) {
153  // PrintRawDataFp(stdout, output, b64data->decoded_len);
154  InspectionBufferCopy(buffer, decoded, num_decoded);
155  }
156 }
157 
158 #ifdef UNITTESTS
159 /* Simple success case -- check buffer */
160 static int DetectTransformFromBase64DecodeTest01(void)
161 {
162  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
163  uint32_t input_len = strlen((char *)input);
164  const char *result = "This is Suricata";
165  uint32_t result_len = strlen((char *)result);
166  SCDetectTransformFromBase64Data b64d = {
167  .nbytes = input_len,
169  };
170 
171  InspectionBuffer buffer;
172  InspectionBufferInit(&buffer, input_len);
173  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
174  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
175  TransformFromBase64Decode(NULL, &buffer, &b64d);
176  FAIL_IF_NOT(buffer.inspect_len == result_len);
177  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
178  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
179  InspectionBufferFree(&buffer);
180  PASS;
181 }
182 
183 /* Simple success case with RFC2045 -- check buffer */
184 static int DetectTransformFromBase64DecodeTest01a(void)
185 {
186  const uint8_t *input = (const uint8_t *)"Zm 9v Ym Fy";
187  uint32_t input_len = strlen((char *)input);
188  const char *result = "foobar";
189  uint32_t result_len = strlen((char *)result);
190  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = SCBase64ModeRFC2045 };
191 
192  InspectionBuffer buffer;
193  InspectionBufferInit(&buffer, input_len);
194  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
195  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
196  TransformFromBase64Decode(NULL, &buffer, &b64d);
197  FAIL_IF_NOT(buffer.inspect_len == result_len);
198  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
199  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
200  InspectionBufferFree(&buffer);
201  PASS;
202 }
203 
204 /* Decode failure case -- ensure no change to buffer */
205 static int DetectTransformFromBase64DecodeTest02(void)
206 {
207  const uint8_t *input = (const uint8_t *)"This is Suricata\n";
208  uint32_t input_len = strlen((char *)input);
209  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = SCBase64ModeStrict };
210  InspectionBuffer buffer;
211  InspectionBuffer buffer_orig;
212  InspectionBufferInit(&buffer, input_len);
213  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
214  buffer_orig = buffer;
215  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
216  TransformFromBase64Decode(NULL, &buffer, &b64d);
217  FAIL_IF_NOT(buffer.inspect_offset == buffer_orig.inspect_offset);
218  FAIL_IF_NOT(buffer.inspect_len == buffer_orig.inspect_len);
219  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
220  InspectionBufferFree(&buffer);
221  PASS;
222 }
223 
224 /* bytes > len so --> no transform */
225 static int DetectTransformFromBase64DecodeTest03(void)
226 {
227  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
228  uint32_t input_len = strlen((char *)input);
229 
230  SCDetectTransformFromBase64Data b64d = {
231  .nbytes = input_len + 1,
232  };
233 
234  InspectionBuffer buffer;
235  InspectionBufferInit(&buffer, input_len);
236  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
237  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
238  TransformFromBase64Decode(NULL, &buffer, &b64d);
239  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
240  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
241  InspectionBufferFree(&buffer);
242  PASS;
243 }
244 
245 /* offset > len so --> no transform */
246 static int DetectTransformFromBase64DecodeTest04(void)
247 {
248  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
249  uint32_t input_len = strlen((char *)input);
250 
251  SCDetectTransformFromBase64Data b64d = {
252  .offset = input_len + 1,
253  };
254 
255  InspectionBuffer buffer;
256  InspectionBufferInit(&buffer, input_len);
257  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
258  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
259  TransformFromBase64Decode(NULL, &buffer, &b64d);
260  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
261  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
262  InspectionBufferFree(&buffer);
263  PASS;
264 }
265 
266 /* partial transform */
267 static int DetectTransformFromBase64DecodeTest05(void)
268 {
269  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
270  uint32_t input_len = strlen((char *)input);
271  const char *result = "This is S";
272  uint32_t result_len = strlen((char *)result);
273 
274  SCDetectTransformFromBase64Data b64d = {
275  .nbytes = 12,
277  };
278 
279  InspectionBuffer buffer;
280  InspectionBufferInit(&buffer, input_len);
281  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
282  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
283  TransformFromBase64Decode(NULL, &buffer, &b64d);
284  FAIL_IF_NOT(buffer.inspect_len == result_len);
285  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
286  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
287  InspectionBufferFree(&buffer);
288  PASS;
289 }
290 
291 /* transform from non-zero offset */
292 static int DetectTransformFromBase64DecodeTest06(void)
293 {
294  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
295  uint32_t input_len = strlen((char *)input);
296  const char *result = "s is Suricata";
297  uint32_t result_len = strlen((char *)result);
298 
299  SCDetectTransformFromBase64Data b64d = {
300  .offset = 4,
302  };
303 
304  InspectionBuffer buffer;
305  InspectionBufferInit(&buffer, input_len);
306  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
307  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
308  TransformFromBase64Decode(NULL, &buffer, &b64d);
309  FAIL_IF_NOT(buffer.inspect_len == result_len);
310  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
311  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
312  InspectionBufferFree(&buffer);
313  PASS;
314 }
315 
316 /* partial decode */
317 static int DetectTransformFromBase64DecodeTest07(void)
318 {
319  /* Full string decodes to Hello World */
320  const uint8_t *input = (const uint8_t *)"SGVs bG8 gV29y bGQ=";
321  uint32_t input_len = strlen((char *)input);
322  const char *result = "Hello Wor";
323  uint32_t result_len = strlen((char *)result);
324 
325  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len - 4, /* NB: stop early */
326  .mode = SCBase64ModeRFC2045 };
327 
328  InspectionBuffer buffer;
329  InspectionBufferInit(&buffer, input_len);
330  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
331  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
332  TransformFromBase64Decode(NULL, &buffer, &b64d);
333  FAIL_IF_NOT(buffer.inspect_len == result_len);
334  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
335  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
336  InspectionBufferFree(&buffer);
337  PASS;
338 }
339 
340 /* input is not base64 encoded */
341 static int DetectTransformFromBase64DecodeTest08(void)
342 {
343  /* A portion of this string will be decoded */
344  const uint8_t *input = (const uint8_t *)"This is not base64-encoded";
345  uint32_t input_len = strlen((char *)input);
346 
347  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = SCBase64ModeRFC2045 };
348 
349  InspectionBuffer buffer;
350  InspectionBufferInit(&buffer, input_len);
351  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
352  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
353  TransformFromBase64Decode(NULL, &buffer, &b64d);
354  FAIL_IF_NOT(buffer.inspect_len == 15);
355  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
356  InspectionBufferFree(&buffer);
357  PASS;
358 }
359 static void DetectTransformFromBase64DecodeRegisterTests(void)
360 {
361  UtRegisterTest("DetectTransformFromBase64DecodeTest01", DetectTransformFromBase64DecodeTest01);
363  "DetectTransformFromBase64DecodeTest01a", DetectTransformFromBase64DecodeTest01a);
364  UtRegisterTest("DetectTransformFromBase64DecodeTest02", DetectTransformFromBase64DecodeTest02);
365  UtRegisterTest("DetectTransformFromBase64DecodeTest03", DetectTransformFromBase64DecodeTest03);
366  UtRegisterTest("DetectTransformFromBase64DecodeTest04", DetectTransformFromBase64DecodeTest04);
367  UtRegisterTest("DetectTransformFromBase64DecodeTest05", DetectTransformFromBase64DecodeTest05);
368  UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06);
369  UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07);
370  UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08);
371 }
372 #endif
SigTableElmt_::url
const char * url
Definition: detect.h:1405
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1404
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:155
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1392
SigTableElmt_::name
const char * name
Definition: detect.h:1402
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
InspectionBuffer
Definition: detect.h:380
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1396
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:920
rust.h
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1387
util-unittest.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
InspectionBufferInit
void InspectionBufferInit(InspectionBuffer *buffer, uint32_t initial_size)
Definition: detect-engine.c:1656
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:18
DetectEngineThreadCtx_
Definition: detect.h:1197
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
detect.h
InspectionBuffer::inspect_offset
uint64_t inspect_offset
Definition: detect.h:382
PrintRawDataFp
void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition: util-print.c:112
DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT
#define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT
Definition: detect-transform-base64.c:43
detect-byte.h
InspectionBufferCopy
void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
Definition: detect-engine.c:1768
suricata-common.h
detect-transform-base64.h
DETECT_TRANSFORM_FROM_BASE64
@ DETECT_TRANSFORM_FROM_BASE64
Definition: detect-engine-register.h:320
SIGMATCH_OPTIONAL_OPT
#define SIGMATCH_OPTIONAL_OPT
Definition: detect.h:1601
InspectionBuffer::inspect_len
uint32_t inspect_len
Definition: detect.h:383
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect.h:381
str
#define str(s)
Definition: suricata-common.h:300
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
InspectionBufferSetup
void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id, InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
Definition: detect-engine.c:1712
detect-parse.h
Signature_
Signature container.
Definition: detect.h:669
DetectSignatureAddTransform
int DetectSignatureAddTransform(Signature *s, int transform, void *options)
Definition: detect-parse.c:2105
SigTableElmt_::Transform
void(* Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context)
Definition: detect.h:1383
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
InspectionBufferFree
void InspectionBufferFree(InspectionBuffer *buffer)
Definition: detect-engine.c:1727
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1394
DetectTransformFromBase64DecodeRegister
void DetectTransformFromBase64DecodeRegister(void)
Definition: detect-transform-base64.c:49