From e1dd19a0eb207a251bb4c0fd24e5ece6fbba9e80 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 11 Dec 2018 08:54:17 +0100 Subject: [PATCH] SNMP: add the "snmp.community" detection keyword --- doc/userguide/rules/snmp-keywords.rst | 24 +++ rust/src/snmp/detect.rs | 15 ++ src/Makefile.am | 1 + src/detect-engine-register.c | 2 + src/detect-engine-register.h | 1 + src/detect-snmp-community.c | 226 ++++++++++++++++++++++++++ src/detect-snmp-community.h | 31 ++++ 7 files changed, 300 insertions(+) create mode 100644 src/detect-snmp-community.c create mode 100644 src/detect-snmp-community.h diff --git a/doc/userguide/rules/snmp-keywords.rst b/doc/userguide/rules/snmp-keywords.rst index 4b24658030..a0b1d70d8b 100644 --- a/doc/userguide/rules/snmp-keywords.rst +++ b/doc/userguide/rules/snmp-keywords.rst @@ -20,3 +20,27 @@ Signature example:: alert snmp any any -> any any (msg:"old SNMP version (<3)"; snmp.version:<3; sid:1; rev:1;) +snmp.community +-------------- + +SNMP community strings are like passwords for SNMP messages in version 1 and 2c. +In version 3, the community string is likely to be encrypted. This keyword will not +match if the value is not accessible. + +The default value for the read-only community string is often "public", and +"private" for the read-write community string. + +Comparison is case-sensitive. + +Syntax:: + + snmp.community; content:"private"; + +Signature example:: + + alert snmp any any -> any any (msg:"SNMP community private"; snmp.community; content:"private"; sid:2; rev:1;) + +``snmp.community`` is a 'sticky buffer'. + +``snmp.community`` can be used as ``fast_pattern``. + diff --git a/rust/src/snmp/detect.rs b/rust/src/snmp/detect.rs index da36d584fa..c83a63b8d9 100644 --- a/rust/src/snmp/detect.rs +++ b/rust/src/snmp/detect.rs @@ -30,3 +30,18 @@ pub extern "C" fn rs_snmp_tx_get_version(tx: &mut SNMPTransaction, } } +#[no_mangle] +pub extern "C" fn rs_snmp_tx_get_community(tx: &mut SNMPTransaction, + buf: *mut *const libc::uint8_t, + len: *mut libc::uint32_t) +{ + match tx.community { + Some(ref c) => { + unsafe { + *buf = (&c).as_ptr(); + *len = c.len() as libc::uint32_t; + } + }, + None => () + } +} diff --git a/src/Makefile.am b/src/Makefile.am index fc0322f7ac..c471b2c313 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -233,6 +233,7 @@ detect-rpc.c detect-rpc.h \ detect-sameip.c detect-sameip.h \ detect-seq.c detect-seq.h \ detect-sid.c detect-sid.h \ +detect-snmp-community.c detect-snmp-community.h \ detect-snmp-version.c detect-snmp-version.h \ detect-ssh-proto.c detect-ssh-proto.h \ detect-ssh-proto-version.c detect-ssh-proto-version.h \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index d64f94275c..ef472c93ab 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -174,6 +174,7 @@ #include "detect-target.h" #include "detect-template-rust-buffer.h" #include "detect-snmp-version.h" +#include "detect-snmp-community.h" #include "detect-template-buffer.h" #include "detect-bypass.h" #include "detect-ftpdata.h" @@ -528,6 +529,7 @@ void SigTableSetup(void) DetectTargetRegister(); DetectTemplateRustBufferRegister(); DetectSNMPVersionRegister(); + DetectSNMPCommunityRegister(); DetectTemplateBufferRegister(); DetectBypassRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 44a698045b..83e2a30dc9 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -229,6 +229,7 @@ enum { DETECT_TARGET, DETECT_AL_TEMPLATE_RUST_BUFFER, DETECT_AL_SNMP_VERSION, + DETECT_AL_SNMP_COMMUNITY, DETECT_AL_TEMPLATE_BUFFER, DETECT_BYPASS, diff --git a/src/detect-snmp-community.c b/src/detect-snmp-community.c new file mode 100644 index 0000000000..aba8c593bd --- /dev/null +++ b/src/detect-snmp-community.c @@ -0,0 +1,226 @@ +/* Copyright (C) 2015-2019 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 + * + * \author Pierre Chifflier + * + * Set up of the "snmp_community" keyword to allow content + * inspections on the decoded snmp community. + */ + +#include "suricata-common.h" +#include "conf.h" +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-snmp-community.h" +#include "app-layer-parser.h" + +#ifndef HAVE_RUST + +void DetectSNMPCommunityRegister(void) +{ +} + +#else + +#include "rust-snmp-snmp-gen.h" +#include "rust-snmp-detect-gen.h" + +static int DetectSNMPCommunitySetup(DetectEngineCtx *, Signature *, + const char *); +static int DetectEngineInspectSNMPCommunity(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id); +static void DetectSNMPCommunityRegisterTests(void); +static int g_snmp_rust_id = 0; + +void DetectSNMPCommunityRegister(void) +{ + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].name = "snmp_community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].desc = + "SNMP content modififier to match on the snmp community"; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].Setup = + DetectSNMPCommunitySetup; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].RegisterTests = + DetectSNMPCommunityRegisterTests; + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].url = DOC_URL DOC_VERSION "/rules/snmp-keywords.html#snmp_community"; + + sigmatch_table[DETECT_AL_SNMP_COMMUNITY].flags |= SIGMATCH_NOOPT; + + /* register inspect engines */ + DetectAppLayerInspectEngineRegister("snmp_community", + ALPROTO_SNMP, SIG_FLAG_TOSERVER, 0, + DetectEngineInspectSNMPCommunity); + DetectAppLayerInspectEngineRegister("snmp_community", + ALPROTO_SNMP, SIG_FLAG_TOCLIENT, 0, + DetectEngineInspectSNMPCommunity); + + g_snmp_rust_id = DetectBufferTypeGetByName("snmp_community"); + + SCLogDebug("SNMP community detect registered."); +} + +static int DetectSNMPCommunitySetup(DetectEngineCtx *de_ctx, Signature *s, + const char *str) +{ + s->init_data->list = g_snmp_rust_id; + + if (DetectSignatureSetAppProto(s, ALPROTO_SNMP) != 0) + return -1; + + return 0; +} + +static int DetectEngineInspectSNMPCommunity(ThreadVars *tv, + DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const Signature *s, const SigMatchData *smd, + Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) +{ + int ret = 0; + const uint8_t *data = NULL; + uint32_t data_len = 0; + + if (flags & STREAM_TOSERVER) { + rs_snmp_tx_get_community(txv, (uint8_t **)&data, &data_len); + } else if (flags & STREAM_TOCLIENT) { + rs_snmp_tx_get_community(txv, (uint8_t **)&data, &data_len); + } + + if (data != NULL) { + ret = DetectEngineContentInspection(de_ctx, det_ctx, s, smd, + f, (uint8_t *)data, data_len, 0, DETECT_CI_FLAGS_SINGLE, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL); + } + + return ret; +} + +#ifdef UNITTESTS + +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "app-layer-parser.h" +#include "detect-engine.h" +#include "detect-parse.h" +#include "flow-util.h" +#include "stream-tcp.h" + +static int DetectSNMPCommunityTest(void) +{ + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineCtx *de_ctx = NULL; + Flow f; + Packet *p; + TcpSession tcp; + ThreadVars tv; + Signature *s; + + /*uint8_t request[] = "\x30\x27\x02\x01\x01\x04\x0b\x5b\x52\x30\x5f\x43\x40\x63\x74\x69" \ + "\x21\x5d\xa1\x15\x02\x04\x2b\x13\x3f\x85\x02\x01\x00\x02\x01\x00" \ + "\x30\x07\x30\x05\x06\x01\x01\x05\x00";*/ + uint8_t request[] = { + 0x30, 0x27, 0x02, 0x01, 0x01, 0x04, 0x0b, 0x5b, + 0x52, 0x30, 0x5f, 0x43, 0x40, 0x63, 0x74, 0x69, + 0x21, 0x5d, 0xa1, 0x15, 0x02, 0x04, 0x2b, 0x13, + 0x3f, 0x85, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, + 0x30, 0x07, 0x30, 0x05, 0x06, 0x01, 0x01, 0x05, + 0x00 + }; + + /* Setup flow. */ + memset(&f, 0, sizeof(Flow)); + memset(&tcp, 0, sizeof(TcpSession)); + memset(&tv, 0, sizeof(ThreadVars)); + p = UTHBuildPacket(request, sizeof(request), IPPROTO_UDP); + FLOW_INITIALIZE(&f); + f.alproto = ALPROTO_SNMP; + f.protoctx = (void *)&tcp; + f.proto = IPPROTO_UDP; + f.protomap = FlowGetProtoMapping(f.proto); + f.flags |= FLOW_IPV4; + p->flow = &f; + p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; + StreamTcpInitConfig(TRUE); + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + /* This rule should match. */ + s = DetectEngineAppendSig(de_ctx, + "alert snmp any any -> any any (" + "msg:\"SNMP Test Rule\"; " + "snmp_community; content:\"[R0_C@cti!]\"; " + "sid:1; rev:1;)"); + FAIL_IF_NULL(s); + + /* This rule should not match. */ + s = DetectEngineAppendSig(de_ctx, + "alert snmp any any -> any any (" + "msg:\"SNMP Test Rule\"; " + "snmp_community; content:\"private\"; " + "sid:2; rev:1;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + FLOWLOCK_WRLOCK(&f); + AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_SNMP, + STREAM_TOSERVER, request, sizeof(request)); + FLOWLOCK_UNLOCK(&f); + + /* Check that we have app-layer state. */ + FAIL_IF_NULL(f.alstate); + + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + FAIL_IF(!PacketAlertCheck(p, 1)); + FAIL_IF(PacketAlertCheck(p, 2)); + + /* Cleanup. */ + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + + PASS; +} + +#endif + +static void DetectSNMPCommunityRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("DetectSNMPCommunityTest", + DetectSNMPCommunityTest); +#endif /* UNITTESTS */ +} + +#endif diff --git a/src/detect-snmp-community.h b/src/detect-snmp-community.h new file mode 100644 index 0000000000..c7b82c86f3 --- /dev/null +++ b/src/detect-snmp-community.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2015-2019 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 + * + * \author FirstName LastName + */ + +#ifndef __DETECT_SNMP_COMMUNITY_H__ +#define __DETECT_SNMP_COMMUNITY_H__ + +#include "app-layer-snmp.h" + +void DetectSNMPCommunityRegister(void); + +#endif /* __DETECT_SNMP_COMMUNITY_H__ */ -- 2.47.2