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