diff options
Diffstat (limited to 'src/detect-asn1.c')
-rw-r--r-- | src/detect-asn1.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/src/detect-asn1.c b/src/detect-asn1.c new file mode 100644 index 0000000..e255e05 --- /dev/null +++ b/src/detect-asn1.c @@ -0,0 +1,459 @@ +/* Copyright (C) 2020-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file detect-asn1.c + * + * Implements "asn1" keyword + */ + +#include "suricata-common.h" +#include "decode.h" +#include "rust.h" + +#include "detect.h" +#include "detect-parse.h" + +#include "flow.h" +#include "detect-asn1.h" + +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-byte.h" +#include "util-debug.h" + +static int DetectAsn1Match(DetectEngineThreadCtx *, Packet *, + const Signature *, const SigMatchCtx *); +static int DetectAsn1Setup (DetectEngineCtx *, Signature *, const char *); +#ifdef UNITTESTS +static void DetectAsn1RegisterTests(void); +#endif +static void DetectAsn1Free(DetectEngineCtx *, void *); + +/** + * \brief Registration function for asn1 + */ +void DetectAsn1Register(void) +{ + sigmatch_table[DETECT_ASN1].name = "asn1"; + sigmatch_table[DETECT_ASN1].Match = DetectAsn1Match; + sigmatch_table[DETECT_ASN1].Setup = DetectAsn1Setup; + sigmatch_table[DETECT_ASN1].Free = DetectAsn1Free; +#ifdef UNITTESTS + sigmatch_table[DETECT_ASN1].RegisterTests = DetectAsn1RegisterTests; +#endif +} + +/** + * \brief This function will decode the asn1 data and inspect the resulting + * nodes to detect if any of the specified checks match this data + * + * \param det_ctx pointer to the detect engine thread context + * \param p pointer to the current packet + * \param s pointer to the signature + * \param ctx pointer to the sigmatch that we will cast into `DetectAsn1Data` + * + * \retval 1 match + * \retval 0 no match + */ +static int DetectAsn1Match(DetectEngineThreadCtx *det_ctx, Packet *p, + const Signature *s, const SigMatchCtx *ctx) +{ + uint8_t ret = 0; + + if (p->payload_len == 0) { + /* No error, parser done, no data in bounds to decode */ + return 0; + } + + const DetectAsn1Data *ad = (const DetectAsn1Data *)ctx; + + Asn1 *asn1 = rs_asn1_decode(p->payload, p->payload_len, det_ctx->buffer_offset, ad); + + ret = rs_asn1_checks(asn1, ad); + + rs_asn1_free(asn1); + + return ret; +} + +/** + * \brief This function is used to parse asn1 options passed via asn1: keyword + * + * \param asn1str pointer to the user provided asn1 options + * + * \retval pointer to `DetectAsn1Data` on success + * \retval NULL on failure + */ +static DetectAsn1Data *DetectAsn1Parse(const char *asn1str) +{ + DetectAsn1Data *ad = rs_detect_asn1_parse(asn1str); + + if (ad == NULL) { + SCLogError("Malformed asn1 argument: %s", asn1str); + } + + return ad; +} + +/** + * \brief this function is used to add the parsed asn1 data into + * the current signature + * + * \param de_ctx pointer to the detection engine context + * \param s pointer to the current signature + * \param asn1str pointer to the user provided asn1 options + * + * \retval 0 on success + * \retval -1 on failure + */ +static int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, const char *asn1str) +{ + DetectAsn1Data *ad = DetectAsn1Parse(asn1str); + if (ad == NULL) + return -1; + + /* Okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + SigMatch *sm = SigMatchAlloc(); + if (sm == NULL) { + DetectAsn1Free(de_ctx, ad); + return -1; + } + + sm->type = DETECT_ASN1; + sm->ctx = (SigMatchCtx *)ad; + + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); + + return 0; +} + +/** + * \brief this function will free memory associated with `DetectAsn1Data` + * + * \param de_ctx pointer to the detection engine context + * \param ptr point to `DetectAsn1Data` + */ +static void DetectAsn1Free(DetectEngineCtx *de_ctx, void *ptr) +{ + DetectAsn1Data *ad = (DetectAsn1Data *)ptr; + rs_detect_asn1_free(ad); +} + +#ifdef UNITTESTS + +/** + * \test DetectAsn1TestReal01 Ensure that all works together + */ +static int DetectAsn1TestReal01(void) +{ + uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen = strlen((char *)buf) - 1; + + /* Check the start with AA (this is to test the relative_offset keyword) */ + uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen2 = strlen((char *)buf2) - 1; + + Packet *p[2]; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + FAIL_IF_NULL(p[0]); + p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); + FAIL_IF_NULL(p[1]); + + const char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " + "content:\"Pablo\"; asn1:absolute_offset 0, " + "oversize_length 130; sid:1;)"; + sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " + "content:\"AA\"; asn1:relative_offset 0, " + "oversize_length 130; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " + "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + uint32_t results[2][3] = { + /* packet 0 match sid 1 */ + {1, 0, 0}, + /* packet 1 match sid 2 */ + {0, 1, 0}}; + /* None of the packets should match sid 3 */ + FAIL_IF_NOT(UTHGenericTest(p, 2, sigs, sid, (uint32_t *)results, 3) == 1); + + UTHFreePackets(p, 2); + PASS; +} + +/** + * \test DetectAsn1TestReal02 Ensure that all works together + */ +static int DetectAsn1TestReal02(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen = strlen((char *)buf) - 1; + + /* Check the start with AA (this is to test the relative_offset keyword) */ + uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen2 = strlen((char *)buf2) - 1; + + Packet *p[2]; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); + + if (p[0] == NULL || p[1] == NULL) + goto end; + + const char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " + "content:\"Pablo\"; asn1:absolute_offset 0, " + "oversize_length 140; sid:1;)"; + sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " + "content:\"AA\"; asn1:relative_offset 0, " + "oversize_length 140; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " + "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[2][3] = { + {0, 0, 0}, + {0, 0, 0}}; + /* None of the packets should match */ + + result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); + + UTHFreePackets(p, 2); +end: + return result; +} + +/** + * \test DetectAsn1TestReal03 Ensure that all works together + */ +static int DetectAsn1TestReal03(void) +{ + int result = 0; + uint8_t buf[261] = ""; + /* universal class, primitive type, tag_num = 9 (Data type Real) */ + buf[0] = '\x09'; + /* length, definite form, 2 octets */ + buf[1] = '\x82'; + /* length is the sum of the following octets (257): */ + buf[2] = '\x01'; + buf[3] = '\x01'; + + /* Fill the content of the number */ + uint16_t i = 4; + for (; i < 257;i++) + buf[i] = '\x05'; + + uint16_t buflen = 261; + + /* Check the start with AA (this is to test the relative_offset keyword) */ + uint8_t *buf2 = (uint8_t *) "AA\x03\x01\xFF"; + + uint16_t buflen2 = 5; + + Packet *p[2] = { NULL, NULL }; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); + + if (p[0] == NULL || p[1] == NULL) + goto end; + + const char *sigs[3]; + /* This should match the first packet */ + sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " + "asn1:absolute_offset 0, double_overflow; sid:1;)"; + /* This should match the second packet */ + sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " + "asn1:relative_offset 2, bitstring_overflow," + "oversize_length 140; sid:2;)"; + /* This should match no packet */ + sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " + "asn1: oversize_length 2000; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[2][3] = {{1, 0, 0}, + {0, 1, 0}}; + + result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); + + UTHFreePackets(p, 2); +end: + return result; +} + +/** + * \test DetectAsn1TestReal04 like the real test 02, but modified the + * relative offset to check negative offset values, in this case + * start decoding from -7 bytes respect the content match "John" + */ +static int DetectAsn1TestReal04(void) +{ + int result = 0; + uint8_t *buf = (uint8_t *) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Pablo""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen = strlen((char *)buf) - 1; + + /* Check the start with AA (this is to test the relative_offset keyword) */ + uint8_t *buf2 = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" + "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" + "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" + "Jones""\xA0\x0A\x43\x08""19590717" + "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" + "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" + "\x42\x01\x33\xA1\x0A\x43\x08""19710917" + "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" + "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" + "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" + "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones" + "\xA0\x0A\x43\x08""19590717"; + + uint16_t buflen2 = strlen((char *)buf2) - 1; + + Packet *p[2]; + + p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); + p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); + + if (p[0] == NULL || p[1] == NULL) + goto end; + + const char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " + "content:\"Pablo\"; asn1:absolute_offset 0, " + "oversize_length 140; sid:1;)"; + sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " + "content:\"John\"; asn1:relative_offset -11, " + "oversize_length 140; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " + "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[2][3] = { + {0, 0, 0}, + {0, 0, 0}}; + /* None of the packets should match */ + + result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); + + UTHFreePackets(p, 2); +end: + return result; +} + +/** + * \brief this function registers unit tests for DetectAsn1 + */ +static void DetectAsn1RegisterTests(void) +{ + UtRegisterTest("DetectAsn1TestReal01", DetectAsn1TestReal01); + UtRegisterTest("DetectAsn1TestReal02", DetectAsn1TestReal02); + UtRegisterTest("DetectAsn1TestReal03", DetectAsn1TestReal03); + UtRegisterTest("DetectAsn1TestReal04", DetectAsn1TestReal04); +} +#endif /* UNITTESTS */ |