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 static void DetectTransformFromBase64DecodeRegisterTests(void);
44 #endif
45 static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options);
46 
47 #define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT (uint8_t) Base64ModeRFC4648
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(InspectionBuffer *buffer, void *options)
117 {
118  SCDetectTransformFromBase64Data *b64d = options;
119  const uint8_t *input = buffer->inspect;
120  const uint32_t input_len = buffer->inspect_len;
121  uint32_t decode_length = input_len;
122 
123  Base64Mode mode = b64d->mode;
124  uint32_t offset = b64d->offset;
125  uint32_t nbytes = b64d->nbytes;
126 
127  if (offset) {
128  if (offset > input_len) {
129  SCLogDebug("offset %d exceeds length %d; returning", offset, input_len);
130  return;
131  }
132  input += offset;
133  decode_length -= offset;
134  }
135 
136  if (nbytes) {
137  if (nbytes > decode_length) {
138  SCLogDebug("byte count %d plus offset %d exceeds length %d; returning", nbytes, offset,
139  input_len);
140  return;
141  }
142  decode_length = nbytes;
143  }
144  if (decode_length == 0) {
145  return;
146  }
147 
148  uint32_t decoded_size = Base64DecodeBufferSize(decode_length);
149  uint8_t decoded[decoded_size];
150  uint32_t num_decoded = Base64Decode((const uint8_t *)input, decode_length, mode, decoded);
151  if (num_decoded > 0) {
152  // PrintRawDataFp(stdout, output, b64data->decoded_len);
153  InspectionBufferCopy(buffer, decoded, num_decoded);
154  }
155 }
156 
157 #ifdef UNITTESTS
158 /* Simple success case -- check buffer */
159 static int DetectTransformFromBase64DecodeTest01(void)
160 {
161  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
162  uint32_t input_len = strlen((char *)input);
163  const char *result = "This is Suricata";
164  uint32_t result_len = strlen((char *)result);
165  SCDetectTransformFromBase64Data b64d = {
166  .nbytes = input_len,
168  };
169 
170  InspectionBuffer buffer;
171  InspectionBufferInit(&buffer, input_len);
172  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
173  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
174  TransformFromBase64Decode(&buffer, &b64d);
175  FAIL_IF_NOT(buffer.inspect_len == result_len);
176  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
177  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
178  InspectionBufferFree(&buffer);
179  PASS;
180 }
181 
182 /* Simple success case with RFC2045 -- check buffer */
183 static int DetectTransformFromBase64DecodeTest01a(void)
184 {
185  const uint8_t *input = (const uint8_t *)"Zm 9v Ym Fy";
186  uint32_t input_len = strlen((char *)input);
187  const char *result = "foobar";
188  uint32_t result_len = strlen((char *)result);
189  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 };
190 
191  InspectionBuffer buffer;
192  InspectionBufferInit(&buffer, input_len);
193  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
194  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
195  TransformFromBase64Decode(&buffer, &b64d);
196  FAIL_IF_NOT(buffer.inspect_len == result_len);
197  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
198  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
199  InspectionBufferFree(&buffer);
200  PASS;
201 }
202 
203 /* Decode failure case -- ensure no change to buffer */
204 static int DetectTransformFromBase64DecodeTest02(void)
205 {
206  const uint8_t *input = (const uint8_t *)"This is Suricata\n";
207  uint32_t input_len = strlen((char *)input);
208  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeStrict };
209  InspectionBuffer buffer;
210  InspectionBuffer buffer_orig;
211  InspectionBufferInit(&buffer, input_len);
212  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
213  buffer_orig = buffer;
214  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
215  TransformFromBase64Decode(&buffer, &b64d);
216  FAIL_IF_NOT(buffer.inspect_offset == buffer_orig.inspect_offset);
217  FAIL_IF_NOT(buffer.inspect_len == buffer_orig.inspect_len);
218  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
219  InspectionBufferFree(&buffer);
220  PASS;
221 }
222 
223 /* bytes > len so --> no transform */
224 static int DetectTransformFromBase64DecodeTest03(void)
225 {
226  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
227  uint32_t input_len = strlen((char *)input);
228 
229  SCDetectTransformFromBase64Data b64d = {
230  .nbytes = input_len + 1,
231  };
232 
233  InspectionBuffer buffer;
234  InspectionBufferInit(&buffer, input_len);
235  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
236  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
237  TransformFromBase64Decode(&buffer, &b64d);
238  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
239  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
240  InspectionBufferFree(&buffer);
241  PASS;
242 }
243 
244 /* offset > len so --> no transform */
245 static int DetectTransformFromBase64DecodeTest04(void)
246 {
247  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
248  uint32_t input_len = strlen((char *)input);
249 
250  SCDetectTransformFromBase64Data b64d = {
251  .offset = input_len + 1,
252  };
253 
254  InspectionBuffer buffer;
255  InspectionBufferInit(&buffer, input_len);
256  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
257  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
258  TransformFromBase64Decode(&buffer, &b64d);
259  FAIL_IF_NOT(strncmp((const char *)input, (const char *)buffer.inspect, input_len) == 0);
260  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
261  InspectionBufferFree(&buffer);
262  PASS;
263 }
264 
265 /* partial transform */
266 static int DetectTransformFromBase64DecodeTest05(void)
267 {
268  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
269  uint32_t input_len = strlen((char *)input);
270  const char *result = "This is S";
271  uint32_t result_len = strlen((char *)result);
272 
273  SCDetectTransformFromBase64Data b64d = {
274  .nbytes = 12,
276  };
277 
278  InspectionBuffer buffer;
279  InspectionBufferInit(&buffer, input_len);
280  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
281  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
282  TransformFromBase64Decode(&buffer, &b64d);
283  FAIL_IF_NOT(buffer.inspect_len == result_len);
284  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
285  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
286  InspectionBufferFree(&buffer);
287  PASS;
288 }
289 
290 /* transform from non-zero offset */
291 static int DetectTransformFromBase64DecodeTest06(void)
292 {
293  const uint8_t *input = (const uint8_t *)"VGhpcyBpcyBTdXJpY2F0YQ==";
294  uint32_t input_len = strlen((char *)input);
295  const char *result = "s is Suricata";
296  uint32_t result_len = strlen((char *)result);
297 
298  SCDetectTransformFromBase64Data b64d = {
299  .offset = 4,
301  };
302 
303  InspectionBuffer buffer;
304  InspectionBufferInit(&buffer, input_len);
305  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
306  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
307  TransformFromBase64Decode(&buffer, &b64d);
308  FAIL_IF_NOT(buffer.inspect_len == result_len);
309  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
310  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
311  InspectionBufferFree(&buffer);
312  PASS;
313 }
314 
315 /* partial decode */
316 static int DetectTransformFromBase64DecodeTest07(void)
317 {
318  /* Full string decodes to Hello World */
319  const uint8_t *input = (const uint8_t *)"SGVs bG8 gV29y bGQ=";
320  uint32_t input_len = strlen((char *)input);
321  const char *result = "Hello Wor";
322  uint32_t result_len = strlen((char *)result);
323 
324  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len - 4, /* NB: stop early */
325  .mode = Base64ModeRFC2045 };
326 
327  InspectionBuffer buffer;
328  InspectionBufferInit(&buffer, input_len);
329  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
330  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
331  TransformFromBase64Decode(&buffer, &b64d);
332  FAIL_IF_NOT(buffer.inspect_len == result_len);
333  FAIL_IF_NOT(strncmp(result, (const char *)buffer.inspect, result_len) == 0);
334  PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
335  InspectionBufferFree(&buffer);
336  PASS;
337 }
338 
339 /* input is not base64 encoded */
340 static int DetectTransformFromBase64DecodeTest08(void)
341 {
342  /* A portion of this string will be decoded */
343  const uint8_t *input = (const uint8_t *)"This is not base64-encoded";
344  uint32_t input_len = strlen((char *)input);
345 
346  SCDetectTransformFromBase64Data b64d = { .nbytes = input_len, .mode = Base64ModeRFC2045 };
347 
348  InspectionBuffer buffer;
349  InspectionBufferInit(&buffer, input_len);
350  InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
351  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
352  TransformFromBase64Decode(&buffer, &b64d);
353  FAIL_IF_NOT(buffer.inspect_len == 15);
354  // PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
355  InspectionBufferFree(&buffer);
356  PASS;
357 }
358 static void DetectTransformFromBase64DecodeRegisterTests(void)
359 {
360  UtRegisterTest("DetectTransformFromBase64DecodeTest01", DetectTransformFromBase64DecodeTest01);
362  "DetectTransformFromBase64DecodeTest01a", DetectTransformFromBase64DecodeTest01a);
363  UtRegisterTest("DetectTransformFromBase64DecodeTest02", DetectTransformFromBase64DecodeTest02);
364  UtRegisterTest("DetectTransformFromBase64DecodeTest03", DetectTransformFromBase64DecodeTest03);
365  UtRegisterTest("DetectTransformFromBase64DecodeTest04", DetectTransformFromBase64DecodeTest04);
366  UtRegisterTest("DetectTransformFromBase64DecodeTest05", DetectTransformFromBase64DecodeTest05);
367  UtRegisterTest("DetectTransformFromBase64DecodeTest06", DetectTransformFromBase64DecodeTest06);
368  UtRegisterTest("DetectTransformFromBase64DecodeTest07", DetectTransformFromBase64DecodeTest07);
369  UtRegisterTest("DetectTransformFromBase64DecodeTest08", DetectTransformFromBase64DecodeTest08);
370 }
371 #endif
SigTableElmt_::url
const char * url
Definition: detect.h:1312
detect-engine.h
SigTableElmt_::desc
const char * desc
Definition: detect.h:1311
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:128
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1299
SigTableElmt_::name
const char * name
Definition: detect.h:1309
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:373
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1303
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:843
rust.h
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1294
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:1558
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
detect.h
InspectionBuffer::inspect_offset
uint64_t inspect_offset
Definition: detect.h:375
PrintRawDataFp
void PrintRawDataFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition: util-print.c:111
DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT
#define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT
Definition: detect-transform-base64.c:47
detect-byte.h
InspectionBufferCopy
void InspectionBufferCopy(InspectionBuffer *buffer, uint8_t *buf, uint32_t buf_len)
Definition: detect-engine.c:1656
suricata-common.h
SigTableElmt_::Transform
void(* Transform)(InspectionBuffer *, void *context)
Definition: detect.h:1290
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:1502
InspectionBuffer::inspect_len
uint32_t inspect_len
Definition: detect.h:376
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect.h:374
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:1596
detect-parse.h
Signature_
Signature container.
Definition: detect.h:603
DetectSignatureAddTransform
int DetectSignatureAddTransform(Signature *s, int transform, void *options)
Definition: detect-parse.c:1713
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
InspectionBufferFree
void InspectionBufferFree(InspectionBuffer *buffer)
Definition: detect-engine.c:1615
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1301
DetectTransformFromBase64DecodeRegister
void DetectTransformFromBase64DecodeRegister(void)
Definition: detect-transform-base64.c:49