From bce8f4b853e97a09296e8b18c826a5b2598d53f7 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 20 Jun 2024 10:50:28 +0200 Subject: [PATCH] detect/ssh: remove deprecated keywords Ticket: 2377 --- doc/userguide/rules/ssh-keywords.rst | 44 --- doc/userguide/rules/xbits.rst | 4 +- doc/userguide/upgrade.rst | 4 + src/detect-ssh-proto-version.c | 542 +-------------------------- src/detect-ssh-proto-version.h | 9 - src/detect-ssh-software-version.c | 537 +------------------------- src/detect-ssh-software-version.h | 5 - 7 files changed, 20 insertions(+), 1125 deletions(-) diff --git a/doc/userguide/rules/ssh-keywords.rst b/doc/userguide/rules/ssh-keywords.rst index 83d2f2fe8d..fe61d14646 100644 --- a/doc/userguide/rules/ssh-keywords.rst +++ b/doc/userguide/rules/ssh-keywords.rst @@ -31,10 +31,6 @@ ssh.software Match on the software string from the SSH banner. ``ssh.software`` is a sticky buffer, and can be used as fast pattern. -``ssh.software`` replaces the previous keyword names: ``ssh_software`` & -``ssh.softwareversion``. You may continue to use the previous name, but it's -recommended that rules be converted to use the new name. - Format:: ssh.software; @@ -49,46 +45,6 @@ The example above matches on SSH connections where the software string contains "openssh". -ssh.protoversion ----------------- -Matches on the version of the SSH protocol used. A value of ``2_compat`` -includes SSH version 1.99. - -Format:: - - ssh.protoversion:[0-9](\.[0-9])?|2_compat; - -Example: - -.. container:: example-rule - - alert ssh any any -> any any (msg:"SSH v2 compatible"; :example-rule-emphasis:`ssh.protoversion:2_compat;` sid:1;) - -The example above matches on SSH connections with SSH version 2 or 1.99. - -.. container:: example-rule - - alert ssh any any -> any any (msg:"SSH v1.10"; :example-rule-emphasis:`ssh.protoversion:1.10;` sid:1;) - -The example above matches on SSH connections with SSH version 1.10 only. - - -ssh.softwareversion -------------------- -This keyword has been deprecated. Please use ``ssh.software`` instead. Matches -on the software string from the SSH banner. - -Example: - -.. container:: example-rule - - alert ssh any any -> any any (msg:"match SSH software string"; :example-rule-emphasis:`ssh.softwareversion:"OpenSSH";` sid:10000040;) - - -Suricata comes with a Hassh integration (https://github.com/salesforce/hassh). Hassh is used to fingerprint ssh clients and servers. - -Hassh must be enabled in the Suricata config file (set 'app-layer.protocols.ssh.hassh' to 'yes'). - ssh.hassh --------- diff --git a/doc/userguide/rules/xbits.rst b/doc/userguide/rules/xbits.rst index 6d45d7b547..120d9dd1d2 100644 --- a/doc/userguide/rules/xbits.rst +++ b/doc/userguide/rules/xbits.rst @@ -95,10 +95,10 @@ They drop the traffic and create an 'xbit' 'badssh' for the source ip. It expires in an hour:: drop ssh any any -> $MYSERVER 22 (msg:"DROP libssh incoming"; \ - flow:to_server,established; ssh.softwareversion:"libssh"; \ + flow:to_server,established; ssh.software; content:"libssh"; \ xbits:set, badssh, track ip_src, expire 3600; sid:4000000005;) drop ssh any any -> $MYSERVER 22 (msg:"DROP PUTTY incoming"; \ - flow:to_server,established; ssh.softwareversion:"PUTTY"; \ + flow:to_server,established; ssh.software; content:"PUTTY"; \ xbits:set, badssh, track ip_src, expire 3600; sid:4000000007;) Then the following rule simply drops any incoming traffic to that server diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 4aadb978e1..8ff94a445b 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -71,6 +71,10 @@ Major changes - PF_RING support has been moved to a plugin. See :doc:`PF_RING plugin `. +Removals +~~~~~~~~ +- The ssh keywords ``ssh.protoversion`` and ``ssh.softwareversion`` have been removed. + Upgrading 6.0 to 7.0 -------------------- diff --git a/src/detect-ssh-proto-version.c b/src/detect-ssh-proto-version.c index 6a852f89ef..06b02dbbca 100644 --- a/src/detect-ssh-proto-version.c +++ b/src/detect-ssh-proto-version.c @@ -20,553 +20,25 @@ * * \author Pablo Rincon * - * Implements the ssh.protoversion keyword - * You can specify a concrete version like ssh.protoversion: 1.66 - * or search for protoversion 2 compat (1.99 is considered as 2) like - * ssh.protoversion:2_compat - * or just the beginning of the string like ssh.protoversion:"1." */ #include "suricata-common.h" -#include "threads.h" -#include "decode.h" - #include "detect.h" -#include "detect-parse.h" - -#include "detect-engine.h" -#include "detect-engine-mpm.h" -#include "detect-engine-state.h" -#include "detect-engine-build.h" - -#include "flow.h" -#include "flow-var.h" -#include "flow-util.h" - -#include "util-debug.h" -#include "util-unittest.h" -#include "util-unittest-helper.h" - -#include "app-layer.h" -#include "app-layer-parser.h" -#include "app-layer-ssh.h" +#include "detect-engine-register.h" #include "detect-ssh-proto-version.h" -#include "rust.h" - -#include "stream-tcp.h" - -/** - * \brief Regex for parsing the protoversion string - */ -#define PARSE_REGEX "^\\s*\"?\\s*([0-9]+([\\.\\-0-9]+)?|2_compat)\\s*\"?\\s*$" - -static DetectParseRegex parse_regex; - -static int DetectSshVersionMatch (DetectEngineThreadCtx *, - Flow *, uint8_t, void *, void *, - const Signature *, const SigMatchCtx *); -static int DetectSshVersionSetup (DetectEngineCtx *, Signature *, const char *); -#ifdef UNITTESTS -static void DetectSshVersionRegisterTests(void); -#endif -static void DetectSshVersionFree(DetectEngineCtx *, void *); -static int g_ssh_banner_list_id = 0; - -/** - * \brief Registration function for keyword: ssh.protoversion - */ -void DetectSshVersionRegister(void) -{ - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].name = "ssh.protoversion"; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].desc = "match SSH protocol version"; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].url = "/rules/ssh-keywords.html#ssh-protoversion"; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].AppLayerTxMatch = DetectSshVersionMatch; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Setup = DetectSshVersionSetup; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Free = DetectSshVersionFree; -#ifdef UNITTESTS - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].RegisterTests = DetectSshVersionRegisterTests; -#endif - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].flags = SIGMATCH_QUOTES_OPTIONAL|SIGMATCH_INFO_DEPRECATED; - sigmatch_table[DETECT_AL_SSH_PROTOVERSION].alternative = DETECT_AL_SSH_PROTOCOL; - - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); - g_ssh_banner_list_id = DetectBufferTypeRegister("ssh_banner"); -} - -/** - * \brief match the specified version on a ssh session - * - * \param t pointer to thread vars - * \param det_ctx pointer to the pattern matcher thread - * \param p pointer to the current packet - * \param m pointer to the sigmatch that we will cast into DetectSshVersionData - * - * \retval 0 no match - * \retval 1 match - */ -static int DetectSshVersionMatch (DetectEngineThreadCtx *det_ctx, - Flow *f, uint8_t flags, void *state, void *txv, - const Signature *s, const SigMatchCtx *m) -{ - SCEnter(); - - SCLogDebug("lets see"); - - DetectSshVersionData *ssh = (DetectSshVersionData *)m; - if (state == NULL) { - SCLogDebug("no ssh state, no match"); - SCReturnInt(0); - } - - int ret = 0; - const uint8_t *protocol = NULL; - uint32_t b_len = 0; - - if (rs_ssh_tx_get_protocol(txv, &protocol, &b_len, flags) != 1) - SCReturnInt(0); - if (protocol == NULL || b_len == 0) - SCReturnInt(0); - - if (ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT) { - SCLogDebug("looking for ssh protoversion 2 compat"); - if (protocol[0] == '2') { - ret = 1; - } else if (b_len >= 4) { - if (memcmp(protocol, "1.99", 4) == 0) { - ret = 1; - } - } - } else { - SCLogDebug("looking for ssh protoversion %s length %"PRIu16"", ssh->ver, ssh->len); - if (b_len == ssh->len) { - if (memcmp(protocol, ssh->ver, ssh->len) == 0) { - ret = 1; - } - } - } - SCReturnInt(ret); -} - -/** - * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" - * - * \param de_ctx Pointer to the detection engine context - * \param idstr Pointer to the user provided id option - * - * \retval id_d pointer to DetectSshVersionData on success - * \retval NULL on failure - */ -static DetectSshVersionData *DetectSshVersionParse (DetectEngineCtx *de_ctx, const char *str) -{ - DetectSshVersionData *ssh = NULL; - int res = 0; - size_t pcre2_len; - - pcre2_match_data *match = NULL; - int ret = DetectParsePcreExec(&parse_regex, &match, str, 0, 0); - if (ret < 1 || ret > 3) { - SCLogError("invalid ssh.protoversion option"); - goto error; - } - - if (ret > 1) { - const char *str_ptr; - res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); - if (res < 0) { - SCLogError("pcre2_substring_get_bynumber failed"); - goto error; - } - - /* We have a correct id option */ - ssh = SCCalloc(1, sizeof(DetectSshVersionData)); - if (unlikely(ssh == NULL)) { - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - goto error; - } - - /* If we expect a protocol version 2 or 1.99 (considered 2, we - * will compare it with both strings) */ - if (strcmp("2_compat", str_ptr) == 0) { - ssh->flags |= SSH_FLAG_PROTOVERSION_2_COMPAT; - SCLogDebug("will look for ssh protocol version 2 (2, 2.0, 1.99 that's considered as 2"); - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - return ssh; - } - - ssh->ver = (uint8_t *)SCStrdup((char*)str_ptr); - if (ssh->ver == NULL) { - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - goto error; - } - ssh->len = (uint16_t)strlen((char *)ssh->ver); - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - - SCLogDebug("will look for ssh %s", ssh->ver); - } - - pcre2_match_data_free(match); - return ssh; - -error: - if (match) { - pcre2_match_data_free(match); - } - if (ssh != NULL) - DetectSshVersionFree(de_ctx, ssh); - return NULL; - -} - -/** - * \brief this function is used to add the parsed "id" option - * \brief into the current signature - * - * \param de_ctx pointer to the Detection Engine Context - * \param s pointer to the Current Signature - * \param idstr pointer to the user provided "id" option - * - * \retval 0 on Success - * \retval -1 on Failure - */ static int DetectSshVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) { - DetectSshVersionData *ssh = NULL; - - if (DetectSignatureSetAppProto(s, ALPROTO_SSH) != 0) - return -1; - - ssh = DetectSshVersionParse(de_ctx, str); - if (ssh == NULL) - goto error; - - /* Okay so far so good, lets get this into a SigMatch - * and put it in the Signature. */ - - if (SigMatchAppendSMToList(de_ctx, s, DETECT_AL_SSH_PROTOVERSION, (SigMatchCtx *)ssh, - g_ssh_banner_list_id) == NULL) { - goto error; - } - return 0; - -error: - if (ssh != NULL) - DetectSshVersionFree(de_ctx, ssh); + SCLogError("ssh.protoversion is obsolete, use now ssh.proto"); return -1; - -} - -/** - * \brief this function will free memory associated with DetectSshVersionData - * - * \param id_d pointer to DetectSshVersionData - */ -void DetectSshVersionFree(DetectEngineCtx *de_ctx, void *ptr) -{ - DetectSshVersionData *sshd = (DetectSshVersionData *)ptr; - SCFree(sshd->ver); - SCFree(sshd); -} - -#ifdef UNITTESTS /* UNITTESTS */ -#include "detect-engine-alert.h" - -/** - * \test DetectSshVersionTestParse01 is a test to make sure that we parse - * a proto version correctly - */ -static int DetectSshVersionTestParse01 (void) -{ - DetectSshVersionData *ssh = NULL; - ssh = DetectSshVersionParse(NULL, "1.0"); - FAIL_IF_NULL(ssh); - FAIL_IF_NOT(strncmp((char *)ssh->ver, "1.0", 3) == 0); - DetectSshVersionFree(NULL, ssh); - - PASS; -} - -/** - * \test DetectSshVersionTestParse02 is a test to make sure that we parse - * the proto version (compatible with proto version 2) correctly - */ -static int DetectSshVersionTestParse02 (void) -{ - DetectSshVersionData *ssh = NULL; - ssh = DetectSshVersionParse(NULL, "2_compat"); - FAIL_IF_NOT(ssh->flags & SSH_FLAG_PROTOVERSION_2_COMPAT); - DetectSshVersionFree(NULL, ssh); - - PASS; } /** - * \test DetectSshVersionTestParse03 is a test to make sure that we - * don't return a ssh_data with an invalid value specified - */ -static int DetectSshVersionTestParse03 (void) -{ - DetectSshVersionData *ssh = NULL; - ssh = DetectSshVersionParse(NULL, "2_com"); - FAIL_IF_NOT_NULL(ssh); - ssh = DetectSshVersionParse(NULL, ""); - FAIL_IF_NOT_NULL(ssh); - ssh = DetectSshVersionParse(NULL, ".1"); - FAIL_IF_NOT_NULL(ssh); - ssh = DetectSshVersionParse(NULL, "lalala"); - FAIL_IF_NOT_NULL(ssh); - - PASS; -} - - -#include "stream-tcp-reassemble.h" -#include "stream-tcp-util.h" - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshVersionTestDetect01(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1."; - uint8_t sshbuf2[] = "10-PuTTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:1.10; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; -} - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshVersionTestDetect02(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1.99-Pu"; - uint8_t sshbuf2[] = "TTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; -} - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshVersionTestDetect03(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1."; - uint8_t sshbuf2[] = "7-PuTTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.protoversion:2_compat; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; -} - -/** - * \brief this function registers unit tests for DetectSshVersion + * \brief Registration function for keyword: ssh.protoversion */ -static void DetectSshVersionRegisterTests(void) +void DetectSshVersionRegister(void) { - UtRegisterTest("DetectSshVersionTestParse01", DetectSshVersionTestParse01); - UtRegisterTest("DetectSshVersionTestParse02", DetectSshVersionTestParse02); - UtRegisterTest("DetectSshVersionTestParse03", DetectSshVersionTestParse03); - UtRegisterTest("DetectSshVersionTestDetect01", - DetectSshVersionTestDetect01); - UtRegisterTest("DetectSshVersionTestDetect02", - DetectSshVersionTestDetect02); - UtRegisterTest("DetectSshVersionTestDetect03", - DetectSshVersionTestDetect03); + sigmatch_table[DETECT_AL_SSH_PROTOVERSION].name = "ssh.protoversion"; + sigmatch_table[DETECT_AL_SSH_PROTOVERSION].desc = "obsolete keyword, use now ssh.proto"; + sigmatch_table[DETECT_AL_SSH_PROTOVERSION].Setup = DetectSshVersionSetup; } -#endif /* UNITTESTS */ diff --git a/src/detect-ssh-proto-version.h b/src/detect-ssh-proto-version.h index eade10a716..1f7084cf30 100644 --- a/src/detect-ssh-proto-version.h +++ b/src/detect-ssh-proto-version.h @@ -24,15 +24,6 @@ #ifndef SURICATA_DETECT_SSH_VERSION_H #define SURICATA_DETECT_SSH_VERSION_H -/** proto version 1.99 is considered proto version 2 */ -#define SSH_FLAG_PROTOVERSION_2_COMPAT 0x01 - -typedef struct DetectSshVersionData_ { - uint8_t *ver; /** ssh version to match */ - uint16_t len; /** ssh version length to match */ - uint8_t flags; -} DetectSshVersionData; - /* prototypes */ void DetectSshVersionRegister (void); diff --git a/src/detect-ssh-software-version.c b/src/detect-ssh-software-version.c index 60602a4e02..512c6f2235 100644 --- a/src/detect-ssh-software-version.c +++ b/src/detect-ssh-software-version.c @@ -20,548 +20,25 @@ * * \author Pablo Rincon * - * Implements the ssh.softwareversion keyword - * You can match over the software version string of ssh, and it will - * be compared from the beginning of the string so you can say for - * example ssh.softwareversion:"PuTTY" and it can match, or you can - * also specify the version, something like - * ssh.softwareversion:"PuTTY-Release-0.55" - * I find this useful to match over a known vulnerable server/client - * software version in combination to other checks, so you can know - * that the risk is higher */ #include "suricata-common.h" -#include "threads.h" -#include "decode.h" - #include "detect.h" -#include "detect-parse.h" - -#include "detect-engine.h" -#include "detect-engine-mpm.h" -#include "detect-engine-state.h" -#include "detect-engine-build.h" - -#include "flow.h" -#include "flow-var.h" -#include "flow-util.h" - -#include "util-debug.h" -#include "util-unittest.h" -#include "util-unittest-helper.h" - -#include "app-layer.h" -#include "app-layer-parser.h" -#include "app-layer-ssh.h" +#include "detect-engine-register.h" #include "detect-ssh-software-version.h" -#include "rust.h" - -#include "stream-tcp.h" - -/** - * \brief Regex for parsing the softwareversion string - */ -#define PARSE_REGEX "^\\s*\"?\\s*?([0-9a-zA-Z\\:\\.\\-\\_\\+\\s+]+)\\s*\"?\\s*$" - -static DetectParseRegex parse_regex; - -static int DetectSshSoftwareVersionMatch (DetectEngineThreadCtx *, - Flow *, uint8_t, void *, void *, - const Signature *, const SigMatchCtx *); -static int DetectSshSoftwareVersionSetup (DetectEngineCtx *, Signature *, const char *); -#ifdef UNITTESTS -static void DetectSshSoftwareVersionRegisterTests(void); -#endif -static void DetectSshSoftwareVersionFree(DetectEngineCtx *de_ctx, void *); -static int g_ssh_banner_list_id = 0; - - -/** - * \brief Registration function for keyword: ssh.softwareversion - */ -void DetectSshSoftwareVersionRegister(void) -{ - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].name = "ssh.softwareversion"; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].desc = "match SSH software string"; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].url = "/rules/ssh-keywords.html#ssh-softwareversion"; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].AppLayerTxMatch = DetectSshSoftwareVersionMatch; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Setup = DetectSshSoftwareVersionSetup; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Free = DetectSshSoftwareVersionFree; -#ifdef UNITTESTS - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].RegisterTests = DetectSshSoftwareVersionRegisterTests; -#endif - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].flags = SIGMATCH_QUOTES_OPTIONAL|SIGMATCH_INFO_DEPRECATED; - sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].alternative = DETECT_AL_SSH_SOFTWARE; - - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); - - g_ssh_banner_list_id = DetectBufferTypeRegister("ssh_banner"); - - DetectAppLayerInspectEngineRegister("ssh_banner", ALPROTO_SSH, SIG_FLAG_TOSERVER, - SshStateBannerDone, DetectEngineInspectGenericList, NULL); - DetectAppLayerInspectEngineRegister("ssh_banner", ALPROTO_SSH, SIG_FLAG_TOCLIENT, - SshStateBannerDone, DetectEngineInspectGenericList, NULL); -} - -/** - * \brief match the specified version on a ssh session - * - * \param t pointer to thread vars - * \param det_ctx pointer to the pattern matcher thread - * \param p pointer to the current packet - * \param m pointer to the sigmatch that we will cast into DetectSshSoftwareVersionData - * - * \retval 0 no match - * \retval 1 match - */ -static int DetectSshSoftwareVersionMatch (DetectEngineThreadCtx *det_ctx, - Flow *f, uint8_t flags, void *state, void *txv, - const Signature *s, const SigMatchCtx *m) -{ - SCEnter(); - - DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)m; - if (state == NULL) { - SCLogDebug("no ssh state, no match"); - SCReturnInt(0); - } - - int ret = 0; - const uint8_t *software = NULL; - uint32_t b_len = 0; - - if (rs_ssh_tx_get_software(txv, &software, &b_len, flags) != 1) - SCReturnInt(0); - if (software == NULL || b_len == 0) - SCReturnInt(0); - if (b_len == ssh->len) { - if (memcmp(software, ssh->software_ver, ssh->len) == 0) { - ret = 1; - } - } - - SCReturnInt(ret); -} - -/** - * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" - * - * \param de_ctx Pointer to the detection engine context - * \param idstr Pointer to the user provided id option - * - * \retval id_d pointer to DetectSshSoftwareVersionData on success - * \retval NULL on failure - */ -static DetectSshSoftwareVersionData *DetectSshSoftwareVersionParse (DetectEngineCtx *de_ctx, const char *str) -{ - DetectSshSoftwareVersionData *ssh = NULL; - int res = 0; - size_t pcre2_len; - - pcre2_match_data *match = NULL; - int ret = DetectParsePcreExec(&parse_regex, &match, str, 0, 0); - - if (ret < 1 || ret > 3) { - SCLogError("invalid ssh.softwareversion option"); - goto error; - } - - if (ret > 1) { - const char *str_ptr = NULL; - res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); - if (res < 0) { - SCLogError("pcre2_substring_get_bynumber failed"); - goto error; - } - - /* We have a correct id option */ - ssh = SCMalloc(sizeof(DetectSshSoftwareVersionData)); - if (unlikely(ssh == NULL)) { - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - goto error; - } - ssh->software_ver = (uint8_t *)SCStrdup((char *)str_ptr); - if (ssh->software_ver == NULL) { - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - goto error; - } - pcre2_substring_free((PCRE2_UCHAR *)str_ptr); - - ssh->len = (uint16_t)strlen((char *)ssh->software_ver); - - SCLogDebug("will look for ssh %s", ssh->software_ver); - } - - pcre2_match_data_free(match); - return ssh; - -error: - if (match) { - pcre2_match_data_free(match); - } - if (ssh != NULL) - DetectSshSoftwareVersionFree(de_ctx, ssh); - return NULL; -} - -/** - * \brief this function is used to add the parsed "id" option - * \brief into the current signature - * - * \param de_ctx pointer to the Detection Engine Context - * \param s pointer to the Current Signature - * \param idstr pointer to the user provided "id" option - * - * \retval 0 on Success - * \retval -1 on Failure - */ static int DetectSshSoftwareVersionSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str) { - DetectSshSoftwareVersionData *ssh = NULL; - - if (DetectSignatureSetAppProto(s, ALPROTO_SSH) != 0) - return -1; - - ssh = DetectSshSoftwareVersionParse(NULL, str); - if (ssh == NULL) - goto error; - - /* Okay so far so good, lets get this into a SigMatch - * and put it in the Signature. */ - - if (SigMatchAppendSMToList(de_ctx, s, DETECT_AL_SSH_SOFTWAREVERSION, (SigMatchCtx *)ssh, - g_ssh_banner_list_id) == NULL) { - goto error; - } - return 0; - -error: - if (ssh != NULL) - DetectSshSoftwareVersionFree(de_ctx, ssh); + SCLogError("ssh.softwareversion is obsolete, use now ssh.software"); return -1; - -} - -/** - * \brief this function will free memory associated with DetectSshSoftwareVersionData - * - * \param id_d pointer to DetectSshSoftwareVersionData - */ -static void DetectSshSoftwareVersionFree(DetectEngineCtx *de_ctx, void *ptr) -{ - if (ptr == NULL) - return; - - DetectSshSoftwareVersionData *ssh = (DetectSshSoftwareVersionData *)ptr; - if (ssh->software_ver != NULL) - SCFree(ssh->software_ver); - SCFree(ssh); -} - -#ifdef UNITTESTS /* UNITTESTS */ -#include "detect-engine-alert.h" - -/** - * \test DetectSshSoftwareVersionTestParse01 is a test to make sure that we parse - * a software version correctly - */ -static int DetectSshSoftwareVersionTestParse01 (void) -{ - DetectSshSoftwareVersionData *ssh = NULL; - ssh = DetectSshSoftwareVersionParse(NULL, "PuTTY_1.0"); - if (ssh != NULL && strncmp((char *) ssh->software_ver, "PuTTY_1.0", 9) == 0) { - DetectSshSoftwareVersionFree(NULL, ssh); - return 1; - } - - return 0; -} - -/** - * \test DetectSshSoftwareVersionTestParse02 is a test to make sure that we parse - * the software version correctly - */ -static int DetectSshSoftwareVersionTestParse02 (void) -{ - DetectSshSoftwareVersionData *ssh = NULL; - ssh = DetectSshSoftwareVersionParse(NULL, "\"SecureCRT-4.0\""); - if (ssh != NULL && strncmp((char *) ssh->software_ver, "SecureCRT-4.0", 13) == 0) { - DetectSshSoftwareVersionFree(NULL, ssh); - return 1; - } - - return 0; -} - -/** - * \test DetectSshSoftwareVersionTestParse03 is a test to make sure that we - * don't return a ssh_data with an empty value specified - */ -static int DetectSshSoftwareVersionTestParse03 (void) -{ - DetectSshSoftwareVersionData *ssh = NULL; - ssh = DetectSshSoftwareVersionParse(NULL, ""); - if (ssh != NULL) { - DetectSshSoftwareVersionFree(NULL, ssh); - return 0; - } - - return 1; -} - - -#include "stream-tcp-reassemble.h" -#include "stream-tcp-util.h" - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshSoftwareVersionTestDetect01(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1."; - uint8_t sshbuf2[] = "10-PuTTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; -} - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshSoftwareVersionTestDetect02(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1.99-Pu"; - uint8_t sshbuf2[] = "TTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:PuTTY_2.123; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; -} - -/** \test Send a get request in three chunks + more data. */ -static int DetectSshSoftwareVersionTestDetect03(void) -{ - TcpReassemblyThreadCtx *ra_ctx = NULL; - ThreadVars tv; - TcpSession ssn; - Flow *f = NULL; - Packet *p = NULL; - - uint8_t sshbuf1[] = "SSH-1."; - uint8_t sshbuf2[] = "7-PuTTY_2.123" ; - uint8_t sshbuf3[] = "\n"; - uint8_t sshbuf4[] = "whatever..."; - - uint8_t* sshbufs[4] = {sshbuf1, sshbuf2, sshbuf3, sshbuf4}; - uint32_t sshlens[4] = {sizeof(sshbuf1) - 1, sizeof(sshbuf2) - 1, sizeof(sshbuf3) - 1, sizeof(sshbuf4) - 1}; - - memset(&tv, 0x00, sizeof(tv)); - - StreamTcpUTInit(&ra_ctx); - StreamTcpUTInitInline(); - StreamTcpUTSetupSession(&ssn); - StreamTcpUTSetupStream(&ssn.server, 1); - StreamTcpUTSetupStream(&ssn.client, 1); - - f = UTHBuildFlow(AF_INET, "1.1.1.1", "2.2.2.2", 1234, 2222); - FAIL_IF_NULL(f); - f->protoctx = &ssn; - f->proto = IPPROTO_TCP; - f->alproto = ALPROTO_SSH; - - p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); - FAIL_IF(unlikely(p == NULL)); - p->flow = f; - - Signature *s = NULL; - ThreadVars th_v; - DetectEngineThreadCtx *det_ctx = NULL; - - memset(&th_v, 0, sizeof(th_v)); - - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); - FAIL_IF_NULL(de_ctx); - - de_ctx->flags |= DE_QUIET; - - s = de_ctx->sig_list = SigInit(de_ctx,"alert ssh any any -> any any (msg:\"SSH\"; ssh.softwareversion:lalala-3.1.4; sid:1;)"); - FAIL_IF_NULL(s); - - SigGroupBuild(de_ctx); - DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); - - uint32_t seq = 2; - for (int i=0; i<4; i++) { - FAIL_IF(StreamTcpUTAddSegmentWithPayload(&tv, ra_ctx, &ssn.server, seq, sshbufs[i], sshlens[i]) == -1); - seq += sshlens[i]; - FAIL_IF(StreamTcpReassembleAppLayer(&tv, ra_ctx, &ssn, &ssn.server, p, UPDATE_DIR_PACKET) < 0); - } - - void *ssh_state = f->alstate; - FAIL_IF_NULL(ssh_state); - - /* do detect */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p); - - FAIL_IF(PacketAlertCheck(p, 1)); - - DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); - - UTHFreePacket(p); - StreamTcpUTClearSession(&ssn); - StreamTcpUTDeinit(ra_ctx); - UTHFreeFlow(f); - PASS; } /** - * \brief this function registers unit tests for DetectSshSoftwareVersion + * \brief Registration function for keyword: ssh.softwareversion */ -static void DetectSshSoftwareVersionRegisterTests(void) +void DetectSshSoftwareVersionRegister(void) { - UtRegisterTest("DetectSshSoftwareVersionTestParse01", - DetectSshSoftwareVersionTestParse01); - UtRegisterTest("DetectSshSoftwareVersionTestParse02", - DetectSshSoftwareVersionTestParse02); - UtRegisterTest("DetectSshSoftwareVersionTestParse03", - DetectSshSoftwareVersionTestParse03); - UtRegisterTest("DetectSshSoftwareVersionTestDetect01", - DetectSshSoftwareVersionTestDetect01); - UtRegisterTest("DetectSshSoftwareVersionTestDetect02", - DetectSshSoftwareVersionTestDetect02); - UtRegisterTest("DetectSshSoftwareVersionTestDetect03", - DetectSshSoftwareVersionTestDetect03); + sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].name = "ssh.softwareversion"; + sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].desc = "obsolete keyword, use now ssh.software"; + sigmatch_table[DETECT_AL_SSH_SOFTWAREVERSION].Setup = DetectSshSoftwareVersionSetup; } -#endif /* UNITTESTS */ diff --git a/src/detect-ssh-software-version.h b/src/detect-ssh-software-version.h index 6ed97e6f22..5fcb8895ad 100644 --- a/src/detect-ssh-software-version.h +++ b/src/detect-ssh-software-version.h @@ -24,11 +24,6 @@ #ifndef SURICATA_DETECT_SSH_SOFTWARE_VERSION_H #define SURICATA_DETECT_SSH_SOFTWARE_VERSION_H -typedef struct DetectSshSoftwareVersionData_ { - uint8_t *software_ver; /** ssh version to match */ - uint16_t len; /** ssh version length to match */ -} DetectSshSoftwareVersionData; - /* prototypes */ void DetectSshSoftwareVersionRegister(void); -- 2.47.2