]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #3022 in SNORT/snort3 from ~AMARNAYA/snort3:feature_vba_macrodata...
authorPranav Bhalerao (prbhaler) <prbhaler@cisco.com>
Mon, 11 Oct 2021 08:59:24 +0000 (08:59 +0000)
committerPranav Bhalerao (prbhaler) <prbhaler@cisco.com>
Mon, 11 Oct 2021 08:59:24 +0000 (08:59 +0000)
Squashed commit of the following:

commit a6e4992d0bf97781fdefc90fe89571c4210f574c
Author: Steve Chew (stechew) <stechew@cisco.com>
Date:   Mon Jul 19 21:49:09 2021 +0000

    decompress, http_inspect: Add support for processing ole files and for vba_data ips option

34 files changed:
doc/reference/snort_reference.text
doc/user/http_inspect.txt
doc/user/snort_user.text
src/decompress/CMakeLists.txt
src/decompress/file_decomp.cc
src/decompress/file_decomp.h
src/decompress/file_decomp_zip.cc
src/decompress/file_decomp_zip.h
src/decompress/file_olefile.cc [new file with mode: 0644]
src/decompress/file_olefile.h [new file with mode: 0644]
src/decompress/file_oleheader.cc [new file with mode: 0644]
src/decompress/file_oleheader.h [new file with mode: 0644]
src/decompress/test/CMakeLists.txt [new file with mode: 0644]
src/decompress/test/file_olefile_test.cc [new file with mode: 0644]
src/decompress/test/file_oleheader_test.cc [new file with mode: 0644]
src/detection/fp_detect.cc
src/detection/fp_utils.cc
src/framework/inspector.h
src/framework/ips_option.h
src/ports/port_group.h
src/service_inspectors/http_inspect/http_api.cc
src/service_inspectors/http_inspect/http_enum.h
src/service_inspectors/http_inspect/http_inspect.cc
src/service_inspectors/http_inspect/http_module.cc
src/service_inspectors/http_inspect/http_module.h
src/service_inspectors/http_inspect/http_msg_body.cc
src/service_inspectors/http_inspect/http_msg_body.h
src/service_inspectors/http_inspect/http_msg_header.cc
src/service_inspectors/http_inspect/http_msg_section.cc
src/service_inspectors/http_inspect/ips_http.cc
src/service_inspectors/http_inspect/ips_http.h
src/utils/endian.h
src/utils/stats.cc
src/utils/stats.h

index 81b3229fbdae18dc6bd27aa51da147432b28bd54..370ba5c7628969b3a19219de98147526601f0df5 100644 (file)
@@ -264,8 +264,9 @@ Table of Contents
     7.115. tos
     7.116. ttl
     7.117. urg
-    7.118. window
-    7.119. wscale
+    7.118. vba_data
+    7.119. window
+    7.120. wscale
 
 8. Search Engine Modules
 9. SO Rule Modules
@@ -3682,6 +3683,8 @@ Configuration:
     response bodies
   * bool http_inspect.decompress_zip = false: decompress zip files in
     response bodies
+  * bool http_inspect.decompress_vba = false: decompress vba macro 
+    data of MS office files in response bodies
   * bool http_inspect.script_detection = false: inspect JavaScript
     immediately upon script end
   * bool http_inspect.normalize_javascript = false: use legacy
@@ -7907,7 +7910,18 @@ Configuration:
     { 0:65535 }
 
 
-7.118. window
+7.118. vba_data
+
+--------------
+
+Help: rule option to set the detection cursor to the MS office visual basic for applications macros buffer
+
+Type: ips_option
+
+Usage: detect
+
+
+7.119. window
 
 --------------
 
@@ -7923,7 +7937,7 @@ Configuration:
     range { 0:65535 }
 
 
-7.119. wscale
+7.120. wscale
 
 --------------
 
@@ -9143,6 +9157,8 @@ these libraries see the Getting Started section of the manual.
     response bodies
   * bool http_inspect.decompress_zip = false: decompress zip files in
     response bodies
+  * bool http_inspect.decompress_vba = false: decompress vba macro
+    data of MS office files in response bodies
   * string http_inspect.ignore_unreserved: do not alert when the
     specified unreserved characters are percent-encoded in a
     URI.Unreserved characters are 0-9, a-z, A-Z, period, underscore,
@@ -14819,6 +14835,8 @@ and are not applicable elsewhere.
   * unified2 (logger): output event and packet in unified2 format
     file
   * urg (ips_option): detection for TCP urgent pointer
+  * vba_data (ips_option): rule option to set the detection cursor to
+    the MS Office Visual Basic for Applications macros buffer 
   * vlan (codec): support for local area network
   * window (ips_option): rule option to check TCP window field
   * wizard (inspector): inspector that implements port-independent
@@ -15147,6 +15165,8 @@ and are not applicable elsewhere.
   * ips_option::tos: rule option to check type of service field
   * ips_option::ttl: rule option to check time to live field
   * ips_option::urg: detection for TCP urgent pointer
+  * ips_option::vba_data: rule option to set the detection cursor to
+    the MS Office Visual Basic for Applications macros buffer
   * ips_option::window: rule option to check TCP window field
   * ips_option::wscale: detection for TCP window scale
   * logger::alert_csv: output event in csv format
index 82b264d969a5e8fe896ae6938af6902669343bc0..c03215bb831092365331733010f6047e9a461321 100755 (executable)
@@ -135,6 +135,15 @@ content is decompressed and made available through the file data rule
 option. The compressed SWF file signature is converted to FWS to indicate
 an uncompressed file.
 
+===== decompress_vba
+
+decompress_vba = true will enable decompression of RLE (Run Length Encoding)
+compressed vba (Visual Basic for Applications) macro data of MS Office 
+files. The MS office files are PKZIP compressed which are parsed to locate 
+the OLE (Object Linking and Embedding) file embedded with the files 
+containing RLE compressed vba macro data. The decompressed vba macro data is
+then made available through the vba_data ips rule option.
+
 ===== normalize_javascript
 
 normalize_javascript = true will enable legacy normalizer of JavaScript within
@@ -601,6 +610,12 @@ js_normalization_depth option is described above.
 Despite what js_data has, file_data still contains the whole HTTP body
 with an original JavaScript in it.
 
+===== vba_data
+
+The vba_data will contain the decompressed Visual Basic for Applications
+(vba) macro data embedded in MS office files. It requires decompress_zip
+and decompress_vba options enabled.
+
 ==== Timing issues and combining rule options
 
 HTTP inspector is stateful. That means it is aware of a bigger picture than
index 303e9eab276c915a05274fc74090b01206402deb..d2cf9f9a205f093dfc2080f6d52c548d2fcf7bbb 100644 (file)
@@ -3930,7 +3930,16 @@ LZMA. The compressed content is decompressed and made available
 through the file data rule option. The compressed SWF file signature
 is converted to FWS to indicate an uncompressed file.
 
-5.10.2.7. normalize_javascript
+5.10.2.7. decompress_vba
+
+decompress_vba = true will enable decompression of RLE (Run Length Encoding)
+compressed vba (Visual Basic for Applications) macro data of MS Office
+files. The MS office files are PKZIP compressed which are parsed to locate
+the OLE (Object Linking and Embedding) file embedded with the files
+containing RLE compressed vba macro data. The decompressed vba macro data is
+then made available through the vba_data ips rule option.
+
+5.10.2.8. normalize_javascript
 
 normalize_javascript = true will enable legacy normalizer of
 JavaScript within the HTTP response body. http_inspect looks for
@@ -3945,7 +3954,7 @@ normalizations refer to basic JavaScript normalization. Cannot be
 used together with js_normalization_depth (doing so will cause Snort
 to fail to load). This is planned to be deprecated at some point.
 
-5.10.2.8. js_normalization_depth
+5.10.2.9. js_normalization_depth
 
 js_normalization_depth = N {-1 : max53} will set a number of input
 JavaScript bytes to normalize and enable the enhanced normalizer. The
@@ -3966,7 +3975,7 @@ function names. The normalized data is available through the js_data
 rule option. This is currently experimental and still under
 development.
 
-5.10.2.9. js_norm_identifier_depth
+5.10.2.10. js_norm_identifier_depth
 
 js_norm_identifier_depth = N {0 : 260000} will set a number of unique
 JavaScript identifiers to normalize. When the depth is reached, a
@@ -3981,7 +3990,7 @@ js_normalization_depth is set to a non-zero value, enabling the
 enhanced normalizer. This is currently experimental and still under
 development.
 
-5.10.2.10. js_norm_max_tmpl_nest
+5.10.2.11. js_norm_max_tmpl_nest
 
 js_norm_max_tmpl_nest = N {0 : 255} (default 32) is an option of the
 enhanced JavaScript normalizer that determines the deepest level of
@@ -3995,7 +4004,7 @@ tracking. This option is used only when js_normalization_depth is not
 0. This feature is currently experimental and still under
 development.
 
-5.10.2.11. xff_headers
+5.10.2.12. xff_headers
 
 This configuration supports defining custom x-forwarded-for type
 headers. In a multi-vendor world, it is quite possible that the
@@ -4010,7 +4019,7 @@ they are defined, e.g "x-forwarded-for" will be preferred than
 "true-client-ip" if both headers are present in the stream. The
 header names should be delimited by a space.
 
-5.10.2.12. maximum_host_length
+5.10.2.13. maximum_host_length
 
 Setting maximum_host_length causes http_inspect to generate 119:25 if
 the Host header value including optional white space exceeds the
@@ -4018,7 +4027,7 @@ specified length. In the abnormal case of multiple Host headers, the
 total length of the combined values is used. The default value is -1,
 meaning do not perform this check.
 
-5.10.2.13. maximum_chunk_length
+5.10.2.14. maximum_chunk_length
 
 http_inspect strictly limits individual chunks within a chunked
 message body to be less than four gigabytes.
@@ -4026,7 +4035,7 @@ message body to be less than four gigabytes.
 A lower limit may be configured by setting maximum_chunk_length. Any
 chunk longer than maximum chunk length will generate a 119:16 alert.
 
-5.10.2.14. URI processing
+5.10.2.15. URI processing
 
 Normalization and inspection of the URI in the HTTP request message
 is a key aspect of what http_inspect does. The best way to normalize
@@ -4427,6 +4436,12 @@ js_normalization_depth option is described above. Despite what
 js_data has, file_data still contains the whole HTTP body with an
 original JavaScript in it.
 
+5.10.5.15. vba_data
+
+The vba_data will contain the decompressed Visual Basic for Applications
+(vba) macro data embedded in MS office files. It requires decompress_zip
+and decompress_vba options enabled.
+
 5.10.6. Timing issues and combining rule options
 
 HTTP inspector is stateful. That means it is aware of a bigger
index 21ba69be9558145c95bc6a518d985559e146721c..e42179c9a582b43deebd69da016e308eda095557 100644 (file)
@@ -1,4 +1,6 @@
 
+add_subdirectory (test)
+
 set( DECOMPRESS_INCLUDES
     file_decomp.h
 )
@@ -12,8 +14,13 @@ add_library (decompress OBJECT
     file_decomp_swf.h
     file_decomp_zip.cc
     file_decomp_zip.h
+    file_olefile.cc
+    file_olefile.h
+    file_oleheader.cc
+    file_oleheader.h
 )
 
 install (FILES ${DECOMPRESS_INCLUDES}
     DESTINATION "${INCLUDE_INSTALL_PATH}/decompress"
 )
