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