/src/suricata8/src/detect-xbits.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2020 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 | | * Implements the xbits keyword |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "decode.h" |
28 | | #include "action-globals.h" |
29 | | #include "detect.h" |
30 | | #include "threads.h" |
31 | | #include "flow.h" |
32 | | #include "flow-util.h" |
33 | | #include "detect-xbits.h" |
34 | | #include "detect-hostbits.h" |
35 | | #include "util-spm.h" |
36 | | #include "util-byte.h" |
37 | | |
38 | | #include "detect-engine-sigorder.h" |
39 | | |
40 | | #include "app-layer-parser.h" |
41 | | |
42 | | #include "detect-parse.h" |
43 | | #include "detect-engine.h" |
44 | | #include "detect-engine-mpm.h" |
45 | | #include "detect-engine-state.h" |
46 | | #include "detect-engine-build.h" |
47 | | |
48 | | #include "flow-bit.h" |
49 | | #include "host-bit.h" |
50 | | #include "ippair-bit.h" |
51 | | #include "tx-bit.h" |
52 | | |
53 | | #include "util-var-name.h" |
54 | | #include "util-unittest.h" |
55 | | #include "util-debug.h" |
56 | | |
57 | | /* |
58 | | xbits:set,bitname,track ip_pair,expire 60 |
59 | | */ |
60 | | |
61 | | static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, |
62 | | void *txv, const Signature *s, const SigMatchCtx *ctx); |
63 | | static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); |
64 | | static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *); |
65 | | #ifdef UNITTESTS |
66 | | static void XBitsRegisterTests(void); |
67 | | #endif |
68 | | static void DetectXbitFree (DetectEngineCtx *, void *); |
69 | | |
70 | | void DetectXbitsRegister (void) |
71 | 79 | { |
72 | 79 | sigmatch_table[DETECT_XBITS].name = "xbits"; |
73 | 79 | sigmatch_table[DETECT_XBITS].desc = "operate on bits"; |
74 | 79 | sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html"; |
75 | 79 | sigmatch_table[DETECT_XBITS].AppLayerTxMatch = DetectXbitTxMatch; |
76 | 79 | sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch; |
77 | 79 | sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup; |
78 | 79 | sigmatch_table[DETECT_XBITS].Free = DetectXbitFree; |
79 | | #ifdef UNITTESTS |
80 | | sigmatch_table[DETECT_XBITS].RegisterTests = XBitsRegisterTests; |
81 | | #endif |
82 | | /* this is compatible to ip-only signatures */ |
83 | 79 | sigmatch_table[DETECT_XBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL); |
84 | 79 | } |
85 | | |
86 | | static int DetectIPPairbitMatchToggle (Packet *p, const DetectXbitsData *fd) |
87 | 0 | { |
88 | 0 | IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst); |
89 | 0 | if (pair == NULL) |
90 | 0 | return 0; |
91 | | |
92 | 0 | IPPairBitToggle(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire)); |
93 | 0 | IPPairRelease(pair); |
94 | 0 | return 1; |
95 | 0 | } |
96 | | |
97 | | /* return true even if bit not found */ |
98 | | static int DetectIPPairbitMatchUnset (Packet *p, const DetectXbitsData *fd) |
99 | 0 | { |
100 | 0 | IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst); |
101 | 0 | if (pair == NULL) |
102 | 0 | return 1; |
103 | | |
104 | 0 | IPPairBitUnset(pair,fd->idx); |
105 | 0 | IPPairRelease(pair); |
106 | 0 | return 1; |
107 | 0 | } |
108 | | |
109 | | static int DetectIPPairbitMatchSet (Packet *p, const DetectXbitsData *fd) |
110 | 86 | { |
111 | 86 | IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst); |
112 | 86 | if (pair == NULL) |
113 | 0 | return 0; |
114 | | |
115 | 86 | IPPairBitSet(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire)); |
116 | 86 | IPPairRelease(pair); |
117 | 86 | return 1; |
118 | 86 | } |
119 | | |
120 | | static int DetectIPPairbitMatchIsset (Packet *p, const DetectXbitsData *fd) |
121 | 138 | { |
122 | 138 | int r = 0; |
123 | 138 | IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst); |
124 | 138 | if (pair == NULL) |
125 | 76 | return 0; |
126 | | |
127 | 62 | r = IPPairBitIsset(pair, fd->idx, p->ts); |
128 | 62 | IPPairRelease(pair); |
129 | 62 | return r; |
130 | 138 | } |
131 | | |
132 | | static int DetectIPPairbitMatchIsnotset (Packet *p, const DetectXbitsData *fd) |
133 | 0 | { |
134 | 0 | int r = 0; |
135 | 0 | IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst); |
136 | 0 | if (pair == NULL) |
137 | 0 | return 1; |
138 | | |
139 | 0 | r = IPPairBitIsnotset(pair, fd->idx, p->ts); |
140 | 0 | IPPairRelease(pair); |
141 | 0 | return r; |
142 | 0 | } |
143 | | |
144 | | static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd) |
145 | 224 | { |
146 | 224 | switch (xd->cmd) { |
147 | 138 | case DETECT_XBITS_CMD_ISSET: |
148 | 138 | return DetectIPPairbitMatchIsset(p,xd); |
149 | 0 | case DETECT_XBITS_CMD_ISNOTSET: |
150 | 0 | return DetectIPPairbitMatchIsnotset(p,xd); |
151 | 86 | case DETECT_XBITS_CMD_SET: |
152 | 86 | return DetectIPPairbitMatchSet(p,xd); |
153 | 0 | case DETECT_XBITS_CMD_UNSET: |
154 | 0 | return DetectIPPairbitMatchUnset(p,xd); |
155 | 0 | case DETECT_XBITS_CMD_TOGGLE: |
156 | 0 | return DetectIPPairbitMatchToggle(p,xd); |
157 | 224 | } |
158 | 0 | return 0; |
159 | 224 | } |
160 | | |
161 | | static int DetectXbitPostMatchTx( |
162 | | DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const DetectXbitsData *xd) |
163 | 0 | { |
164 | 0 | if (p->flow == NULL) |
165 | 0 | return 0; |
166 | 0 | if (!det_ctx->tx_id_set) |
167 | 0 | return 0; |
168 | 0 | Flow *f = p->flow; |
169 | 0 | void *txv = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id); |
170 | 0 | if (txv == NULL) |
171 | 0 | return 0; |
172 | 0 | AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv); |
173 | |
|
174 | 0 | if (xd->cmd != DETECT_XBITS_CMD_SET) |
175 | 0 | return 0; |
176 | | |
177 | 0 | SCLogDebug("sid %u: post-match SET for bit %u on tx:%" PRIu64 ", txd:%p", s->id, xd->idx, |
178 | 0 | det_ctx->tx_id, txd); |
179 | |
|
180 | 0 | return TxBitSet(txd, xd->idx); |
181 | 0 | } |
182 | | |
183 | | /* |
184 | | * returns 0: no match |
185 | | * 1: match |
186 | | * -1: error |
187 | | */ |
188 | | |
189 | | static int DetectXbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) |
190 | 228 | { |
191 | 228 | const DetectXbitsData *fd = (const DetectXbitsData *)ctx; |
192 | 228 | if (fd == NULL) |
193 | 0 | return 0; |
194 | | |
195 | 228 | switch (fd->type) { |
196 | 0 | case VAR_TYPE_HOST_BIT: |
197 | 0 | return DetectXbitMatchHost(p, (const DetectXbitsData *)fd); |
198 | 0 | break; |
199 | 224 | case VAR_TYPE_IPPAIR_BIT: |
200 | 224 | return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd); |
201 | 0 | break; |
202 | 0 | case VAR_TYPE_TX_BIT: |
203 | | // TODO this is for PostMatch only. Can we validate somehow? |
204 | 0 | return DetectXbitPostMatchTx(det_ctx, p, s, fd); |
205 | 0 | break; |
206 | 4 | default: |
207 | 4 | break; |
208 | 228 | } |
209 | 4 | return 0; |
210 | 228 | } |
211 | | |
212 | | static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, |
213 | | void *txv, const Signature *s, const SigMatchCtx *ctx) |
214 | 0 | { |
215 | 0 | const DetectXbitsData *xd = (const DetectXbitsData *)ctx; |
216 | 0 | DEBUG_VALIDATE_BUG_ON(xd == NULL); |
217 | | |
218 | 0 | AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv); |
219 | |
|
220 | 0 | SCLogDebug("sid:%u: tx:%" PRIu64 ", txd->txbits:%p", s->id, det_ctx->tx_id, txd->txbits); |
221 | 0 | int r = TxBitIsset(txd, xd->idx); |
222 | 0 | if (r == 1) { |
223 | 0 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
224 | 0 | } |
225 | 0 | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
226 | 0 | } |
227 | | |
228 | | /** \internal |
229 | | * \brief parse xbits rule options |
230 | | * \retval 0 ok |
231 | | * \retval -1 bad |
232 | | * \param[out] cdout return DetectXbitsData structure or NULL if noalert |
233 | | */ |
234 | | static int DetectXbitParse(DetectEngineCtx *de_ctx, |
235 | | const char *rawstr, DetectXbitsData **cdout) |
236 | 16.5k | { |
237 | 16.5k | bool cmd_set = false; |
238 | 16.5k | bool name_set = false; |
239 | 16.5k | bool track_set = false; |
240 | 16.5k | bool expire_set = false; |
241 | 16.5k | uint8_t cmd = 0; |
242 | 16.5k | uint8_t track = 0; |
243 | 16.5k | enum VarTypes var_type = VAR_TYPE_NOT_SET; |
244 | 16.5k | uint32_t expire = DETECT_XBITS_EXPIRE_DEFAULT; |
245 | 16.5k | DetectXbitsData *cd = NULL; |
246 | 16.5k | char name[256] = ""; |
247 | 16.5k | char copy[strlen(rawstr) + 1]; |
248 | 16.5k | strlcpy(copy, rawstr, sizeof(copy)); |
249 | 16.5k | char *context = NULL; |
250 | 16.5k | char *token = strtok_r(copy, ",", &context); |
251 | 61.0k | while (token != NULL) { |
252 | 75.1k | while (*token != '\0' && isblank(*token)) { |
253 | 24.6k | token++; |
254 | 24.6k | } |
255 | 50.4k | char *val = strchr(token, ' '); |
256 | 50.4k | if (val != NULL) { |
257 | 19.0k | *val++ = '\0'; |
258 | 23.7k | while (*val != '\0' && isblank(*val)) { |
259 | 4.74k | val++; |
260 | 4.74k | } |
261 | 31.4k | } else { |
262 | 31.4k | SCLogDebug("val %s", token); |
263 | 31.4k | } |
264 | 50.4k | if (strlen(token) == 0) { |
265 | 873 | goto next; |
266 | 873 | } |
267 | 49.5k | if (strcmp(token, "noalert") == 0 && !cmd_set) { |
268 | 42 | if (strtok_r(NULL, ",", &context) != NULL) { |
269 | 3 | return -1; |
270 | 3 | } |
271 | 39 | if (val && strlen(val) != 0) { |
272 | 1 | return -1; |
273 | 1 | } |
274 | 38 | *cdout = NULL; |
275 | 38 | return 0; |
276 | 39 | } |
277 | 49.5k | if (!cmd_set) { |
278 | 16.5k | if (val && strlen(val) != 0) { |
279 | 185 | return -1; |
280 | 185 | } |
281 | 16.3k | if (strcmp(token, "set") == 0) { |
282 | 4.23k | cmd = DETECT_XBITS_CMD_SET; |
283 | 12.0k | } else if (strcmp(token, "isset") == 0) { |
284 | 8.21k | cmd = DETECT_XBITS_CMD_ISSET; |
285 | 8.21k | } else if (strcmp(token, "unset") == 0) { |
286 | 26 | cmd = DETECT_XBITS_CMD_UNSET; |
287 | 3.83k | } else if (strcmp(token, "isnotset") == 0) { |
288 | 853 | cmd = DETECT_XBITS_CMD_ISNOTSET; |
289 | 2.98k | } else if (strcmp(token, "toggle") == 0) { |
290 | 1.55k | cmd = DETECT_XBITS_CMD_TOGGLE; |
291 | 1.55k | } else { |
292 | 1.43k | SCLogError("Invalid xbits cmd: %s", token); |
293 | 1.43k | return -1; |
294 | 1.43k | } |
295 | 14.8k | cmd_set = true; |
296 | 33.0k | } else if (!name_set) { |
297 | 14.8k | if (val && strlen(val) != 0) { |
298 | 1.16k | return -1; |
299 | 1.16k | } |
300 | 13.7k | strlcpy(name, token, sizeof(name)); |
301 | 13.7k | name_set = true; |
302 | 18.1k | } else if (!track_set || !expire_set) { |
303 | 18.1k | if (val == NULL) { |
304 | 1.52k | return -1; |
305 | 1.52k | } |
306 | 16.6k | if (strcmp(token, "track") == 0) { |
307 | 7.24k | if (track_set) { |
308 | 4 | return -1; |
309 | 4 | } |
310 | 7.24k | if (strcmp(val, "ip_src") == 0) { |
311 | 72 | track = DETECT_XBITS_TRACK_IPSRC; |
312 | 72 | var_type = VAR_TYPE_HOST_BIT; |
313 | 7.17k | } else if (strcmp(val, "ip_dst") == 0) { |
314 | 111 | track = DETECT_XBITS_TRACK_IPDST; |
315 | 111 | var_type = VAR_TYPE_HOST_BIT; |
316 | 7.05k | } else if (strcmp(val, "ip_pair") == 0) { |
317 | 5.51k | track = DETECT_XBITS_TRACK_IPPAIR; |
318 | 5.51k | var_type = VAR_TYPE_IPPAIR_BIT; |
319 | 5.51k | } else if (strcmp(val, "tx") == 0) { |
320 | 0 | track = DETECT_XBITS_TRACK_TX; |
321 | 0 | var_type = VAR_TYPE_TX_BIT; |
322 | 1.54k | } else { |
323 | 1.54k | SCLogError("Invalid xbits tracker: %s", val); |
324 | 1.54k | return -1; |
325 | 1.54k | } |
326 | 5.69k | track_set = true; |
327 | 9.41k | } else if (strcmp(token, "expire") == 0) { |
328 | 1.11k | if (expire_set) { |
329 | 1 | return -1; |
330 | 1 | } |
331 | 1.10k | if ((StringParseUint32(&expire, 10, 0, val) < 0) || (expire == 0)) { |
332 | 40 | SCLogError("Invalid expire value: %s", val); |
333 | 40 | return -1; |
334 | 40 | } |
335 | 1.06k | expire_set = true; |
336 | 1.06k | } |
337 | 16.6k | } else { |
338 | 2 | SCLogError("Invalid xbits keyword: %s", token); |
339 | 2 | return -1; |
340 | 2 | } |
341 | 44.5k | next: |
342 | 44.5k | token = strtok_r(NULL, ",", &context); |
343 | 44.5k | } |
344 | | |
345 | 10.5k | if (track == DETECT_XBITS_TRACK_TX) { |
346 | 0 | if (cmd != DETECT_XBITS_CMD_ISSET && cmd != DETECT_XBITS_CMD_SET) { |
347 | 0 | SCLogError("tx xbits only support set and isset"); |
348 | 0 | return -1; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 10.5k | cd = SCMalloc(sizeof(DetectXbitsData)); |
353 | 10.5k | if (unlikely(cd == NULL)) |
354 | 0 | return -1; |
355 | | |
356 | 10.5k | uint32_t varname_id = VarNameStoreRegister(name, var_type); |
357 | 10.5k | if (unlikely(varname_id == 0)) { |
358 | 0 | SCFree(cd); |
359 | 0 | return -1; |
360 | 0 | } |
361 | 10.5k | cd->idx = varname_id; |
362 | 10.5k | cd->cmd = cmd; |
363 | 10.5k | cd->tracker = track; |
364 | 10.5k | cd->type = var_type; |
365 | 10.5k | cd->expire = expire; |
366 | | |
367 | 10.5k | SCLogDebug("idx %" PRIu32 ", cmd %d, name %s", cd->idx, cmd, strlen(name) ? name : "(none)"); |
368 | | |
369 | 10.5k | *cdout = cd; |
370 | 10.5k | return 0; |
371 | 10.5k | } |
372 | | |
373 | | int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
374 | 4.37k | { |
375 | 4.37k | DetectXbitsData *cd = NULL; |
376 | | |
377 | 4.37k | int result = DetectXbitParse(de_ctx, rawstr, &cd); |
378 | 4.37k | if (result < 0) { |
379 | 1.97k | return -1; |
380 | 2.39k | } else if (cd == NULL) { |
381 | | /* noalert doesn't use a cd/sm struct. It flags the sig. We're done. */ |
382 | 0 | s->action &= ~ACTION_ALERT; |
383 | 0 | return 0; |
384 | 0 | } |
385 | | |
386 | | /* Okay so far so good, lets get this into a SigMatch |
387 | | * and put it in the Signature. */ |
388 | 2.39k | switch (cd->cmd) { |
389 | | /* case DETECT_XBITS_CMD_NOALERT can't happen here */ |
390 | 10 | case DETECT_XBITS_CMD_ISNOTSET: |
391 | 1.56k | case DETECT_XBITS_CMD_ISSET: { |
392 | 1.56k | int list = DETECT_SM_LIST_MATCH; |
393 | 1.56k | if (cd->tracker == DETECT_XBITS_TRACK_TX) { |
394 | 0 | SCLogDebug("tx xbit isset"); |
395 | 0 | if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_APP) { |
396 | 0 | SCLogError("tx xbits require an explicit rule hook"); |
397 | 0 | SCFree(cd); |
398 | 0 | return -1; |
399 | 0 | } |
400 | 0 | list = s->init_data->hook.sm_list; |
401 | 0 | SCLogDebug("setting list %d", list); |
402 | |
|
403 | 0 | if (list == -1) { |
404 | 0 | SCLogError("tx xbits failed to set up"); // TODO how would we get here? |
405 | 0 | SCFree(cd); |
406 | 0 | return -1; |
407 | 0 | } |
408 | 0 | } |
409 | | |
410 | 1.56k | SCLogDebug("adding match/txmatch"); |
411 | | /* checks, so packet list */ |
412 | 1.56k | if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, list) == |
413 | 1.56k | NULL) { |
414 | 0 | SCFree(cd); |
415 | 0 | return -1; |
416 | 0 | } |
417 | 1.56k | break; |
418 | 1.56k | } |
419 | | // all other cases |
420 | | // DETECT_XBITS_CMD_SET, DETECT_XBITS_CMD_UNSET, DETECT_XBITS_CMD_TOGGLE: |
421 | 1.56k | default: |
422 | 825 | SCLogDebug("adding post-match"); |
423 | | /* modifiers, only run when entire sig has matched */ |
424 | 825 | if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, |
425 | 825 | DETECT_SM_LIST_POSTMATCH) == NULL) { |
426 | 0 | SCFree(cd); |
427 | 0 | return -1; |
428 | 0 | } |
429 | 825 | break; |
430 | 2.39k | } |
431 | | |
432 | 2.39k | return 0; |
433 | 2.39k | } |
434 | | |
435 | | static void DetectXbitFree (DetectEngineCtx *de_ctx, void *ptr) |
436 | 10.5k | { |
437 | 10.5k | DetectXbitsData *fd = (DetectXbitsData *)ptr; |
438 | | |
439 | 10.5k | if (fd == NULL) |
440 | 0 | return; |
441 | 10.5k | VarNameStoreUnregister(fd->idx, fd->type); |
442 | | |
443 | 10.5k | SCFree(fd); |
444 | 10.5k | } |
445 | | |
446 | | #ifdef UNITTESTS |
447 | | |
448 | | static void XBitsTestSetup(void) |
449 | | { |
450 | | StorageInit(); |
451 | | HostBitInitCtx(); |
452 | | IPPairBitInitCtx(); |
453 | | StorageFinalize(); |
454 | | HostInitConfig(true); |
455 | | IPPairInitConfig(true); |
456 | | } |
457 | | |
458 | | static void XBitsTestShutdown(void) |
459 | | { |
460 | | HostCleanup(); |
461 | | IPPairCleanup(); |
462 | | StorageCleanup(); |
463 | | } |
464 | | |
465 | | |
466 | | static int XBitsTestParse01(void) |
467 | | { |
468 | | DetectEngineCtx *de_ctx = NULL; |
469 | | de_ctx = DetectEngineCtxInit(); |
470 | | FAIL_IF_NULL(de_ctx); |
471 | | de_ctx->flags |= DE_QUIET; |
472 | | DetectXbitsData *cd = NULL; |
473 | | |
474 | | #define BAD_INPUT(str) \ |
475 | | FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == -1); |
476 | | |
477 | | BAD_INPUT("alert"); |
478 | | BAD_INPUT("n0alert"); |
479 | | BAD_INPUT("nOalert"); |
480 | | BAD_INPUT("set,abc,track nonsense, expire 3600"); |
481 | | BAD_INPUT("set,abc,track ip_source, expire 3600"); |
482 | | BAD_INPUT("set,abc,track ip_src, expire -1"); |
483 | | BAD_INPUT("set,abc,track ip_src, expire 0"); |
484 | | |
485 | | #undef BAD_INPUT |
486 | | |
487 | | #define GOOD_INPUT(str, command, trk, typ, exp) \ |
488 | | FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == 0); \ |
489 | | FAIL_IF_NULL(cd); \ |
490 | | FAIL_IF_NOT(cd->cmd == (command)); \ |
491 | | FAIL_IF_NOT(cd->tracker == (trk)); \ |
492 | | FAIL_IF_NOT(cd->type == (typ)); \ |
493 | | FAIL_IF_NOT(cd->expire == (exp)); \ |
494 | | DetectXbitFree(NULL, cd); \ |
495 | | cd = NULL; |
496 | | |
497 | | GOOD_INPUT("set,abc,track ip_pair", |
498 | | DETECT_XBITS_CMD_SET, |
499 | | DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT, |
500 | | DETECT_XBITS_EXPIRE_DEFAULT); |
501 | | GOOD_INPUT("set,abc,track ip_pair, expire 3600", |
502 | | DETECT_XBITS_CMD_SET, |
503 | | DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT, |
504 | | 3600); |
505 | | GOOD_INPUT("set,abc,track ip_src, expire 1234", |
506 | | DETECT_XBITS_CMD_SET, |
507 | | DETECT_XBITS_TRACK_IPSRC, VAR_TYPE_HOST_BIT, |
508 | | 1234); |
509 | | |
510 | | #undef GOOD_INPUT |
511 | | |
512 | | DetectEngineCtxFree(de_ctx); |
513 | | PASS; |
514 | | } |
515 | | |
516 | | /** |
517 | | * \test |
518 | | */ |
519 | | |
520 | | static int XBitsTestSig01(void) |
521 | | { |
522 | | uint8_t *buf = (uint8_t *) |
523 | | "GET /one/ HTTP/1.1\r\n" |
524 | | "Host: one.example.org\r\n" |
525 | | "\r\n"; |
526 | | uint16_t buflen = strlen((char *)buf); |
527 | | Packet *p = PacketGetFromAlloc(); |
528 | | FAIL_IF_NULL(p); |
529 | | Signature *s = NULL; |
530 | | ThreadVars th_v; |
531 | | DetectEngineThreadCtx *det_ctx = NULL; |
532 | | DetectEngineCtx *de_ctx = NULL; |
533 | | |
534 | | memset(&th_v, 0, sizeof(th_v)); |
535 | | p->src.family = AF_INET; |
536 | | p->dst.family = AF_INET; |
537 | | p->payload = buf; |
538 | | p->payload_len = buflen; |
539 | | p->proto = IPPROTO_TCP; |
540 | | |
541 | | XBitsTestSetup(); |
542 | | |
543 | | de_ctx = DetectEngineCtxInit(); |
544 | | FAIL_IF_NULL(de_ctx); |
545 | | de_ctx->flags |= DE_QUIET; |
546 | | |
547 | | s = DetectEngineAppendSig(de_ctx, |
548 | | "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:1;)"); |
549 | | FAIL_IF_NULL(s); |
550 | | |
551 | | SigGroupBuild(de_ctx); |
552 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
553 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
554 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
555 | | DetectEngineCtxFree(de_ctx); |
556 | | XBitsTestShutdown(); |
557 | | SCFree(p); |
558 | | StatsThreadCleanup(&th_v); |
559 | | StatsReleaseResources(); |
560 | | PASS; |
561 | | } |
562 | | |
563 | | /** |
564 | | * \test various options |
565 | | * |
566 | | * \retval 1 on success |
567 | | * \retval 0 on failure |
568 | | */ |
569 | | |
570 | | static int XBitsTestSig02(void) |
571 | | { |
572 | | Signature *s = NULL; |
573 | | DetectEngineCtx *de_ctx = NULL; |
574 | | de_ctx = DetectEngineCtxInit(); |
575 | | FAIL_IF_NULL(de_ctx); |
576 | | de_ctx->flags |= DE_QUIET; |
577 | | |
578 | | s = DetectEngineAppendSig(de_ctx, |
579 | | "alert ip any any -> any any (xbits:isset,abc,track ip_src; content:\"GET \"; sid:1;)"); |
580 | | FAIL_IF_NULL(s); |
581 | | |
582 | | s = DetectEngineAppendSig(de_ctx, |
583 | | "alert ip any any -> any any (xbits:isnotset,abc,track ip_dst; content:\"GET \"; sid:2;)"); |
584 | | FAIL_IF_NULL(s); |
585 | | |
586 | | s = DetectEngineAppendSig(de_ctx, |
587 | | "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:3;)"); |
588 | | FAIL_IF_NULL(s); |
589 | | |
590 | | s = DetectEngineAppendSig(de_ctx, |
591 | | "alert ip any any -> any any (xbits:unset,abc,track ip_src; content:\"GET \"; sid:4;)"); |
592 | | FAIL_IF_NULL(s); |
593 | | |
594 | | s = DetectEngineAppendSig(de_ctx, |
595 | | "alert ip any any -> any any (xbits:toggle,abc,track ip_dst; content:\"GET \"; sid:5;)"); |
596 | | FAIL_IF_NULL(s); |
597 | | |
598 | | s = DetectEngineAppendSig(de_ctx, |
599 | | "alert ip any any -> any any (xbits:!set,abc,track ip_dst; content:\"GET \"; sid:6;)"); |
600 | | FAIL_IF_NOT_NULL(s); |
601 | | |
602 | | DetectEngineCtxFree(de_ctx); |
603 | | PASS; |
604 | | } |
605 | | |
606 | | /* Test to demonstrate redmine bug 4820 */ |
607 | | static int XBitsTestSig03(void) |
608 | | { |
609 | | DetectEngineCtx *de_ctx = NULL; |
610 | | XBitsTestSetup(); |
611 | | de_ctx = DetectEngineCtxInit(); |
612 | | FAIL_IF_NULL(de_ctx); |
613 | | de_ctx->flags |= DE_QUIET; |
614 | | |
615 | | Signature *s = DetectEngineAppendSig( |
616 | | de_ctx, "alert http any any -> any any (msg:\"TEST - No Error\")\";\ |
617 | | flow:established,to_server; http.method; content:\"GET\"; \ |
618 | | xbits:set,ET.2020_8260.1,track ip_src,expire 10; sid:1;)"); |
619 | | FAIL_IF_NULL(s); |
620 | | |
621 | | DetectEngineCtxFree(de_ctx); |
622 | | XBitsTestShutdown(); |
623 | | PASS; |
624 | | } |
625 | | |
626 | | /* Test to demonstrate redmine bug 4820 */ |
627 | | static int XBitsTestSig04(void) |
628 | | { |
629 | | DetectEngineCtx *de_ctx = NULL; |
630 | | XBitsTestSetup(); |
631 | | de_ctx = DetectEngineCtxInit(); |
632 | | FAIL_IF_NULL(de_ctx); |
633 | | de_ctx->flags |= DE_QUIET; |
634 | | |
635 | | Signature *s = |
636 | | DetectEngineAppendSig(de_ctx, "alert http any any -> any any (msg:\"TEST - Error\")\"; \ |
637 | | flow:established,to_server; http.method; content:\"GET\"; \ |
638 | | xbits:set,ET.2020_8260.1,noalert,track ip_src,expire 10; sid:2;)"); |
639 | | FAIL_IF_NOT_NULL(s); |
640 | | |
641 | | DetectEngineCtxFree(de_ctx); |
642 | | XBitsTestShutdown(); |
643 | | PASS; |
644 | | } |
645 | | |
646 | | static int XBitsTestSig05(void) |
647 | | { |
648 | | DetectEngineCtx *de_ctx = NULL; |
649 | | XBitsTestSetup(); |
650 | | de_ctx = DetectEngineCtxInit(); |
651 | | FAIL_IF_NULL(de_ctx); |
652 | | de_ctx->flags |= DE_QUIET; |
653 | | |
654 | | Signature *s = DetectEngineAppendSig(de_ctx, |
655 | | "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE " |
656 | | "Chain Stage 1 Inbound - Request Config Backup (CVE-2020-8260)\"; " |
657 | | "flow:established,to_server; http.method; content:\"GET\"; http.uri; " |
658 | | "content:\"/dana-admin/cached/config/config.cgi?type=system\"; fast_pattern; " |
659 | | "xbits:set,ET.2020_8260.1,track ip_src,expire 10; xbits:noalert; " |
660 | | "classtype:attempted-admin; sid:2033750; rev:1;"); |
661 | | FAIL_IF_NULL(s); |
662 | | |
663 | | DetectEngineCtxFree(de_ctx); |
664 | | XBitsTestShutdown(); |
665 | | PASS; |
666 | | } |
667 | | |
668 | | static int XBitsTestSig06(void) |
669 | | { |
670 | | DetectEngineCtx *de_ctx = NULL; |
671 | | XBitsTestSetup(); |
672 | | de_ctx = DetectEngineCtxInit(); |
673 | | FAIL_IF_NULL(de_ctx); |
674 | | de_ctx->flags |= DE_QUIET; |
675 | | |
676 | | Signature *s = DetectEngineAppendSig(de_ctx, |
677 | | "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE " |
678 | | "Chain Stage 2 Inbound - Upload Malicious Config (CVE-2020-8260)\"; " |
679 | | "flow:established,to_server; http.method; content:\"POST\"; http.uri; " |
680 | | "content:\"/dana-admin/cached/config/import.cgi\"; " |
681 | | "xbits:isset,ET.2020_8260.1,track ip_src,expire 10;" |
682 | | "xbits:set,ET.2020_8260.2,track ip_src,expire 10; " |
683 | | "classtype:attempted-admin; sid:2033751; rev:1;"); |
684 | | FAIL_IF_NULL(s); |
685 | | |
686 | | DetectEngineCtxFree(de_ctx); |
687 | | XBitsTestShutdown(); |
688 | | PASS; |
689 | | } |
690 | | |
691 | | static int DetectXBitsTestBadRules(void) |
692 | | { |
693 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
694 | | FAIL_IF_NULL(de_ctx); |
695 | | |
696 | | const char *sigs[] = { |
697 | | "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,noalert,track " |
698 | | "ip_src;sid:1;)", |
699 | | "alert http any any -> any any (content:\"abc\"; xbits:noalert,set,bit1,noalert,track " |
700 | | "ip_src;sid:10;)", |
701 | | "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track " |
702 | | "ip_dst,asdf;sid:2;)", |
703 | | "alert http any any -> any any (content:\"abc\"; xbits:isnotset,track ip_pair;sid:3;)", |
704 | | "alert http any any -> any any (content:\"abc\"; xbits:toggle,track ip_pair,bit4;sid:4;)", |
705 | | "alert http any any -> any any (content:\"abc\"; xbits:unset,bit5,track ipsrc;sid:5;)", |
706 | | "alert http any any -> any any (content:\"abc\"; xbits:bit6,set,track ip_src,expire " |
707 | | "10;sid:6;)", |
708 | | "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,track " |
709 | | "ip_pair,expire;sid:7;)", |
710 | | "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,trackk ip_pair,expire " |
711 | | "3600, noalert;sid:8;)", |
712 | | NULL, |
713 | | }; |
714 | | |
715 | | const char **sig = sigs; |
716 | | while (*sig) { |
717 | | SCLogDebug("sig %s", *sig); |
718 | | Signature *s = DetectEngineAppendSig(de_ctx, *sig); |
719 | | FAIL_IF_NOT_NULL(s); |
720 | | sig++; |
721 | | } |
722 | | |
723 | | DetectEngineCtxFree(de_ctx); |
724 | | PASS; |
725 | | } |
726 | | |
727 | | static int DetectXBitsTestGoodRules(void) |
728 | | { |
729 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
730 | | FAIL_IF_NULL(de_ctx); |
731 | | |
732 | | const char *sigs[] = { |
733 | | "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,track ip_src;sid:1;)", |
734 | | "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track ip_dst;sid:2;)", |
735 | | "alert http any any -> any any (content:\"abc\"; xbits:isnotset, bit3, track " |
736 | | "ip_pair;sid:3;)", |
737 | | "alert http any any -> any any (content:\"abc\"; xbits:toggle,bit4, track " |
738 | | "ip_pair;sid:4;)", |
739 | | "alert http any any -> any any (content:\"abc\"; xbits: unset ,bit5,track ip_src;sid:5;)", |
740 | | "alert http any any -> any any (content:\"abc\"; xbits:set,bit6 ,track ip_src, expire " |
741 | | "10 ;sid:6;)", |
742 | | "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire " |
743 | | "3600;sid:7;)", |
744 | | "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire " |
745 | | "3600; xbits:noalert; sid:8;)", |
746 | | "alert http any any -> any any (content:\"abc\"; xbits:noalert; xbits:set, bit7, track " |
747 | | "ip_pair, expire " |
748 | | "3600;sid:9;)", |
749 | | NULL, |
750 | | }; |
751 | | |
752 | | const char **sig = sigs; |
753 | | while (*sig) { |
754 | | SCLogDebug("sig %s", *sig); |
755 | | Signature *s = DetectEngineAppendSig(de_ctx, *sig); |
756 | | FAIL_IF_NULL(s); |
757 | | sig++; |
758 | | } |
759 | | |
760 | | DetectEngineCtxFree(de_ctx); |
761 | | PASS; |
762 | | } |
763 | | |
764 | | /** |
765 | | * \brief this function registers unit tests for XBits |
766 | | */ |
767 | | static void XBitsRegisterTests(void) |
768 | | { |
769 | | UtRegisterTest("XBitsTestParse01", XBitsTestParse01); |
770 | | UtRegisterTest("XBitsTestSig01", XBitsTestSig01); |
771 | | UtRegisterTest("XBitsTestSig02", XBitsTestSig02); |
772 | | UtRegisterTest("XBitsTestSig03", XBitsTestSig03); |
773 | | UtRegisterTest("XBitsTestSig04", XBitsTestSig04); |
774 | | UtRegisterTest("XBitsTestSig05", XBitsTestSig05); |
775 | | UtRegisterTest("XBitsTestSig06", XBitsTestSig06); |
776 | | UtRegisterTest("DetectXBitsTestBadRules", DetectXBitsTestBadRules); |
777 | | UtRegisterTest("DetectXBitsTestGoodRules", DetectXBitsTestGoodRules); |
778 | | } |
779 | | #endif /* UNITTESTS */ |