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
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
{ 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
--------------
range { 0:65535 }
-7.119. wscale
+7.120. wscale
--------------
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,
* 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
* 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
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
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
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
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
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
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
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
"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
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.
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
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
+add_subdirectory (test)
+
set( DECOMPRESS_INCLUDES
file_decomp.h
)
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"
)
+
}
case ( FILE_TYPE_ZIP ):
{
+ if (SessionPtr->Modes & FILE_VBA_EXTR_BIT)
+ SessionPtr->vba_analysis = true;
+
Ret_Code = File_Decomp_ZIP(SessionPtr);
break;
}
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;
}
#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
#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. */
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 */
#endif
#include "file_decomp_zip.h"
+#include "file_olefile.h"
#include "helpers/boyer_moore_search.h"
#include "utils/util.h"
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;
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;
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 )
// 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;
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;
if ( Inflate_Init(SessionPtr) == File_Decomp_Error )
return File_Decomp_Error;
- // fallthrough
+ // fallthrough
// perform zlib inflate
case ZIP_STATE_INFLATE:
{
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
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);
return File_Decomp_BlockIn;
}
+
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;
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
uint32_t compressed_size;
uint16_t filename_length;
uint16_t extra_length;
-
+ char* file_name;
// field index
uint32_t Index;
fd_status_t File_Decomp_ZIP(fd_session_t*);
#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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];
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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
+
--- /dev/null
+add_cpputest( file_olefile_test
+ SOURCES ../file_olefile.cc
+ ../file_oleheader.cc
+)
+
+add_cpputest(file_oleheader_test
+ SOURCES ../file_oleheader.cc
+)
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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);
+}
+
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] )
case CAT_SET_KEY:
return PM_TYPE_KEY;
+ case CAT_SET_VBA:
+ return PM_TYPE_VBA;
+
default:
break;
}
if ( !strncmp(opt, "sip_", 4) )
return "sip";
+ if ( !strncmp(opt, "vba_data", 8) )
+ return "file";
+
return nullptr;
}
// 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;
CAT_SET_BODY,
CAT_SET_HEADER,
CAT_SET_KEY,
+ CAT_SET_VBA,
};
enum RuleDirection
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
"http_true_ip",
"http_uri",
"http_version",
+ "vba_data",
nullptr
};
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[] =
ips_http_true_ip,
ips_http_uri,
ips_http_version,
+ ips_vba_data,
nullptr
};
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
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",
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;
}
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;
{ "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" },
{
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();
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;
#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"
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;
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);
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;
};
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;
(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;
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:
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
//-------------------------------------------------------------------------
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;
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
{
(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
+
{ 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" },
PegCount stat_code_searches;
PegCount stat_msg_searches;
PegCount cookie_searches;
+ PegCount vba_searches;
PegCount offloads;
PegCount alert_pkts;
PegCount total_alert_pkts;