+
index 957196ee4cc95526ef9b5c33a451030272fc9b6b..78371587dbc60f9b9d8c36f9ebe02f0c2927743a 100644 (file)
@@ -231,6 +231,9 @@ static fd_status_t Process_Decompression(fd_session_t* SessionPtr)
     }
     case ( FILE_TYPE_ZIP ):
     {
+        if (SessionPtr->Modes & FILE_VBA_EXTR_BIT)
+            SessionPtr->vba_analysis = true;
+
         Ret_Code = File_Decomp_ZIP(SessionPtr);
         break;
     }
@@ -298,6 +301,9 @@ fd_session_t* File_Decomp_New()
     New_Session->Avail_Out = 0;
     New_Session->Next_Out = nullptr;
     New_Session->File_Type = FILE_TYPE_NONE;
+    New_Session->vba_analysis = false;
+    New_Session->ole_data_ptr = nullptr;
+    New_Session->ole_data_ptr = 0;
 
     return New_Session;
 }
index 055538930ec449a69cff3d72989a51b01e3e3bd6..f5a8cda10256de27cba0272be417b15b985da3d3 100644 (file)
@@ -27,6 +27,9 @@
 
 #include "main/snort_types.h"
 
+#define NEW_OFFICE_FRMT 120
+#define OLD_OFFICE_FRMT  27
+
 /* Function return codes used internally and with caller */
 // FIXIT-L these need to be split into internal-only codes and things that may be returned to the
 // application. The codes used by PDF and SWF should be standardized. PDF is returning BlockIn and
@@ -61,10 +64,11 @@ enum file_compression_type_t
 #define FILE_SWF_ZLIB_BIT    (0x00000002)
 #define FILE_PDF_DEFL_BIT    (0x00000004)
 #define FILE_ZIP_DEFL_BIT    (0x00000008)
+#define FILE_VBA_EXTR_BIT    (0x00000010)
 
 #define FILE_PDF_ANY         (FILE_PDF_DEFL_BIT)
 #define FILE_SWF_ANY         (FILE_SWF_LZMA_BIT | FILE_SWF_ZLIB_BIT)
-#define FILE_ZIP_ANY         (FILE_ZIP_DEFL_BIT)
+#define FILE_ZIP_ANY         (FILE_ZIP_DEFL_BIT | FILE_VBA_EXTR_BIT)
 
 /* Error codes either passed to caller via the session->Error_Alert of
    the File_Decomp_Alert() call-back function. */
@@ -136,6 +140,9 @@ struct fd_session_t
     uint8_t Decomp_Type; // Active decompression type
     uint8_t Sig_State;   // Sig search state machine
     uint8_t State;       // main state machine
+    uint8_t* ole_data_ptr; // compressed ole file.
+    uint32_t ole_data_len; 
+    bool vba_analysis;   
 };
 
 /* Macros */
index 3905a4849f0d7a639ec3ebe60161a63e25450bc5..2f929056d6f9cfb7ef39355906a33833da3f196d 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include "file_decomp_zip.h"
+#include "file_olefile.h"
 
 #include "helpers/boyer_moore_search.h"
 #include "utils/util.h"
@@ -120,6 +121,12 @@ fd_status_t File_Decomp_End_ZIP(fd_session_t* SessionPtr)
     if ( SessionPtr->ZIP->State == ZIP_STATE_INFLATE )
         Inflate_End(SessionPtr);
 
+    if (SessionPtr->ZIP->file_name)
+    {
+        delete[] SessionPtr->ZIP->file_name;
+        SessionPtr->ZIP->file_name = nullptr;
+    }
+
     if ( SessionPtr->ZIP->header_searcher )
     {
         delete SessionPtr->ZIP->header_searcher;
@@ -169,6 +176,12 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
                 parser->filename_length = 0;
                 parser->extra_length = 0;
 
+                if (parser->file_name)
+                {
+                    delete[] parser->file_name;
+                    parser->file_name = nullptr;
+                }
+
                 // reset decompression progress
                 parser->progress = 0;
 
@@ -266,7 +279,7 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
             byte = *SessionPtr->Next_In;
             parser->filename_length |= byte << (parser->Index * 8);
             break;
-        // extra length
+        //extra length
         case ZIP_STATE_EXTRALEN:
             // check if we are done with the extra length
             if ( parser->Index == parser->Length )
@@ -274,15 +287,51 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
                 // read the extra length, reset the index
                 parser->Index = 0;
 
-                // skip the filename and extra fields
+                // read the filename next
+                if (parser->file_name == nullptr and parser->filename_length > 0)
+                    parser->file_name = new char[parser->filename_length + 1];
+
+                parser->State = ZIP_STATE_FILENAME;
+                parser->Length = parser->filename_length;
+                continue;
+            }
+            // read the extra length
+            byte = *SessionPtr->Next_In;
+            parser->extra_length |= byte << (parser->Index * 8);
+            break;
+
+        // filename
+        case ZIP_STATE_FILENAME:
+            // check if we are done with filename
+            if ( parser->Index == parser->Length )
+            {
+                // read the extra length, reset the index
+                parser->Index = 0;
+                if (parser->file_name)
+                {
+                    parser->file_name[parser->filename_length] = '\0';
+                }
+
+                //skip the extra fields
                 parser->State = ZIP_STATE_SKIP;
-                parser->Length = parser->filename_length + parser->extra_length;
+                parser->Length = parser->extra_length;
 
                 if ( (SessionPtr->Avail_Out > 0) && (parser->method == 8) )
                 {
                     // we have available output space and
                     // the compression type is deflate (8),
                     // land on the compressed stream, init zlib
+                    //If the filename ends with vbaProject.bin, then
+                    //the file has vbaMacros.
+
+                    if (( parser->filename_length >= MACRO_BINNAME_LEN )and (parser->file_name)and (strcmp(
+                        parser->file_name + parser->filename_length - MACRO_BINNAME_LEN,
+                        macro_binname)==0))
+                    {
+                        parser->Next = ZIP_STATE_OLE_FILE;
+                        parser->Next_Length = 0;
+                        continue;
+                    }
                     parser->Next = ZIP_STATE_INFLATE_INIT;
                     parser->Next_Length = 0;
                     continue;
@@ -305,10 +354,16 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
                 parser->Next_Length = 4;
                 continue;
             }
-            // read the extra length
-            byte = *SessionPtr->Next_In;
-            parser->extra_length |= byte << (parser->Index * 8);
+
+            //read the filename
+            if (parser->file_name && parser->Index < parser->filename_length)
+                parser->file_name[parser->Index] = (char)*SessionPtr->Next_In;
             break;
+
+        case ZIP_STATE_OLE_FILE:
+            if (SessionPtr->vba_analysis)
+                SessionPtr->ole_data_ptr = SessionPtr->Next_Out;
+        //fallthrough
         // initialize zlib inflate
         case ZIP_STATE_INFLATE_INIT:
             parser->State = ZIP_STATE_INFLATE;
@@ -316,7 +371,7 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
             if ( Inflate_Init(SessionPtr) == File_Decomp_Error )
                 return File_Decomp_Error;
 
-            // fallthrough
+        // fallthrough
         // perform zlib inflate
         case ZIP_STATE_INFLATE:
         {
@@ -331,6 +386,9 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
                 return File_Decomp_Error;
             }
 
+            if (SessionPtr->ole_data_ptr)
+                SessionPtr->ole_data_len = SessionPtr->Next_Out - SessionPtr->ole_data_ptr;
+
             if ( status == File_Decomp_BlockOut )
             {
                 // ran out of output space
@@ -435,7 +493,7 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
                     parser->Index += SessionPtr->Avail_In;
 
                     uint32_t min = SessionPtr->Avail_In < SessionPtr->Avail_Out ?
-                                   SessionPtr->Avail_In : SessionPtr->Avail_Out;
+                        SessionPtr->Avail_In : SessionPtr->Avail_Out;
 
                     // copy what we can
                     Move_N(SessionPtr, min);
@@ -495,3 +553,4 @@ fd_status_t File_Decomp_ZIP(fd_session_t* SessionPtr)
 
     return File_Decomp_BlockIn;
 }
+
index c79a52cd2e5547e127a662a248d7afe039e72e15..9a950bf6d615e87870b687658194f24ab816d7b3 100644 (file)
@@ -29,6 +29,10 @@ namespace snort
 class BoyerMooreSearchCase;
 }
 
+#define MACRO_BINNAME_LEN 14
+
+static const char* const macro_binname = "vbaProject.bin";
+
 static const uint32_t ZIP_LOCAL_HEADER = 0x04034B50;
 static const uint8_t header_pattern[4] = { 0x50, 0x4B, 0x03, 0x04 };
 static const uint8_t DATA_DESC_BIT = 0x08;
@@ -56,11 +60,13 @@ enum fd_ZIP_states
     ZIP_STATE_FILENAMELEN,    // filename length (2 bytes)
     ZIP_STATE_EXTRALEN,       // extra field length (2 bytes)
 
-    // skipped:
-    // ZIP_STATE_FILENAME,    // filename field (filenamelen bytes)
+    ZIP_STATE_FILENAME,    // filename field (filenamelen bytes)
+
+    //skipped:
     // ZIP_STATE_EXTRA,       // extra field (extralen bytes)
     // ZIP_STATE_STREAM,      // compressed stream (compsize bytes)
 
+    ZIP_STATE_OLE_FILE,
     ZIP_STATE_INFLATE_INIT,   // initialize zlib inflate
     ZIP_STATE_INFLATE,        // perform zlib inflate
     ZIP_STATE_SEARCH,         // search for local header
@@ -83,7 +89,7 @@ struct fd_ZIP_t
     uint32_t compressed_size;
     uint16_t filename_length;
     uint16_t extra_length;
-
+    char* file_name;
     // field index
     uint32_t Index;
 
@@ -109,3 +115,4 @@ fd_status_t File_Decomp_End_ZIP(fd_session_t*);
 fd_status_t File_Decomp_ZIP(fd_session_t*);
 
 #endif
