suricata
detect-engine-content-inspection.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2017 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 Victor Julien <victor@inliniac.net>
22  *
23  * Tests for the content inspection engine.
24  */
25 
26 #include "../suricata-common.h"
27 #include "../decode.h"
28 #include "../flow.h"
29 #include "../detect.h"
30 
31 #define TEST_HEADER \
32  ThreadVars tv; \
33  memset(&tv, 0, sizeof(tv)); \
34  Flow f; \
35  memset(&f, 0, sizeof(f));
36 
37 #define TEST_RUN(buf, buflen, sig, match, steps) \
38 { \
39  DetectEngineCtx *de_ctx = DetectEngineCtxInit(); \
40  FAIL_IF_NULL(de_ctx); \
41  DetectEngineThreadCtx *det_ctx = NULL; \
42  char rule[2048]; \
43  snprintf(rule, sizeof(rule), "alert tcp any any -> any any (%s sid:1; rev:1;)", (sig)); \
44  Signature *s = DetectEngineAppendSig(de_ctx, rule); \
45  FAIL_IF_NULL(s); \
46  SigGroupBuild(de_ctx); \
47  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); \
48  FAIL_IF_NULL(det_ctx); \
49  int r = DetectEngineContentInspection(de_ctx, det_ctx, \
50  s, s->sm_arrays[DETECT_SM_LIST_PMATCH], NULL, &f, \
51  (uint8_t *)(buf), (buflen), 0, DETECT_CI_FLAGS_SINGLE, \
52  DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD); \
53  FAIL_IF_NOT(r == (match)); \
54  FAIL_IF_NOT(det_ctx->inspection_recursion_counter == (steps)); \
55  DetectEngineThreadCtxDeinit(&tv, det_ctx); \
56  DetectEngineCtxFree(de_ctx); \
57 }
58 #define TEST_FOOTER \
59  PASS
60 
61 /** \test simple match with distance */
62 static int DetectEngineContentInspectionTest01(void) {
64  TEST_RUN("ab", 2, "content:\"a\"; content:\"b\";", true, 2);
65  TEST_RUN("ab", 2, "content:\"a\"; content:\"b\"; distance:0; ", true, 2);
66  TEST_RUN("ba", 2, "content:\"a\"; content:\"b\"; distance:0; ", false, 2);
68 }
69 
70 /** \test simple match with pcre/R */
71 static int DetectEngineContentInspectionTest02(void) {
73  TEST_RUN("ab", 2, "content:\"a\"; pcre:\"/b/\";", true, 2);
74  TEST_RUN("ab", 2, "content:\"a\"; pcre:\"/b/R\";", true, 2);
75  TEST_RUN("ba", 2, "content:\"a\"; pcre:\"/b/R\";", false, 2);
77 }
78 
79 /** \test simple recursion logic */
80 static int DetectEngineContentInspectionTest03(void) {
82  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"c\";", true, 3);
83  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"d\";", false, 3);
84 
85  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0;", true, 3);
86  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; content:\"d\"; distance:0;", false, 3);
87 
88  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"d\"; distance:0; within:1;", false, 5);
89 
90  // 5 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found
91  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1;", true, 5);
92  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, (6) bab
93  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; content:\"bab\";", true, 6);
94  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, (6) no not found
95  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; content:\"no\";", false, 6);
96 
97  // 5 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found
98  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; pcre:\"/^c$/R\";", true, 5);
99  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, (6) bab
100  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; pcre:\"/^c$/R\"; content:\"bab\";", true, 6);
101  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, (6) no not found
102  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; pcre:\"/^c$/R\"; content:\"no\";", false, 6);
103 
104  TEST_FOOTER;
105 }
106 
107 /** \test pcre recursion logic */
108 static int DetectEngineContentInspectionTest04(void) {
109  TEST_HEADER;
110  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"c\";", true, 3);
111  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"d\";", false, 3);
112 
113  // simple chain of pcre
114  TEST_RUN("ababc", 5, "pcre:\"/^a/\"; pcre:\"/^b/R\"; pcre:\"/c/R\"; ", true, 3);
115  TEST_RUN("ababc", 5, "pcre:\"/a/\"; pcre:\"/^b/R\"; pcre:\"/^c/R\"; ", true, 5);
116  TEST_RUN("ababc", 5, "pcre:\"/^a/\"; pcre:\"/^b/R\"; pcre:\"/d/R\"; ", false, 3);
117  TEST_RUN("ababc", 5, "pcre:\"/^a/\"; pcre:\"/^b/R\"; pcre:\"/c/R\"; pcre:\"/d/\"; ", false, 4);
118 
119  TEST_FOOTER;
120 }
121 
122 /** \test multiple independent blocks recursion logic */
123 static int DetectEngineContentInspectionTest05(void) {
124  TEST_HEADER;
125  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"c\";", true, 3);
126  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"d\";", false, 3);
127 
128  // first block 2: (1) a, (2) b
129  // second block 3: (1) b, (2) c not found, (x) b continues within loop, (3) c found
130  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"b\"; content:\"c\"; distance:0; within:1;", true, 5);
131 
132  TEST_FOOTER;
133 }
134 
135 /** \test isdataat recursion logic */
136 static int DetectEngineContentInspectionTest06(void) {
137  TEST_HEADER;
138  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"c\";", true, 3);
139  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:\"d\";", false, 3);
140 
141  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, isdataat
142  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; isdataat:!1,relative;", true, 5);
143  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; isdataat:1,relative;", false, 6);
144 
145  TEST_RUN("ababcabc", 8, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; isdataat:!1,relative;", true, 7);
146  TEST_RUN("ababcabc", 8, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; isdataat:1,relative;", true, 6);
147 
148  TEST_RUN("abcXYZ", 6, "content:\"abc\"; content:\"XYZ\"; distance:0; within:3; isdataat:!1,relative;", true, 2);
149  TEST_RUN("abcXYZ", 6, "content:\"XYZ\"; distance:3; within:3; isdataat:!1,relative;", true, 1);
150  TEST_RUN("abcXYZ", 6, "content:\"cXY\"; distance:2; within:3; isdataat:!1,relative;", false, 1);
151 
152  TEST_RUN("xxxxxxxxxxxxxxxxxyYYYYYYYYYYYYYYYY", 34, "content:\"yYYYYYYYYYYYYYYYY\"; distance:9; within:29; isdataat:!1,relative;", true, 1);
153  TEST_FOOTER;
154 }
155 
156 /** \test extreme recursion */
157 static int DetectEngineContentInspectionTest07(void) {
158  TEST_HEADER;
159  TEST_RUN("abcabcabcabcabcabcabcabcabcabcd", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; content:\"d\";", true, 4);
160  TEST_RUN("abcabcabcabcabcabcabcabcabcabcd", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; content:\"d\"; within:1; distance:0; ", true, 31);
161  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; content:\"d\"; within:1; distance:0; ", false, 31);
162 
163  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; content:\"d\"; distance:0; ", false, 4);
164  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; pcre:\"/^d/R\"; ", false, 13);
165  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; isdataat:!1,relative; ", false, 3);
166  TEST_RUN("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdx", 41,
167  "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; content:\"d\"; distance:0; content:\"e\"; distance:0; ", false, 5);
168  TEST_RUN("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdx", 41,
169  "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; content:\"d\"; distance:0; pcre:\"/^e/R\"; ", false, 14); // TODO should be 5?
170  TEST_RUN("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdx", 41,
171  "content:\"a\"; content:\"b\"; distance:0; content:\"c\"; distance:0; content:\"d\"; distance:0; isdataat:!1,relative; ", false, 4);
172 
173  TEST_RUN("abcabcabcabcabcabcabcabcabcabcd", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/d/\";", true, 4);
174  TEST_RUN("abcabcabcabcabcabcabcabcabcabcd", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/d/R\";", true, 4);
175  TEST_RUN("abcabcabcabcabcabcabcabcabcabcd", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/^d/R\";", true, 31);
176 
177  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/d/\";", false, 4);
178  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/d/R\";", false, 31);
179  TEST_RUN("abcabcabcabcabcabcabcabcabcabcx", 31, "content:\"a\"; content:\"b\"; within:1; distance:0; content:\"c\"; distance:0; within:1; pcre:\"/^d/R\";", false, 31);
180  TEST_FOOTER;
181 }
182 
183 /** \test mix in negation */
184 static int DetectEngineContentInspectionTest08(void) {
185  TEST_HEADER;
186  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"d\";", true, 3);
187  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"c\";", false, 3);
188 
189  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:!\"a\"; distance:0; within:1;", true, 5);
190  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:!\"a\"; distance:0; ", true, 5);
191 
192  TEST_RUN("abcdefghy", 9, "content:\"a\"; content:!\"x\"; content:\"c\"; distance:0; within:2; ",
193  true, 3);
194  TEST_RUN("abcdefghx", 9, "content:\"a\"; content:!\"x\"; content:\"c\"; distance:0; within:2; ",
195  false, 2);
196  TEST_RUN("abcdefghy", 9,
197  "content:\"a\"; content:!\"x\"; content:!\"c\"; distance:2; within:1; ", true, 3);
198 
199  TEST_FOOTER;
200 }
201 
202 /** \test mix in byte_jump */
203 static int DetectEngineContentInspectionTest09(void) {
204  TEST_HEADER;
205  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"d\";", true, 3);
206  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"c\";", false, 3);
207 
208  TEST_RUN("abc03abcxyz", 11, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3;", true, 3);
209  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3;", true, 5);
210  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; isdataat:!1,relative;", true, 5);
211  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/klm$/R\";", false, 7);
212  TEST_RUN("abc03abc03abcxyzklm", 19, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/klm$/R\";", true, 6);
213  TEST_RUN("abc03abc03abcxyzklx", 19, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/^klm$/R\";", false, 7);
214  TEST_RUN("abc03abc03abc03abcxyzklm", 24, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/^klm$/R\";", true, 8);
215 
216  TEST_FOOTER;
217 }
218 
219 /** \test mix in byte_extract */
220 static int DetectEngineContentInspectionTest10(void) {
221  TEST_HEADER;
222  /* extract first byte as length field and check with isdataat */
223  TEST_RUN("9abcdefghi", 10, "byte_extract:1,0,data_size,string; isdataat:data_size;", true, 2);
224  TEST_RUN("9abcdefgh", 9, "byte_extract:1,0,data_size,string; isdataat:!data_size;", true, 2);
225  /* anchor len field to pattern 'x' to test recursion */
226  TEST_RUN("x9x9abcdefghi", 13, "content:\"x\"; byte_extract:1,0,data_size,string,relative; isdataat:data_size,relative;", true, 3);
227  TEST_RUN("x9x9abcdefgh", 12, "content:\"x\"; byte_extract:1,0,data_size,string,relative; isdataat:!data_size,relative;", true, 5);
228  TEST_RUN("x9x9abcdefgh", 12, "content:\"x\"; depth:1; byte_extract:1,0,data_size,string,relative; isdataat:!data_size,relative;", false, 3);
229  /* check for super high extracted values */
230  TEST_RUN("100000000abcdefghi", 18, "byte_extract:0,0,data_size,string; isdataat:data_size;", false, 2);
231  TEST_RUN("100000000abcdefghi", 18, "byte_extract:0,0,data_size,string; isdataat:!data_size;", true, 2);
232  TEST_FOOTER;
233 }
234 
235 static int DetectEngineContentInspectionTest11(void) {
236  TEST_HEADER;
237  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\";", true, 2);
238  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0;", true, 2);
239  TEST_RUN("ab", 2, "content:\"ab\"; startswith;", true, 1);
240  TEST_RUN("ab", 2, "content:\"a\"; startswith;", true, 1);
241  TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1);
242  TEST_FOOTER;
243 }
244 
245 /** \test endswith (isdataat) recursion logic
246  * based on DetectEngineContentInspectionTest06 */
247 static int DetectEngineContentInspectionTest12(void) {
248  TEST_HEADER;
249  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, endswith
250  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; endswith;", true, 5);
251 
252  TEST_RUN("ababcabc", 8, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; endswith;", true, 7);
253 
254  TEST_RUN("abcXYZ", 6, "content:\"abc\"; content:\"XYZ\"; distance:0; within:3; endswith;", true, 2);
255  TEST_RUN("abcXYZ", 6, "content:\"XYZ\"; distance:3; within:3; endswith;", true, 1);
256  TEST_RUN("abcXYZ", 6, "content:\"cXY\"; distance:2; within:3; endswith;", false, 1);
257 
258  TEST_RUN("xxxxxxxxxxxxxxxxxyYYYYYYYYYYYYYYYY", 34, "content:\"yYYYYYYYYYYYYYYYY\"; distance:9; within:29; endswith;", true, 1);
259  TEST_FOOTER;
260 }
261 
262 static int DetectEngineContentInspectionTest13(void) {
263  TEST_HEADER;
264  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; endswith;", true, 2);
265  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0; endswith;", true, 2);
266  TEST_RUN("ab", 2, "content:\"ab\"; startswith; endswith;", true, 1);
267  TEST_RUN("ab", 2, "content:\"a\"; startswith; endswith;", false, 1);
268  TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1);
269  TEST_RUN("ab", 2, "content:\"b\"; startswith; endswith;", false, 1);
270  TEST_FOOTER;
271 }
272 
274 {
275  UtRegisterTest("DetectEngineContentInspectionTest01",
276  DetectEngineContentInspectionTest01);
277  UtRegisterTest("DetectEngineContentInspectionTest02",
278  DetectEngineContentInspectionTest02);
279  UtRegisterTest("DetectEngineContentInspectionTest03",
280  DetectEngineContentInspectionTest03);
281  UtRegisterTest("DetectEngineContentInspectionTest04",
282  DetectEngineContentInspectionTest04);
283  UtRegisterTest("DetectEngineContentInspectionTest05",
284  DetectEngineContentInspectionTest05);
285  UtRegisterTest("DetectEngineContentInspectionTest06",
286  DetectEngineContentInspectionTest06);
287  UtRegisterTest("DetectEngineContentInspectionTest07",
288  DetectEngineContentInspectionTest07);
289  UtRegisterTest("DetectEngineContentInspectionTest08",
290  DetectEngineContentInspectionTest08);
291  UtRegisterTest("DetectEngineContentInspectionTest09",
292  DetectEngineContentInspectionTest09);
293  UtRegisterTest("DetectEngineContentInspectionTest10",
294  DetectEngineContentInspectionTest10);
295  UtRegisterTest("DetectEngineContentInspectionTest11 startswith",
296  DetectEngineContentInspectionTest11);
297  UtRegisterTest("DetectEngineContentInspectionTest12 endswith",
298  DetectEngineContentInspectionTest12);
299  UtRegisterTest("DetectEngineContentInspectionTest13 mix startswith/endswith",
300  DetectEngineContentInspectionTest13);
301 }
302 
303 #undef TEST_HEADER
304 #undef TEST_RUN
305 #undef TEST_FOOTER
DetectEngineContentInspectionRegisterTests
void DetectEngineContentInspectionRegisterTests(void)
Definition: detect-engine-content-inspection.c:273
TEST_HEADER
#define TEST_HEADER
Definition: detect-engine-content-inspection.c:31
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
TEST_FOOTER
#define TEST_FOOTER
Definition: detect-engine-content-inspection.c:58
TEST_RUN
#define TEST_RUN(buf, buflen, sig, match, steps)
Definition: detect-engine-content-inspection.c:37