From: Masud Hasan (mashasan) Date: Mon, 21 Sep 2020 19:02:28 +0000 (+0000) Subject: Merge pull request #2485 in SNORT/snort3 from ~MASHASAN/snort3:ua_fp to master X-Git-Tag: 3.0.3-1~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b7baa1b27c0067d8e11aeeecdaded5ac227ece5d;p=thirdparty%2Fsnort3.git Merge pull request #2485 in SNORT/snort3 from ~MASHASAN/snort3:ua_fp to master Squashed commit of the following: commit b363e332c5bca6a23f0d434171c2ebeb8f1bd79a Author: Masud Hasan Date: Tue Sep 15 13:09:27 2020 -0400 rna: Supporting user agent fingerprints --- diff --git a/src/host_tracker/host_tracker.cc b/src/host_tracker/host_tracker.cc index 591cb25b1..a5770c499 100644 --- a/src/host_tracker/host_tracker.cc +++ b/src/host_tracker/host_tracker.cc @@ -369,6 +369,38 @@ bool HostTracker::add_tcp_fingerprint(uint32_t fpid) return result.second; } +DeviceFingerprint::DeviceFingerprint(uint32_t id, uint32_t type, bool jb, const char* dev) : + fpid(id), fp_type(type), jail_broken(jb) +{ + if ( dev ) + { + strncpy(device, dev, INFO_SIZE); + device[INFO_SIZE-1] = '\0'; + } +} + +bool HostTracker::add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken, + const char* device, uint8_t max_devices) +{ + lock_guard lck(host_tracker_lock); + + int count = 0; + for ( const auto& fp : ua_fps ) + { + if ( fpid != fp.fpid or fp_type != fp.fp_type ) + continue; + ++count; // only count same fpid with different device information + if ( count >= max_devices ) + return false; + if ( jail_broken == fp.jail_broken and ( ( !device and fp.device[0] == '\0') or + ( device and strncmp(fp.device, device, INFO_SIZE) == 0) ) ) + return false; + } + + ua_fps.emplace_back(fpid, fp_type, jail_broken, device); + return true; +} + size_t HostTracker::get_client_count() { lock_guard lck(host_tracker_lock); @@ -484,4 +516,19 @@ void HostTracker::stringify(string& str) for ( const auto& fpid : tcp_fpids ) str += to_string(fpid) + (--total ? ", " : ""); } + + total = ua_fps.size(); + if ( total ) + { + str += "\nua fingerprint: "; + for ( const auto& fp : ua_fps ) + { + str += to_string(fp.fpid) + " (type: " + to_string(fp.fp_type); + if ( fp.jail_broken ) + str += ", jail-broken"; + if ( fp.device[0] != '\0' ) + str += ", device: " + string(fp.device); + str += string(")") + (--total ? ", " : ""); + } + } } diff --git a/src/host_tracker/host_tracker.h b/src/host_tracker/host_tracker.h index e34f5db59..1d7890b73 100644 --- a/src/host_tracker/host_tracker.h +++ b/src/host_tracker/host_tracker.h @@ -100,6 +100,15 @@ struct HostClient AppId service; }; +struct DeviceFingerprint +{ + DeviceFingerprint(uint32_t id, uint32_t type, bool jb, const char* dev); + uint32_t fpid; + uint32_t fp_type; + bool jail_broken; + char device[INFO_SIZE] = { 0 }; +}; + enum HostType { HOST_TYPE_HOST=0, @@ -112,6 +121,7 @@ enum HostType typedef HostCacheAllocIp HostMacAllocator; typedef HostCacheAllocIp HostAppAllocator; typedef HostCacheAllocIp HostClientAllocator; +typedef HostCacheAllocIp HostDeviceFpAllocator; class SO_PUBLIC HostTracker { @@ -212,6 +222,8 @@ public: size_t get_client_count(); HostClient get_client(AppId id, const char* version, AppId service, bool& is_new); bool add_tcp_fingerprint(uint32_t fpid); + bool add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken, + const char* device_info, uint8_t max_devices); // This should be updated whenever HostTracker data members are changed void stringify(std::string& str); @@ -227,6 +239,7 @@ private: std::vector services; std::vector clients; std::set, HostCacheAllocIp> tcp_fpids; + std::vector ua_fps; bool vlan_tag_present = false; vlan::VlanTagHdr vlan_tag; diff --git a/src/network_inspectors/rna/CMakeLists.txt b/src/network_inspectors/rna/CMakeLists.txt index 57d672cf7..800fec670 100644 --- a/src/network_inspectors/rna/CMakeLists.txt +++ b/src/network_inspectors/rna/CMakeLists.txt @@ -1,6 +1,7 @@ set (RNA_INCLUDES rna_fingerprint.h rna_fingerprint_tcp.h + rna_fingerprint_ua.h rna_inspector.h rna_logger.h rna_name.h @@ -15,7 +16,7 @@ set ( RNA_SOURCES rna_fingerprint.cc rna_fingerprint.h rna_fingerprint_tcp.cc - rna_fingerprint_tcp.h + rna_fingerprint_ua.cc rna_inspector.cc rna_inspector.h rna_logger.cc diff --git a/src/network_inspectors/rna/dev_notes.txt b/src/network_inspectors/rna/dev_notes.txt index c1cc66c27..2f59c2cbe 100644 --- a/src/network_inspectors/rna/dev_notes.txt +++ b/src/network_inspectors/rna/dev_notes.txt @@ -219,3 +219,18 @@ topts: tcp options ws: window scale FpElementType::RANGE FpElementType::DONT_CARE + +Similar to the TCP fingerprints, user-agent based fingerprints loads different types of +fingerprint patterns from Lua configuration, namely os (operating system), device +(mobile device information), jail-broken (hacked system), and jail-broken-host +(host information of the hacked system to confirm matching). During packet processing, +the rna module depends on the HTTP user agent and host information found by the appid +module and tries to match all parts of user-agent patterns. A sample configuration +looks like this: +{ + fpid = 1, + uuid = "10000000-0000-0000-0000-111111111111", + ua_type = "device", + user_agent = { { substring = "CPU" }, { substring = "OS 3_0" }, { substring = "My Company" } }, + device = "My Mobile", +} diff --git a/src/network_inspectors/rna/rna_app_discovery.cc b/src/network_inspectors/rna/rna_app_discovery.cc index 0ebd1402a..0163c8722 100644 --- a/src/network_inspectors/rna/rna_app_discovery.cc +++ b/src/network_inspectors/rna/rna_app_discovery.cc @@ -27,6 +27,7 @@ #include "detection/detection_engine.h" #include "network_inspectors/appid/appid_session_api.h" +#include "rna_fingerprint_ua.h" #include "rna_logger_common.h" using namespace snort; @@ -90,6 +91,24 @@ void RnaAppDiscovery::process(AppidEvent* appid_event, DiscoveryFilter& filter, appid_session_api.get_service_info(vendor, version, subtype); update_service_info(p, proto, vendor, version, ht, src_ip, src_mac, logger); } + + if ( p->is_from_client() and ( appid_change_bits[APPID_HOST_BIT] or + appid_change_bits[APPID_USERAGENT_BIT] ) ) + { + const AppIdHttpSession* hsession; + + if ( appid_event->get_is_http2() ) + hsession = appid_session_api.get_http_session(appid_event->get_http2_stream_index()); + else + hsession = appid_session_api.get_http_session(); + + if ( hsession ) + { + const char* host = hsession->get_cfield(REQ_HOST_FID); + const char* uagent = hsession->get_cfield(REQ_AGENT_FID); + analyze_user_agent_fingerprint(p, host, uagent, ht, src_ip, src_mac, logger); + } + } } void RnaAppDiscovery::discover_service(const Packet* p, IpProtocol proto, RnaTracker& rt, @@ -166,3 +185,27 @@ void RnaAppDiscovery::discover_client(const Packet* p, RnaTracker& rt, logger.log(RNA_EVENT_NEW, NEW_CLIENT_APP, p, &rt, src_ip, src_mac, &hc); } } + +void RnaAppDiscovery::analyze_user_agent_fingerprint(const Packet* p, const char* host, + const char* uagent, RnaTracker& rt, const SfIp* ip, const uint8_t* src_mac, RnaLogger& logger) +{ + if ( !host or !uagent ) + return; + + const auto& processor = get_ua_fp_processor(); + if ( !processor ) + return; + + const UaFingerprint* uafp = nullptr; + const char* device_info = nullptr; + bool jail_broken = false; + processor->match_mpse(host, uagent, uafp, device_info, jail_broken); + + if ( uafp and rt->add_ua_fingerprint(uafp->fpid, uafp->fp_type, jail_broken, + device_info, MAX_USER_AGENT_DEVICES) ) + { + logger.log(RNA_EVENT_NEW, NEW_OS, p, &rt, (const struct in6_addr*)ip->get_ip6_ptr(), + src_mac, (FpFingerprint*)uafp); + } +} + diff --git a/src/network_inspectors/rna/rna_app_discovery.h b/src/network_inspectors/rna/rna_app_discovery.h index 3717b7679..6e44f28a5 100644 --- a/src/network_inspectors/rna/rna_app_discovery.h +++ b/src/network_inspectors/rna/rna_app_discovery.h @@ -38,6 +38,9 @@ private: static void update_service_info(const snort::Packet* p, IpProtocol proto, const char* vendor, const char* version, RnaTracker& rt, const snort::SfIp* ip, const uint8_t* src_mac, RnaLogger& logger); + static void analyze_user_agent_fingerprint(const snort::Packet* p, const char* host, + const char* uagent, RnaTracker& rt, const snort::SfIp* ip, const uint8_t* src_mac, + RnaLogger& logger); }; #endif diff --git a/src/network_inspectors/rna/rna_config.h b/src/network_inspectors/rna/rna_config.h index b9626f243..781802c13 100644 --- a/src/network_inspectors/rna/rna_config.h +++ b/src/network_inspectors/rna/rna_config.h @@ -24,6 +24,7 @@ namespace snort { class TcpFpProcessor; +class UaFpProcessor; } struct RnaModuleConfig @@ -31,7 +32,8 @@ struct RnaModuleConfig std::string rna_conf_path; bool enable_logger; bool log_when_idle; - snort::TcpFpProcessor* processor = nullptr; + snort::TcpFpProcessor* tcp_processor = nullptr; + snort::UaFpProcessor* ua_processor = nullptr; }; // Give default values so that RNA can work even if rna_conf_path is not provided diff --git a/src/network_inspectors/rna/rna_fingerprint.h b/src/network_inspectors/rna/rna_fingerprint.h index a0eb1c523..3bb35452f 100644 --- a/src/network_inspectors/rna/rna_fingerprint.h +++ b/src/network_inspectors/rna/rna_fingerprint.h @@ -23,6 +23,7 @@ #include #include +#include #include "main/snort_types.h" @@ -114,6 +115,14 @@ private: } +enum UserAgentInfoType +{ + OS_INFO, + DEVICE_INFO, + JAIL_BROKEN_INFO, + JAIL_BROKEN_HOST +}; + class RawFingerprint { public: @@ -130,6 +139,11 @@ public: std::string ws; bool df = false; + UserAgentInfoType ua_type = OS_INFO; + std::vector user_agent; + std::string host_name; + std::string device; + void clear() { fpid = 0; @@ -141,9 +155,12 @@ public: id.clear(); topts.clear(); ws.clear(); - df=false; + df = false; + ua_type = OS_INFO; + user_agent.clear(); + host_name.clear(); + device.clear(); } - }; #endif diff --git a/src/network_inspectors/rna/rna_fingerprint_ua.cc b/src/network_inspectors/rna/rna_fingerprint_ua.cc new file mode 100644 index 000000000..7929c94cf --- /dev/null +++ b/src/network_inspectors/rna/rna_fingerprint_ua.cc @@ -0,0 +1,206 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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. +//-------------------------------------------------------------------------- + +// rna_fingerprint_ua.cc author Masud Hasan + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rna_fingerprint_ua.h" + +#include +#include + +#include "main/thread.h" + +using namespace snort; +using namespace std; + +static THREAD_LOCAL UaFpProcessor* ua_fp_processor = nullptr; + +UaFpProcessor* get_ua_fp_processor() +{ + return ua_fp_processor; +} + +void set_ua_fp_processor(UaFpProcessor* processor) +{ + ua_fp_processor = processor; +} + +bool UaFingerprint::operator==(const UaFingerprint& y) const +{ + return fpid == y.fpid and part_num == y.part_num and total_parts == y.total_parts; +} + +UaFpProcessor::~UaFpProcessor() +{ + delete os_mpse; + delete device_mpse; + delete jb_mpse; + delete jb_host_mpse; +} + +void UaFpProcessor::push(const RawFingerprint& rfp) +{ + if ( rfp.ua_type == JAIL_BROKEN_HOST ) + { + if ( rfp.host_name.empty() ) + return; + UaFingerprint uafp; + uafp.fpid = rfp.fpid; + uafp.fpuuid = rfp.fpuuid; + uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE; + uafp.host_name = rfp.host_name; + uafp.part_num = 0; + uafp.total_parts = 1; + push_jb_host(uafp); + } + else + { + for (size_t i = 0; i < rfp.user_agent.size(); ++i) + { + UaFingerprint uafp; + uafp.fpid = rfp.fpid; + uafp.fpuuid = rfp.fpuuid; + uafp.user_agent = rfp.user_agent[i]; + uafp.part_num = i; + uafp.total_parts = rfp.user_agent.size(); + if ( rfp.ua_type == OS_INFO ) + { + uafp.fp_type = FpFingerprint::FpType::FP_TYPE_USERAGENT; + push_agent(uafp); + } + else if ( rfp.ua_type == DEVICE_INFO ) + { + uafp.device = rfp.device; + uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE; + push_device(uafp); + } + else + { + uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE; + push_jb(uafp); + } + } + } +} + +void UaFpProcessor::make_mpse(SnortConfig* sc) +{ + SearchTool::set_conf(sc); + if ( !os_fps.empty() ) + { + os_mpse = new SearchTool; + for (auto& fp : os_fps) + os_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp); + os_mpse->prep(); + } + + if ( !device_fps.empty() ) + { + device_mpse = new SearchTool; + for (auto& fp : device_fps) + device_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp); + device_mpse->prep(); + } + + if ( !jb_fps.empty() ) + { + jb_mpse = new SearchTool; + for (auto& fp : jb_fps) + jb_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp); + jb_mpse->prep(); + } + + if ( !jb_host_fps.empty() ) + { + jb_host_mpse = new SearchTool; + for (auto& fp : jb_host_fps) + jb_host_mpse->add(fp.host_name.c_str(), fp.host_name.size(), &fp); + jb_host_mpse->prep(); + } + SearchTool::set_conf(nullptr); +} + +static int match_ua_part(void* id, void*, int, void* data, void*) +{ + auto cur_fp = (UaFingerprint*) id; + auto matched_parts = (vector*)data; + + for (const auto& fp : *matched_parts) + if ( *fp == *cur_fp ) + return 0; // ignore already recorded matching part + + matched_parts->emplace_back(cur_fp); + return 0; // search continues for the next match +} + +struct CompareParts +{ + bool operator()(const UaFingerprint* p1, const UaFingerprint* p2) const + { + return (p1->fpid < p2->fpid) or (p1->fpid == p2->fpid and p1->part_num < p2->part_num); + } +}; + +static inline UaFingerprint* search_ua_fp(SearchTool* mpse, const char* start, unsigned len) +{ + if ( !mpse ) + return nullptr; + + vector matched_parts; + mpse->find_all(start, len, match_ua_part, false, (void*)&matched_parts); + if ( matched_parts.empty() ) + return nullptr; + + sort(matched_parts.begin(), matched_parts.end(), CompareParts()); + + uint32_t cur_fpid = 0, part_num = 0; + for (auto& fp : matched_parts) + { + if ( cur_fpid != fp->fpid ) + { + cur_fpid = fp->fpid; + part_num = 0; + } + + if ( part_num == fp->part_num ) + { + if ( ++part_num == fp->total_parts ) + return fp; + } + } + return nullptr; +} + +void UaFpProcessor::match_mpse(const char* host, const char* uagent, const UaFingerprint*& osfp, + const char*& device_info, bool& jail_broken) +{ + unsigned len = strlen(uagent); + osfp = search_ua_fp(os_mpse, uagent, len); + + auto devicefp = search_ua_fp(device_mpse, uagent, len); + if ( devicefp ) + device_info = devicefp->device.c_str(); + + auto jbfp = search_ua_fp(jb_mpse, uagent, len); + if ( jbfp and search_ua_fp(jb_host_mpse, host, strlen(host)) ) + jail_broken = true; +} diff --git a/src/network_inspectors/rna/rna_fingerprint_ua.h b/src/network_inspectors/rna/rna_fingerprint_ua.h new file mode 100644 index 000000000..b9bafa97d --- /dev/null +++ b/src/network_inspectors/rna/rna_fingerprint_ua.h @@ -0,0 +1,88 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 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. +//-------------------------------------------------------------------------- + +// rna_fingerprint_ua.h author Masud Hasan + +#ifndef RNA_FINGERPRINT_UA_H +#define RNA_FINGERPRINT_UA_H + +#include "main/snort_config.h" +#include "main/snort_types.h" +#include "search_engines/search_tool.h" + +#include "rna_fingerprint.h" + +#define MAX_USER_AGENT_DEVICES 16 + +namespace snort +{ + +class SO_PUBLIC UaFingerprint : public FpFingerprint +{ +public: + std::string user_agent; + std::string host_name; + std::string device; + uint32_t part_num = 0; + uint32_t total_parts = 0; + + bool operator==(const UaFingerprint& y) const; +}; + +class SO_PUBLIC UaFpProcessor +{ +public: + ~UaFpProcessor(); + + void make_mpse(SnortConfig* sc); + + void match_mpse(const char* host, const char* uagent, const UaFingerprint*& osfp, + const char*& device_info, bool& jail_broken); + + void push(const RawFingerprint& rfp); + + void push_agent(const UaFingerprint& uafp) + { os_fps.emplace_back(uafp); } + + void push_device(const UaFingerprint& uafp) + { device_fps.emplace_back(uafp); } + + void push_jb(const UaFingerprint& uafp) + { jb_fps.emplace_back(uafp); } + + void push_jb_host(const UaFingerprint& uafp) + { jb_host_fps.emplace_back(uafp); } + +private: + std::vector os_fps; + std::vector device_fps; + std::vector jb_fps; + std::vector jb_host_fps; + + snort::SearchTool* os_mpse = nullptr; + snort::SearchTool* device_mpse = nullptr; + snort::SearchTool* jb_mpse = nullptr; + snort::SearchTool* jb_host_mpse = nullptr; +}; + +} // end of namespace snort + +snort::UaFpProcessor* get_ua_fp_processor(); +SO_PUBLIC void set_ua_fp_processor(snort::UaFpProcessor*); + +#endif diff --git a/src/network_inspectors/rna/rna_inspector.cc b/src/network_inspectors/rna/rna_inspector.cc index a12238243..88698318c 100644 --- a/src/network_inspectors/rna/rna_inspector.cc +++ b/src/network_inspectors/rna/rna_inspector.cc @@ -35,6 +35,7 @@ #include "rna_event_handler.h" #include "rna_fingerprint_tcp.h" +#include "rna_fingerprint_ua.h" #include "rna_module.h" #include "rna_pnd.h" @@ -67,8 +68,11 @@ RnaInspector::~RnaInspector() delete pnd; delete rna_conf; if (mod_conf) - delete mod_conf->processor; - delete mod_conf; + { + delete mod_conf->tcp_processor; + delete mod_conf->ua_processor; + delete mod_conf; + } } bool RnaInspector::configure(SnortConfig* sc) @@ -128,7 +132,8 @@ void RnaInspector::show(const SnortConfig*) const void RnaInspector::tinit() { // thread local initialization - set_tcp_fp_processor(mod_conf->processor); + set_tcp_fp_processor(mod_conf->tcp_processor); + set_ua_fp_processor(mod_conf->ua_processor); } void RnaInspector::tterm() @@ -193,9 +198,9 @@ TcpFpProcessor* RnaInspector::get_or_create_fp_processor() { if (mod_conf) { - if (!mod_conf->processor) - mod_conf->processor = new TcpFpProcessor; - return mod_conf->processor; + if (!mod_conf->tcp_processor) + mod_conf->tcp_processor = new TcpFpProcessor; + return mod_conf->tcp_processor; } return nullptr; } @@ -204,8 +209,8 @@ void RnaInspector::set_fp_processor(TcpFpProcessor* tfp) { if ( mod_conf ) { - delete mod_conf->processor; - mod_conf->processor = tfp; + delete mod_conf->tcp_processor; + mod_conf->tcp_processor = tfp; } } diff --git a/src/network_inspectors/rna/rna_logger.cc b/src/network_inspectors/rna/rna_logger.cc index a4e7a9c16..da8e94266 100644 --- a/src/network_inspectors/rna/rna_logger.cc +++ b/src/network_inspectors/rna/rna_logger.cc @@ -30,7 +30,7 @@ #include "managers/event_manager.h" #include "protocols/packet.h" -#include "rna_fingerprint_tcp.h" +#include "rna_fingerprint.h" #include "rna_logger_common.h" #include "rna_module.h" @@ -90,10 +90,10 @@ void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker } void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht, - const struct in6_addr* src_ip, const uint8_t* src_mac, const TcpFingerprint* tfp) + const struct in6_addr* src_ip, const uint8_t* src_mac, const FpFingerprint* fp) { log(type, subtype, src_ip, src_mac, ht, p, 0, 0, - nullptr, nullptr, tfp, nullptr, nullptr); + nullptr, nullptr, fp, nullptr, nullptr); } void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht, @@ -126,7 +126,7 @@ void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, const uint bool RnaLogger::log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip, const uint8_t* src_mac, RnaTracker* ht, const Packet* p, uint32_t event_time, - uint16_t proto, const HostMac* hm, const HostApplication* ha, const TcpFingerprint* tfp, + uint16_t proto, const HostMac* hm, const HostApplication* ha, const FpFingerprint* fp, void* cond_var, const HostClient* hc) { if ( !enabled ) @@ -134,7 +134,7 @@ bool RnaLogger::log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ assert(ht); - RnaLoggerEvent rle(type, subtype, src_mac, ht, hm, proto, cond_var, ha, tfp, hc); + RnaLoggerEvent rle(type, subtype, src_mac, ht, hm, proto, cond_var, ha, fp, hc); if ( src_ip and (!IN6_IS_ADDR_V4MAPPED(src_ip) or src_ip->s6_addr32[3]) ) rle.ip = src_ip; else diff --git a/src/network_inspectors/rna/rna_logger.h b/src/network_inspectors/rna/rna_logger.h index 34e9539e4..af41ae810 100644 --- a/src/network_inspectors/rna/rna_logger.h +++ b/src/network_inspectors/rna/rna_logger.h @@ -28,7 +28,7 @@ namespace snort { class Flow; struct Packet; -class TcpFingerprint; +class FpFingerprint; } using RnaTracker = std::shared_ptr; @@ -37,8 +37,8 @@ struct RnaLoggerEvent : public Event { RnaLoggerEvent (uint16_t t, uint16_t st, const uint8_t* mc, const RnaTracker* rt, const snort::HostMac* hmp, uint16_t pr, void* cv, const snort::HostApplication* hap, - const snort::TcpFingerprint* tf, const snort::HostClient* hcp) : type(t), subtype(st), - mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), tfp(tf), hc(hcp) { } + const snort::FpFingerprint* fpr, const snort::HostClient* hcp) : type(t), subtype(st), + mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), fp(fpr), hc(hcp) { } uint16_t type; uint16_t subtype; @@ -49,7 +49,7 @@ struct RnaLoggerEvent : public Event uint16_t proto; void* cond_var; const snort::HostApplication* ha; - const snort::TcpFingerprint* tfp; + const snort::FpFingerprint* fp; const snort::HostClient* hc; }; @@ -66,9 +66,9 @@ public: void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht, const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::HostClient* hcp); - // for tcp fingerprint + // for fingerprint void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht, - const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::TcpFingerprint* tfp); + const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::FpFingerprint* fp); // for event time void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht, @@ -92,7 +92,7 @@ public: bool log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip, const uint8_t* src_mac, RnaTracker* ht, const snort::Packet* p = nullptr, uint32_t event_time = 0, uint16_t proto = 0, const snort::HostMac* hm = nullptr, - const snort::HostApplication* ha = nullptr, const snort::TcpFingerprint* tfp = nullptr, + const snort::HostApplication* ha = nullptr, const snort::FpFingerprint* fp = nullptr, void* cond_var = nullptr, const snort::HostClient* hc = nullptr); private: diff --git a/src/network_inspectors/rna/rna_module.cc b/src/network_inspectors/rna/rna_module.cc index a04703998..9e26d9d15 100644 --- a/src/network_inspectors/rna/rna_module.cc +++ b/src/network_inspectors/rna/rna_module.cc @@ -37,6 +37,7 @@ #include "utils/util.h" #include "rna_fingerprint_tcp.h" +#include "rna_fingerprint_ua.h" #include "rna_mac_cache.h" #ifdef UNIT_TEST @@ -81,6 +82,13 @@ static const Command rna_cmds[] = { nullptr, nullptr, nullptr, nullptr } }; +static const Parameter user_agent_parts[] = +{ + { "substring", Parameter::PT_STRING, nullptr, nullptr, "a substring of user agent string" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + static const Parameter rna_fp_params[] = { { "fpid", Parameter::PT_INT, "0:max32", "0", @@ -113,6 +121,18 @@ static const Parameter rna_fp_params[] = { "df", Parameter::PT_BOOL, nullptr, "false", "fingerprint don't fragment flag" }, + { "ua_type", Parameter::PT_ENUM, "os | device | jail-broken | jail-broken-host", + "os", "type of user agent fingerprints" }, + + { "user_agent", Parameter::PT_LIST, user_agent_parts, nullptr, + "list of user agent information parts to match" }, + + { "host_name", Parameter::PT_STRING, nullptr, nullptr, + "host name information" }, + + { "device", Parameter::PT_STRING, nullptr, nullptr, + "device information" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -131,7 +151,10 @@ static const Parameter rna_params[] = "file name to dump RNA mac cache on shutdown; won't dump by default" }, { "tcp_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr, - "list tcp fingerprints" }, + "list of tcp fingerprints" }, + + { "ua_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr, + "list of user agent fingerprints" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -182,8 +205,14 @@ bool RnaModule::begin(const char* fqn, int, SnortConfig*) if (!strcmp(fqn, "rna.tcp_fingerprints")) { fingerprint.clear(); - if (!mod_conf->processor) - mod_conf->processor = new TcpFpProcessor; + if (!mod_conf->tcp_processor) + mod_conf->tcp_processor = new TcpFpProcessor; + } + else if (!strcmp(fqn, "rna.ua_fingerprints")) + { + fingerprint.clear(); + if (!mod_conf->ua_processor) + mod_conf->ua_processor = new UaFpProcessor; } return true; @@ -203,8 +232,8 @@ bool RnaModule::set(const char* fqn, Value& v, SnortConfig*) snort_free((void*)dump_file); dump_file = snort_strdup(v.get_string()); } - - else if (fqn && strstr(fqn, "rna.tcp_fingerprints")) + else if ( fqn and ( strstr(fqn, "rna.tcp_fingerprints") or + strstr(fqn, "rna.ua_fingerprints") ) ) { if (v.is("fpid")) fingerprint.fpid = v.get_uint32(); @@ -226,8 +255,24 @@ bool RnaModule::set(const char* fqn, Value& v, SnortConfig*) fingerprint.ws = v.get_string(); else if (v.is("df")) fingerprint.df = v.get_uint8(); + else if (v.is("ua_type")) + fingerprint.ua_type = (UserAgentInfoType)v.get_uint8(); + else if (v.is("host_name")) + fingerprint.host_name = v.get_string(); + else if (v.is("device")) + fingerprint.device = v.get_string(); + else if (v.is("user_agent")) + return true; + else if (v.is("substring")) + { + const auto& ua_part = v.get_string(); + if ( !ua_part ) + return false; + fingerprint.user_agent.emplace_back(ua_part); + } + else + return false; } - else return false; @@ -249,18 +294,26 @@ bool RnaModule::end(const char* fqn, int index, SnortConfig* sc) sc->clear_run_flags(RUN_FLAG__IP_FRAGS_ONLY); } - if (mod_conf->processor) + if ( mod_conf->tcp_processor ) { - mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER); - mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT); + mod_conf->tcp_processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER); + mod_conf->tcp_processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT); } + + if ( mod_conf->ua_processor ) + mod_conf->ua_processor->make_mpse(sc); } - if ( index > 0 && mod_conf->processor && !strcmp(fqn, "rna.tcp_fingerprints") ) + if ( index > 0 and mod_conf->tcp_processor and !strcmp(fqn, "rna.tcp_fingerprints") ) { // there is an implicit conversion here from raw fingerprint (all // strings) to tcp fingerprint, done by the tcp fingerprint constructor - mod_conf->processor->push(fingerprint); + mod_conf->tcp_processor->push(fingerprint); + fingerprint.clear(); + } + else if ( index > 0 and mod_conf->ua_processor and !strcmp(fqn, "rna.ua_fingerprints") ) + { + mod_conf->ua_processor->push(fingerprint); fingerprint.clear(); } @@ -337,7 +390,8 @@ bool RnaModule::log_mac_cache(const char* outfile) bool RnaModule::is_valid_fqn(const char* fqn) const { - return !strcmp(fqn, RNA_NAME) || !strcmp(fqn, "rna.tcp_fingerprints"); + return !strcmp(fqn, RNA_NAME) or !strcmp(fqn, "rna.tcp_fingerprints") or + !strcmp(fqn, "rna.ua_fingerprints") or !strcmp(fqn, "rna.ua_fingerprints.user_agent"); } diff --git a/src/network_inspectors/rna/test/rna_module_mock.h b/src/network_inspectors/rna/test/rna_module_mock.h index b983ee230..d32d8dba1 100644 --- a/src/network_inspectors/rna/test/rna_module_mock.h +++ b/src/network_inspectors/rna/test/rna_module_mock.h @@ -63,6 +63,10 @@ void set_tcp_fp_processor(TcpFpProcessor*) { } TcpFingerprint::TcpFingerprint(const RawFingerprint&) { } bool TcpFingerprint::operator==(const TcpFingerprint&) const { return true; } +UaFpProcessor::~UaFpProcessor() { } +void UaFpProcessor::make_mpse(SnortConfig*) { } +void UaFpProcessor::push(RawFingerprint const&) { } + // inspector class RnaInspector { @@ -79,15 +83,15 @@ RnaInspector(RnaModule* mod) { if (mod_conf) { - if (mod_conf->processor) - delete mod_conf->processor; - delete mod_conf; - } + delete mod_conf->tcp_processor; + delete mod_conf->ua_processor; + delete mod_conf; + } } TcpFpProcessor* get_fp_processor() { - return mod_conf->processor; + return mod_conf->tcp_processor; } private: