suricata
detect-file-data.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2022 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 Giuseppe Longo <giuseppelng@gmail.com>
22  * \author Victor Julien <victor@inliniac.net>
23  *
24  */
25 
26 #ifdef UNITTESTS
27 
28 #include "../stream-tcp.h"
29 #include "../detect.h"
30 #include "../detect-isdataat.h"
31 #include "../detect-engine-inspect-buffer.h"
32 #include "../util-file-decompression.h"
33 #include "../util-file-swf-decompression.h"
34 #include "../app-layer-events.h"
35 #include "../app-layer-htp.h"
36 
37 #include <zlib.h>
38 
39 static uint8_t *DetectFiledataSwfCreateCws(const uint8_t *plain, uint32_t plain_len,
40  uint32_t file_len, int compression_level, uint32_t *cws_len)
41 {
42  uLongf compressed_len = compressBound(plain_len);
43  uint8_t *cws = SCMalloc(SWF_HEADER_LEN + compressed_len);
44  if (cws == NULL)
45  return NULL;
46 
47  cws[0] = 'C';
48  cws[1] = 'W';
49  cws[2] = 'S';
50  cws[3] = 0x06;
51  cws[4] = (uint8_t)file_len;
52  cws[5] = (uint8_t)(file_len >> 8);
53  cws[6] = (uint8_t)(file_len >> 16);
54  cws[7] = (uint8_t)(file_len >> 24);
55 
56  int r = compress2(cws + SWF_HEADER_LEN, &compressed_len, plain, plain_len, compression_level);
57  if (r != Z_OK || compressed_len > UINT32_MAX - SWF_HEADER_LEN) {
58  SCFree(cws);
59  return NULL;
60  }
61  *cws_len = SWF_HEADER_LEN + (uint32_t)compressed_len;
62  return cws;
63 }
64 
65 // clang-format off
66 static const uint8_t swf_lzma_fixture[] = {
67  0x5a, 0x57, 0x53, 0x17, 0x5c, 0x24, 0x00, 0x00, 0xb7, 0x21, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x20,
68  0x00, 0x00, 0x3b, 0xff, 0xfc, 0x8e, 0x19, 0xfa, 0xdf, 0xe7, 0x66, 0x08, 0xa0, 0x3d, 0x3e, 0x85,
69  0xf5, 0x75, 0x6f, 0xd0, 0x7e, 0x61, 0x35, 0x1b, 0x1a, 0x8b, 0x16, 0x4d, 0xdf, 0x05, 0x32, 0xfe,
70  0xa4, 0x4c, 0x46, 0x49, 0xb7, 0x7b, 0x6b, 0x75, 0xf9, 0x2b, 0x5c, 0x37, 0x29, 0x0b, 0x91, 0x37,
71  0x01, 0x37, 0x0e, 0xe9, 0xf2, 0xe1, 0xfc, 0x9e, 0x64, 0xda, 0x6c, 0x11, 0x21, 0x33, 0xed, 0xa0,
72  0x0e, 0x76, 0x70, 0xa0, 0xcd, 0x98, 0x2e, 0x76, 0x80, 0xf0, 0xe0, 0x59, 0x56, 0x06, 0x08, 0xe9,
73  0xca, 0xeb, 0xa2, 0xc6, 0xdb, 0x5a, 0x86,
74 };
75 // clang-format on
76 
77 /**
78  * \test A malicious SWF whose header claims a huge decompressed size but
79  * carries only a few bytes of body must not drive a large allocation
80  * of the inspection buffer.
81  */
82 static int DetectFiledataSwfDecompressAllocTest01(void)
83 {
84  uint8_t buffer[] = { 'C', 'W', 'S', 0x06, 0x80, 0xF0, 0xFA, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04,
85  0x05, 0x06, 0x07 };
86 
87  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
88  FAIL_IF_NULL(det_ctx);
89 
90  InspectionBuffer out_buffer;
91  memset(&out_buffer, 0, sizeof(out_buffer));
92 
93  int r = FileSwfDecompression(buffer, (uint32_t)sizeof(buffer), det_ctx, &out_buffer,
95 
96  FAIL_IF(r != 0);
98 
99  InspectionBufferFree(&out_buffer);
101  SCFree(det_ctx);
102  PASS;
103 }
104 
105 /**
106  * \test A valid, small CWS with a forged large FileLength must only retain the
107  * buffer needed for its actual output.
108  */
109 static int DetectFiledataSwfDecompressAllocTest02(void)
110 {
111  const uint8_t plain[] = "small compressed SWF body";
112  const uint32_t file_len = MAX_SWF_DECOMPRESSED_LEN;
113  uint32_t cws_len = 0;
114  uint8_t *cws = DetectFiledataSwfCreateCws(
115  plain, sizeof(plain), file_len, Z_BEST_COMPRESSION, &cws_len);
116  FAIL_IF_NULL(cws);
117 
118  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
119  FAIL_IF_NULL(det_ctx);
120  InspectionBuffer out_buffer = { 0 };
121 
122  int r = FileSwfDecompression(
123  cws, cws_len, det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_ZLIB, 0, 0);
124 
125  FAIL_IF(r != 1);
127  FAIL_IF(out_buffer.len != SWF_HEADER_LEN + sizeof(plain));
128  FAIL_IF(memcmp(out_buffer.buf + 4, cws + 4, 4) != 0);
129  FAIL_IF(memcmp(out_buffer.buf + SWF_HEADER_LEN, plain, sizeof(plain)) != 0);
130 
131  InspectionBufferFree(&out_buffer);
133  SCFree(det_ctx);
134  SCFree(cws);
135  PASS;
136 }
137 
138 /**
139  * \test A weakly compressed CWS must not reserve the maximum possible zlib
140  * expansion in the per-thread inspection buffer.
141  */
142 static int DetectFiledataSwfDecompressAllocTest03(void)
143 {
144  const uint32_t plain_len = 48 * 1024;
145  uint8_t *plain = SCMalloc(plain_len);
146  FAIL_IF_NULL(plain);
147  for (uint32_t i = 0; i < plain_len; i++)
148  plain[i] = (uint8_t)i;
149 
150  uint32_t cws_len = 0;
151  uint8_t *cws = DetectFiledataSwfCreateCws(
152  plain, plain_len, plain_len + SWF_HEADER_LEN, Z_NO_COMPRESSION, &cws_len);
153  FAIL_IF_NULL(cws);
154 
155  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
156  FAIL_IF_NULL(det_ctx);
157  InspectionBuffer out_buffer = { 0 };
158 
159  int r = FileSwfDecompression(
160  cws, cws_len, det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_ZLIB, 0, 0);
161 
162  FAIL_IF(r != 1);
163  FAIL_IF(out_buffer.size > 64 * 1024);
164  FAIL_IF(out_buffer.len != SWF_HEADER_LEN + plain_len);
165  FAIL_IF(memcmp(out_buffer.buf + SWF_HEADER_LEN, plain, plain_len) != 0);
166 
167  InspectionBufferFree(&out_buffer);
169  SCFree(det_ctx);
170  SCFree(cws);
171  SCFree(plain);
172  PASS;
173 }
174 
175 /**
176  * \test A ZWS with a forged large FileLength and invalid LZMA properties must
177  * fail without growing the inspection buffer.
178  */
179 static int DetectFiledataSwfDecompressAllocTest04(void)
180 {
181  uint8_t buffer[] = { 'Z', 'W', 'S', 0x0D, 0x80, 0xF0, 0xFA, 0x02, 0x01, 0x00, 0x00, 0x00, 0xFF,
182  0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
183 
184  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
185  FAIL_IF_NULL(det_ctx);
186  InspectionBuffer out_buffer = { 0 };
187 
188  int r = FileSwfDecompression(buffer, (uint32_t)sizeof(buffer), det_ctx, &out_buffer,
190 
191  FAIL_IF(r != 0);
193 
194  InspectionBufferFree(&out_buffer);
196  SCFree(det_ctx);
197  PASS;
198 }
199 
200 /**
201  * \test A CWS FileLength value of one must not truncate decompression or
202  * discard content beyond the first output byte.
203  */
204 static int DetectFiledataSwfDecompressFileLengthTest01(void)
205 {
206  const uint32_t plain_len = 16 * 1024;
207  uint8_t *plain = SCMalloc(plain_len);
208  FAIL_IF_NULL(plain);
209  memset(plain, 'A', plain_len);
210  plain[plain_len - 1] = 'B';
211 
212  uint32_t cws_len = 0;
213  uint8_t *cws = DetectFiledataSwfCreateCws(plain, plain_len, 1, Z_BEST_COMPRESSION, &cws_len);
214  FAIL_IF_NULL(cws);
215 
216  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
217  FAIL_IF_NULL(det_ctx);
218  InspectionBuffer out_buffer = { 0 };
219 
220  int r = FileSwfDecompression(
221  cws, cws_len, det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_ZLIB, 0, 0);
222 
223  FAIL_IF(r != 1);
224  FAIL_IF(out_buffer.len != SWF_HEADER_LEN + plain_len);
225  FAIL_IF(out_buffer.buf[out_buffer.len - 1] != 'B');
226  FAIL_IF(memcmp(out_buffer.buf + 4, cws + 4, 4) != 0);
227 
228  InspectionBufferFree(&out_buffer);
230  SCFree(det_ctx);
231  SCFree(cws);
232  SCFree(plain);
233  PASS;
234 }
235 
236 /**
237  * \test A ZWS FileLength value of one must not truncate decompression.
238  */
239 static int DetectFiledataSwfDecompressFileLengthTest02(void)
240 {
241  uint8_t zws[sizeof(swf_lzma_fixture)];
242  memcpy(zws, swf_lzma_fixture, sizeof(zws));
243  zws[4] = 1;
244  zws[5] = 0;
245  zws[6] = 0;
246  zws[7] = 0;
247 
248  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
249  FAIL_IF_NULL(det_ctx);
250  InspectionBuffer out_buffer = { 0 };
251 
252  int r = FileSwfDecompression(
253  zws, (uint32_t)sizeof(zws), det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_LZMA, 0, 0);
254 
255  FAIL_IF(r != 1);
256  FAIL_IF(out_buffer.len <= SWF_HEADER_LEN + 1);
257  FAIL_IF(memcmp(out_buffer.buf + 4, zws + 4, 4) != 0);
258 
259  InspectionBufferFree(&out_buffer);
261  SCFree(det_ctx);
262  PASS;
263 }
264 
265 /**
266  * \test A valid small FileLength is preserved in the synthesized FWS header.
267  */
268 static int DetectFiledataSwfDecompressHeaderTest01(void)
269 {
270  const uint8_t plain[] = "small body";
271  const uint32_t file_len = SWF_HEADER_LEN + sizeof(plain);
272  uint32_t cws_len = 0;
273  uint8_t *cws = DetectFiledataSwfCreateCws(
274  plain, sizeof(plain), file_len, Z_BEST_COMPRESSION, &cws_len);
275  FAIL_IF_NULL(cws);
276 
277  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
278  FAIL_IF_NULL(det_ctx);
279  InspectionBuffer out_buffer = { 0 };
280 
281  int r = FileSwfDecompression(
282  cws, cws_len, det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_ZLIB, 0, 0);
283 
284  FAIL_IF(r != 1);
285  FAIL_IF(memcmp(out_buffer.buf + 4, cws + 4, 4) != 0);
286 
287  InspectionBufferFree(&out_buffer);
289  SCFree(det_ctx);
290  SCFree(cws);
291  PASS;
292 }
293 
294 /**
295  * \test The configured decompression depth remains the CWS output limit.
296  */
297 static int DetectFiledataSwfDecompressDepthTest01(void)
298 {
299  const uint32_t plain_len = 16 * 1024;
300  const uint32_t decompress_depth = 5000;
301  uint8_t *plain = SCMalloc(plain_len);
302  FAIL_IF_NULL(plain);
303  memset(plain, 'A', plain_len);
304  plain[plain_len - 1] = 'B';
305 
306  uint32_t cws_len = 0;
307  uint8_t *cws = DetectFiledataSwfCreateCws(
308  plain, plain_len, plain_len + SWF_HEADER_LEN, Z_BEST_COMPRESSION, &cws_len);
309  FAIL_IF_NULL(cws);
310 
311  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
312  FAIL_IF_NULL(det_ctx);
313  InspectionBuffer out_buffer = { 0 };
314 
315  int r = FileSwfDecompression(
316  cws, cws_len, det_ctx, &out_buffer, HTTP_SWF_COMPRESSION_ZLIB, decompress_depth, 0);
317 
318  FAIL_IF(r != 1);
319  FAIL_IF(out_buffer.len != SWF_HEADER_LEN + decompress_depth);
320  FAIL_IF(memcmp(out_buffer.buf + SWF_HEADER_LEN, plain, decompress_depth) != 0);
321 
322  InspectionBufferFree(&out_buffer);
324  SCFree(det_ctx);
325  SCFree(cws);
326  SCFree(plain);
327  PASS;
328 }
329 
330 /**
331  * \test The configured decompression depth remains the ZWS output limit.
332  */
333 static int DetectFiledataSwfDecompressDepthTest02(void)
334 {
335  const uint32_t decompress_depth = 1;
336  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
337  FAIL_IF_NULL(det_ctx);
338  InspectionBuffer out_buffer = { 0 };
339 
340  int r = FileSwfDecompression(swf_lzma_fixture, (uint32_t)sizeof(swf_lzma_fixture), det_ctx,
341  &out_buffer, HTTP_SWF_COMPRESSION_LZMA, decompress_depth, 0);
342 
343  FAIL_IF(r != 1);
344  FAIL_IF(out_buffer.len != SWF_HEADER_LEN + decompress_depth);
345 
346  InspectionBufferFree(&out_buffer);
348  SCFree(det_ctx);
349  PASS;
350 }
351 
352 /**
353  * \test SWF decompression rejects depth values above the supported limits.
354  */
355 static int DetectFiledataSwfDecompressDepthTest03(void)
356 {
357  DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
358  FAIL_IF_NULL(det_ctx);
359  InspectionBuffer out_buffer = { 0 };
360 
361  int r = FileSwfDecompression(swf_lzma_fixture, (uint32_t)sizeof(swf_lzma_fixture), det_ctx,
363  FAIL_IF(r != 0);
364  FAIL_IF_NOT_NULL(out_buffer.buf);
365 
366  r = FileSwfDecompression(swf_lzma_fixture, (uint32_t)sizeof(swf_lzma_fixture), det_ctx,
368  FAIL_IF(r != 0);
369  FAIL_IF_NOT_NULL(out_buffer.buf);
370 
372  SCFree(det_ctx);
373  PASS;
374 }
375 
376 static int DetectEngineSMTPFiledataTest02(void)
377 {
380  de_ctx->flags |= DE_QUIET;
381 
382  Signature *s = DetectEngineAppendSig(de_ctx, "alert smtp any any -> any any "
383  "(msg:\"file_data smtp test\"; "
384  "file_data; content:\"message\"; sid:1;)");
385  FAIL_IF_NULL(s);
386 
389  PASS;
390 }
391 
392 /**
393  * \test Test the file_data fails with flow:to_server.
394  */
395 static int DetectFiledataParseTest04(void)
396 {
399  de_ctx->flags |= DE_QUIET;
401  "alert smtp any any -> any any "
402  "(msg:\"test\"; flow:to_client,established; file_data; content:\"abc\"; sid:1;)");
403  FAIL_IF_NOT_NULL(s);
405  PASS;
406 }
407 
409 {
411  "DetectFiledataSwfDecompressAllocTest01", DetectFiledataSwfDecompressAllocTest01);
413  "DetectFiledataSwfDecompressAllocTest02", DetectFiledataSwfDecompressAllocTest02);
415  "DetectFiledataSwfDecompressAllocTest03", DetectFiledataSwfDecompressAllocTest03);
417  "DetectFiledataSwfDecompressAllocTest04", DetectFiledataSwfDecompressAllocTest04);
418  UtRegisterTest("DetectFiledataSwfDecompressFileLengthTest01",
419  DetectFiledataSwfDecompressFileLengthTest01);
420  UtRegisterTest("DetectFiledataSwfDecompressFileLengthTest02",
421  DetectFiledataSwfDecompressFileLengthTest02);
423  "DetectFiledataSwfDecompressHeaderTest01", DetectFiledataSwfDecompressHeaderTest01);
425  "DetectFiledataSwfDecompressDepthTest01", DetectFiledataSwfDecompressDepthTest01);
427  "DetectFiledataSwfDecompressDepthTest02", DetectFiledataSwfDecompressDepthTest02);
429  "DetectFiledataSwfDecompressDepthTest03", DetectFiledataSwfDecompressDepthTest03);
430  UtRegisterTest("DetectEngineSMTPFiledataTest02", DetectEngineSMTPFiledataTest02);
431  UtRegisterTest("DetectFiledataParseTest04", DetectFiledataParseTest04);
432 }
433 #endif
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SWF_DECOMPRESS_INITIAL_BUFFER_LEN
#define SWF_DECOMPRESS_INITIAL_BUFFER_LEN
Definition: util-file-swf-decompression.h:35
DetectEngineThreadCtx_::decoder_events
AppLayerDecoderEvents * decoder_events
Definition: detect.h:1437
FileSwfDecompression
int FileSwfDecompression(const uint8_t *buffer, uint32_t buffer_len, DetectEngineThreadCtx *det_ctx, InspectionBuffer *out_buffer, int swf_type, uint32_t decompress_depth, uint32_t compress_depth)
This function decompresses a buffer with zlib/lzma algorithm.
Definition: util-file-decompression.c:71
InspectionBuffer
Definition: detect-engine-inspect-buffer.h:34
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:987
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2878
HTTP_SWF_COMPRESSION_ZLIB
@ HTTP_SWF_COMPRESSION_ZLIB
Definition: app-layer-htp.h:90
DE_QUIET
#define DE_QUIET
Definition: detect.h:333
InspectionBuffer::size
uint32_t size
Definition: detect-engine-inspect-buffer.h:45
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:3859
MAX_SWF_DECOMPRESS_DEPTH
#define MAX_SWF_DECOMPRESS_DEPTH
Definition: util-file-decompression.h:33
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
SIG_FLAG_TOSERVER
#define SIG_FLAG_TOSERVER
Definition: detect.h:273
InspectionBufferFree
void InspectionBufferFree(InspectionBuffer *buffer)
Definition: detect-engine-inspect-buffer.c:205
SWF_HEADER_LEN
#define SWF_HEADER_LEN
Definition: util-file-decompression.h:30
FAIL_IF_NOT_NULL
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Definition: util-unittest.h:96
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
DetectEngineThreadCtx_
Definition: detect.h:1306
InspectionBuffer::buf
uint8_t * buf
Definition: detect-engine-inspect-buffer.h:44
Signature_::flags
uint32_t flags
Definition: detect.h:689
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
SCAppLayerDecoderEventsFreeEvents
void SCAppLayerDecoderEventsFreeEvents(AppLayerDecoderEvents **events)
Definition: app-layer-events.c:137
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
DetectFiledataRegisterTests
void DetectFiledataRegisterTests(void)
Definition: detect-file-data.c:408
InspectionBuffer::len
uint32_t len
Definition: detect-engine-inspect-buffer.h:43
SCFree
#define SCFree(p)
Definition: util-mem.h:61
Signature_
Signature container.
Definition: detect.h:688
HTTP_SWF_COMPRESSION_LZMA
@ HTTP_SWF_COMPRESSION_LZMA
Definition: app-layer-htp.h:91
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2839
MAX_SWF_COMPRESS_DEPTH
#define MAX_SWF_COMPRESS_DEPTH
Definition: util-file-decompression.h:34
MAX_SWF_DECOMPRESSED_LEN
#define MAX_SWF_DECOMPRESSED_LEN
Definition: util-file-swf-decompression.h:33
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:989
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53