From: Michael Altizer (mialtize) Date: Wed, 14 Dec 2016 23:51:48 +0000 (-0500) Subject: Merge pull request #744 in SNORT/snort3 from nhttp58 to master X-Git-Tag: 3.0.0-233~144 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1a42d8189349c45570812d5411349ca7139badc5;p=thirdparty%2Fsnort3.git Merge pull request #744 in SNORT/snort3 from nhttp58 to master Squashed commit of the following: commit 4150d37b3afef6615b88e8350d273976290d145c Author: allewi Date: Mon Nov 28 20:57:00 2016 -0500 JavaScript Normalization --- diff --git a/src/detection/detect.cc b/src/detection/detect.cc index eb71b1b57..1b0f1d3ef 100644 --- a/src/detection/detect.cc +++ b/src/detection/detect.cc @@ -76,9 +76,9 @@ void snort_ignore(Packet*) { } void snort_inspect(Packet* p) { + bool inspected = false; { PacketLatency::Context pkt_latency_ctx { p }; - bool inspected = false; // If the packet has errors, we won't analyze it. if ( p->ptrs.decode_flags & DECODE_ERR_FLAGS ) @@ -121,9 +121,6 @@ void snort_inspect(Packet* p) if ( p->has_ip() ) CheckTagging(p); - if ( inspected ) - InspectorManager::clear(p); - // clear closed sessions here after inspection since non-stream // inspectors may depend on flow information // FIXIT-H but this result in double clearing? should normal @@ -136,6 +133,9 @@ void snort_inspect(Packet* p) SnortEventqLog(p); SnortEventqReset(); + if ( inspected ) + InspectorManager::clear(p); + // Handle block pending state Stream::check_flow_block_pending(p); } diff --git a/src/pub_sub/http_events.cc b/src/pub_sub/http_events.cc index ea0561356..d24175b75 100644 --- a/src/pub_sub/http_events.cc +++ b/src/pub_sub/http_events.cc @@ -71,7 +71,7 @@ const uint8_t* HttpEvent::get_referer(int32_t& length) int32_t HttpEvent::get_response_code() { - return http_msg_header->get_status_code(); + return http_msg_header->get_status_code_num(); } const uint8_t* HttpEvent::get_server(int32_t& length) diff --git a/src/service_inspectors/http_inspect/CMakeLists.txt b/src/service_inspectors/http_inspect/CMakeLists.txt index 92659dd98..756dc7792 100644 --- a/src/service_inspectors/http_inspect/CMakeLists.txt +++ b/src/service_inspectors/http_inspect/CMakeLists.txt @@ -60,6 +60,8 @@ set (FILE_LIST http_cutter.h http_infractions.h http_event_gen.h + http_js_norm.cc + http_js_norm.h ) #if (STATIC_INSPECTORS) diff --git a/src/service_inspectors/http_inspect/Makefile.am b/src/service_inspectors/http_inspect/Makefile.am index f6cc10d50..f0d4db812 100644 --- a/src/service_inspectors/http_inspect/Makefile.am +++ b/src/service_inspectors/http_inspect/Makefile.am @@ -29,6 +29,7 @@ http_test_manager.cc http_test_manager.h \ http_field.cc http_field.h \ http_infractions.h \ http_event_gen.h \ +http_js_norm.cc http_js_norm.h \ ips_http.cc ips_http.h #if STATIC_INSPECTORS diff --git a/src/service_inspectors/http_inspect/http_enum.h b/src/service_inspectors/http_inspect/http_enum.h index 3d48efce5..ca21a6280 100644 --- a/src/service_inspectors/http_inspect/http_enum.h +++ b/src/service_inspectors/http_inspect/http_enum.h @@ -196,6 +196,9 @@ enum Infraction INF_UNSUPPORTED_ENCODING, INF_UNKNOWN_ENCODING, INF_STACKED_ENCODINGS, + INF_JS_OBFUSCATION_EXCD, + INF_JS_EXCESS_WS, + INF_MIXED_ENCODINGS, INF__MAX_VALUE }; diff --git a/src/service_inspectors/http_inspect/http_field.h b/src/service_inspectors/http_inspect/http_field.h index 95ff60092..3ab1736fb 100644 --- a/src/service_inspectors/http_inspect/http_field.h +++ b/src/service_inspectors/http_inspect/http_field.h @@ -47,6 +47,10 @@ public: // Only call this method if the field owns the dynamically allocated buffer you are deleting. // This method is a convenience but you still must know where the buffer came from. Many fields // refer to static buffers or a subfield of someone else's buffer. + + // FIXIT-M the following test should undoubtedly be > 0. But this cannot be changed until a + // survey is done to ensure that no old code creates zero-length buffers. In practice this will + // happen naturally when start and length become private. void delete_buffer() { if (length >= 0) delete[] start; } #ifdef REG_TEST diff --git a/src/service_inspectors/http_inspect/http_flow_data.cc b/src/service_inspectors/http_inspect/http_flow_data.cc index a093b1d75..2ea6c3657 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.cc +++ b/src/service_inspectors/http_inspect/http_flow_data.cc @@ -21,6 +21,7 @@ #include "http_test_manager.h" #include "http_flow_data.h" #include "http_transaction.h" +#include "http_js_norm.h" using namespace HttpEnums; diff --git a/src/service_inspectors/http_inspect/http_flow_data.h b/src/service_inspectors/http_inspect/http_flow_data.h index dd9ed9552..ff858fddc 100644 --- a/src/service_inspectors/http_inspect/http_flow_data.h +++ b/src/service_inspectors/http_inspect/http_flow_data.h @@ -32,6 +32,7 @@ #include "http_event_gen.h" class HttpTransaction; +class HttpJsNorm; class HttpFlowData : public FlowData { @@ -108,7 +109,7 @@ private: int64_t detect_depth_remaining[2] = { HttpEnums::STAT_NOT_PRESENT, HttpEnums::STAT_NOT_PRESENT }; MimeSession* mime_state[2] = { nullptr, nullptr }; - UtfDecodeSession* utf_state = nullptr; //SRC_SERVER only + UtfDecodeSession* utf_state = nullptr; // SRC_SERVER only uint64_t expected_trans_num[2] = { 1, 1 }; // number of user data octets seen so far (regular body or chunks) diff --git a/src/service_inspectors/http_inspect/http_js_norm.cc b/src/service_inspectors/http_inspect/http_js_norm.cc new file mode 100644 index 000000000..ba51b1eeb --- /dev/null +++ b/src/service_inspectors/http_inspect/http_js_norm.cc @@ -0,0 +1,187 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016 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. +//-------------------------------------------------------------------------- +// http_js_norm.cc author Tom Peters + +#include "http_js_norm.h" +#include "utils/util_jsnorm.h" +#include "utils/util.h" +#include "utils/safec.h" +#include "http_enum.h" + +using namespace HttpEnums; + +HttpJsNorm::HttpJsNorm(int max_javascript_whitespaces_, const HttpParaList::UriParam& uri_param_) : + max_javascript_whitespaces(max_javascript_whitespaces_), uri_param(uri_param_), + javascript_search_mpse(new SearchTool()), htmltype_search_mpse(new SearchTool()) +{ + javascript_search_mpse->add(script_start, script_start_length, JS_JAVASCRIPT); + javascript_search_mpse->prep(); + + struct HiSearchToken + { + const char* name; + int name_len; + int search_id; + }; + + const HiSearchToken html_patterns[] = + { + { "JAVASCRIPT", 10, HTML_JS }, + { "ECMASCRIPT", 10, HTML_EMA }, + { "VBSCRIPT", 8, HTML_VB }, + { nullptr, 0, 0 } + }; + + for (const HiSearchToken* tmp = &html_patterns[0]; tmp->name != nullptr; tmp++) + { + htmltype_search_mpse->add(tmp->name, tmp->name_len, tmp->search_id); + } + htmltype_search_mpse->prep(); +} + +HttpJsNorm::~HttpJsNorm() +{ + delete javascript_search_mpse; + delete htmltype_search_mpse; +} + +void HttpJsNorm::normalize(const Field& input, Field& output, bool& js_norm_alloc, + HttpInfractions& infractions, HttpEventGen& events) const +{ + bool js_present = false; + int index = 0; + const char* ptr = (const char*)input.start; + const char* const end = ptr + input.length; + + JSState js; + js.allowed_spaces = max_javascript_whitespaces; + js.allowed_levels = MAX_ALLOWED_OBFUSCATION; + js.alerts = 0; + + uint8_t* buffer = new uint8_t[input.length]; + + while (ptr < end) + { + int bytes_copied = 0; + int mindex; + + // Search for beginning of a javascript + if (javascript_search_mpse->find(ptr, end-ptr, search_js_found, false, &mindex) > 0) + { + const char* js_start = ptr + mindex; + const char* const angle_bracket = (char*)SnortStrnStr(js_start, end - js_start, ">"); + if (angle_bracket == nullptr) + break; + + bool type_js = false; + if (angle_bracket > js_start) + { + int mid; + const int script_found = htmltype_search_mpse->find( + js_start, (angle_bracket-js_start), search_html_found, false, &mid); + + js_start = angle_bracket; + if (script_found > 0) + { + switch (mid) + { + case HTML_JS: + js_present = true; + type_js = true; + break; + default: + type_js = false; + break; + } + } + else + { + // if no type or language is found we assume it is a javascript + js_present = true; + type_js = true; + } + } + // Save before the