/src/suricata7/src/detect-ssh-proto-version.c
Line | Count | Source (jump to first uncovered line) |
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 Pablo Rincon <pablo.rincon.crespo@gmail.com> |
22 | | * |
23 | | * Implements the ssh.protoversion keyword |
24 | | * You can specify a concrete version like ssh.protoversion: 1.66 |
25 | | * or search for protoversion 2 compat (1.99 is considered as 2) like |
26 | | * ssh.protoversion:2_compat |
27 | | * or just the beginning of the string like ssh.protoversion:"1." |
28 | | */ |
29 | | |
30 | | #include "suricata-common.h" |
31 | | #include "threads.h" |
32 | | #include "decode.h" |
33 | | |
34 | | #include "detect.h" |
35 | | #include "detect-parse.h" |
36 | | |
37 | | #include "detect-engine.h" |
38 | | #include "detect-engine-mpm.h" |
39 | | #include "detect-engine-state.h" |
40 | | #include "detect-engine-build.h" |
41 | | |
42 | | #include "flow.h" |
43 | | #include "flow-var.h" |
44 | | #include "flow-util.h" |
45 | | |
46 | | #include "util-debug.h" |
47 | | #include "util-unittest.h" |
48 | | #include "util-unittest-helper.h" |
49 | | |
50 | | #include "app-layer.h" |
51 | | #include "app-layer-parser.h" |
52 | | #include "app-layer-ssh.h" |
53 | | #include "detect-ssh-proto-version.h" |
54 | | #include "rust.h" |
55 | | |
56 | | #include "stream-tcp.h" |
57 | | |
58 | | /** |
59 | | * \brief Regex for parsing the protoversion string |
60 | | */ |
61 | 73 | #define PARSE_REGEX "^\\s*\"?\\s*([0-9]+([\\.\\-0-9]+)?|2_compat)\\s*\"?\\s*$" |
62 | | |
63 | | static DetectParseRegex parse_regex; |
64 | | |
65 | | static int DetectSshVersionMatch (DetectEngineThreadCtx *, |
66 | | Flow *, uint8_t, void *, void *, |
67 | | const Signature *, const SigMatchCtx *); |
68 | | static int DetectSshVersionSetup (DetectEngineCtx *, Signature *, const char *); |
69 | | #ifdef UNITTESTS |
70 | | static void DetectSshVersionRegisterTests(void); |
71 | | #endif |
72 | | static void DetectSshVersionFree(DetectEngineCtx *, void *); |
73 | | static int g_ssh_banner_list_id = 0; |
74 | | |
75 | | /** |
76 | | * \brief Registration function for keyword: ssh.protoversion |
77 | | */ |
78 | | void DetectSshVersionRegister(void) |
79 | 73 | { |
80 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].name = "ssh.protoversion"; |
81 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].desc = "match SSH protocol version"; |
82 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].url = "/rules/ssh-keywords.html#ssh-protoversion"; |
83 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].AppLayerTxMatch = DetectSshVersionMatch; |
84 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Setup = DetectSshVersionSetup; |
85 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Free = DetectSshVersionFree; |
86 | | #ifdef UNITTESTS |
87 | | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].RegisterTests = DetectSshVersionRegisterTests; |
88 | | #endif |
89 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].flags = SIGMATCH_QUOTES_OPTIONAL|SIGMATCH_INFO_DEPRECATED; |
90 | 73 | sigmatch_table[DETECT_AL_SSH_PROTOVERSION].alternative = DETECT_AL_SSH_PROTOCOL; |
91 | | |
92 | 73 | DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); |
93 | | |
94 | 73 | g_ssh_banner_list_id = DetectBufferTypeRegister("ssh_banner"); |
95 | 73 | } |
96 | | |
97 | | /** |
98 | | * \brief match the specified version on a ssh session |
99 | | * |
100 | | * \param t pointer to thread vars |
101 | | * \param det_ctx pointer to the pattern matcher thread |
102 | | * \param p pointer to the current packet |
103 | | * \param m pointer to the sigmatch that we will cast into DetectSshVersionData |
104 | | * |
105 | | * \retval 0 no match |
106 | | * \retval 1 match |
107 | | */ |
108 | | static int DetectSshVersionMatch (DetectEngineThreadCtx *det_ctx, |
109 | | Flow *f, uint8_t flags, void *state, void *txv, |
110 | | const Signature *s, const SigMatchCtx *m) |
111 | 0 | { |
112 | 0 | SCEnter(); |
113 | |
|
114 | 0 | SCLogDebug("lets see"); |
115 | |
|
116 | 0 | DetectSshVersionData *ssh = (DetectSshVersionData *)m; |
117 | 0 | if (state == NULL) { |
118 | 0 | SCLogDebug("no ssh state, no match"); |
119 | 0 | SCReturnInt(0); |
120 | 0 | } |
121 | | |
122 | 0 | int ret = 0; |
123 | 0 | const uint8_t *protocol = NULL; |
124 | 0 | uint32_t b_len = 0; |
125 | |
|
126 | 0 | if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flags) != 1) |
127 | 0 | SCReturnInt(0); |
128 | 0 | if (protocol == NULL || b_len == 0) |
129 | 0 | SCReturnInt(0); |
130 | | |
131 | 0 | if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { |
132 | 0 | SCLogDebug("looking for ssh protoversion 2 compat"); |
133 | 0 | if (protocol[0] == '2') { |
134 | 0 | ret = 1; |
135 | 0 | } else if (b_len >= 4) { |
136 | 0 | if (memcmp(protocol, "1.99", 4) == 0) { |
137 | 0 | ret = 1; |
138 | 0 | } |
139 | 0 | } |
140 | 0 | } else { |
141 | 0 | SCLogDebug("looking for ssh protoversion %s length %"PRIu16"", ssh->ver, ssh->len); |
142 | 0 | if (b_len == ssh->len) { |
143 | 0 | if (memcmp(protocol, ssh->ver, ssh->len) == 0) { |
144 | 0 | ret = 1; |
145 | 0 | } |
146 | 0 | } |
147 | 0 | } |
148 | 0 | SCReturnInt(ret); |
149 | 0 | } |
150 | | |
151 | | /** |
152 | | * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" |
153 | | * |
154 | | * \param de_ctx Pointer to the detection engine context |
155 | | * \param idstr Pointer to the user provided id option |
156 | | * |
157 | | * \retval id_d pointer to DetectSshVersionData on success |
158 | | * \retval NULL on failure |
159 | | */ |
160 | | static DetectSshVersionData *DetectSshVersionParse (DetectEngineCtx *de_ctx, const char *str) |
161 | 4 | { |
162 | 4 | DetectSshVersionData *ssh = NULL; |
163 | 4 | int res = 0; |
164 | 4 | size_t pcre2_len; |
165 | | |
166 | 4 | pcre2_match_data *match = NULL; |
167 | 4 | int ret = DetectParsePcreExec(&parse_regex, &match, str, 0, 0); |
168 | 4 | if (ret < 1 || ret > 3) { |
169 | 3 | SCLogError("invalid ssh.protoversion option"); |
170 | 3 | goto error; |
171 | 3 | } |
172 | | |
173 | 1 | if (ret > 1) { |
174 | 1 | const char *str_ptr; |
175 | 1 | res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); |
176 | 1 | if (res < 0) { |
177 | 0 | SCLogError("pcre2_substring_get_bynumber failed"); |
178 | 0 | goto error; |
179 | 0 | } |
180 | | |
181 | | /* We have a correct id option */ |
182 | 1 | ssh = SCMalloc(sizeof(DetectSshVersionData)); |
183 | 1 | if (unlikely(ssh == NULL)) { |
184 | 0 | pcre2_substring_free((PCRE2_UCHAR *)str_ptr); |
185 | 0 | goto error; |
186 | 0 | } |
187 | 1 | memset(ssh, 0x00, sizeof(DetectSshVersionData)); |
188 | | |
189 | | /* If we expect a protocol version 2 or 1.99 (considered 2, we |
190 | | * will compare it with both strings) */ |
191 | 1 | if (strcmp("2_compat", str_ptr) == 0) { |
192 | 0 | ssh->flags |= SSH_FLAG_PROTOVERSION_2_COMPAT; |
193 | 0 | SCLogDebug("will look for ssh protocol version 2 (2, 2.0, 1.99 that's considered as 2"); |
194 | 0 | pcre2_substring_free((PCRE2_UCHAR *)str_ptr); |
195 | 0 | return ssh; |
196 | 0 | } |
197 | | |
198 | 1 | ssh->ver = (uint8_t *)SCStrdup((char*)str_ptr); |
199 | 1 | if (ssh->ver == NULL) { |
200 | 0 | pcre2_substring_free((PCRE2_UCHAR *)str_ptr); |
201 | 0 | goto error; |
202 | 0 | } |
203 | 1 | ssh->len = (uint16_t)strlen((char *)ssh->ver); |
204 | 1 | pcre2_substring_free((PCRE2_UCHAR *)str_ptr); |
205 | | |
206 | 1 | SCLogDebug("will look for ssh %s", ssh->ver); |
207 | 1 | } |
208 | | |
209 | 1 | pcre2_match_data_free(match); |
210 | 1 | return ssh; |
211 | | |
212 | 3 | error: |
213 | 3 | if (match) { |
214 | 3 | pcre2_match_data_free(match); |
215 | 3 | } |
216 | 3 | if (ssh != NULL) |
217 | 0 | DetectSshVersionFree(de_ctx, ssh); |
218 | 3 | return NULL; |
219 | | |
220 | 1 | } |
221 | | |
222 | | /** |
223 | | * \brief this function is used to add the parsed "id" option |
224 | | * \brief into the current signature |
225 | | * |
226 | | * \param de_ctx pointer to the Detection Engine Context |
227 | | * \param s pointer to the Current Signature |
228 | | * \param idstr pointer to the user provided "id" option |
229 | | * |
230 | | * \retval 0 on Success |
231 | | * \retval -1 on Failure |
232 | | */ |
233 | | static int DetectSshVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) |
234 | 5 | { |
235 | 5 | DetectSshVersionData *ssh = NULL; |
236 | 5 | SigMatch *sm = NULL; |
237 | | |
238 | 5 | if (DetectSignatureSetAppProto(s, ALPROTO_SSH) != 0) |
239 | 1 | return -1; |
240 | | |
241 | 4 | ssh = DetectSshVersionParse(de_ctx, str); |
242 | 4 | if (ssh == NULL) |
243 | 3 | goto error; |
244 | | |
245 | | /* Okay so far so good, lets get this into a SigMatch |
246 | | * and put it in the Signature. */ |
247 | 1 | sm = SigMatchAlloc(); |
248 | 1 | if (sm == NULL) |
249 | 0 | goto error; |
250 | | |
251 | 1 | sm->type = DETECT_AL_SSH_PROTOVERSION; |
252 | 1 | sm->ctx = (void *)ssh; |
253 | | |
254 | 1 | SigMatchAppendSMToList(s, sm, g_ssh_banner_list_id); |
255 | 1 | return 0; |
256 | | |
257 | 3 | error: |
258 | 3 | if (ssh != NULL) |
259 | 0 | DetectSshVersionFree(de_ctx, ssh); |
260 | 3 | if (sm != NULL) |
261 | 0 | SCFree(sm); |
262 | 3 | return -1; |
263 | | |
264 | 1 | } |
265 | | |
266 | | /** |
267 | | * \brief this function will free memory associated with DetectSshVersionData |
268 | | * |
269 | | * \param id_d pointer to DetectSshVersionData |
270 | | */ |
271 | | void DetectSshVersionFree(DetectEngineCtx *de_ctx, void *ptr) |
272 | 1 | { |
273 | 1 | DetectSshVersionData *sshd = (DetectSshVersionData *)ptr; |
274 | 1 | SCFree(sshd->ver); |
275 | 1 | SCFree(sshd); |
276 | 1 | } |
277 | | |
278 | | #ifdef UNITTESTS /* UNITTESTS */ |
279 | | #include "detect-engine-alert.h" |
280 | | |
281 | | /** |
282 | | * \test DetectSshVersionTestParse01 is a test to make sure that we parse |
283 | | * a proto version correctly |
284 | | */ |
285 | | static int DetectSshVersionTestParse01 (void) |
286 | | { |
287 | | DetectSshVersionData *ssh = NULL; |
288 | | ssh = DetectSshVersionParse(NULL, "1.0"); |
289 | | FAIL_IF_NULL(ssh); |
290 | | FAIL_IF_NOT(strncmp((char *)ssh->ver, "1.0", 3) == 0); |
291 | | DetectSshVersionFree(NULL, ssh); |
292 | | |
293 | | PASS; |
294 | | } |
295 | | |
296 | | /** |
297 | | * \test DetectSshVersionTestParse02 is a test to make sure that we parse |
298 | | * the proto version (compatible with proto version 2) correctly |
299 | | */ |
300 | | static int DetectSshVersionTestParse02 (void) |
301 | | { |
302 | | DetectSshVersionData *ssh = NULL; |
303 | | ssh = DetectSshVersionParse(NULL, "2_compat"); |
304 | | FAIL_IF_NOT(ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT); |
305 | | DetectSshVersionFree(NULL, ssh); |
306 | | |
307 | | PASS; |
308 | | } |
309 | | |
310 | | /** |
311 | | * \test DetectSshVersionTestParse03 is a test to make sure that we |
312 | | * don't return a ssh_data with an invalid value specified |
313 | | */ |
314 | | static int DetectSshVersionTestParse03 (void) |
315 | | { |
316 | | DetectSshVersionData *ssh = NULL; |
317 | | ssh = DetectSshVersionParse(NULL, "2_com"); |
318 | | FAIL_IF_NOT_NULL(ssh); |
319 | | ssh = DetectSshVersionParse(NULL, ""); |
320 | | FAIL_IF_NOT_NULL(ssh); |
321 | | ssh = DetectSshVersionParse(NULL, ".1"); |
322 | | FAIL_IF_NOT_NULL(ssh); |
323 | | ssh = DetectSshVersionParse(NULL, "lalala"); |
324 | | FAIL_IF_NOT_NULL(ssh); |
325 | | |
326 | | PASS; |
327 | | } |
328 | | |
329 | | |
330 | | #include "stream-tcp-reassemble.h" |
331 | | #include "stream-tcp-util.h" |
332 | | |
333 | | /** \test Send a get request in three chunks + more data. */ |
334 | | static int DetectSshVersionTestDetect01(void) |
335 | | { |
336 | | TcpReassemblyThreadCtx *ra_ctx = NULL; |
337 | | ThreadVars tv; |
338 | | TcpSession ssn; |
339 | | Flow *f = NULL; |
340 | | Packet *p = NULL; |
341 | | |
342 | | uint8_t sshbuf1[] = "SSH-1."; |
343 | | uint8_t sshbuf2[] = "10-PuTTY_2.123" ; |
344 | | uint8_t sshbuf3[] = "\n"; |
345 | | uint8_t sshbuf4[] = "whatever..."; |
346 | | |
347 | | uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; |
348 | | uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; |
349 | | |
350 | | memset(&tv, 0x00, sizeof(tv)); |
351 | | |
352 | | StreamTcpUTInit(&ra_ctx); |
353 | | StreamTcpUTInitInline(); |
354 | | StreamTcpUTSetupSession(&ssn); |
355 | | StreamTcpUTSetupStream(&ssn.server, 1); |
356 | | StreamTcpUTSetupStream(&ssn.client, 1); |
357 | | |
358 | | f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); |
359 | | FAIL_IF_NULL(f); |
360 | | f->protoctx = &ssn; |
361 | | f->proto = IPPROTO_TCP; |
362 | | f->alproto = ALPROTO_SSH; |
363 | | |
364 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
365 | | FAIL_IF(unlikely(p == NULL)); |
366 | | p->flow = f; |
367 | | |
368 | | Signature *s = NULL; |
369 | | ThreadVars th_v; |
370 | | DetectEngineThreadCtx *det_ctx = NULL; |
371 | | |
372 | | memset(&th_v, 0, sizeof(th_v)); |
373 | | |
374 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
375 | | FAIL_IF_NULL(de_ctx); |
376 | | |
377 | | de_ctx->flags |= DE_QUIET; |
378 | | |
379 | | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)"); |
380 | | FAIL_IF_NULL(s); |
381 | | |
382 | | SigGroupBuild(de_ctx); |
383 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
384 | | |
385 | | uint32_t seq = 2; |
386 | | for (int i=0; i<4; i++) { |
387 | | FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); |
388 | | seq += sshlens[i]; |
389 | | FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); |
390 | | } |
391 | | |
392 | | void *ssh_state = f->alstate; |
393 | | FAIL_IF_NULL(ssh_state); |
394 | | |
395 | | /* do detect */ |
396 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
397 | | |
398 | | FAIL_IF(PacketAlertCheck(p, 1)); |
399 | | |
400 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
401 | | DetectEngineCtxFree(de_ctx); |
402 | | |
403 | | UTHFreePacket(p); |
404 | | StreamTcpUTClearSession(&ssn); |
405 | | StreamTcpUTDeinit(ra_ctx); |
406 | | UTHFreeFlow(f); |
407 | | PASS; |
408 | | } |
409 | | |
410 | | /** \test Send a get request in three chunks + more data. */ |
411 | | static int DetectSshVersionTestDetect02(void) |
412 | | { |
413 | | TcpReassemblyThreadCtx *ra_ctx = NULL; |
414 | | ThreadVars tv; |
415 | | TcpSession ssn; |
416 | | Flow *f = NULL; |
417 | | Packet *p = NULL; |
418 | | |
419 | | uint8_t sshbuf1[] = "SSH-1.99-Pu"; |
420 | | uint8_t sshbuf2[] = "TTY_2.123" ; |
421 | | uint8_t sshbuf3[] = "\n"; |
422 | | uint8_t sshbuf4[] = "whatever..."; |
423 | | |
424 | | uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; |
425 | | uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; |
426 | | |
427 | | memset(&tv, 0x00, sizeof(tv)); |
428 | | |
429 | | StreamTcpUTInit(&ra_ctx); |
430 | | StreamTcpUTInitInline(); |
431 | | StreamTcpUTSetupSession(&ssn); |
432 | | StreamTcpUTSetupStream(&ssn.server, 1); |
433 | | StreamTcpUTSetupStream(&ssn.client, 1); |
434 | | |
435 | | f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); |
436 | | FAIL_IF_NULL(f); |
437 | | f->protoctx = &ssn; |
438 | | f->proto = IPPROTO_TCP; |
439 | | f->alproto = ALPROTO_SSH; |
440 | | |
441 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
442 | | FAIL_IF(unlikely(p == NULL)); |
443 | | p->flow = f; |
444 | | |
445 | | Signature *s = NULL; |
446 | | ThreadVars th_v; |
447 | | DetectEngineThreadCtx *det_ctx = NULL; |
448 | | |
449 | | memset(&th_v, 0, sizeof(th_v)); |
450 | | |
451 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
452 | | FAIL_IF_NULL(de_ctx); |
453 | | |
454 | | de_ctx->flags |= DE_QUIET; |
455 | | |
456 | | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); |
457 | | FAIL_IF_NULL(s); |
458 | | |
459 | | SigGroupBuild(de_ctx); |
460 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
461 | | |
462 | | uint32_t seq = 2; |
463 | | for (int i=0; i<4; i++) { |
464 | | FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); |
465 | | seq += sshlens[i]; |
466 | | FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); |
467 | | } |
468 | | |
469 | | void *ssh_state = f->alstate; |
470 | | FAIL_IF_NULL(ssh_state); |
471 | | |
472 | | /* do detect */ |
473 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
474 | | |
475 | | FAIL_IF(PacketAlertCheck(p, 1)); |
476 | | |
477 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
478 | | DetectEngineCtxFree(de_ctx); |
479 | | |
480 | | UTHFreePacket(p); |
481 | | StreamTcpUTClearSession(&ssn); |
482 | | StreamTcpUTDeinit(ra_ctx); |
483 | | UTHFreeFlow(f); |
484 | | PASS; |
485 | | } |
486 | | |
487 | | /** \test Send a get request in three chunks + more data. */ |
488 | | static int DetectSshVersionTestDetect03(void) |
489 | | { |
490 | | TcpReassemblyThreadCtx *ra_ctx = NULL; |
491 | | ThreadVars tv; |
492 | | TcpSession ssn; |
493 | | Flow *f = NULL; |
494 | | Packet *p = NULL; |
495 | | |
496 | | uint8_t sshbuf1[] = "SSH-1."; |
497 | | uint8_t sshbuf2[] = "7-PuTTY_2.123" ; |
498 | | uint8_t sshbuf3[] = "\n"; |
499 | | uint8_t sshbuf4[] = "whatever..."; |
500 | | |
501 | | uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; |
502 | | uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; |
503 | | |
504 | | memset(&tv, 0x00, sizeof(tv)); |
505 | | |
506 | | StreamTcpUTInit(&ra_ctx); |
507 | | StreamTcpUTInitInline(); |
508 | | StreamTcpUTSetupSession(&ssn); |
509 | | StreamTcpUTSetupStream(&ssn.server, 1); |
510 | | StreamTcpUTSetupStream(&ssn.client, 1); |
511 | | |
512 | | f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); |
513 | | FAIL_IF_NULL(f); |
514 | | f->protoctx = &ssn; |
515 | | f->proto = IPPROTO_TCP; |
516 | | f->alproto = ALPROTO_SSH; |
517 | | |
518 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
519 | | FAIL_IF(unlikely(p == NULL)); |
520 | | p->flow = f; |
521 | | |
522 | | Signature *s = NULL; |
523 | | ThreadVars th_v; |
524 | | DetectEngineThreadCtx *det_ctx = NULL; |
525 | | |
526 | | memset(&th_v, 0, sizeof(th_v)); |
527 | | |
528 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
529 | | FAIL_IF_NULL(de_ctx); |
530 | | |
531 | | de_ctx->flags |= DE_QUIET; |
532 | | |
533 | | s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); |
534 | | FAIL_IF_NULL(s); |
535 | | |
536 | | SigGroupBuild(de_ctx); |
537 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
538 | | |
539 | | uint32_t seq = 2; |
540 | | for (int i=0; i<4; i++) { |
541 | | FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); |
542 | | seq += sshlens[i]; |
543 | | FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); |
544 | | } |
545 | | |
546 | | void *ssh_state = f->alstate; |
547 | | FAIL_IF_NULL(ssh_state); |
548 | | |
549 | | /* do detect */ |
550 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
551 | | |
552 | | FAIL_IF(PacketAlertCheck(p, 1)); |
553 | | |
554 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
555 | | DetectEngineCtxFree(de_ctx); |
556 | | |
557 | | UTHFreePacket(p); |
558 | | StreamTcpUTClearSession(&ssn); |
559 | | StreamTcpUTDeinit(ra_ctx); |
560 | | UTHFreeFlow(f); |
561 | | PASS; |
562 | | } |
563 | | |
564 | | /** |
565 | | * \brief this function registers unit tests for DetectSshVersion |
566 | | */ |
567 | | static void DetectSshVersionRegisterTests(void) |
568 | | { |
569 | | UtRegisterTest("DetectSshVersionTestParse01", DetectSshVersionTestParse01); |
570 | | UtRegisterTest("DetectSshVersionTestParse02", DetectSshVersionTestParse02); |
571 | | UtRegisterTest("DetectSshVersionTestParse03", DetectSshVersionTestParse03); |
572 | | UtRegisterTest("DetectSshVersionTestDetect01", |
573 | | DetectSshVersionTestDetect01); |
574 | | UtRegisterTest("DetectSshVersionTestDetect02", |
575 | | DetectSshVersionTestDetect02); |
576 | | UtRegisterTest("DetectSshVersionTestDetect03", |
577 | | DetectSshVersionTestDetect03); |
578 | | } |
579 | | #endif /* UNITTESTS */ |