/src/suricata7/src/detect-replace.c
Line | Count | Source |
1 | | /* Copyright (C) 2011-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 Eric Leblond <eric@regit.org> |
22 | | * |
23 | | * Replace part of the detection engine. |
24 | | * |
25 | | * If previous filter is of content type, replace can be used to change |
26 | | * the matched part to a new value. |
27 | | */ |
28 | | |
29 | | #include "suricata-common.h" |
30 | | |
31 | | #include "runmodes.h" |
32 | | |
33 | | extern int run_mode; |
34 | | |
35 | | #include "decode.h" |
36 | | |
37 | | #include "detect.h" |
38 | | #include "detect-parse.h" |
39 | | #include "detect-content.h" |
40 | | #include "detect-uricontent.h" |
41 | | #include "detect-byte-extract.h" |
42 | | #include "detect-replace.h" |
43 | | #include "app-layer.h" |
44 | | |
45 | | #include "detect-engine-mpm.h" |
46 | | #include "detect-engine.h" |
47 | | #include "detect-engine-state.h" |
48 | | #include "detect-engine-build.h" |
49 | | |
50 | | #include "util-checksum.h" |
51 | | |
52 | | #include "util-unittest.h" |
53 | | #include "util-unittest-helper.h" |
54 | | |
55 | | #include "flow-var.h" |
56 | | |
57 | | #include "util-debug.h" |
58 | | |
59 | | #include "pkt-var.h" |
60 | | #include "host.h" |
61 | | #include "util-profiling.h" |
62 | | |
63 | | static int DetectReplaceSetup(DetectEngineCtx *, Signature *, const char *); |
64 | | #ifdef UNITTESTS |
65 | | static void DetectReplaceRegisterTests(void); |
66 | | #endif |
67 | | static int DetectReplacePostMatch(DetectEngineThreadCtx *det_ctx, |
68 | | Packet *p, const Signature *s, const SigMatchCtx *ctx); |
69 | | |
70 | | void DetectReplaceRegister (void) |
71 | 73 | { |
72 | 73 | sigmatch_table[DETECT_REPLACE].name = "replace"; |
73 | 73 | sigmatch_table[DETECT_REPLACE].desc = "only to be used in IPS-mode. Change the following content into another"; |
74 | 73 | sigmatch_table[DETECT_REPLACE].url = "/rules/payload-keywords.html#replace"; |
75 | 73 | sigmatch_table[DETECT_REPLACE].Match = DetectReplacePostMatch; |
76 | 73 | sigmatch_table[DETECT_REPLACE].Setup = DetectReplaceSetup; |
77 | | #ifdef UNITTESTS |
78 | | sigmatch_table[DETECT_REPLACE].RegisterTests = DetectReplaceRegisterTests; |
79 | | #endif |
80 | 73 | sigmatch_table[DETECT_REPLACE].flags = (SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION); |
81 | 73 | } |
82 | | |
83 | | static int DetectReplacePostMatch(DetectEngineThreadCtx *det_ctx, |
84 | | Packet *p, const Signature *s, const SigMatchCtx *ctx) |
85 | 0 | { |
86 | 0 | if (det_ctx->replist) { |
87 | 0 | DetectReplaceExecuteInternal(p, det_ctx->replist); |
88 | 0 | det_ctx->replist = NULL; |
89 | 0 | } |
90 | 0 | return 1; |
91 | 0 | } |
92 | | |
93 | | int DetectReplaceSetup(DetectEngineCtx *de_ctx, Signature *s, const char *replacestr) |
94 | 748 | { |
95 | 748 | uint8_t *content = NULL; |
96 | 748 | uint16_t len = 0; |
97 | | |
98 | 748 | if (s->init_data->negated) { |
99 | 13 | SCLogError("Can't negate replacement string: %s", replacestr); |
100 | 13 | return -1; |
101 | 13 | } |
102 | | |
103 | 735 | switch (run_mode) { |
104 | 0 | case RUNMODE_NFQ: |
105 | 0 | case RUNMODE_IPFW: |
106 | 0 | break; |
107 | 735 | default: |
108 | 735 | SCLogWarning("Can't use 'replace' keyword in non IPS mode: %s", s->sig_str); |
109 | | /* this is a success, having the alert is interesting */ |
110 | 735 | return 0; |
111 | 735 | } |
112 | | |
113 | 0 | int ret = DetectContentDataParse("replace", replacestr, &content, &len); |
114 | 0 | if (ret == -1) |
115 | 0 | return -1; |
116 | | |
117 | | /* add to the latest "content" keyword from pmatch */ |
118 | 0 | const SigMatch *pm = DetectGetLastSMByListId(s, DETECT_SM_LIST_PMATCH, |
119 | 0 | DETECT_CONTENT, -1); |
120 | 0 | if (pm == NULL) { |
121 | 0 | SCLogError("replace needs" |
122 | 0 | "preceding content option for raw sig"); |
123 | 0 | SCFree(content); |
124 | 0 | return -1; |
125 | 0 | } |
126 | | |
127 | | /* we can remove this switch now with the unified structure */ |
128 | 0 | DetectContentData *ud = (DetectContentData *)pm->ctx; |
129 | 0 | if (ud == NULL) { |
130 | 0 | SCLogError("invalid argument"); |
131 | 0 | SCFree(content); |
132 | 0 | return -1; |
133 | 0 | } |
134 | 0 | if (ud->flags & DETECT_CONTENT_NEGATED) { |
135 | 0 | SCLogError("can't have a relative " |
136 | 0 | "negated keyword set along with a replacement"); |
137 | 0 | goto error; |
138 | 0 | } |
139 | 0 | if (ud->content_len != len) { |
140 | 0 | SCLogError("can't have a content " |
141 | 0 | "length different from replace length"); |
142 | 0 | goto error; |
143 | 0 | } |
144 | | |
145 | 0 | ud->replace = SCMalloc(len); |
146 | 0 | if (ud->replace == NULL) { |
147 | 0 | goto error; |
148 | 0 | } |
149 | 0 | memcpy(ud->replace, content, len); |
150 | 0 | ud->replace_len = len; |
151 | 0 | ud->flags |= DETECT_CONTENT_REPLACE; |
152 | | /* want packet matching only won't be able to replace data with |
153 | | * a flow. |
154 | | */ |
155 | 0 | s->flags |= SIG_FLAG_REQUIRE_PACKET; |
156 | 0 | SCFree(content); |
157 | 0 | content = NULL; |
158 | |
|
159 | 0 | SigMatch *sm = SigMatchAlloc(); |
160 | 0 | if (unlikely(sm == NULL)) { |
161 | 0 | SCFree(ud->replace); |
162 | 0 | ud->replace = NULL; |
163 | 0 | goto error; |
164 | 0 | } |
165 | 0 | sm->type = DETECT_REPLACE; |
166 | 0 | sm->ctx = NULL; |
167 | 0 | SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH); |
168 | 0 | return 0; |
169 | | |
170 | 0 | error: |
171 | 0 | SCFree(ud->replace); |
172 | 0 | ud->replace = NULL; |
173 | 0 | SCFree(content); |
174 | 0 | return -1; |
175 | 0 | } |
176 | | |
177 | | /* Add to the head of the replace-list. |
178 | | * |
179 | | * The first to add to the replace-list has the highest priority. So, |
180 | | * adding the head of the list results in the newest modifications |
181 | | * of content being applied first, so later changes can over ride |
182 | | * earlier changes. Thus the highest priority modifications should be |
183 | | * applied last. |
184 | | */ |
185 | | DetectReplaceList *DetectReplaceAddToList(DetectReplaceList *replist, |
186 | | uint8_t *found, |
187 | | DetectContentData *cd) |
188 | 0 | { |
189 | 0 | DetectReplaceList *newlist; |
190 | |
|
191 | 0 | if (cd->content_len != cd->replace_len) |
192 | 0 | return NULL; |
193 | 0 | SCLogDebug("replace: Adding match"); |
194 | |
|
195 | 0 | newlist = SCMalloc(sizeof(DetectReplaceList)); |
196 | 0 | if (unlikely(newlist == NULL)) |
197 | 0 | return replist; |
198 | 0 | newlist->found = found; |
199 | 0 | newlist->cd = cd; |
200 | | /* Push new value onto the front of the list. */ |
201 | 0 | newlist->next = replist; |
202 | |
|
203 | 0 | return newlist; |
204 | 0 | } |
205 | | |
206 | | |
207 | | void DetectReplaceExecuteInternal(Packet *p, DetectReplaceList *replist) |
208 | 0 | { |
209 | 0 | DetectReplaceList *tlist = NULL; |
210 | |
|
211 | 0 | SCLogDebug("replace: Executing match"); |
212 | 0 | while (replist) { |
213 | 0 | memcpy(replist->found, replist->cd->replace, replist->cd->replace_len); |
214 | 0 | SCLogDebug("replace: replaced data"); |
215 | 0 | p->flags |= PKT_STREAM_MODIFIED; |
216 | 0 | ReCalculateChecksum(p); |
217 | 0 | tlist = replist; |
218 | 0 | replist = replist->next; |
219 | 0 | SCFree(tlist); |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | |
224 | | void DetectReplaceFreeInternal(DetectReplaceList *replist) |
225 | 0 | { |
226 | 0 | DetectReplaceList *tlist = NULL; |
227 | 0 | while (replist) { |
228 | 0 | SCLogDebug("replace: Freeing match"); |
229 | 0 | tlist = replist; |
230 | 0 | replist = replist->next; |
231 | 0 | SCFree(tlist); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | #ifdef UNITTESTS /* UNITTESTS */ |
236 | | #include "detect-engine-alert.h" |
237 | | #include "packet.h" |
238 | | |
239 | | /** |
240 | | * \test Test packet Matches |
241 | | * \param raw_eth_pkt pointer to the ethernet packet |
242 | | * \param pktsize size of the packet |
243 | | * \param sig pointer to the signature to test |
244 | | * \param sid sid number of the signature |
245 | | * \retval return 1 if match |
246 | | * \retval return 0 if not |
247 | | */ |
248 | | static |
249 | | int DetectReplaceLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize, |
250 | | const char *sig, uint32_t sid, uint8_t *pp, |
251 | | uint16_t *len) |
252 | | { |
253 | | int result = 0; |
254 | | |
255 | | Packet *p = NULL; |
256 | | p = PacketGetFromAlloc(); |
257 | | if (unlikely(p == NULL)) |
258 | | return 0; |
259 | | |
260 | | DecodeThreadVars dtv; |
261 | | |
262 | | ThreadVars th_v; |
263 | | DetectEngineThreadCtx *det_ctx = NULL; |
264 | | |
265 | | if (pp == NULL) { |
266 | | SCLogDebug("replace: looks like a second run"); |
267 | | } |
268 | | |
269 | | PacketCopyData(p, raw_eth_pkt, pktsize); |
270 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
271 | | memset(&th_v, 0, sizeof(th_v)); |
272 | | dtv.app_tctx = AppLayerGetCtxThread(&th_v); |
273 | | |
274 | | FlowInitConfig(FLOW_QUIET); |
275 | | DecodeEthernet(&th_v, &dtv, p, GET_PKT_DATA(p), pktsize); |
276 | | |
277 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
278 | | if (de_ctx == NULL) { |
279 | | goto end; |
280 | | } |
281 | | de_ctx->flags |= DE_QUIET; |
282 | | |
283 | | de_ctx->sig_list = SigInit(de_ctx, sig); |
284 | | if (de_ctx->sig_list == NULL) { |
285 | | goto end; |
286 | | } |
287 | | de_ctx->sig_list->next = NULL; |
288 | | |
289 | | if (de_ctx->sig_list->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) { |
290 | | DetectContentData *co = (DetectContentData *)de_ctx->sig_list->init_data |
291 | | ->smlists_tail[DETECT_SM_LIST_PMATCH] |
292 | | ->ctx; |
293 | | if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) { |
294 | | printf("relative next flag set on final match which is content: "); |
295 | | goto end; |
296 | | } |
297 | | } |
298 | | |
299 | | SigGroupBuild(de_ctx); |
300 | | DetectEngineAddToMaster(de_ctx); |
301 | | DetectEngineThreadCtxInit(&th_v, NULL, (void *)&det_ctx); |
302 | | |
303 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
304 | | DetectEngineMoveToFreeList(de_ctx); |
305 | | |
306 | | if (PacketAlertCheck(p, sid) != 1) { |
307 | | SCLogDebug("replace: no alert on sig %d", sid); |
308 | | goto end; |
309 | | } |
310 | | |
311 | | if (pp) { |
312 | | memcpy(pp, GET_PKT_DATA(p), GET_PKT_LEN(p)); |
313 | | *len = pktsize; |
314 | | SCLogDebug("replace: copying %d on %p", *len, pp); |
315 | | } |
316 | | |
317 | | |
318 | | result = 1; |
319 | | end: |
320 | | if (dtv.app_tctx != NULL) |
321 | | AppLayerDestroyCtxThread(dtv.app_tctx); |
322 | | if (det_ctx != NULL) |
323 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
324 | | DetectEnginePruneFreeList(); |
325 | | PacketRecycle(p); |
326 | | FlowShutdown(); |
327 | | SCFree(p); |
328 | | |
329 | | |
330 | | return result; |
331 | | } |
332 | | |
333 | | |
334 | | /** |
335 | | * \brief Wrapper for DetectContentLongPatternMatchTest |
336 | | */ |
337 | | static int DetectReplaceLongPatternMatchTestWrp(const char *sig, uint32_t sid, const char *sig_rep, uint32_t sid_rep) |
338 | | { |
339 | | int ret; |
340 | | /** Real packet with the following tcp data: |
341 | | * "Hi, this is a big test to check content matches of splitted" |
342 | | * "patterns between multiple chunks!" |
343 | | * (without quotes! :) ) |
344 | | */ |
345 | | uint8_t raw_eth_pkt[] = { |
346 | | 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, |
347 | | 0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00, |
348 | | 0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06, |
349 | | 0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00, |
350 | | 0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00, |
351 | | 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02, |
352 | | 0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69, |
353 | | 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69, |
354 | | 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20, |
355 | | 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20, |
356 | | 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f, |
357 | | 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61, |
358 | | 0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66, |
359 | | 0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65, |
360 | | 0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72, |
361 | | 0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65, |
362 | | 0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69, |
363 | | 0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e, |
364 | | 0x6b,0x73,0x21 }; /* end raw_eth_pkt */ |
365 | | uint8_t p[sizeof(raw_eth_pkt)]; |
366 | | uint16_t psize = sizeof(raw_eth_pkt); |
367 | | |
368 | | /* would be unittest */ |
369 | | int run_mode_backup = run_mode; |
370 | | run_mode = RUNMODE_NFQ; |
371 | | ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt), |
372 | | sig, sid, p, &psize); |
373 | | if (ret == 1) { |
374 | | SCLogDebug("replace: test1 phase1"); |
375 | | ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL); |
376 | | } |
377 | | run_mode = run_mode_backup; |
378 | | return ret; |
379 | | } |
380 | | |
381 | | |
382 | | /** |
383 | | * \brief Wrapper for DetectContentLongPatternMatchTest |
384 | | */ |
385 | | static int DetectReplaceLongPatternMatchTestUDPWrp(const char *sig, uint32_t sid, const char *sig_rep, uint32_t sid_rep) |
386 | | { |
387 | | int ret; |
388 | | /** Real UDP DNS packet with a request A to a1.twimg.com |
389 | | */ |
390 | | uint8_t raw_eth_pkt[] = { |
391 | | 0x8c, 0xa9, 0x82, 0x75, 0x5d, 0x62, 0xb4, 0x07, |
392 | | 0xf9, 0xf3, 0xc7, 0x0a, 0x08, 0x00, 0x45, 0x00, |
393 | | 0x00, 0x3a, 0x92, 0x4f, 0x40, 0x00, 0x40, 0x11, |
394 | | 0x31, 0x1a, 0xc0, 0xa8, 0x00, 0x02, 0xc1, 0xbd, |
395 | | 0xf4, 0xe1, 0x3b, 0x7e, 0x00, 0x35, 0x00, 0x26, |
396 | | 0xcb, 0x81, 0x37, 0x62, 0x01, 0x00, 0x00, 0x01, |
397 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, |
398 | | 0x31, 0x05, 0x74, 0x77, 0x69, 0x6d, 0x67, 0x03, |
399 | | 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 }; |
400 | | |
401 | | uint8_t p[sizeof(raw_eth_pkt)]; |
402 | | uint16_t psize = sizeof(raw_eth_pkt); |
403 | | |
404 | | int run_mode_backup = run_mode; |
405 | | run_mode = RUNMODE_NFQ; |
406 | | ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt), |
407 | | sig, sid, p, &psize); |
408 | | if (ret == 1) { |
409 | | SCLogDebug("replace: test1 phase1 ok: %" PRIuMAX" vs %d",(uintmax_t)sizeof(raw_eth_pkt),psize); |
410 | | ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL); |
411 | | } |
412 | | run_mode = run_mode_backup; |
413 | | return ret; |
414 | | } |
415 | | |
416 | | /** |
417 | | * \test Check if replace is working |
418 | | */ |
419 | | static int DetectReplaceMatchTest01(void) |
420 | | { |
421 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
422 | | " content:\"big\"; replace:\"pig\"; sid:1;)"; |
423 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
424 | | " content:\"this is a pig test\"; sid:2;)"; |
425 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
426 | | PASS; |
427 | | } |
428 | | |
429 | | /** |
430 | | * \test Check if replace is working with offset |
431 | | */ |
432 | | static int DetectReplaceMatchTest02(void) |
433 | | { |
434 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
435 | | " content:\"th\"; offset: 4; replace:\"TH\"; sid:1;)"; |
436 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
437 | | " content:\"THis\"; offset:4; sid:2;)"; |
438 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
439 | | PASS; |
440 | | } |
441 | | |
442 | | /** |
443 | | * \test Check if replace is working with offset and keyword inversion |
444 | | */ |
445 | | static int DetectReplaceMatchTest03(void) |
446 | | { |
447 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
448 | | " content:\"th\"; replace:\"TH\"; offset: 4; sid:1;)"; |
449 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
450 | | " content:\"THis\"; offset:4; sid:2;)"; |
451 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
452 | | PASS; |
453 | | } |
454 | | |
455 | | /** |
456 | | * \test Check if replace is working with second content |
457 | | */ |
458 | | static int DetectReplaceMatchTest04(void) |
459 | | { |
460 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
461 | | " content:\"th\"; replace:\"TH\"; content:\"patter\"; replace:\"matter\"; sid:1;)"; |
462 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
463 | | " content:\"THis\"; content:\"matterns\"; sid:2;)"; |
464 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
465 | | PASS; |
466 | | } |
467 | | |
468 | | /** |
469 | | * \test Check if replace is not done when second content don't match |
470 | | */ |
471 | | static int DetectReplaceMatchTest05(void) |
472 | | { |
473 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
474 | | " content:\"th\"; replace:\"TH\"; content:\"nutella\"; sid:1;)"; |
475 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
476 | | " content:\"TH\"; sid:2;)"; |
477 | | FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
478 | | PASS; |
479 | | } |
480 | | |
481 | | /** |
482 | | * \test Check if replace is not done when second content match and not |
483 | | * first |
484 | | */ |
485 | | static int DetectReplaceMatchTest06(void) |
486 | | { |
487 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
488 | | " content:\"nutella\"; replace:\"commode\"; content:\"this is\"; sid:1;)"; |
489 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
490 | | " content:\"commode\"; sid:2;)"; |
491 | | FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
492 | | PASS; |
493 | | } |
494 | | |
495 | | /** |
496 | | * \test Check if replace is working when nocase used |
497 | | */ |
498 | | static int DetectReplaceMatchTest07(void) |
499 | | { |
500 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
501 | | " content:\"BiG\"; nocase; replace:\"pig\"; sid:1;)"; |
502 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
503 | | " content:\"this is a pig test\"; sid:2;)"; |
504 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
505 | | PASS; |
506 | | } |
507 | | |
508 | | /** |
509 | | * \test Check if replace is working when depth is used |
510 | | */ |
511 | | static int DetectReplaceMatchTest08(void) |
512 | | { |
513 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
514 | | " content:\"big\"; depth:17; replace:\"pig\"; sid:1;)"; |
515 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
516 | | " content:\"this is a pig test\"; sid:2;)"; |
517 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
518 | | PASS; |
519 | | } |
520 | | |
521 | | /** |
522 | | * \test Check if replace is working when depth block match used |
523 | | */ |
524 | | static int DetectReplaceMatchTest09(void) |
525 | | { |
526 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
527 | | " content:\"big\"; depth:16; replace:\"pig\"; sid:1;)"; |
528 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
529 | | " content:\"this is a pig test\"; sid:2;)"; |
530 | | FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
531 | | PASS; |
532 | | } |
533 | | |
534 | | /** |
535 | | * \test Check if replace is working when depth block match used |
536 | | */ |
537 | | static int DetectReplaceMatchTest10(void) |
538 | | { |
539 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
540 | | " content:\"big\"; depth:17; replace:\"pig\"; offset: 14; sid:1;)"; |
541 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
542 | | " content:\"pig\"; depth:17; offset:14; sid:2;)"; |
543 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
544 | | PASS; |
545 | | } |
546 | | |
547 | | /** |
548 | | * \test Check if replace is working with within |
549 | | */ |
550 | | static int DetectReplaceMatchTest11(void) |
551 | | { |
552 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
553 | | " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 11; sid:1;)"; |
554 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
555 | | " content:\"pig\"; depth:17; offset:14; sid:2;)"; |
556 | | |
557 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
558 | | PASS; |
559 | | } |
560 | | |
561 | | /** |
562 | | * \test Check if replace is working with within |
563 | | */ |
564 | | static int DetectReplaceMatchTest12(void) |
565 | | { |
566 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
567 | | " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 4; sid:1;)"; |
568 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
569 | | " content:\"pig\"; depth:17; offset:14; sid:2;)"; |
570 | | FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
571 | | PASS; |
572 | | } |
573 | | |
574 | | /** |
575 | | * \test Check if replace is working with within |
576 | | */ |
577 | | static int DetectReplaceMatchTest13(void) |
578 | | { |
579 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
580 | | " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 1; sid:1;)"; |
581 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
582 | | " content:\"pig\"; depth:17; offset:14; sid:2;)"; |
583 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
584 | | PASS; |
585 | | } |
586 | | |
587 | | /** |
588 | | * \test Check if replace is working with within |
589 | | */ |
590 | | static int DetectReplaceMatchTest14(void) |
591 | | { |
592 | | const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" |
593 | | " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 2; sid:1;)"; |
594 | | const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";" |
595 | | " content:\"pig\"; depth:17; offset:14; sid:2;)"; |
596 | | FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2)); |
597 | | PASS; |
598 | | } |
599 | | |
600 | | /** |
601 | | * \test Check if replace is working with within |
602 | | */ |
603 | | static int DetectReplaceMatchTest15(void) |
604 | | { |
605 | | const char *sig = "alert udp any any -> any any (msg:\"Nothing..\";" |
606 | | " content:\"com\"; replace:\"org\"; sid:1;)"; |
607 | | const char *sig_rep = "alert udp any any -> any any (msg:\"replace worked\";" |
608 | | " content:\"twimg|03|org\"; sid:2;)"; |
609 | | FAIL_IF_NOT(DetectReplaceLongPatternMatchTestUDPWrp(sig, 1, sig_rep, 2)); |
610 | | PASS; |
611 | | } |
612 | | |
613 | | |
614 | | /** |
615 | | * \test Parsing test |
616 | | */ |
617 | | static int DetectReplaceParseTest01(void) |
618 | | { |
619 | | int run_mode_backup = run_mode; |
620 | | run_mode = RUNMODE_NFQ; |
621 | | |
622 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
623 | | FAIL_IF_NULL(de_ctx); |
624 | | |
625 | | de_ctx->flags |= DE_QUIET; |
626 | | FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, |
627 | | "alert udp any any -> any any " |
628 | | "(msg:\"test\"; content:\"doh\"; replace:\"; sid:238012;)")); |
629 | | |
630 | | run_mode = run_mode_backup; |
631 | | DetectEngineCtxFree(de_ctx); |
632 | | PASS; |
633 | | } |
634 | | |
635 | | /** |
636 | | * \test Parsing test: non valid because of http protocol |
637 | | */ |
638 | | static int DetectReplaceParseTest02(void) |
639 | | { |
640 | | int run_mode_backup = run_mode; |
641 | | run_mode = RUNMODE_NFQ; |
642 | | |
643 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
644 | | FAIL_IF_NULL(de_ctx); |
645 | | |
646 | | de_ctx->flags |= DE_QUIET; |
647 | | FAIL_IF_NULL(DetectEngineAppendSig(de_ctx, |
648 | | "alert http any any -> any any " |
649 | | "(msg:\"test\"; content:\"doh\"; replace:\"bon\"; sid:238012;)")); |
650 | | |
651 | | run_mode = run_mode_backup; |
652 | | DetectEngineCtxFree(de_ctx); |
653 | | PASS; |
654 | | } |
655 | | |
656 | | /** |
657 | | * \test Parsing test: non valid because of http_header on same content |
658 | | * as replace keyword |
659 | | */ |
660 | | static int DetectReplaceParseTest03(void) |
661 | | { |
662 | | int run_mode_backup = run_mode; |
663 | | run_mode = RUNMODE_NFQ; |
664 | | |
665 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
666 | | |
667 | | FAIL_IF_NULL(de_ctx); |
668 | | |
669 | | de_ctx->flags |= DE_QUIET; |
670 | | FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, |
671 | | "alert tcp any any -> any any " |
672 | | "(msg:\"test\"; content:\"doh\"; replace:\"don\"; http_header; sid:238012;)")); |
673 | | |
674 | | run_mode = run_mode_backup; |
675 | | DetectEngineCtxFree(de_ctx); |
676 | | PASS; |
677 | | } |
678 | | |
679 | | /** |
680 | | * \test Parsing test no content |
681 | | */ |
682 | | static int DetectReplaceParseTest04(void) |
683 | | { |
684 | | int run_mode_backup = run_mode; |
685 | | run_mode = RUNMODE_NFQ; |
686 | | |
687 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
688 | | FAIL_IF_NULL(de_ctx); |
689 | | |
690 | | de_ctx->flags |= DE_QUIET; |
691 | | FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " |
692 | | "(msg:\"test\"; replace:\"don\"; sid:238012;)")); |
693 | | |
694 | | run_mode = run_mode_backup; |
695 | | DetectEngineCtxFree(de_ctx); |
696 | | PASS; |
697 | | } |
698 | | |
699 | | /** |
700 | | * \test Parsing test content after replace |
701 | | */ |
702 | | static int DetectReplaceParseTest05(void) |
703 | | { |
704 | | int run_mode_backup = run_mode; |
705 | | run_mode = RUNMODE_NFQ; |
706 | | |
707 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
708 | | FAIL_IF_NULL(de_ctx); |
709 | | |
710 | | de_ctx->flags |= DE_QUIET; |
711 | | FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, |
712 | | "alert tcp any any -> any any " |
713 | | "(msg:\"test\"; replace:\"don\"; content:\"doh\"; sid:238012;)")); |
714 | | |
715 | | run_mode = run_mode_backup; |
716 | | DetectEngineCtxFree(de_ctx); |
717 | | PASS; |
718 | | } |
719 | | |
720 | | /** |
721 | | * \test Parsing test content and replace length differ |
722 | | */ |
723 | | static int DetectReplaceParseTest06(void) |
724 | | { |
725 | | int run_mode_backup = run_mode; |
726 | | run_mode = RUNMODE_NFQ; |
727 | | |
728 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
729 | | FAIL_IF_NULL(de_ctx); |
730 | | |
731 | | de_ctx->flags |= DE_QUIET; |
732 | | FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, |
733 | | "alert tcp any any -> any any " |
734 | | "(msg:\"test\"; content:\"don\"; replace:\"donut\"; sid:238012;)")); |
735 | | |
736 | | run_mode = run_mode_backup; |
737 | | DetectEngineCtxFree(de_ctx); |
738 | | PASS; |
739 | | } |
740 | | |
741 | | /** |
742 | | * \test Parsing test content and replace length differ |
743 | | */ |
744 | | static int DetectReplaceParseTest07(void) |
745 | | { |
746 | | int run_mode_backup = run_mode; |
747 | | run_mode = RUNMODE_NFQ; |
748 | | |
749 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
750 | | FAIL_IF_NULL(de_ctx); |
751 | | |
752 | | de_ctx->flags |= DE_QUIET; |
753 | | FAIL_IF_NOT_NULL( |
754 | | DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " |
755 | | "(msg:\"test\"; content:\"don\"; replace:\"dou\"; " |
756 | | "content:\"jpg\"; http_header; sid:238012;)")); |
757 | | |
758 | | run_mode = run_mode_backup; |
759 | | DetectEngineCtxFree(de_ctx); |
760 | | PASS; |
761 | | } |
762 | | |
763 | | /** |
764 | | * \brief this function registers unit tests for DetectContent |
765 | | */ |
766 | | void DetectReplaceRegisterTests(void) |
767 | | { |
768 | | /* matching */ |
769 | | UtRegisterTest("DetectReplaceMatchTest01", DetectReplaceMatchTest01); |
770 | | UtRegisterTest("DetectReplaceMatchTest02", DetectReplaceMatchTest02); |
771 | | UtRegisterTest("DetectReplaceMatchTest03", DetectReplaceMatchTest03); |
772 | | UtRegisterTest("DetectReplaceMatchTest04", DetectReplaceMatchTest04); |
773 | | UtRegisterTest("DetectReplaceMatchTest05", DetectReplaceMatchTest05); |
774 | | UtRegisterTest("DetectReplaceMatchTest06", DetectReplaceMatchTest06); |
775 | | UtRegisterTest("DetectReplaceMatchTest07", DetectReplaceMatchTest07); |
776 | | UtRegisterTest("DetectReplaceMatchTest08", DetectReplaceMatchTest08); |
777 | | UtRegisterTest("DetectReplaceMatchTest09", DetectReplaceMatchTest09); |
778 | | UtRegisterTest("DetectReplaceMatchTest10", DetectReplaceMatchTest10); |
779 | | UtRegisterTest("DetectReplaceMatchTest11", DetectReplaceMatchTest11); |
780 | | UtRegisterTest("DetectReplaceMatchTest12", DetectReplaceMatchTest12); |
781 | | UtRegisterTest("DetectReplaceMatchTest13", DetectReplaceMatchTest13); |
782 | | UtRegisterTest("DetectReplaceMatchTest14", DetectReplaceMatchTest14); |
783 | | UtRegisterTest("DetectReplaceMatchTest15", DetectReplaceMatchTest15); |
784 | | /* parsing */ |
785 | | UtRegisterTest("DetectReplaceParseTest01", DetectReplaceParseTest01); |
786 | | UtRegisterTest("DetectReplaceParseTest02", DetectReplaceParseTest02); |
787 | | UtRegisterTest("DetectReplaceParseTest03", DetectReplaceParseTest03); |
788 | | UtRegisterTest("DetectReplaceParseTest04", DetectReplaceParseTest04); |
789 | | UtRegisterTest("DetectReplaceParseTest05", DetectReplaceParseTest05); |
790 | | UtRegisterTest("DetectReplaceParseTest06", DetectReplaceParseTest06); |
791 | | UtRegisterTest("DetectReplaceParseTest07", DetectReplaceParseTest07); |
792 | | } |
793 | | #endif /* UNITTESTS */ |