+
diff --git a/src/decompress/file_olefile.cc b/src/decompress/file_olefile.cc
new file mode 100644 (file)
index 0000000..d2ad7a1
--- /dev/null
@@ -0,0 +1,684 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+// file_olefile.cc author Vigneshwari Viswanathan vignvisw@cisco.com
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "file_olefile.h"
+
+DirectoryList :: ~DirectoryList()
+{
+    std::unordered_map<char*, FileProperty*>::iterator it = oleentry.begin();
+
+    while (it != oleentry.end())
+    {
+        FileProperty* node =  it->second;
+        delete[] node->get_name();
+        delete node;
+        it = oleentry.erase(it);
+    }
+}
+
+// The function walk_directory_list() will read the entries of all the directory entry
+// arrays of an ole file and will create a mapping between the storage/stream name and
+// the fileproperty object. Each entry of directory entry array will be of 64 bytes.
+//
+// The first directory entry array value is obtained from the ole header. The subsequent
+// sectors will be obtained by referring the fat list array.
+//
+// Each object of fileproperty will give us the information about the starting sector of
+// that storage/stream, overall size of the stream/storage and other metadata.
+
+// The content of any storage/stream is read by combining all the sectors of that stream/
+// storage and it will begin with starting sector value mentioned in fileproperty object.
+// Also, this starting sector value can be used to obtain the next sector to read by
+// referring the FAT list array.
+void OleFile :: walk_directory_list()
+{
+    int32_t current_sector;
+    uint16_t sector_size;
+    uint8_t* name_buf;
+    int bytes_copied;
+    FileProperty* node;
+    char* file_name;
+
+    current_sector = header->get_first_dir();
+    sector_size = header->get_sector_size();
+
+    dir_list = new DirectoryList();
+
+    while (current_sector > INVALID_SECTOR)
+    {
+        const uint8_t* buf = file_buf;
+        uint32_t start_offset = get_fat_offset(current_sector);
+
+        if ((start_offset + sector_size) > buf_len)
+            return;
+
+        buf += start_offset;
+
+        int32_t count = 0;
+
+        while (count < (sector_size/DIR_ENTRY_SIZE))
+        {
+            node = new FileProperty;
+            name_buf = new uint8_t[32];
+
+            // The filename is UTF16 encoded and will be of the size 64 bytes.
+            dir_list->utf_state = new snort::UtfDecodeSession();
+            if (!header->get_byte_order())
+                dir_list->utf_state->set_decode_utf_state_charset(CHARSET_UTF16LE);
+            else
+                dir_list->utf_state->set_decode_utf_state_charset(CHARSET_UTF16BE);
+            dir_list->utf_state->decode_utf(buf, OLE_MAX_FILENAME_LEN_UTF16, name_buf,
+                OLE_MAX_FILENAME_ASCII, &bytes_copied);
+
+            node->set_name(name_buf);
+
+            node->set_file_type(buf + DIR_FILE_TYPE_OFFSET);
+
+            node->set_color(buf + DIR_COLOR_OFFSET);
+
+            node->set_lef_sib_id(buf + DIR_LEFT_SIB_OFFSET, header->get_byte_order());
+
+            node->set_rig_sib_id(buf + DIR_RIGHT_SIB_OFFSET, header->get_byte_order());
+
+            node->set_root_node_id(buf + DIR_ROOT_NODE_OFFSET, header->get_byte_order());
+
+            node->set_cls_id(buf + DIR_CLS_ID_OFFSET);
+
+            node->set_starting_sector(buf + DIR_STARTING_SEC_OFFSET, header->get_byte_order());
+
+            node->set_stream_size(buf + DIR_STREAM_SIZE_OFFSET, header->get_byte_order());
+
+            buf += DIR_NEXT_ENTR_OFFSET;
+
+            //Insert the oleentry
+            file_name = (char*)name_buf;
+
+            if (strcmp(file_name, ROOT_ENTRY) == 0)
+                dir_list->set_mini_stream_sector(node->get_starting_sector());
+            object_type type = node->get_file_type();
+            // check for all the empty/non valid entries in the directory list.
+            if (!(type == ROOT_STORAGE or type == STORAGE or type == STREAM))
+            {
+                delete node;
+                delete[] name_buf;
+            }
+            else
+                dir_list->oleentry.insert({ file_name, node });
+            count++;
+            delete dir_list->utf_state;
+        }
+        // Reading the next sector of current_sector by referring the FAT list array.
+        // A negative number suggests the end of directory entry array and there are
+        // no more stream/storage to read.
+        int32_t next_sector = get_next_fat_sector(current_sector);
+        if (next_sector > INVALID_SECTOR)
+            current_sector = next_sector;
+        else
+            current_sector = INVALID_SECTOR;
+    }
+}
+
+FileProperty* DirectoryList :: get_file_node(char* name)
+{
+    std::unordered_map<char*, FileProperty*>::iterator it;
+
+    it = oleentry.find(name);
+
+    if (it != oleentry.end())
+        return(it->second);
+    return nullptr;
+}
+
+// Every index of fat_list array is the fat sector ID and the value present
+// at that index will be its corresponding next fat sector ID.
+int32_t OleFile :: get_next_fat_sector(int32_t sec_id)
+{
+    if (fat_list and sec_id > INVALID_SECTOR and sec_id < fat_list_len)
+        return fat_list[sec_id];
+    else
+        return INVALID_SECTOR;
+}
+
+// Every index of mini_fat_list array is the minifat sector ID and the value present
+// at that index will be its corresponding next minifat sector ID.
+int32_t OleFile :: get_next_mini_fat_sector(int32_t sec_id)
+{
+    if (mini_fat_list and sec_id > INVALID_SECTOR and sec_id < mini_fat_list_len)
+        return mini_fat_list[sec_id];
+    else
+        return INVALID_SECTOR;
+}
+
+// The offset of a sector is header_size + (sector_number * size_of_each_sector).
+int32_t OleFile :: get_fat_offset(int32_t sec_id)
+{
+    int32_t byte_offset;
+    byte_offset = OLE_HEADER_LEN + (sec_id * header->get_sector_size());
+    return(byte_offset);
+}
+
+// Example to get the mini fat sector offset.
+// If,
+// sector size = 512 bytes
+// mini sector size = 64 bytes
+// and sector 2 and 5 are storing the mini fat sectors (assuming the mini fat sector is
+// starting with sector 2) , then the offset of 12th mini fat sector is calculated as
+// below:
+//
+// mini fat sector per sector = 512 bytes / 64 bytes = 8.
+// The first 8 mini fat sectors would be stored in the fat sector 2 and therefore the
+// 12th minifat sector would be stored in the next fat sector of sector 2 which is sector
+//  5.( we'll get this info from the fat sector array ( get_next_fat_sector() ) where
+// sector 5 would be mapped to sector 2 as the next fat sector. -2 will be mapped against
+// sector 5, as sector 5 is the last sector storing mini fat sectors )
+//
+// The 12th mini fat sector would be the 4th ( index = 3) mini fat sector stored in fat sector 5.
+//
+// The offset of sector 5  = header_size + (sector_size) * 5 = 512 + 512 * 5 = 3072 bytes.
+//
+// The offset of 12th mini fat sector = (offset of 5th fat sector ) + ( offset of 4th(index is 3)
+//  mini fat sector in 5th fat sector)
+//                                    =  3072 + 64 * 3
+//                                    =  3264 bytes.
+int32_t OleFile :: get_mini_fat_offset(int32_t sec_id)
+{
+    int32_t sec_position, mini_sec_position, count, current_sector;
+    int32_t byte_offset, mini_fat_persector;
+
+    mini_fat_persector = header->get_sector_size() / header->get_mini_sector_size();
+
+    if (sec_id >=  mini_fat_persector)
+    {
+        sec_position = sec_id/mini_fat_persector;
+        mini_sec_position = sec_id % mini_fat_persector;
+    }
+    else
+    {
+        sec_position = 0;
+        mini_sec_position = sec_id;
+    }
+
+    count = 0;
+
+    current_sector = dir_list->get_mini_stream_sector();
+
+    while (count < sec_position)
+    {
+        int32_t next_sector = get_next_fat_sector(current_sector);
+        if (next_sector <= INVALID_SECTOR)
+            return -1;
+        count++;
+        current_sector = next_sector;
+    }
+    byte_offset = OLE_HEADER_LEN + (current_sector * header->get_sector_size()) +
+        (mini_sec_position *
+        header->get_mini_sector_size());
+    return byte_offset;
+}
+
+void OleFile :: get_file_data(char* file, uint8_t*& file_data, int32_t& data_len)
+{
+    FileProperty* node;
+    uint16_t sector_size,mini_sector_size;
+    node = dir_list->get_file_node(file);
+    data_len = 0;
+
+    sector_size = header->get_sector_size();
+    mini_sector_size = header->get_mini_sector_size();
+    if (node)
+    {
+        int32_t starting_sector;
+        int64_t stream_size;
+        sector_type is_fat = FAT_SECTOR;
+        uint32_t byte_offset, bytes_to_copy;
+        uint8_t* temp_data;
+
+        starting_sector = node->get_starting_sector();
+        stream_size = node->get_stream_size();
+
+        file_data = new uint8_t[stream_size];
+        temp_data = file_data;
+        if (stream_size <= header->get_minifat_cutoff())
+            is_fat = MINIFAT_SECTOR;
+
+        if (is_fat == FAT_SECTOR)
+        {
+            int32_t current_sector;
+            current_sector = starting_sector;
+            while (current_sector > INVALID_SECTOR)
+            {
+                byte_offset = get_fat_offset(current_sector);
+                if (byte_offset > buf_len)
+                    return;
+
+                if ((byte_offset + sector_size)> buf_len)
+                {
+                    memcpy(temp_data, (file_buf + byte_offset), (buf_len - byte_offset));
+                    data_len += buf_len - byte_offset;
+                    return;
+                }
+                if ((data_len + sector_size) < stream_size)
+                    bytes_to_copy = sector_size;
+                else
+                    bytes_to_copy = stream_size - data_len;
+
+                memcpy(temp_data, (file_buf + byte_offset), bytes_to_copy);
+                temp_data += sector_size;
+                data_len += bytes_to_copy;
+
+                int32_t next_sector = get_next_fat_sector(current_sector);
+                current_sector = next_sector;
+            }
+        }
+        else
+        {
+            int32_t mini_sector;
+            mini_sector = node->get_starting_sector();
+            while (mini_sector > INVALID_SECTOR)
+            {
+                byte_offset = get_mini_fat_offset(mini_sector);
+                if (byte_offset > buf_len)
+                    return;
+
+                if ((byte_offset + mini_sector_size) > buf_len)
+                {
+                    memcpy(temp_data, (file_buf + byte_offset), (buf_len - byte_offset));
+                    data_len += buf_len - byte_offset;
+                    return;
+                }
+                if ((data_len + mini_sector_size) < stream_size)
+                    bytes_to_copy = mini_sector_size;
+                else
+                    bytes_to_copy = stream_size - data_len;
+
+                memcpy(temp_data, (file_buf + byte_offset), bytes_to_copy);
+                temp_data += mini_sector_size;
+                data_len += bytes_to_copy;
+
+                int32_t next_sector = get_next_mini_fat_sector(mini_sector);
+                mini_sector =  next_sector;
+            }
+        }
+    }
+}
+
+// The function populate_fat_list() reads the contents of FAT array sectors to create
+// the the fat_list array where each of the indices represents the current sector
+// ID and the value at that index would be its next sector ID.
+void OleFile :: populate_fat_list()
+{
+    int32_t current_sector, fat_sector_curr_cnt = 0;
+    int32_t fat_sector = header->get_difat_array(fat_sector_curr_cnt);
+    int32_t max_secchain_cnt = header->get_sector_size()/4;
+    int32_t count = 0;
+
+    fat_list_len = ( header->get_fat_sector_count() * header->get_sector_size() ) / 4;
+    if (fat_list_len < 1)
+        return;
+
+    fat_list = new int32_t[fat_list_len];
+
+    memset(fat_list, -1, fat_list_len);
+
+    current_sector = fat_sector;
+    while (current_sector > INVALID_SECTOR)
+    {
+        uint32_t byte_offset = OLE_HEADER_LEN + (current_sector * header->get_sector_size());
+
+        const uint8_t* buf = file_buf;
+
+        buf += byte_offset;
+
+        if ((byte_offset + header->get_sector_size()) > buf_len)
+            return;
+
+        while ((count - (fat_sector_curr_cnt * max_secchain_cnt)) < (max_secchain_cnt))
+        {
+            if (!header->get_byte_order())
+                fat_list[count] = LETOHL_UNALIGNED(buf);
+            else
+                fat_list[count] = BETOHL_UNALIGNED(buf);
+            count++;
+            buf += 4;
+        }
+        fat_sector_curr_cnt++;
+        if (fat_sector_curr_cnt < MAX_DIFAT_SECTORS)
+            current_sector = header->get_difat_array(fat_sector_curr_cnt);
+        else
+            return;
+    }
+}
+
+// The function populate_mini_fat_list() reads the contents of mini FAT array sectors to
+// create the the mini_fat_list array where each of the indices represents the
+// current mini sector ID and the value at that index would be its next mini
+// sector ID.
+void OleFile :: populate_mini_fat_list()
+{
+    int32_t minifat_sector = header->get_first_minifat(), current_sector;
+
+    int32_t max_secchain_cnt = header->get_sector_size()/4;
+    int32_t count = 0;
+
+    mini_fat_list_len = ( header->get_minifat_count() * header->get_sector_size() )  / 4;
+    if (mini_fat_list_len < 1)
+        return;
+
+    mini_fat_list = new int32_t[mini_fat_list_len];
+
+    memset(mini_fat_list, -1, mini_fat_list_len);
+
+    current_sector = minifat_sector;
+    int32_t minfat_curr_cnt = 0;
+    while (current_sector > INVALID_SECTOR)
+    {
+        uint32_t byte_offset = OLE_HEADER_LEN + (current_sector * header->get_sector_size());
+
+        if ((byte_offset + header->get_sector_size()) > buf_len)
+            return;
+
+        const uint8_t* buf = file_buf;
+
+        buf += byte_offset;
+
+        while ((count - (minfat_curr_cnt * max_secchain_cnt)) < max_secchain_cnt)
+        {
+            if (!header->get_byte_order())
+                mini_fat_list[count] = LETOHL_UNALIGNED(buf);
+            else
+                mini_fat_list[count] = BETOHL_UNALIGNED(buf);
+            count++;
+            buf += 4;
+        }
+        minfat_curr_cnt++;
+        int32_t next_sector = get_next_fat_sector(current_sector);
+        if (next_sector > INVALID_SECTOR)
+            current_sector = next_sector;
+        else
+            current_sector = INVALID_SECTOR;
+    }
+}
+
+// API to parse the OLE File Header.
+// The header is always located at the beginning of the file,
+// and its size is exactly 512 bytes. This implies that the first
+// sector (with SecID 0) always starts at file offset 512.
+bool OleFile :: parse_ole_header()
+{
+    header = new OleHeader;
+    if (!header->set_byte_order(file_buf + HEADER_BYTE_ORDER_OFFSET))
+        return false;
+
+    // Header Signature (8 bytes) is Identification signature of the OLE file,
+    // and must be of the value 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1.
+    if (!header->match_ole_sig(file_buf))
+        return false;
+
+    // Minor Version field should be set to 0x003E.
+    header->set_minor_version(file_buf + HEADER_MINOR_VER_OFFSET);
+
+    // Major Version field is set to either 0x0003 (version 3) or 0x0004 (version 4).
+    header->set_major_version(file_buf + HEADER_MAJOR_VER_OFFSET);
+
+    // This field specifies the sector size of the compound file as a power of 2.
+    header->set_sector_size(file_buf + HEADER_SECTR_SIZE_OFFSET);
+
+    // This field specifies the sector size of the Mini Stream as a power of 2.
+    header->set_mini_sector_size(file_buf + HEADER_MIN_SECTR_SIZE_OFFSET);
+
+    header->set_fat_sector_count(file_buf + HEADER_FAT_SECTR_CNT_OFFSET);
+
+    header->set_first_dir(file_buf + HEADER_FIRST_DIR_SECTR_OFFSET);
+
+    header->set_minifat_cutoff(file_buf + HEADER_MINFAT_CUTOFF_OFFSET);
+
+    header->set_first_minifat(file_buf + HEADER_FIRST_MINFAT_OFFSET);
+
+    header->set_minifat_count(file_buf + HEADER_MINFAT_COUNT_OFFSET);
+
+    header->set_first_difat(file_buf + HEADER_FIRST_DIFAT_OFFSET);
+
+    header->set_difat_count(file_buf + HEADER_DIFAT_CNT_OFFSET);
+
+    header->set_dir_sector_count(file_buf + HEADER_DIR_SECTR_CNT_OFFSET);
+
+    // DIFAT array of 32-bit integer fields contains the first 109 FAT sector locations of the compound file.
+    header->set_difat_array(file_buf + HEADER_DIFAT_ARRY_OFFSET);
+
+    return true;
+}
+
+// The vba code in a VBA macro file begins with the keyword "ATTRIBUT" .This
+// keyword is used to calculate the offset of vba code and is decompressed using
+// RLE algorithm.
+int32_t OleFile :: get_file_offset(const uint8_t* data, int32_t data_len)
+{
+    search_handle = snort::LiteralSearch::setup();
+    searcher = snort::LiteralSearch::instantiate(search_handle,
+        (const uint8_t*)"ATTRIBUT", 8, true);
+    if (searcher == nullptr)
+        return -1;
+
+    int32_t offset = searcher->search(search_handle, data, data_len);
+    delete searcher;
+    snort::LiteralSearch::cleanup(search_handle);
+    return offset;
+}
+
+int32_t cli_readn(const uint8_t*& fd, int32_t& data_len, void* buff, int32_t count)
+{
+    int32_t i;
+
+    for (i = 0; i < count; i++)
+    {
+        if (data_len)
+        {
+            *((uint8_t*)buff + i) = *(fd + i);
+            data_len -= 1;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    fd += i;
+    return i;
+}
+
+// Function for RLE decompression.
+//
+// Run-length encoding (RLE) is a very simple form of data compression
+// in which a stream of data is given as the input (i.e. "AAABBCCCC") and
+// the output is a sequence of counts of consecutive data values in a row
+// (i.e. "3A2B4C"). This type of data compression is lossless, meaning that
+// when decompressed, all of the original data will be recovered when decoded.
+void OleFile :: decompression(const uint8_t* data, int32_t* data_len, uint8_t*& local_vba_buffer,
+    uint32_t* vba_buffer_offset)
+{
+    int16_t header;
+    bool flagCompressed;
+    unsigned char buffer[VBA_COMPRESSION_WINDOW]={ };
+    uint16_t token;
+    unsigned int pos, shift, mask, distance;
+    uint8_t flag;
+    bool clean;
+
+    if (!data)
+        return;
+
+    if (*data!= SIG_COMP_CONTAINER)
+        return;
+
+    header = LETOHS_UNALIGNED(data + 1);
+
+    flagCompressed = header & 0x8000;
+
+    if (((header >> 12) & 0x07) != 0b011)
+    {
+    }
+
+    data += 3;
+    *data_len -= 3;
+
+    if (flagCompressed == 0)
+    {
+        memcpy(&buffer, data, *data_len);
+        return;
+    }
+
+    pos = 0;
+    clean = 1;
+    int32_t size = *data_len;
+    while (cli_readn(data, size, &flag, 1))
+    {
+        for (mask = 1; mask < 0x100; mask <<= 1)
+        {
+            unsigned int winpos = pos % VBA_COMPRESSION_WINDOW;
+            if (flag & mask)
+            {
+                uint16_t len;
+                uint32_t srcpos;
+
+                if (!cli_readn(data, size, &token, 2))
+                    return;
+
+                shift    = 12 - (winpos > 0x10) - (winpos > 0x20) - (winpos > 0x40) - (winpos >
+                    0x80) - (winpos > 0x100) - (winpos > 0x200) - (winpos > 0x400) - (winpos >
+                    0x800);
+                len      = (uint16_t)((token & ((1 << shift) - 1)) + 3);
+                distance = token >> shift;
+
+                srcpos = pos - distance - 1;
+                if ((((srcpos + len) % VBA_COMPRESSION_WINDOW) < winpos)and
+                        ((winpos + len) < VBA_COMPRESSION_WINDOW) and
+                        (((srcpos % VBA_COMPRESSION_WINDOW) + len) < VBA_COMPRESSION_WINDOW) and
+                        (len <= VBA_COMPRESSION_WINDOW))
+                {
+                    srcpos %= VBA_COMPRESSION_WINDOW;
+                    memcpy(&buffer[winpos], &buffer[srcpos],
+                        len);
+                    pos += len;
+                }
+                else
+                    while (len-- > 0)
+                    {
+                        srcpos = (pos - distance - 1) % VBA_COMPRESSION_WINDOW;
+                        buffer[pos++ % VBA_COMPRESSION_WINDOW] = buffer[srcpos];
+                    }
+            }
+            else
+            {
+                if ((pos != 0)and (winpos == 0) and clean)
+                {
+                    if (cli_readn(data, size, &token, 2) != 2)
+                    {
+                        return;
+                    }
+                    clean = 0;
+                    break;
+                }
+                if (cli_readn(data, size,  &buffer[winpos], 1) == 1)
+                    pos++;
+            }
+            clean = 1;
+        }
+    }
+
+    int32_t decomp_len = strlen((char*)buffer);
+
+    if ((*vba_buffer_offset + decomp_len) > MAX_VBA_BUFFER_LEN)
+    {
+        decomp_len =  MAX_VBA_BUFFER_LEN - *vba_buffer_offset;
+    }
+    memcpy((local_vba_buffer + *vba_buffer_offset), buffer, decomp_len);
+    *vba_buffer_offset += decomp_len;
+}
+
+// Function to extract the VBA data and send it for RLE decompression.
+void OleFile :: find_and_extract_vba(uint8_t*& vba_buf, uint32_t& vba_buf_len)
+{
+    std::unordered_map<char*, FileProperty*>::iterator it = dir_list->oleentry.begin();
+    uint32_t vba_buffer_offset = 0;
+    vba_buf = new uint8_t[MAX_VBA_BUFFER_LEN + 1]();
+
+    while (it != dir_list->oleentry.end())
+    {
+        FileProperty* node;
+        uint8_t* data = nullptr;
+        int32_t data_len;
+        node = it->second;
+        ++it;
+        if (node->get_file_type() == STREAM)
+        {
+            get_file_data(node->get_name(), data, data_len);
+            uint8_t* data1 = data;
+            int32_t offset = get_file_offset(data, data_len);
+            if (offset <= 0)
+            {
+                delete[] data1;
+                continue;
+            }
+
+            data += offset - 4;
+            data_len = data_len - offset + 4;
+            decompression(data, &data_len, vba_buf, &vba_buffer_offset);
+            delete[] data1;
+            if ( vba_buffer_offset >= MAX_VBA_BUFFER_LEN)
+                break;
+        }
+    }
+    vba_buf_len = vba_buffer_offset;
+}
+
+// Beginning function of ole file processing.
+//
+// An OLE file contains streams of data that look like files embedded within the
+// OLE file.It can also contain storages, which is a folder that contains streams
+// or other storages.
+//
+// Ole file processing begins with OLE header matching, followed by populating
+// the FAT array-list which contains the mapping between current fat sector and
+// its next fat sector.Followed by populating the mini-FAT array-list which
+// contains the mapping between current mini-fat sector and its next mini-fat
+// sector. Followed by reading the entries of all the directory entry arrays of
+// an ole file and creating a mapping between the storage/stream name and the
+// fileproperty object.Afterwards, based on the directory the data is fetched and
+// extracted & RLE decompression is done.
+void oleprocess(const uint8_t* const ole_file, const uint32_t ole_length, uint8_t*& vba_buf, uint32_t& vba_buf_len)
+{
+    if (ole_length < OLE_HEADER_LEN)
+        return;
+
+    std::unique_ptr<OleFile> olefile (new OleFile(ole_file,ole_length));
+
+    if (!olefile->parse_ole_header())
+        return;
+
+    olefile->populate_fat_list();
+    olefile->populate_mini_fat_list();
+    olefile->walk_directory_list();
+    olefile->find_and_extract_vba(vba_buf, vba_buf_len);
+}
+
diff --git a/src/decompress/file_olefile.h b/src/decompress/file_olefile.h
new file mode 100644 (file)
index 0000000..05179ee
--- /dev/null
@@ -0,0 +1,281 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+// file_olefile.h author Vigneshwari Viswanathan vignvisw@cisco.com
+
+#ifndef FILE_OLE_H
+#define FILE_OLE_H
+
+#include "file_oleheader.h"
+
+#include <memory>
+#include <unordered_map>
+
+#include "helpers/literal_search.h"
+#include "utils/util.h"
+#include "utils/util_utf.h"
+
+#define OLE_MAX_FILENAME_LEN_UTF16  64
+#define OLE_MAX_FILENAME_ASCII      32
+
+#define OLE_HEADER_LEN             512
+#define DIR_ENTRY_SIZE             128
+#define ROOT_ENTRY             "Root Entry"
+
+#define SIG_COMP_CONTAINER        0x01
+#define VBA_COMPRESSION_WINDOW    4096
+#define MAX_VBA_BUFFER_LEN       16384
+
+#define INVALID_SECTOR              -1
+
+#define DIR_FILE_TYPE_OFFSET        66
+#define DIR_COLOR_OFFSET            67
+#define DIR_LEFT_SIB_OFFSET         68
+#define DIR_RIGHT_SIB_OFFSET        72
+#define DIR_ROOT_NODE_OFFSET        76
+#define DIR_CLS_ID_OFFSET           80
+#define DIR_STARTING_SEC_OFFSET    116
+#define DIR_STREAM_SIZE_OFFSET     120
+#define DIR_NEXT_ENTR_OFFSET       128
+
+#define memcpy_id(destn, dsize, src, ssize) \
+    ((dsize>=ssize) ? memcpy(destn, src, ssize) : memcpy( \
+    destn, src, dsize))
+
+enum object_type
+{
+    EMPTY = 0x00,
+    STORAGE = 0x01,
+    STREAM = 0x02,
+    ROOT_STORAGE = 0x05
+};
+
+enum color_flag
+{
+    RED = 0x00,
+    BLACK = 0x01
+};
+
+enum sector_type
+{
+    FAT_SECTOR = 0,
+    MINIFAT_SECTOR = 1
+};
+
+int32_t cli_readn(const uint8_t*& fd, int32_t& data_len, void* buff, int32_t count);
+
+struct FileProperty
+{
+public:
+    void set_name(uint8_t* f_name)
+    {
+        name = (char*)f_name;
+    }
+
+    char* get_name()
+    {
+        return name;
+    }
+
+    void set_file_type(const uint8_t* buf)
+    {
+        file_type = (object_type)*buf;
+    }
+
+    object_type get_file_type()
+    {
+        return file_type;
+    }
+
+    void set_color(const uint8_t* buf)
+    {
+        color = (color_flag)*buf;
+    }
+
+    color_flag get_color()
+    {
+        return color;
+    }
+
+    void set_lef_sib_id(const uint8_t* buf, byte_order_endianess endian)
+    {
+        lef_sib_id = (!endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+    }
+
+    int32_t get_lef_sib_id()
+    {
+        return lef_sib_id;
+    }
+
+    void set_rig_sib_id(const uint8_t* buf, byte_order_endianess endian)
+    {
+        rig_sib_id = (!endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+    }
+
+    int32_t get_rig_sib_id()
+    {
+        return rig_sib_id;
+    }
+
+    void set_root_node_id(const uint8_t* buf, byte_order_endianess endian)
+    {
+        root_node_id = (!endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+    }
+
+    int32_t get_root_node_id()
+    {
+        return root_node_id;
+    }
+
+    void set_cls_id(const uint8_t* buf)
+    {
+        memcpy_id(cls_id, sizeof(cls_id), buf, 16);
+    }
+
+    char* get_cls_id()
+    {
+        return cls_id;
+    }
+
+    void set_starting_sector(const uint8_t* buf, byte_order_endianess endian)
+    {
+        starting_sector = (!endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+    }
+
+    int32_t get_starting_sector()
+    {
+        return starting_sector;
+    }
+
+    void set_stream_size(const uint8_t* buf, byte_order_endianess endian)
+    {
+        stream_size = (!endian) ? LETOHLL_UNALIGNED(buf) : BETOHLL_UNALIGNED(buf);
+    }
+
+    int64_t get_stream_size()
+    {
+        return stream_size;
+    }
+
+    FileProperty()
+    {
+        name = nullptr;
+    }
+
+private:
+    char* name;
+    object_type file_type;
+    color_flag color;
+    int32_t lef_sib_id;
+    int32_t rig_sib_id;
+    int32_t root_node_id;
+    char cls_id[16];
+    int32_t starting_sector;
+    int64_t stream_size;
+};
+
+class DirectoryList
+{
+public:
+    std::unordered_map<char*, FileProperty*> oleentry;
+    snort::UtfDecodeSession* utf_state;
+
+    bool is_file_exists(char* name);
+    FileProperty* get_file_node(char* name);
+    int32_t get_file_sector(char* name);
+    bool is_mini_sector(char* name);
+    void set_mini_stream_sector(int32_t mini_stream_sector)
+    {
+        this->mini_stream_sector = mini_stream_sector;
+    }
+
+    int32_t get_mini_stream_sector()
+    {
+        return mini_stream_sector;
+    }
+
+    DirectoryList()
+    {
+        utf_state = nullptr;
+        mini_stream_sector = -1;
+    }
+
+    ~DirectoryList();
+
+private:
+    int32_t mini_stream_sector;
+};
+
+class OleFile
+{
+public:
+    bool parse_ole_header();
+    void populate_fat_list();
+    void populate_mini_fat_list();
+    void walk_directory_list();
+    void find_and_extract_vba(uint8_t*&, uint32_t&);
+    int32_t get_next_fat_sector(int32_t sec_id);
+    int32_t get_next_mini_fat_sector(int32_t sec_id);
+    int32_t get_fat_offset(int32_t sec_id);
+    int32_t get_mini_fat_offset(int32_t sec_id);
+    int32_t get_file_offset(const uint8_t*, int32_t data_len);
+    void get_file_data(char*, uint8_t*&, int32_t&);
+
+    void decompression(const uint8_t* data, int32_t* data_len, uint8_t*& buffer,
+        uint32_t* buffer_ofset);
+
+    int search_nocase(const uint8_t* buffer, unsigned buffer_len) const
+    {
+        return searcher->search(search_handle, buffer, buffer_len);
+    }
+
+    OleFile(const uint8_t* file_buf, const uint32_t buf_len)
+    {
+        //header = new OleHeader;
+        this->file_buf = file_buf;
+        this->buf_len = buf_len;
+        //dir_list = new DirectoryList();
+    }
+
+    ~OleFile()
+    {
+        delete header;
+        delete dir_list;
+        delete[] fat_list;
+        delete[] mini_fat_list;
+    }
+
+    snort::LiteralSearch* searcher = nullptr;
+    snort::LiteralSearch::Handle* search_handle = nullptr;
+
+private:
+    const uint8_t* file_buf;
+    uint32_t buf_len;
+
+    OleHeader* header = nullptr;
+    DirectoryList* dir_list = nullptr;
+
+    int32_t* fat_list = nullptr;
+    int32_t fat_list_len = 0;
+    int32_t* mini_fat_list = nullptr;
+    int32_t mini_fat_list_len = 0;
+};
+
+void oleprocess(const uint8_t* const, const uint32_t, uint8_t*&, uint32_t&);
+#endif
+
diff --git a/src/decompress/file_oleheader.cc b/src/decompress/file_oleheader.cc
new file mode 100644 (file)
index 0000000..fb72c0f
--- /dev/null
@@ -0,0 +1,189 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+// file_oleheader.cc author Amarnath Nayak amarnaya@cisco.com
+
+#include "file_oleheader.h"
+
+unsigned char hdr_sig[8] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+
+bool OleHeader::set_byte_order(const uint8_t* buf)
+{
+    byte_order = buf[0]<< 8 | buf[1];
+    if (byte_order == 0XFEFF)
+        byte_order_endian = LITL_END;
+
+    else if (byte_order == 0XFFFE)
+        byte_order_endian = BIG_END;
+
+    else
+        return false;
+
+    return true;
+}
+
+byte_order_endianess OleHeader::get_byte_order()
+{
+    return byte_order_endian;
+}
+
+bool OleHeader::match_ole_sig(const uint8_t* buf)
+{
+    memcpy(sig ,(const char*)buf, 8);
+
+    if (memcmp(sig, hdr_sig, 8) == 0)
+        return true;
+    else
+        return false;
+}
+
+void OleHeader::set_minor_version(const uint8_t* buf)
+{
+    minor_version = (!byte_order_endian) ? LETOHS_UNALIGNED(buf) : BETOHS_UNALIGNED(buf);
+}
+
+uint16_t OleHeader::get_minor_version()
+{
+    return minor_version;
+}
+
+void OleHeader::set_major_version(const uint8_t* buf)
+{
+    major_version = (!byte_order_endian) ? LETOHS_UNALIGNED(buf) : BETOHS_UNALIGNED(buf);
+}
+
+uint16_t OleHeader::get_major_version()
+{
+    return major_version;
+}
+
+void OleHeader::set_sector_size(const uint8_t* buf)
+{
+    sector_size = (!byte_order_endian) ? exp2(LETOHS_UNALIGNED(buf)) : exp2(BETOHS_UNALIGNED(buf));
+}
+
+uint16_t OleHeader::get_sector_size()
+{
+    return sector_size;
+}
+
+void OleHeader::set_mini_sector_size(const uint8_t* buf)
+{
+    mini_sector_size = (!byte_order_endian) ? exp2(LETOHS_UNALIGNED(buf)) : exp2(BETOHS_UNALIGNED(
+        buf));
+}
+
+uint16_t OleHeader::get_mini_sector_size()
+{
+    return mini_sector_size;
+}
+
+void OleHeader::set_dir_sector_count(const uint8_t* buf)
+{
+    dir_sector_count = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_dir_sector_count()
+{
+    return dir_sector_count;
+}
+
+void OleHeader::set_first_dir(const uint8_t* buf)
+{
+    first_dir = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_first_dir()
+{
+    return first_dir;
+}
+
+void OleHeader::set_difat_count(const uint8_t* buf)
+{
+    difat_count = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_difat_count()
+{
+    return difat_count;
+}
+
+void OleHeader::set_fat_sector_count(const uint8_t* buf)
+{
+    fat_sector_count = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_fat_sector_count()
+{
+    return fat_sector_count;
+}
+
+void OleHeader::set_minifat_cutoff(const uint8_t* buf)
+{
+    minifat_cutoff = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_minifat_cutoff()
+{
+    return minifat_cutoff;
+}
+
+void OleHeader::set_first_minifat(const uint8_t* buf)
+{
+    first_minifat = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_first_minifat()
+{
+    return first_minifat;
+}
+
+void OleHeader::set_minifat_count(const uint8_t* buf)
+{
+    minifat_count = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_minifat_count()
+{
+    return minifat_count;
+}
+
+void OleHeader::set_first_difat(const uint8_t* buf)
+{
+    first_difat = (!byte_order_endian) ? LETOHL_UNALIGNED(buf) : BETOHL_UNALIGNED(buf);
+}
+
+int32_t OleHeader::get_first_difat()
+{
+    return first_difat;
+}
+
+void OleHeader::set_difat_array(const uint8_t* buf)
+{
+    for (int i = 0; i < MAX_DIFAT_SECTORS; i++)
+    {
+        difat_array[i] = (!byte_order_endian) ? LETOHL_UNALIGNED(buf + (i * 4)) :
+            BETOHL_UNALIGNED(buf + (i * 4));
+    }
+}
+
+int32_t OleHeader::get_difat_array(int num)
+{
+    return difat_array[num];
+}
+
diff --git a/src/decompress/file_oleheader.h b/src/decompress/file_oleheader.h
new file mode 100644 (file)
index 0000000..e32a2c1
--- /dev/null
@@ -0,0 +1,112 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+// file_oleheader.h author Vigneshwari Viswanathan vignvisw@cisco.com
+
+#ifndef FILE_OLE_HEADER_H
+#define FILE_OLE_HEADER_H
+
+#include <cmath>
+#include <cstring>
+
+#include "main/snort_types.h"
+#include "utils/endian.h"
+
+#define MAX_DIFAT_SECTORS                 109
+
+#define HEADER_MINOR_VER_OFFSET            24
+#define HEADER_MAJOR_VER_OFFSET            26
+#define HEADER_BYTE_ORDER_OFFSET           28
+#define HEADER_SECTR_SIZE_OFFSET           30
+#define HEADER_MIN_SECTR_SIZE_OFFSET       32
+#define HEADER_DIR_SECTR_CNT_OFFSET        40
+#define HEADER_FAT_SECTR_CNT_OFFSET        44
+#define HEADER_FIRST_DIR_SECTR_OFFSET      48
+#define HEADER_MINFAT_CUTOFF_OFFSET        56
+#define HEADER_FIRST_MINFAT_OFFSET         60
+#define HEADER_MINFAT_COUNT_OFFSET         64
+#define HEADER_FIRST_DIFAT_OFFSET          68
+#define HEADER_DIFAT_CNT_OFFSET            72
+#define HEADER_DIFAT_ARRY_OFFSET           76
+
+enum byte_order_endianess
+{
+    LITL_END = 0,
+    BIG_END = 1
+};
+
+class OleHeader
+{
+public:
+    bool set_byte_order(const uint8_t* buf);
+    byte_order_endianess get_byte_order();
+    bool match_ole_sig(const uint8_t* buf);
+    void set_minor_version(const uint8_t* buf);
+    uint16_t get_minor_version();
+    void set_major_version(const uint8_t* buf);
+    uint16_t get_major_version();
+    void set_sector_size(const uint8_t* buf);
+    uint16_t get_sector_size();
+    void set_mini_sector_size(const uint8_t* buf);
+    uint16_t get_mini_sector_size();
+    void set_dir_sector_count(const uint8_t* buf);
+    int32_t get_dir_sector_count();
+    void set_first_dir(const uint8_t* buf);
+    int32_t get_first_dir();
+    void set_difat_count(const uint8_t* buf);
+    int32_t get_difat_count();
+    void set_fat_sector_count(const uint8_t* buf);
+    int32_t get_fat_sector_count();
+    void set_minifat_cutoff(const uint8_t* buf);
+    int32_t get_minifat_cutoff();
+    void set_first_minifat(const uint8_t* buf);
+    int32_t get_first_minifat();
+    void set_minifat_count(const uint8_t* buf);
+    int32_t get_minifat_count();
+    void set_first_difat(const uint8_t* buf);
+    int32_t get_first_difat();
+    void set_difat_array(const uint8_t* buf);
+    int32_t get_difat_array(int num);
+    OleHeader()
+    {
+        first_dir = -1;
+        fat_sector_count = 0;
+        first_minifat = 0;
+    }
+
+private:
+    unsigned char sig[8]; //0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1
+    uint16_t minor_version;
+    uint16_t major_version;
+    uint16_t byte_order;
+    uint16_t sector_size;
+    uint16_t mini_sector_size;
+    int32_t dir_sector_count;
+    int32_t first_dir;
+    int32_t difat_count;
+    int32_t fat_sector_count;
+    int32_t minifat_cutoff;
+    int32_t first_minifat;
+    int32_t minifat_count;
+    int32_t first_difat;
+    int32_t difat_array[MAX_DIFAT_SECTORS];
+
+    byte_order_endianess byte_order_endian;
+};
+#endif
+
diff --git a/src/decompress/test/CMakeLists.txt b/src/decompress/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a19d798
--- /dev/null
@@ -0,0 +1,9 @@
+add_cpputest( file_olefile_test
+    SOURCES ../file_olefile.cc
+            ../file_oleheader.cc
+)
+
+add_cpputest(file_oleheader_test
+    SOURCES ../file_oleheader.cc
+)
+
diff --git a/src/decompress/test/file_olefile_test.cc b/src/decompress/test/file_olefile_test.cc
new file mode 100644 (file)
index 0000000..090d528
--- /dev/null
@@ -0,0 +1,231 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+//file_olefile_test.cc author Amarnath Nayak <amarnaya@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../file_olefile.h"
+#include "../file_oleheader.h"
+
+#include "detection/detection_engine.h"
+#include "helpers/literal_search.h"
+#include "utils/util_utf.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+namespace snort
+{
+LiteralSearch::Handle* LiteralSearch::setup() { return nullptr; }
+void LiteralSearch::cleanup(LiteralSearch::Handle*) { }
+LiteralSearch* LiteralSearch::instantiate(LiteralSearch::Handle*, const uint8_t*, unsigned, bool,
+    bool) { return nullptr; }
+void UtfDecodeSession::set_decode_utf_state_charset(CharsetCode) { }
+bool UtfDecodeSession::decode_utf(unsigned char const*, unsigned int, unsigned char*, unsigned int,
+    int*) { return true; }
+UtfDecodeSession::UtfDecodeSession() { }
+}
+
+TEST_GROUP(Olefile_oleprocess_test)
+{
+};
+
+TEST(Olefile_oleprocess_test, wrong_ole_sig)
+{
+    uint8_t ole_file[1000] = { 0 };
+    ole_file[28] = 0XFE;
+    ole_file[29] = 0XFF;
+    uint32_t ole_length = 1000;
+    uint8_t* vba_buf = nullptr;
+    uint32_t vba_buf_len = 0;
+    oleprocess(ole_file, ole_length, vba_buf, vba_buf_len);
+}
+
+TEST(Olefile_oleprocess_test, wrong_byte_order)
+{
+    uint8_t ole_file[1000] = { 0 };
+    const uint32_t ole_length = 1000;
+    uint8_t* vba_buf = nullptr;
+    uint32_t vba_buf_len = 0;
+    oleprocess(ole_file, ole_length, vba_buf, vba_buf_len);
+}
+
+TEST(Olefile_oleprocess_test, short_header_len)
+{
+    uint8_t ole_file[100] = { 0 };
+    uint32_t ole_length = 100;
+    uint8_t* vba_buf = nullptr;
+    uint32_t vba_buf_len = 0;
+    oleprocess(ole_file, ole_length, vba_buf, vba_buf_len);
+}
+
+TEST_GROUP(Olefile_ole)
+{
+};
+
+TEST(Olefile_ole, get_file_offset)
+{
+    OleFile* olefile = new OleFile(nullptr, 0);
+    int32_t res = olefile->get_file_offset(nullptr,0);
+    CHECK(res == -1);
+    delete olefile;
+}
+
+TEST(Olefile_ole, decompression_empty_data)
+{
+    uint8_t* ole_data = nullptr;
+    uint8_t* buf = nullptr;
+    int32_t len = 0;
+    OleFile* olefile = new OleFile(nullptr, 0);
+    olefile->decompression(ole_data, &len, buf, nullptr);
+    CHECK(buf == nullptr);
+    delete olefile;
+}
+
+TEST(Olefile_ole, decompression_invalid_data_1)
+{
+    uint8_t ole_data[10] = { 0 };
+    uint8_t* buf = nullptr;
+    int32_t len = 10;
+    OleFile* olefile = new OleFile(nullptr, 0);
+    olefile->decompression(ole_data,&len, buf, nullptr);
+    CHECK(buf == nullptr);
+    delete olefile;
+}
+
+TEST(Olefile_ole, decompression_invalid_chunk_header)
+{
+    uint8_t ole_data[10] ={ 0 };
+    uint8_t* buf = nullptr;
+    int32_t len = 10;
+    ole_data[0] = 0x01;
+    OleFile* olefile = new OleFile(nullptr, 0);
+    olefile->decompression(ole_data,&len, buf, nullptr);
+    CHECK(buf == nullptr);
+    delete olefile;
+}
+
+TEST(Olefile_ole, decompression_flag_0)
+{
+    uint8_t ole_data[10] ={ 0 };
+    uint8_t* buf = nullptr;
+    int32_t len = 10;
+    ole_data[0] = 0x01;
+    OleFile* olefile = new OleFile(nullptr, 0);
+    olefile->decompression(ole_data,&len, buf, nullptr);
+    CHECK(buf == nullptr);
+    delete olefile;
+}
+
+TEST(Olefile_ole, get_next_fat_sector_failure)
+{
+    OleFile* olefile = new OleFile(nullptr, 0);
+    int32_t res = olefile->get_next_fat_sector(20);
+    CHECK(res == -1);
+    delete olefile;
+}
+
+TEST(Olefile_ole, get_next_mini_fat_sector_failure)
+{
+    OleFile* olefile = new OleFile(nullptr, 0);
+    int32_t res = olefile->get_next_mini_fat_sector(20);
+    CHECK(res == -1);
+    delete olefile;
+}
+
+TEST(Olefile_ole, get_file_node_failure)
+{
+    char test[] = {'a','b',0};
+    DirectoryList* dir_list = new DirectoryList();
+    FileProperty* res = dir_list->get_file_node(test);
+    delete dir_list;
+    CHECK(res == nullptr);
+}
+
+TEST_GROUP(fat_mini_fat_list)
+{
+};
+
+TEST(fat_mini_fat_list, fat_list_short_buf_len)
+{
+    uint8_t ole_buf[520] = { 0 };
+    uint8_t ole_header_sig[8] =  { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+    memcpy(ole_buf, ole_header_sig, 8);
+    ole_buf[28] = 0xFF;
+    ole_buf[29] = 0xFE;
+    ole_buf[30] = 0x00;
+    ole_buf[31] = 0x09;
+    ole_buf[519] = 0xFE;
+    OleFile* olefile = new OleFile(ole_buf, 520);
+    olefile->parse_ole_header();
+    olefile->populate_fat_list();
+    CHECK(olefile->get_next_fat_sector(1) == -1);
+    delete olefile;
+}
+
+TEST(fat_mini_fat_list, fat_list_read_r)
+{
+    uint8_t ole_buf[1025] = { 0 };
+    uint8_t ole_header_sig[8] =  { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+    memcpy(ole_buf, ole_header_sig, 8);
+    ole_buf[28] = 0xFF;
+    ole_buf[29] = 0xFE;
+    ole_buf[30] = 0x00;
+    ole_buf[31] = 0x09;
+    ole_buf[47] = 0x01;
+    memset(ole_buf + 80, 0xFF, 435);
+    ole_buf[519] = 0x02;
+    OleFile* olefile = new OleFile(ole_buf, 1025);
+    olefile->parse_ole_header();
+    olefile->populate_fat_list();
+    CHECK(olefile->get_next_fat_sector(1) == 2);
+    delete olefile;
+}
+
+TEST(fat_mini_fat_list, mini_fat_list_short_buf_len)
+{
+    uint8_t ole_buf[1550] = { 0 };
+    uint8_t ole_header_sig[8] =  { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+    memcpy(ole_buf, ole_header_sig, 8);
+    ole_buf[28] = 0xFF;
+    ole_buf[29] = 0xFE;
+    ole_buf[30] = 0x00;
+    ole_buf[31] = 0x09;
+    ole_buf[32] = 0x00;
+    ole_buf[33] = 0x06;
+    ole_buf[61] = 0x03;
+    ole_buf[519] = 0xFE;
+    ole_buf[527] = 0xFE;
+    ole_buf[1539] = 0x01;
+    OleFile* olefile = new OleFile(ole_buf, 1550);
+    olefile->parse_ole_header();
+    olefile->populate_fat_list();
+    olefile->populate_mini_fat_list();
+    CHECK(olefile->get_next_mini_fat_sector(1) == -1);
+    delete olefile;
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
diff --git a/src/decompress/test/file_oleheader_test.cc b/src/decompress/test/file_oleheader_test.cc
new file mode 100644 (file)
index 0000000..88c5f9e
--- /dev/null
@@ -0,0 +1,114 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2021 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.
+//--------------------------------------------------------------------------
+
+// file_oleheader_test.cc author Amarnath Nayak <amarnaya@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "../file_oleheader.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+TEST_GROUP(OleHeader_big_endian_test)
+{
+    OleHeader header;
+    void setup() override
+    {
+        const uint8_t buf[2] =  { 0xFF, 0xFE };
+        header.set_byte_order(buf);
+    }
+};
+
+TEST(OleHeader_big_endian_test, set_byte_order)
+{
+    byte_order_endianess byte_order = header.get_byte_order();
+    CHECK(byte_order == BIG_END);
+}
+
+TEST(OleHeader_big_endian_test, set_minor_version)
+{
+    const uint8_t buf[2] =  { 0x00, 0x3B };
+    header.set_minor_version(buf);
+    uint16_t minor_version = header.get_minor_version();
+    CHECK(minor_version == 0X003B);
+}
+
+TEST(OleHeader_big_endian_test, set_major_version)
+{
+    const uint8_t buf[2] =  { 0x00, 0x03 };
+    header.set_major_version(buf);
+    uint16_t major_version = header.get_major_version();
+    CHECK(major_version == 0x0003);
+}
+
+TEST(OleHeader_big_endian_test, set_dir_sector_count)
+{
+    const uint8_t buf[4] = { 0x00, 0x00, 0x00, 0x01 };
+    header.set_dir_sector_count(buf);
+    int32_t dir_sec_count = header.get_dir_sector_count();
+    CHECK(dir_sec_count == 0x00000001);
+}
+
+TEST(OleHeader_big_endian_test, set_difat_count)
+{
+    const uint8_t buf[4] = { 0x00, 0x00, 0x00, 0x01 };
+    header.set_difat_count(buf);
+    int32_t difat_count = header.get_difat_count();
+    CHECK(difat_count == 0x00000001);
+}
+
+TEST(OleHeader_big_endian_test, set_fat_sector_count)
+{
+    const uint8_t buf[4] = { 0x00, 0x00, 0x00, 0x01 };
+    header.set_fat_sector_count(buf);
+    int32_t fat_sector_count = header.get_fat_sector_count();
+    CHECK(fat_sector_count == 0x00000001);
+}
+
+TEST(OleHeader_big_endian_test, set_minifat_count)
+{
+    const uint8_t buf[4] = { 0x00, 0x00, 0x00, 0x01 };
+    header.set_minifat_count(buf);
+    int32_t minfat_sector_count = header.get_minifat_count();
+    CHECK(minfat_sector_count == 0x00000001);
+}
+
+TEST(OleHeader_big_endian_test, set_first_difat)
+{
+    const uint8_t buf[4] = { 0x00, 0x00, 0x02, 0x00 };
+    header.set_first_difat(buf);
+    int32_t first_difat = header.get_first_difat();
+    CHECK(first_difat == 0x00000200);
+}
+
+TEST(OleHeader_big_endian_test, BETOHLL_UNALIGNED)
+{
+    const uint8_t buf[8] = { 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04 };
+    int64_t value  = BETOHLL_UNALIGNED(buf);
+    CHECK(value == 0x0102030401020304);
+}
+
+int main(int argc, char** argv)
+{
+    return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
index 5b96e3a05b4682abebb1dfa1021ee630b98ebc6b..82ad63da58401732e24b1b8d5ff46e6f5dd10ca9 100644 (file)
@@ -957,6 +957,8 @@ static int fp_search(PortGroup* port_group, Packet* p, bool srvc)
 
         search_buffer(
             gadget, buf, buf.IBT_COOKIE, p, port_group, PM_TYPE_COOKIE, pc.cookie_searches);
+
+        search_buffer(gadget, buf, buf.IBT_VBA, p, port_group, PM_TYPE_VBA, pc.vba_searches);
     }
 
     if ( MpseGroup* so = port_group->mpsegrp[PM_TYPE_SCRIPT] )
index 90989638a9abdf1b1920322aa3b126e3838ca8b5..e0ec958ef83436c40b287567d2fcf2c308c77e3e 100644 (file)
@@ -107,6 +107,9 @@ PmType get_pm_type(CursorActionType cat)
     case CAT_SET_KEY:
         return PM_TYPE_KEY;
 
+    case CAT_SET_VBA:
+        return PM_TYPE_VBA;
+
     default:
         break;
     }
@@ -152,6 +155,9 @@ static const char* get_service(const char* opt)
     if ( !strncmp(opt, "sip_", 4) )
         return "sip";
 
+    if ( !strncmp(opt, "vba_data", 8) )
+        return "file";
+
     return nullptr;
 }
 
index 86091ab462bb367cf0c78d3dcc67d0ee370eb8d3..8e4171b0ddcb140a929d40d07166dd0c4e604b90 100644 (file)
@@ -47,7 +47,7 @@ struct InspectionBuffer
         // FIXIT-L file data is tbd
         IBT_KEY, IBT_HEADER, IBT_BODY, IBT_FILE, IBT_ALT,
         IBT_RAW_KEY, IBT_RAW_HEADER, IBT_METHOD, IBT_STAT_CODE,
-        IBT_STAT_MSG, IBT_COOKIE, IBT_MAX
+        IBT_STAT_MSG, IBT_COOKIE, IBT_VBA, IBT_MAX
     };
     const uint8_t* data;
     unsigned len;
index 9ffb003cc75083fa07408e0155f34ce99411562d..978f442e67051f961d7ce65bb9f38f1938e75d64 100644 (file)
@@ -63,6 +63,7 @@ enum CursorActionType
     CAT_SET_BODY,
     CAT_SET_HEADER,
     CAT_SET_KEY,
+    CAT_SET_VBA,
 };
 
 enum RuleDirection
index 7fd6c5d8ed28a8ba0ffff0769ef81ba427506cc8..fef2c6b9b1c8a774203a2d91034e7d556ee2923c 100644 (file)
@@ -51,13 +51,14 @@ enum PmType
     PM_TYPE_STAT_CODE,
     PM_TYPE_STAT_MSG,
     PM_TYPE_COOKIE,
+    PM_TYPE_VBA,
     PM_TYPE_MAX
 };
 
 const char* const pm_type_strings[PM_TYPE_MAX] =
 {
     "packet", "alt", "key", "header", "body", "file", "raw_key", "raw_header",
-    "method", "script", "stat_code", "stat_msg", "cookie"
+    "method", "script", "stat_code", "stat_msg", "cookie" , "vba"
 };
 
 struct RULE_NODE
index 9cae42e56b26cdd18ba9c7c4cf6bce29e3921ba7..8e6f5f03ee2dcdbd85d152fa000c50d655cba15f 100644 (file)
@@ -67,6 +67,7 @@ const char* HttpApi::classic_buffer_names[] =
     "http_true_ip",
     "http_uri",
     "http_version",
+    "vba_data",
     nullptr
 };
 
@@ -116,6 +117,7 @@ extern const BaseApi* ips_http_trailer;
 extern const BaseApi* ips_http_true_ip;
 extern const BaseApi* ips_http_uri;
 extern const BaseApi* ips_http_version;
+extern const BaseApi* ips_vba_data;
 
 #ifdef BUILDING_SO
 SO_PUBLIC const BaseApi* snort_plugins[] =
@@ -142,6 +144,7 @@ const BaseApi* sin_http[] =
     ips_http_true_ip,
     ips_http_uri,
     ips_http_version,
+    ips_vba_data,
     nullptr
 };
 
index 043cc23e25ce7544864cb5a4e75706edf773eafa..811c91403834d25ba19cdb49b32664e9385ca5d7 100755 (executable)
@@ -60,7 +60,7 @@ enum HTTP_BUFFER { HTTP_BUFFER_CLIENT_BODY = 1, HTTP_BUFFER_COOKIE, HTTP_BUFFER_
     HTTP_BUFFER_RAW_HEADER, HTTP_BUFFER_RAW_REQUEST, HTTP_BUFFER_RAW_STATUS,
     HTTP_BUFFER_RAW_TRAILER, HTTP_BUFFER_RAW_URI, HTTP_BUFFER_STAT_CODE, HTTP_BUFFER_STAT_MSG,
     HTTP_BUFFER_TRAILER, HTTP_BUFFER_TRUE_IP, HTTP_BUFFER_URI, HTTP_BUFFER_VERSION,
-    HTTP_BUFFER_MAX };
+    BUFFER_VBA_DATA, HTTP_BUFFER_MAX };
 
 // Peg counts
 // This enum must remain synchronized with HttpModule::peg_names[] in http_tables.cc
index 3060bab20eb7c82c3160d042b181c2beeac8840f..ddb9098fcf73329c7294aa92bd1204ea444d053b 100755 (executable)
@@ -154,6 +154,7 @@ void HttpInspect::show(const SnortConfig*) const
     ConfigLogger::log_flag("decompress_pdf", params->decompress_pdf);
     ConfigLogger::log_flag("decompress_swf", params->decompress_swf);
     ConfigLogger::log_flag("decompress_zip", params->decompress_zip);
+    ConfigLogger::log_flag("decompress_vba", params->decompress_vba);
     ConfigLogger::log_flag("script_detection", params->script_detection);
     ConfigLogger::log_flag("normalize_javascript", params->js_norm_param.normalize_javascript);
     ConfigLogger::log_value("max_javascript_whitespaces",
@@ -242,6 +243,9 @@ bool HttpInspect::get_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBuffe
     case InspectionBuffer::IBT_COOKIE:
         return get_buf(HTTP_BUFFER_COOKIE, p , b);
 
+    case InspectionBuffer::IBT_VBA:
+        return get_buf(BUFFER_VBA_DATA, p, b);
+
     default:
         return false;
     }
@@ -296,6 +300,7 @@ bool HttpInspect::get_fp_buf(InspectionBuffer::Type ibt, Packet* p, InspectionBu
             return false;
         break;
     case InspectionBuffer::IBT_BODY:
+    case InspectionBuffer::IBT_VBA:
         if ((get_latest_is(p) != IS_FIRST_BODY) && (get_latest_is(p) != IS_BODY))
             return false;
         break;
index e924619506db3939840303b9ce0991b2f82d9cdd..af0729769488170f5003443faff6dd039b4d53d3 100755 (executable)
@@ -74,6 +74,9 @@ const Parameter HttpModule::http_params[] =
     { "decompress_zip", Parameter::PT_BOOL, nullptr, "false",
       "decompress zip files in response bodies" },
 
+    { "decompress_vba", Parameter::PT_BOOL, nullptr, "false",
+      "decompress MS Office Visual Basic for Applications macro files in response bodies" },
+
     { "script_detection", Parameter::PT_BOOL, nullptr, "false",
       "inspect JavaScript immediately upon script end" },
 
@@ -238,6 +241,10 @@ bool HttpModule::set(const char*, Value& val, SnortConfig*)
     {
         params->decompress_zip = val.get_bool();
     }
+    else if (val.is("decompress_vba"))
+    {
+        params->decompress_vba = val.get_bool();
+    }
     else if (val.is("script_detection"))
     {
         params->script_detection = val.get_bool();
index ac27843dc6fa79511f799d06a17f6f3408d6e9ee..01e10c1f2b91ac1b8ef83a38ee49aec940a4c1fb 100755 (executable)
@@ -55,6 +55,7 @@ public:
     bool decompress_pdf = false;
     bool decompress_swf = false;
     bool decompress_zip = false;
+    bool decompress_vba = false;
     bool script_detection = false;
     snort::LiteralSearch::Handle* script_detection_handle = nullptr;
     bool publish_request_body = true;
index fc16227c36f32557c2f6da1cfbb147c2803c372e..2a364ff6089927c768c8d91fff20f4586c121e3c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "http_msg_body.h"
 
+#include "decompress/file_olefile.h"
 #include "file_api/file_flows.h"
 #include "file_api/file_service.h"
 #include "pub_sub/http_request_body_event.h"
@@ -514,6 +515,30 @@ const Field& HttpMsgBody::get_classic_client_body()
     return classic_normalize(detect_data, classic_client_body, false, params->uri_param);
 }
 
+const Field& HttpMsgBody::get_decomp_vba_data()
+{
+    if (decompressed_vba_data.length() != STAT_NOT_COMPUTE)
+        return decompressed_vba_data;
+
+    if (!session_data->fd_state->ole_data_ptr || !session_data->fd_state->ole_data_len)
+        return Field::FIELD_NULL;
+
+    uint8_t* buf = nullptr;
+    uint32_t buf_len = 0;
+
+    oleprocess(session_data->fd_state->ole_data_ptr, session_data->fd_state->ole_data_len, buf,
+        buf_len);
+    if (buf && buf_len)
+        decompressed_vba_data.set(buf_len, buf, true);
+    else
+        decompressed_vba_data.set(STAT_NOT_PRESENT);
+
+    session_data->fd_state->ole_data_ptr = nullptr;
+    session_data->fd_state->ole_data_len = 0;
+
+    return decompressed_vba_data;
+}
+
 int32_t HttpMsgBody::get_publish_length() const
 {
     return publish_length;
index 460b44a7d33e3993a04e2b269d4f2cd120c555b3..ee132718143c4b48db00cfdab866ad173d4524ee 100644 (file)
@@ -38,6 +38,7 @@ public:
     bool detection_required() const override { return (detect_data.length() > 0); }
     HttpMsgBody* get_body() override { return this; }
     const Field& get_classic_client_body();
+    const Field& get_decomp_vba_data();
     const Field& get_detect_data() { return detect_data; }
     const Field& get_msg_text_new() const { return msg_text_new; }
     static void fd_event_callback(void* context, int event);
@@ -80,6 +81,7 @@ private:
     Field detect_data;
     Field enhanced_js_norm_body;
     Field classic_client_body;   // URI normalization applied
+    Field decompressed_vba_data;
 
     int32_t publish_length = HttpCommon::STAT_NOT_PRESENT;
 };
index 724b9546ac4ef44f9c5f49d4762ad76ef7d58bd9..e4ea11165d1fa217468819bd7fc8737db1691e67 100755 (executable)
@@ -665,7 +665,8 @@ void HttpMsgHeader::setup_file_decompression()
     session_data->fd_state->Modes =
         (params->decompress_pdf ? FILE_PDF_DEFL_BIT : 0) |
         (params->decompress_swf ? (FILE_SWF_ZLIB_BIT | FILE_SWF_LZMA_BIT) : 0) |
-        (params->decompress_zip ? FILE_ZIP_DEFL_BIT : 0);
+        (params->decompress_zip ? FILE_ZIP_DEFL_BIT : 0) |
+        (params->decompress_vba ? FILE_VBA_EXTR_BIT : 0);
     session_data->fd_state->Alert_Callback = HttpMsgBody::fd_event_callback;
     session_data->fd_state->Alert_Context = &session_data->fd_alert_context;
     session_data->fd_state->Compr_Depth = 0;
index 0f6922f80a0f680c17c56cf6c1ab4527687d3849..fa0bc52daf721cac6f8a27f9538582ed2f37034d 100644 (file)
@@ -382,6 +382,14 @@ const Field& HttpMsgSection::get_classic_buffer(Cursor& c, const HttpBufferInfo&
             (HttpMsgStart*)request : (HttpMsgStart*)status;
         return (start != nullptr) ? start->get_version() : Field::FIELD_NULL;
       }
+    case BUFFER_VBA_DATA:
+      {
+        HttpMsgBody* msg_body = get_body();
+        if (session_data->fd_state and msg_body)
+            return msg_body->get_decomp_vba_data(); 
+        else
+            return Field::FIELD_NULL;
+      }
     default:
         assert(false);
         return Field::FIELD_NULL;
index 5940ac00c98481c30b777012e77aadb966099254..3a4a9af8d68fff2d2331c14d9975464279cc8d76 100644 (file)
@@ -70,6 +70,7 @@ bool HttpCursorModule::begin(const char*, int, SnortConfig*)
         break;
     case HTTP_BUFFER_CLIENT_BODY:
     case HTTP_BUFFER_RAW_BODY:
+    case BUFFER_VBA_DATA:
         inspect_section = IS_BODY;
         break;
     case HTTP_BUFFER_RAW_TRAILER:
@@ -1201,6 +1202,46 @@ static const IpsApi version_api =
     nullptr
 };
 
+//-------------------------------------------------------------------------
+// vba_data
+//-------------------------------------------------------------------------
+//
+
+#undef IPS_OPT
+#define IPS_OPT "vba_data"
+#undef IPS_HELP
+#define IPS_HELP "rule option to set the detection cursor to the MS Office Visual Basic for Applications macros buffer"
+static Module* vba_data_mod_ctor()
+{
+    return new HttpCursorModule(IPS_OPT, IPS_HELP, BUFFER_VBA_DATA, CAT_SET_VBA,
+        PSI_VBA_DATA);
+}
+
+static const IpsApi vba_data_api =
+{
+    {
+        PT_IPS_OPTION,
+        sizeof(IpsApi),
+        IPSAPI_VERSION,
+        1,
+        API_RESERVED,
+        API_OPTIONS,
+        IPS_OPT,
+        IPS_HELP,
+        vba_data_mod_ctor,
+        HttpCursorModule::mod_dtor
+    },
+    OPT_TYPE_DETECTION,
+    0, PROTO_BIT__TCP,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    HttpIpsOption::opt_ctor,
+    HttpIpsOption::opt_dtor,
+    nullptr
+};
+
 //-------------------------------------------------------------------------
 // plugins
 //-------------------------------------------------------------------------
@@ -1223,4 +1264,5 @@ const BaseApi* ips_http_trailer = &trailer_api.base;
 const BaseApi* ips_http_true_ip = &true_ip_api.base;
 const BaseApi* ips_http_uri = &uri_api.base;
 const BaseApi* ips_http_version = &version_api.base;
+const BaseApi* ips_vba_data = &vba_data_api.base;
 
index 25d9d60c886ce32a3068f1871d9d67e863adbbf9..81174d236a4ae2c803f31b90827e9fdf4045b72c 100644 (file)
@@ -32,7 +32,7 @@
 enum PsIdx { PSI_CLIENT_BODY, PSI_COOKIE, PSI_HEADER, PSI_METHOD, PSI_PARAM,
     PSI_RAW_BODY, PSI_RAW_COOKIE, PSI_RAW_HEADER, PSI_RAW_REQUEST, PSI_RAW_STATUS,
     PSI_RAW_TRAILER, PSI_RAW_URI, PSI_STAT_CODE, PSI_STAT_MSG, PSI_TRAILER,
-    PSI_TRUE_IP, PSI_URI, PSI_VERSION, PSI_MAX };
+    PSI_TRUE_IP, PSI_URI, PSI_VERSION, PSI_VBA_DATA, PSI_MAX };
 
 class HttpCursorModule : public snort::Module
 {
index 0610654f559a2804a2d4c6cecdf768b350b1bc0c..39ea8aca7adf91fae2fc654bab4d077c60d97381 100644 (file)
      (uint32_t)(*((const uint8_t*)(p) + 1) <<  8) | \
      (uint32_t)(*((const uint8_t*)(p))))
 
+#define LETOHLL_UNALIGNED(p) \
+    (((uint64_t)(LETOHL_UNALIGNED(p + 4)) << 32) | ((uint64_t)(LETOHL_UNALIGNED(p))))
+
+#define BETOHS_UNALIGNED(p) \
+    ((uint16_t)(*((const uint8_t*)(p)) << 8) | \
+     (uint16_t)(*((const uint8_t*)(p) + 1)))
+
+#define BETOHL_UNALIGNED(p) \
+    ((uint32_t)(*((const uint8_t*)(p)) << 24) | \
+     (uint32_t)(*((const uint8_t*)(p) + 1) << 16) | \
+     (uint32_t)(*((const uint8_t*)(p) + 2) <<  8) | \
+     (uint32_t)(*((const uint8_t*)(p) + 3)))
+
+#define BETOHLL_UNALIGNED(p) \
+    (((uint64_t)(BETOHL_UNALIGNED(p)) << 32) | ((uint64_t)(BETOHL_UNALIGNED(p + 4))))
+
 #endif
+
index 344fd9135c6f4f87769bae339b68ccd8ae78ee4e..470f44948cbec2c91b5e04e7b59439ea59c24faf 100644 (file)
@@ -202,6 +202,7 @@ const PegInfo pc_names[] =
     { CountType::SUM, "stat_code_searches", "fast pattern searches in status code buffer" },
     { CountType::SUM, "stat_msg_searches", "fast pattern searches in status message buffer" },
     { CountType::SUM, "cookie_searches", "fast pattern searches in cookie buffer" },
+    { CountType::SUM, "vba_searches", "fast pattern searches in MS Office Visual Basic for Applications buffer" },
     { CountType::SUM, "offloads", "fast pattern searches that were offloaded" },
     { CountType::SUM, "alerts", "alerts not including IP reputation" },
     { CountType::SUM, "total_alerts", "alerts including IP reputation" },
index 50c825ecb57a1ca59d9cae97066261420c041cf1..947ad2b39e60ecd9cd5eee6bb2ad944b2288b2ad 100644 (file)
@@ -53,6 +53,7 @@ struct PacketCount
     PegCount stat_code_searches;
     PegCount stat_msg_searches;
     PegCount cookie_searches;
+    PegCount vba_searches;
     PegCount offloads;
     PegCount alert_pkts;
     PegCount total_alert_pkts;