From: Mike Stepanek (mstepane) Date: Mon, 29 Jun 2020 20:32:01 +0000 (+0000) Subject: Merge pull request #2290 in SNORT/snort3 from ~MDAGON/snort3:packet_inj to master X-Git-Tag: 3.0.2-1~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=980de3110ab01c50d15744a216e76b49d0c93f19;p=thirdparty%2Fsnort3.git Merge pull request #2290 in SNORT/snort3 from ~MDAGON/snort3:packet_inj to master Squashed commit of the following: commit b400fbede446c8e1e817f83763128e38fcd3ddad Author: mdagon Date: Wed May 27 14:48:03 2020 -0400 payload_injector: add payload injection utility --- diff --git a/lua/snort.lua b/lua/snort.lua index f48d1ba4b..e4eb1fa8e 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -177,6 +177,9 @@ rewrite = { } -- react = { } -- reject = { } +-- use this to enable payload injection utility +-- payload_injector = { } + --------------------------------------------------------------------------- -- 6. configure filters --------------------------------------------------------------------------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4285aac27..c5dc29dc4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,6 +103,7 @@ add_subdirectory(memory) add_subdirectory(mime) add_subdirectory(packet_io) add_subdirectory(parser) +add_subdirectory(payload_injector) add_subdirectory(ports) add_subdirectory(protocols) add_subdirectory(sfip) @@ -160,6 +161,7 @@ add_executable( snort $ $ $ + $ $ $ $ diff --git a/src/main/modules.cc b/src/main/modules.cc index 11a6bf74f..714c62ff8 100644 --- a/src/main/modules.cc +++ b/src/main/modules.cc @@ -51,6 +51,7 @@ #include "parser/parse_conf.h" #include "parser/parse_ip.h" #include "parser/parser.h" +#include "payload_injector/payload_injector_module.h" #include "profiler/profiler.h" #include "search_engines/pat_stats.h" #include "side_channel/side_channel_module.h" @@ -2024,8 +2025,9 @@ void module_init() ModuleManager::add_module(new RuleStateModule); ModuleManager::add_module(new SearchEngineModule); ModuleManager::add_module(new SFDAQModule); + ModuleManager::add_module(new PayloadInjectorModule); - // these could but prolly shouldn't be policy specific + // these could but probably shouldn't be policy specific // or should be broken into policy and non-policy parts ModuleManager::add_module(new AlertsModule); ModuleManager::add_module(new EventQueueModule); diff --git a/src/payload_injector/CMakeLists.txt b/src/payload_injector/CMakeLists.txt new file mode 100644 index 000000000..b3d82db5e --- /dev/null +++ b/src/payload_injector/CMakeLists.txt @@ -0,0 +1,14 @@ +set (PAYLOAD_INJECTOR_INCLUDES + payload_injector_module.h +) + +add_library ( payload_injector OBJECT + ${PAYLOAD_INJECTOR_INCLUDES} + payload_injector_module.cc +) + +install(FILES ${PAYLOAD_INJECTOR_INCLUDES} + DESTINATION "${INCLUDE_INSTALL_PATH}/payload_injector/" +) + +add_subdirectory(test) diff --git a/src/payload_injector/dev_notes.txt b/src/payload_injector/dev_notes.txt new file mode 100644 index 000000000..cf38ddb8a --- /dev/null +++ b/src/payload_injector/dev_notes.txt @@ -0,0 +1,9 @@ +Payload injector is a utility class intended to help other Snort components with +application-protocol-level injection. The calling components decide when and +what to inject. Payload injector is responsible for flow-control concerns such +as transmitting RST flags to endpoints as well as blocking flows within Snort. +It coordinates with the Active component of Snort to perform these functions. + +Currently it is being used for HTTP/1 injections. HTTP/2 support is in developement. + + diff --git a/src/payload_injector/payload_injector_module.cc b/src/payload_injector/payload_injector_module.cc new file mode 100644 index 000000000..37f6f0698 --- /dev/null +++ b/src/payload_injector/payload_injector_module.cc @@ -0,0 +1,97 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// payload_injector_module.cc author Maya Dagon + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "payload_injector_module.h" + +#include "detection/detection_engine.h" +#include "packet_io/active.h" +#include "protocols/packet.h" + +#ifdef UNIT_TEST +#include "catch/snort_catch.h" +#endif + +#define s_name "payload_injector" +#define s_help \ + "payload injection utility" + +using namespace snort; + +THREAD_LOCAL PayloadInjectorCounts payload_injector_stats; + +const PegInfo payload_injector_pegs[] = +{ + { CountType::SUM, "http_injects", "total number of http injections" }, + { CountType::END, nullptr, nullptr } +}; + +bool PayloadInjectorModule::configured = false; + +PayloadInjectorModule::PayloadInjectorModule() : + Module(s_name, s_help) +{ } + +const PegInfo* PayloadInjectorModule::get_pegs() const +{ return payload_injector_pegs; } + +PegCount* PayloadInjectorModule::get_counts() const +{ return (PegCount*)&payload_injector_stats; } + +bool PayloadInjectorModule::end(const char*, int, SnortConfig*) +{ + configured = true; + return true; +} + +InjectionReturnStatus PayloadInjectorModule::inject_http_payload(Packet* p, InjectionControl& control) +{ + InjectionReturnStatus status = INJECTION_SUCCESS; + + assert(p != nullptr); + + if (configured) + { + EncodeFlags df = (p->packet_flags & PKT_FROM_SERVER) ? ENC_FLAG_FWD : 0; + df |= ENC_FLAG_RST_SRVR; // Send RST to server. + + if (p->packet_flags & PKT_STREAM_EST) + { + payload_injector_stats.http_injects++; + p->active->send_data(p, df, control.http_page, control.http_page_len); + } + else + status = ERR_STREAM_NOT_ESTABLISHED; + } + else + status = ERR_INJECTOR_NOT_CONFIGURED; + + p->active->block_session(p, true); + + DetectionEngine::disable_all(p); + + if ( p->flow ) + p->flow->set_state(Flow::FlowState::BLOCK); + + return status; +} diff --git a/src/payload_injector/payload_injector_module.h b/src/payload_injector/payload_injector_module.h new file mode 100644 index 000000000..a196545a5 --- /dev/null +++ b/src/payload_injector/payload_injector_module.h @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// payload_injector_module.h author Maya Dagon + +#ifndef PAYLOAD_INJECTOR_MODULE_H +#define PAYLOAD_INJECTOR_MODULE_H + +#include "framework/module.h" + +namespace snort +{ +struct Packet; +} + +struct PayloadInjectorCounts +{ + PegCount http_injects; +}; + +extern THREAD_LOCAL PayloadInjectorCounts payload_injection_stats; + +enum InjectionReturnStatus : int8_t +{ + INJECTION_SUCCESS = 1, + ERR_INJECTOR_NOT_CONFIGURED = -1, + ERR_STREAM_NOT_ESTABLISHED = -2, +}; + +struct InjectionControl +{ + const uint8_t* http_page = nullptr; + uint32_t http_page_len = 0; +}; + +class SO_PUBLIC PayloadInjectorModule : public snort::Module +{ +public: + PayloadInjectorModule(); + const PegInfo* get_pegs() const override; + PegCount* get_counts() const override; + + Usage get_usage() const override + { return GLOBAL; } + + bool end(const char*, int, snort::SnortConfig*) override; + + static InjectionReturnStatus inject_http_payload(snort::Packet* p, InjectionControl& control); + +#ifdef UNIT_TEST + void set_configured(bool val) { configured = val; } +#endif + +private: + static bool configured; +}; + +#endif + diff --git a/src/payload_injector/test/CMakeLists.txt b/src/payload_injector/test/CMakeLists.txt new file mode 100644 index 000000000..693bcc56f --- /dev/null +++ b/src/payload_injector/test/CMakeLists.txt @@ -0,0 +1,5 @@ +add_cpputest (payload_injector_test + SOURCES + ../payload_injector_module.cc + ../../framework/module.cc +) diff --git a/src/payload_injector/test/payload_injector_test.cc b/src/payload_injector/test/payload_injector_test.cc new file mode 100644 index 000000000..f072a506f --- /dev/null +++ b/src/payload_injector/test/payload_injector_test.cc @@ -0,0 +1,124 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- + +// payload_injector_test.cc author Maya Dagon +// unit test main + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "payload_injector/payload_injector_module.h" + +#include "detection/detection_engine.h" +#include "flow/flow.h" +#include "packet_io/active.h" +#include "protocols/packet.h" + +#include +#include + +using namespace snort; + +//-------------------------------------------------------------------------- +// mocks +//-------------------------------------------------------------------------- +namespace snort +{ +uint32_t Active::send_data(snort::Packet*, unsigned long, unsigned char const*, unsigned int) +{ + return 1; +} +void Active::block_session(snort::Packet*, bool) { } +void DetectionEngine::disable_all(snort::Packet*) { } +Flow::Flow() { } +Flow::~Flow() { } +Packet::Packet(bool) { packet_flags = 0; flow = nullptr; } +Packet::~Packet() { } +} + +void show_stats(PegCount*, const PegInfo*, unsigned, const char*) { } +void show_stats(PegCount*, const PegInfo*, const IndexVec&, const char*, FILE*) { } + +TEST_GROUP(payload_injector_test) +{ + PayloadInjectorModule mod; + InjectionControl control; + PegCount* counts = mod.get_counts(); + Flow flow; + + void setup() override + { + counts[0] = 0; + control.http_page = (const uint8_t*)"test"; + control.http_page_len = 4; + flow.set_state(Flow::FlowState::INSPECT); + } +}; + +TEST(payload_injector_test, not_configured_stream_not_established) +{ + mod.set_configured(false); + Packet p(false); + p.flow = &flow; + InjectionReturnStatus status = mod.inject_http_payload(&p, control); + CHECK(counts[0] == 0); + CHECK(status == ERR_INJECTOR_NOT_CONFIGURED); + CHECK(flow.flow_state == Flow::FlowState::BLOCK); +} + +TEST(payload_injector_test, not_configured_stream_established) +{ + mod.set_configured(false); + Packet p(false); + p.packet_flags = PKT_STREAM_EST; + p.flow = &flow; + InjectionReturnStatus status = mod.inject_http_payload(&p, control); + CHECK(counts[0] == 0); + CHECK(status == ERR_INJECTOR_NOT_CONFIGURED); + CHECK(flow.flow_state == Flow::FlowState::BLOCK); +} + +TEST(payload_injector_test, configured_stream_not_established) +{ + mod.set_configured(true); + Packet p(false); + p.flow = &flow; + InjectionReturnStatus status = mod.inject_http_payload(&p, control); + CHECK(counts[0] == 0); + CHECK(status == ERR_STREAM_NOT_ESTABLISHED); + CHECK(flow.flow_state == Flow::FlowState::BLOCK); +} + +TEST(payload_injector_test, configured_stream_established) +{ + mod.set_configured(true); + Packet p(false); + p.packet_flags = PKT_STREAM_EST; + p.flow = &flow; + InjectionReturnStatus status = mod.inject_http_payload(&p, control); + CHECK(counts[0] == 1); + CHECK(status == INJECTION_SUCCESS); + CHECK(flow.flow_state == Flow::FlowState::BLOCK); +} + +int main(int argc, char** argv) +{ + return CommandLineTestRunner::RunAllTests(argc, argv); +} +