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) Base64ModeRFC4648
44 static void DetectTransformFromBase64DecodeRegisterTests(void);
45 #endif
46 static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options);
47 
49 {
51  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer";
52  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64";
53  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup;
54  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode;
55  sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree;
56 #ifdef UNITTESTS
58  DetectTransformFromBase64DecodeRegisterTests;
59 #endif
61 }
62 
63 static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *de_ctx, void *ptr)
64 {
65  SCTransformBase64Free(ptr);
66 }
67 
68 static SCDetectTransformFromBase64Data *DetectTransformFromBase64DecodeParse(const char *str)
69 {
70  SCDetectTransformFromBase64Data *tbd = SCTransformBase64Parse(str);
71  if (tbd == NULL) {
72  SCLogError("invalid transform_base64 values");
73  }
74  return tbd;
75 }
76 
77 /**
78  * \internal
79  * \brief Base64 decode the input buffer
80  * \param det_ctx detection engine ctx
81  * \param s signature
82  * \param opts_str transform options, if any
83  * \retval 0 No decode
84  * \retval >0 Decoded byte count
85  */
86 static int DetectTransformFromBase64DecodeSetup(
87  DetectEngineCtx *de_ctx, Signature *s, const char *opts_str)
88 {
89  int r = -1;
90 
91  SCEnter();
92 
93  SCDetectTransformFromBase64Data *b64d = DetectTransformFromBase64DecodeParse(opts_str);
94  if (b64d == NULL)
95  SCReturnInt(r);
96 
97  if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR) {
98  SCLogError("offset value must be a value, not a variable name");
99  goto exit_path;
100  }
101 
102  if (b64d->flags & DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR) {
103  SCLogError("byte value must be a value, not a variable name");
104  goto exit_path;
105  }
106 
108 
109 exit_path:
110  if (r != 0)
111  DetectTransformFromBase64DecodeFree(de_ctx, b64d);
112  SCReturnInt(r);
113 }
114 
115 static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options)
116 {
117  SCDetectTransformFromBase64Data *b64d = options;
118  const uint8_t *input = buffer->inspect;
119  const uint32_t input_len = buffer->inspect_len;
120  uint32_t decode_length = input_len;
121 
122  Base64Mode mode = b64d->mode;
123  uint32_t offset = b64d->offset;
124  uint32_t nbytes = b64d->nbytes;
125 
126  if (offset) {
127  if (offset > input_len) {
128  SCLogDebug("offset %d exceeds length %d; returning", offset, input_len);
129  return;
130  }
131  input += offset;
132  decode_length -= offset;
133  }
134 
135  if (nbytes) {
136  if (nbytes > decode_length) {
137  SCLogDebug("byte count %d plus offset %d exceeds length %d; returning", nbytes, offset,
138  input_len);
139  return;
140  }
141  decode_length = nbytes;
142  }
143  if (decode_length == 0) {
144  return;
145  }
146 
147  uint32_t decoded_size = Base64DecodeBufferSize(decode_length);
148  uint8_t decoded[decoded_size];
149  uint32_t num_decoded = Base64Decode((const uint8_t *)input, decode_length, mode, decoded);
150  if (num_decoded > 0) {
151  // PrintRawDataFp(stdout, output, b64data->decoded_len);
152  InspectionBufferCopy(buffer, decoded, num_decoded);
153  }
154 }
155 
156 #ifdef UNITTESTS
157 /* Simple success case -- check buffer */
158 static int DetectTransformFromBase64DecodeTest01(void)
159 {
160  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
161  uint32_t input_len = strlen((char *)input);
162  const char *result = "This is Suricata";
163  uint32_t result_len = strlen((char *)result);
164  SCDetectTransformFromBase64Data b64d = {
165  .nbytes = input_len,
167  };
168 
169  InspectionBuffer buffer;
170  InspectionBufferInit(&buffer, input_len);
171  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
172  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
173  TransformFromBase64Decode(&buffer, &b64d);
174  FAIL_IF_NOT(buffer.inspect_len == result_len);
175  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
176  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
177  InspectionBufferFree(&buffer);
178  PASS;
179 }
180 
181 /* Simple success case with RFC2045 -- check buffer */
182 static int DetectTransformFromBase64DecodeTest01a(void)
183 {
184  const uint8_t *input = (const uint8_t *)"Zm 9v Ym Fy";
185  uint32_t input_len = strlen((char *)input);
186  const char *result = "foobar";
187  uint32_t result_len = strlen((char *)result);
188  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 };
189 
190  InspectionBuffer buffer;
191  InspectionBufferInit(&buffer, input_len);
192  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
193  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
194  TransformFromBase64Decode(&buffer, &b64d);
195  FAIL_IF_NOT(buffer.inspect_len == result_len);
196  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
197  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
198  InspectionBufferFree(&buffer);
199  PASS;
200 }
201 
202 /* Decode failure case -- ensure no change to buffer */
203 static int DetectTransformFromBase64DecodeTest02(void)
204 {
205  const uint8_t *input = (const uint8_t *)"This is Suricata\n";
206  uint32_t input_len = strlen((char *)input);
207  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeStrict };
208  InspectionBuffer buffer;
209  InspectionBuffer buffer_orig;
210  InspectionBufferInit(&buffer, input_len);
211  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
212  buffer_orig = buffer;
213  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
214  TransformFromBase64Decode(&buffer, &b64d);
215  FAIL_IF_NOT(buffer.inspect_offset == buffer_orig.inspect_offset);
216  FAIL_IF_NOT(buffer.inspect_len == buffer_orig.inspect_len);
217  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
218  InspectionBufferFree(&buffer);
219  PASS;
220 }
221 
222 /* bytes > len so --> no transform */
223 static int DetectTransformFromBase64DecodeTest03(void)
224 {
225  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
226  uint32_t input_len = strlen((char *)input);
227 
228  SCDetectTransformFromBase64Data b64d = {
229  .nbytes = input_len + 1,
230  };
231 
232  InspectionBuffer buffer;
233  InspectionBufferInit(&buffer, input_len);
234  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
235  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
236  TransformFromBase64Decode(&buffer, &b64d);
237  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
238  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
239  InspectionBufferFree(&buffer);
240  PASS;
241 }
242 
243 /* offset > len so --> no transform */
244 static int DetectTransformFromBase64DecodeTest04(void)
245 {
246  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
247  uint32_t input_len = strlen((char *)input);
248 
249  SCDetectTransformFromBase64Data b64d = {
250  .offset = input_len + 1,
251  };
252 
253  InspectionBuffer buffer;
254  InspectionBufferInit(&buffer, input_len);
255  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
256  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
257  TransformFromBase64Decode(&buffer, &b64d);
258  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
259  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
260  InspectionBufferFree(&buffer);
261  PASS;
262 }
263 
264 /* partial transform */
265 static int DetectTransformFromBase64DecodeTest05(void)
266 {
267  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
268  uint32_t input_len = strlen((char *)input);
269  const char *result = "This is S";
270  uint32_t result_len = strlen((char *)result);
271 
272  SCDetectTransformFromBase64Data b64d = {
273  .nbytes = 12,
275  };
276 
277  InspectionBuffer buffer;
278  InspectionBufferInit(&buffer, input_len);
279  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
280  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
281  TransformFromBase64Decode(&buffer, &b64d);
282  FAIL_IF_NOT(buffer.inspect_len == result_len);
283  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
284  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
285  InspectionBufferFree(&buffer);
286  PASS;
287 }
288 
289 /* transform from non-zero offset */
290 static int DetectTransformFromBase64DecodeTest06(void)
291 {
292  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
293  uint32_t input_len = strlen((char *)input);
294  const char *result = "s is Suricata";
295  uint32_t result_len = strlen((char *)result);
296 
297  SCDetectTransformFromBase64Data b64d = {
298  .offset = 4,
300  };
301 
302  InspectionBuffer buffer;
303  InspectionBufferInit(&buffer, input_len);
304  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
305  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
306  TransformFromBase64Decode(&buffer, &b64d);
307  FAIL_IF_NOT(buffer.inspect_len == result_len);
308  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
309  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
310  InspectionBufferFree(&buffer);
311  PASS;
312 }
313 
314 /* partial decode */
315 static int DetectTransformFromBase64DecodeTest07(void)
316 {
317  /* Full string decodes to Hello World */
318  const uint8_t *input = (const uint8_t *)"SGVs bG8 gV29y bGQ=";
319  uint32_t input_len = strlen((char *)input);
320  const char *result = "Hello Wor";
321  uint32_t result_len = strlen((char *)result);
322 
323  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len - 4, /* NB: stop early */
324  .mode = Base64ModeRFC2045 };
325 
326  InspectionBuffer buffer;
327  InspectionBufferInit(&buffer, input_len);
328  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
329  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
330  TransformFromBase64Decode(&buffer, &b64d);
331  FAIL_IF_NOT(buffer.inspect_len == result_len);
332  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
333  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
334  InspectionBufferFree(&buffer);
335  PASS;
336 }
337 
338 /* input is not base64 encoded */
339 static int DetectTransformFromBase64DecodeTest08(void)
340 {
341  /* A portion of this string will be decoded */
342  const uint8_t *input = (const uint8_t *)"This is not base64-encoded";
343  uint32_t input_len = strlen((char *)input);
344 
345  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 };
346 
347  InspectionBuffer buffer;
348  InspectionBufferInit(&buffer, input_len);
349  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
350  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
351  TransformFromBase64Decode(&buffer, &b64d);
352  FAIL_IF_NOT(buffer.inspect_len == 15);
353  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
354  InspectionBufferFree(&buffer);
355  PASS;
356 }
357 static void DetectTransformFromBase64DecodeRegisterTests(void)
358 {
359  UtRegisterTest("DetectTransformFromBase64DecodeTest01", DetectTransformFromBase64DecodeTest01);
361  "DetectTransformFromBase64DecodeTest01a", DetectTransformFromBase64DecodeTest01a);
362  UtRegisterTest("DetectTransformFromBase64DecodeTest02", DetectTransformFromBase64DecodeTest02);
363  UtRegisterTest("DetectTransformFromBase64DecodeTest03", DetectTransformFromBase64DecodeTest03);
364  UtRegisterTest("DetectTransformFromBase64DecodeTest04", DetectTransformFromBase64DecodeTest04);
365  UtRegisterTest("DetectTransformFromBase64DecodeTest05", DetectTransformFromBase64DecodeTest05);
366  UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06);
367  UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07);
368  UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08);
369 }
370 #endif
SigTableElmt_::url
const char * url
Definition: detect.h:1314
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1313
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:153
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1301
SigTableElmt_::name
const char * name
Definition: detect.h:1311
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:375
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1305
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:846
rust.h
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1296
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:1560
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:18
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
detect.h
InspectionBuffer::inspect_offset
uint64_t inspect_offset
Definition: detect.h:377
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:1658
suricata-common.h
SigTableElmt_::Transform
void(* Transform)(InspectionBuffer *, void *context)
Definition: detect.h:1292
detect-transform-base64.h
DETECT_TRANSFORM_FROM_BASE64
@ DETECT_TRANSFORM_FROM_BASE64
Definition: detect-engine-register.h:322
SIGMATCH_OPTIONAL_OPT
#define SIGMATCH_OPTIONAL_OPT
Definition: detect.h:1504
InspectionBuffer::inspect_len
uint32_t inspect_len
Definition: detect.h:378
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect.h:376
str
#define str(s)
Definition: suricata-common.h:291
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:1598
detect-parse.h
Signature_
Signature container.
Definition: detect.h:606
DetectSignatureAddTransform
int DetectSignatureAddTransform(Signature *s, int transform, void *options)
Definition: detect-parse.c:1740
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
InspectionBufferFree
void InspectionBufferFree(InspectionBuffer *buffer)
Definition: detect-engine.c:1617
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1303
DetectTransformFromBase64DecodeRegister
void DetectTransformFromBase64DecodeRegister(void)
Definition: detect-transform-base64.c:48