From: Russ Combs (rucombs) Date: Fri, 2 Aug 2019 19:41:18 +0000 (-0400) Subject: Merge pull request #1682 in SNORT/snort3 from ~BRASTULT/snort3:ber to master X-Git-Tag: 3.0.0-259~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2efa8bc8d53c5679730f74911e3dc105d3e5a712;p=thirdparty%2Fsnort3.git Merge pull request #1682 in SNORT/snort3 from ~BRASTULT/snort3:ber to master Squashed commit of the following: commit 946ac40b14e4d79b740f31ce7589134e6fe77a68 Author: Brandon Stultz Date: Fri Jul 5 18:43:10 2019 -0400 ips_options: add ber_data and ber_skip --- diff --git a/src/ips_options/CMakeLists.txt b/src/ips_options/CMakeLists.txt index 2ee6946be..d6ff245e7 100644 --- a/src/ips_options/CMakeLists.txt +++ b/src/ips_options/CMakeLists.txt @@ -7,6 +7,8 @@ SET( PLUGIN_LIST ips_ack.cc ips_asn1.cc ips_base64.cc + ips_ber_data.cc + ips_ber_skip.cc ips_bufferlen.cc ips_byte_extract.cc ips_byte_jump.cc @@ -95,6 +97,8 @@ else (STATIC_IPS_OPTIONS) add_dynamic_module(ips_ack ips_options ips_ack.cc) add_dynamic_module(ips_asn1 ips_options ips_asn1.cc asn1_detect.cc asn1_detect.h asn1_util.h asn1_util.cc) add_dynamic_module(ips_base64 ips_options ips_base64.cc) + add_dynamic_module(ips_ber_data ips_options ips_ber_data.cc) + add_dynamic_module(ips_ber_skip ips_options ips_ber_skip.cc) add_dynamic_module(ips_bufferlen ips_options ips_bufferlen.cc) add_dynamic_module(ips_byte_test ips_options ips_byte_test.cc) add_dynamic_module(ips_byte_jump ips_options ips_byte_jump.cc) diff --git a/src/ips_options/ips_ber_data.cc b/src/ips_options/ips_ber_data.cc new file mode 100644 index 000000000..f483fef73 --- /dev/null +++ b/src/ips_options/ips_ber_data.cc @@ -0,0 +1,206 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// ips_ber_data.cc author Brandon Stultz + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "framework/cursor.h" +#include "framework/ips_option.h" +#include "framework/module.h" +#include "hash/hashfcn.h" +#include "profiler/profiler.h" +#include "utils/util_ber.h" + +using namespace snort; + +#define s_name "ber_data" + +static THREAD_LOCAL ProfileStats berDataPerfStats; + +class BerDataOption : public IpsOption +{ +public: + BerDataOption(uint32_t t) : IpsOption(s_name) + { type = t; } + + uint32_t hash() const override; + bool operator==(const IpsOption&) const override; + + bool is_relative() override + { return true; } + + EvalStatus eval(Cursor&, Packet*) override; + +private: + uint32_t type; +}; + +//------------------------------------------------------------------------- +// class methods +//------------------------------------------------------------------------- + +uint32_t BerDataOption::hash() const +{ + uint32_t a = type, b = 0, c = 0; + + mix_str(a,b,c,s_name); + finalize(a,b,c); + + return c; +} + +bool BerDataOption::operator==(const IpsOption& ips) const +{ + const BerDataOption& rhs = (const BerDataOption&)ips; + + if ( type != rhs.type ) + return false; + + return true; +} + +IpsOption::EvalStatus BerDataOption::eval(Cursor& c, Packet*) +{ + RuleProfile profile(berDataPerfStats); + + BerReader ber(c); + BerElement e; + + if ( !ber.read(c.start(), e) ) + return NO_MATCH; + + if ( e.type != type ) + return NO_MATCH; + + if ( !c.add_pos(e.total_length - e.length) ) + return NO_MATCH; + + return MATCH; +} + +//------------------------------------------------------------------------- +// module +//------------------------------------------------------------------------- + +static const Parameter s_params[] = +{ + { "~type", Parameter::PT_INT, "0:255", nullptr, + "move to the data for the specified BER element type" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#define s_help \ + "rule option to move to the data for a specified BER element" + +class BerDataModule : public Module +{ +public: + BerDataModule() : Module(s_name, s_help, s_params) { } + + bool begin(const char*, int, SnortConfig*) override; + bool set(const char*, Value&, SnortConfig*) override; + + ProfileStats* get_profile() const override + { return &berDataPerfStats; } + + Usage get_usage() const override + { return DETECT; } + +public: + uint32_t type; +}; + +bool BerDataModule::begin(const char*, int, SnortConfig*) +{ + type = 0; + return true; +} + +bool BerDataModule::set(const char*, Value& v, SnortConfig*) +{ + if ( v.is("~type") ) + type = v.get_uint32(); + else + return false; + + return true; +} + +//------------------------------------------------------------------------- +// api methods +//------------------------------------------------------------------------- + +static Module* mod_ctor() +{ + return new BerDataModule; +} + +static void mod_dtor(Module* m) +{ + delete m; +} + +static IpsOption* ber_data_ctor(Module* p, OptTreeNode*) +{ + BerDataModule* m = (BerDataModule*)p; + return new BerDataOption(m->type); +} + +static void ber_data_dtor(IpsOption* p) +{ + delete p; +} + +static const IpsApi ber_data_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + s_name, + s_help, + mod_ctor, + mod_dtor + }, + OPT_TYPE_DETECTION, + 0, 0, + nullptr, + nullptr, + nullptr, + nullptr, + ber_data_ctor, + ber_data_dtor, + nullptr +}; + +#ifdef BUILDING_SO +SO_PUBLIC const BaseApi* snort_plugins[] = +#else +const BaseApi* ips_ber_data[] = +#endif +{ + &ber_data_api.base, + nullptr +}; + diff --git a/src/ips_options/ips_ber_skip.cc b/src/ips_options/ips_ber_skip.cc new file mode 100644 index 000000000..b95e678e4 --- /dev/null +++ b/src/ips_options/ips_ber_skip.cc @@ -0,0 +1,223 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// ips_ber_skip.cc author Brandon Stultz + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "framework/cursor.h" +#include "framework/ips_option.h" +#include "framework/module.h" +#include "hash/hashfcn.h" +#include "profiler/profiler.h" +#include "utils/util_ber.h" + +using namespace snort; + +#define s_name "ber_skip" + +static THREAD_LOCAL ProfileStats berSkipPerfStats; + +class BerSkipOption : public IpsOption +{ +public: + BerSkipOption(uint32_t t, bool o) : IpsOption(s_name) + { type = t; optional = o; } + + uint32_t hash() const override; + bool operator==(const IpsOption&) const override; + + bool is_relative() override + { return true; } + + EvalStatus eval(Cursor&, Packet*) override; + +private: + uint32_t type; + bool optional; +}; + +//------------------------------------------------------------------------- +// class methods +//------------------------------------------------------------------------- + +uint32_t BerSkipOption::hash() const +{ + uint32_t a = type, b = optional, c = 0; + + mix_str(a,b,c,s_name); + finalize(a,b,c); + + return c; +} + +bool BerSkipOption::operator==(const IpsOption& ips) const +{ + const BerSkipOption& rhs = (const BerSkipOption&)ips; + + if ( type != rhs.type ) + return false; + + if ( optional != rhs.optional ) + return false; + + return true; +} + +IpsOption::EvalStatus BerSkipOption::eval(Cursor& c, Packet*) +{ + RuleProfile profile(berSkipPerfStats); + + BerReader ber(c); + BerElement e; + + if ( !ber.read(c.start(), e) ) + return NO_MATCH; + + if ( e.type != type ) + { + if ( optional ) + return MATCH; + else + return NO_MATCH; + } + + if ( !c.add_pos(e.total_length) ) + return NO_MATCH; + + return MATCH; +} + +//------------------------------------------------------------------------- +// module +//------------------------------------------------------------------------- + +static const Parameter s_params[] = +{ + { "~type", Parameter::PT_INT, "0:255", nullptr, + "BER element type to skip" }, + + { "optional", Parameter::PT_IMPLIED, nullptr, nullptr, + "match even if the specified BER type is not found" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +#define s_help "rule option to skip BER element" + +class BerSkipModule : public Module +{ +public: + BerSkipModule() : Module(s_name, s_help, s_params) { } + + bool begin(const char*, int, SnortConfig*) override; + bool set(const char*, Value&, SnortConfig*) override; + + ProfileStats* get_profile() const override + { return &berSkipPerfStats; } + + Usage get_usage() const override + { return DETECT; } + +public: + uint32_t type; + bool optional; +}; + +bool BerSkipModule::begin(const char*, int, SnortConfig*) +{ + type = 0; + optional = false; + return true; +} + +bool BerSkipModule::set(const char*, Value& v, SnortConfig*) +{ + if ( v.is("~type") ) + type = v.get_uint32(); + + else if ( v.is("optional") ) + optional = true; + + else + return false; + + return true; +} + +//------------------------------------------------------------------------- +// api methods +//------------------------------------------------------------------------- + +static Module* mod_ctor() +{ + return new BerSkipModule; +} + +static void mod_dtor(Module* m) +{ + delete m; +} + +static IpsOption* ber_skip_ctor(Module* p, OptTreeNode*) +{ + BerSkipModule* m = (BerSkipModule*)p; + return new BerSkipOption(m->type, m->optional); +} + +static void ber_skip_dtor(IpsOption* p) +{ + delete p; +} + +static const IpsApi ber_skip_api = +{ + { + PT_IPS_OPTION, + sizeof(IpsApi), + IPSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + s_name, + s_help, + mod_ctor, + mod_dtor + }, + OPT_TYPE_DETECTION, + 0, 0, + nullptr, + nullptr, + nullptr, + nullptr, + ber_skip_ctor, + ber_skip_dtor, + nullptr +}; + +#ifdef BUILDING_SO +SO_PUBLIC const BaseApi* snort_plugins[] = +#else +const BaseApi* ips_ber_skip[] = +#endif +{ + &ber_skip_api.base, + nullptr +}; + diff --git a/src/ips_options/ips_options.cc b/src/ips_options/ips_options.cc index 1bfd8b946..42649cb6d 100644 --- a/src/ips_options/ips_options.cc +++ b/src/ips_options/ips_options.cc @@ -48,6 +48,8 @@ extern const BaseApi* ips_so; extern const BaseApi* ips_ack[]; extern const BaseApi* ips_asn1[]; extern const BaseApi* ips_base64[]; +extern const BaseApi* ips_ber_data[]; +extern const BaseApi* ips_ber_skip[]; extern const BaseApi* ips_byte_extract[]; extern const BaseApi* ips_byte_jump[]; extern const BaseApi* ips_byte_math[]; @@ -118,6 +120,8 @@ void load_ips_options() PluginManager::load_plugins(ips_ack); PluginManager::load_plugins(ips_asn1); PluginManager::load_plugins(ips_base64); + PluginManager::load_plugins(ips_ber_data); + PluginManager::load_plugins(ips_ber_skip); PluginManager::load_plugins(ips_byte_extract); PluginManager::load_plugins(ips_byte_jump); PluginManager::load_plugins(ips_byte_math); diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index ebe7046f6..b96f459f9 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -13,6 +13,7 @@ set( UTIL_INCLUDES sfmemcap.h stats.h util.h + util_ber.h util_cstring.h util_jsnorm.h util_unfold.h @@ -38,6 +39,7 @@ add_library ( utils OBJECT snort_bounds.h stats.cc util.cc + util_ber.cc util_cstring.cc util_jsnorm.cc util_net.cc diff --git a/src/utils/util_ber.cc b/src/utils/util_ber.cc new file mode 100644 index 000000000..0c339e4c1 --- /dev/null +++ b/src/utils/util_ber.cc @@ -0,0 +1,232 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// util_ber.cc author Brandon Stultz + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "util_ber.h" + +namespace snort +{ + +bool BerReader::read_int(uint32_t size, uint32_t& intval) +{ + unsigned bytes = 0; + + intval = 0; + + // check if we can read int data + if ( cursor + size > end ) + return false; + + for ( unsigned i = 0; i < size; i++ ) + { + uint8_t b = *cursor++; + + // handle null padding + if ( bytes == 0 && b == 0 ) + continue; + + intval <<= 8; + intval |= b; + bytes++; + + // check if int fits into uint32_t + if ( bytes > 4 ) + return false; + } + + return true; +} + +bool BerReader::read_type(uint32_t& type) +{ + unsigned bytes = 0; + uint8_t b; + + type = 0; + + if ( cursor + 1 > end ) + return false; + + b = *cursor++; + + if ( (b & 0x1F) != 0x1F ) + { + // short-form type + type = b; + return true; + } + + // long-form type + while ( true ) + { + if ( cursor + 1 > end ) + return false; + + b = *cursor++; + + // handle null padding + if ( bytes == 0 && b == 0x80 ) + continue; + + type <<= 7; + type |= b & 0x7F; + bytes++; + + // check if type fits into uint32_t + if ( bytes > 4 ) + return false; + + // check continuation bit + if ( (b & 0x80) == 0 ) + break; + } + + return true; +} + +bool BerReader::read_length(uint32_t& length) +{ + unsigned size; + uint8_t b; + + length = 0; + + if ( cursor + 1 > end ) + return false; + + b = *cursor++; + + if ( (b & 0x80) == 0 ) + { + // short-form length + length = b; + return true; + } + + // long-form length + size = b & 0x7F; + + if ( size == 0 ) + return false; + + if ( !read_int(size, length) ) + return false; + + return true; +} + +bool BerReader::read(const uint8_t* c, BerElement& e) +{ + const uint8_t* start = c; + + if ( c < beg || c > end ) + return false; + + cursor = c; + + if ( !read_type(e.type) ) + return false; + + if ( !read_length(e.length) ) + return false; + + // set BER data pointer + e.data = cursor; + + // jump BER data + cursor += e.length; + + // cursor must be > start + if ( cursor <= start ) + return false; + + // calculate total BER length + e.total_length = cursor - start; + + return true; +} + +bool BerReader::convert(BerElement& e, uint32_t& intval) +{ + if ( e.type != BerType::INTEGER ) + return false; + + if ( e.data < beg || e.data > end ) + return false; + + // set cursor to int data + cursor = e.data; + + if ( !read_int(e.length, intval) ) + return false; + + return true; +} + +bool BerReader::extract(const uint8_t*& c, uint32_t& intval) +{ + BerElement e; + + if ( !read(c, e) ) + return false; + + // save end of element position + c = cursor; + + if ( !convert(e, intval) ) + return false; + + return true; +} + +bool BerReader::skip(const uint8_t*& c, uint32_t type) +{ + BerElement e; + + if ( !read(c, e) ) + return false; + + if ( e.type != type ) + return false; + + c = cursor; + + return true; +} + +bool BerReader::data(const uint8_t*& c, uint32_t type) +{ + BerElement e; + + if ( !read(c, e) ) + return false; + + if ( e.type != type ) + return false; + + c = e.data; + + return true; +} + +} + diff --git a/src/utils/util_ber.h b/src/utils/util_ber.h new file mode 100644 index 000000000..6b77c6b9f --- /dev/null +++ b/src/utils/util_ber.h @@ -0,0 +1,75 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// util_ber.h author Brandon Stultz + +#ifndef UTIL_BER_H +#define UTIL_BER_H + +#include "main/snort_types.h" +#include "framework/cursor.h" + +namespace snort +{ + +enum BerType +{ + BOOLEAN = 1, + INTEGER, + BIT_STRING, + STRING, +}; + +struct BerElement +{ + uint32_t type; + uint32_t length; + uint32_t total_length; + const uint8_t* data; +}; + +class SO_PUBLIC BerReader +{ +public: + BerReader(Cursor& c) + { + beg = c.buffer(); + end = c.endo(); + } + + bool read(const uint8_t* c, BerElement& e); + + bool convert(BerElement& e, uint32_t& intval); + bool extract(const uint8_t*& c, uint32_t& intval); + + bool skip(const uint8_t*& c, uint32_t type); + bool data(const uint8_t*& c, uint32_t type); + +private: + bool read_int(uint32_t size, uint32_t& intval); + + bool read_type(uint32_t& type); + bool read_length(uint32_t& length); + + const uint8_t* beg; + const uint8_t* cursor; + const uint8_t* end; +}; + +} +#endif +