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], &f, \
51  (uint8_t *)(buf), (buflen), 0, DETECT_CI_FLAGS_SINGLE, \
52  DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD, NULL); \
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_FOOTER;
193 }
194 
195 /** \test mix in byte_jump */
196 static int DetectEngineContentInspectionTest09(void) {
197  TEST_HEADER;
198  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"d\";", true, 3);
199  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; content:!\"c\";", false, 3);
200 
201  TEST_RUN("abc03abcxyz", 11, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3;", true, 3);
202  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3;", true, 5);
203  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; isdataat:!1,relative;", true, 5);
204  TEST_RUN("abc03abc03abcxyz", 16, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/klm$/R\";", false, 7);
205  TEST_RUN("abc03abc03abcxyzklm", 19, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/klm$/R\";", true, 6);
206  TEST_RUN("abc03abc03abcxyzklx", 19, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/^klm$/R\";", false, 7);
207  TEST_RUN("abc03abc03abc03abcxyzklm", 24, "content:\"abc\"; byte_jump:2,0,relative,string,dec; content:\"xyz\"; within:3; pcre:\"/^klm$/R\";", true, 8);
208 
209  TEST_FOOTER;
210 }
211 
212 /** \test mix in byte_extract */
213 static int DetectEngineContentInspectionTest10(void) {
214  TEST_HEADER;
215  /* extract first byte as lenght field and check with isdataat */
216  TEST_RUN("9abcdefghi", 10, "byte_extract:1,0,data_size,string; isdataat:data_size;", true, 2);
217  TEST_RUN("9abcdefgh", 9, "byte_extract:1,0,data_size,string; isdataat:!data_size;", true, 2);
218  /* anchor len field to pattern 'x' to test recursion */
219  TEST_RUN("x9x9abcdefghi", 13, "content:\"x\"; byte_extract:1,0,data_size,string,relative; isdataat:data_size,relative;", true, 3);
220  TEST_RUN("x9x9abcdefgh", 12, "content:\"x\"; byte_extract:1,0,data_size,string,relative; isdataat:!data_size,relative;", true, 5);
221  TEST_RUN("x9x9abcdefgh", 12, "content:\"x\"; depth:1; byte_extract:1,0,data_size,string,relative; isdataat:!data_size,relative;", false, 3);
222  /* check for super high extracted values */
223  TEST_RUN("100000000abcdefghi", 18, "byte_extract:0,0,data_size,string; isdataat:data_size;", false, 2);
224  TEST_RUN("100000000abcdefghi", 18, "byte_extract:0,0,data_size,string; isdataat:!data_size;", true, 2);
225  TEST_FOOTER;
226 }
227 
228 static int DetectEngineContentInspectionTest11(void) {
229  TEST_HEADER;
230  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\";", true, 2);
231  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0;", true, 2);
232  TEST_RUN("ab", 2, "content:\"ab\"; startswith;", true, 1);
233  TEST_RUN("ab", 2, "content:\"a\"; startswith;", true, 1);
234  TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1);
235  TEST_FOOTER;
236 }
237 
238 /** \test endswith (isdataat) recursion logic
239  * based on DetectEngineContentInspectionTest06 */
240 static int DetectEngineContentInspectionTest12(void) {
241  TEST_HEADER;
242  // 6 steps: (1) a, (2) 1st b, (3) c not found, (4) 2nd b, (5) c found, endswith
243  TEST_RUN("ababc", 5, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; endswith;", true, 5);
244 
245  TEST_RUN("ababcabc", 8, "content:\"a\"; content:\"b\"; distance:0; within:1; content:\"c\"; distance:0; within:1; endswith;", true, 7);
246 
247  TEST_RUN("abcXYZ", 6, "content:\"abc\"; content:\"XYZ\"; distance:0; within:3; endswith;", true, 2);
248  TEST_RUN("abcXYZ", 6, "content:\"XYZ\"; distance:3; within:3; endswith;", true, 1);
249  TEST_RUN("abcXYZ", 6, "content:\"cXY\"; distance:2; within:3; endswith;", false, 1);
250 
251  TEST_RUN("xxxxxxxxxxxxxxxxxyYYYYYYYYYYYYYYYY", 34, "content:\"yYYYYYYYYYYYYYYYY\"; distance:9; within:29; endswith;", true, 1);
252  TEST_FOOTER;
253 }
254 
255 static int DetectEngineContentInspectionTest13(void) {
256  TEST_HEADER;
257  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; endswith;", true, 2);
258  TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0; endswith;", true, 2);
259  TEST_RUN("ab", 2, "content:\"ab\"; startswith; endswith;", true, 1);
260  TEST_RUN("ab", 2, "content:\"a\"; startswith; endswith;", false, 1);
261  TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1);
262  TEST_RUN("ab", 2, "content:\"b\"; startswith; endswith;", false, 1);
263  TEST_FOOTER;
264 }
265 
267 {
268  UtRegisterTest("DetectEngineContentInspectionTest01",
269  DetectEngineContentInspectionTest01);
270  UtRegisterTest("DetectEngineContentInspectionTest02",
271  DetectEngineContentInspectionTest02);
272  UtRegisterTest("DetectEngineContentInspectionTest03",
273  DetectEngineContentInspectionTest03);
274  UtRegisterTest("DetectEngineContentInspectionTest04",
275  DetectEngineContentInspectionTest04);
276  UtRegisterTest("DetectEngineContentInspectionTest05",
277  DetectEngineContentInspectionTest05);
278  UtRegisterTest("DetectEngineContentInspectionTest06",
279  DetectEngineContentInspectionTest06);
280  UtRegisterTest("DetectEngineContentInspectionTest07",
281  DetectEngineContentInspectionTest07);
282  UtRegisterTest("DetectEngineContentInspectionTest08",
283  DetectEngineContentInspectionTest08);
284  UtRegisterTest("DetectEngineContentInspectionTest09",
285  DetectEngineContentInspectionTest09);
286  UtRegisterTest("DetectEngineContentInspectionTest10",
287  DetectEngineContentInspectionTest10);
288  UtRegisterTest("DetectEngineContentInspectionTest11 startswith",
289  DetectEngineContentInspectionTest11);
290  UtRegisterTest("DetectEngineContentInspectionTest12 endswith",
291  DetectEngineContentInspectionTest12);
292  UtRegisterTest("DetectEngineContentInspectionTest13 mix startswith/endswith",
293  DetectEngineContentInspectionTest13);
294 }
295 
296 #undef TEST_HEADER
297 #undef TEST_RUN
298 #undef TEST_FOOTER
void DetectEngineContentInspectionRegisterTests(void)
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define TEST_RUN(buf, buflen, sig, match, steps)