mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 07:38:19 +00:00
Merge remote-tracking branch 'security/topic/awelzel/155-reassem-validate-seq-upper-overflow'
* security/topic/awelzel/155-reassem-validate-seq-upper-overflow: file_analysis/File: Report overflowing chunks as weird and discard/truncate Reassem: Reject blocks overflowing 64bit upper zeek-setup: Load scrips before running unit tests
This commit is contained in:
commit
d8c1a1babf
8 changed files with 122 additions and 19 deletions
27
CHANGES
27
CHANGES
|
@ -1,3 +1,30 @@
|
||||||
|
6.0.0-dev.348 | 2023-04-11 15:30:45 -0700
|
||||||
|
|
||||||
|
* file_analysis/File: Report overflowing chunks as weird and discard/truncate (Arne Welzel, Corelight)
|
||||||
|
|
||||||
|
This is one level above the Reassembler where we still have information
|
||||||
|
about the file and source. A weird entry may looks as follows:
|
||||||
|
|
||||||
|
1679759398.237353 ... file_offset_overflow FXPLGt4SeMmlMKahJc: offset=fffffffffffffff7 len=10 F zeek HTTP
|
||||||
|
|
||||||
|
* Reassem: Reject blocks overflowing 64bit upper (Arne Welzel, Corelight)
|
||||||
|
|
||||||
|
The reassembler logic isn't wrap around safe, so just truncate or
|
||||||
|
reject such blocks. For files specifically, a byte offset in the
|
||||||
|
2**64 bytes represents 16EiB which is the maximum size supported
|
||||||
|
by BTRFS or NTFS (and probably nothing we'd ever see in practice).
|
||||||
|
|
||||||
|
* zeek-setup: Load scrips before running unit tests (Arne Welzel, Corelight)
|
||||||
|
|
||||||
|
It is currently not possible to call a->Conn()->GetVal() or construct a
|
||||||
|
zeek/file_analysis/File object from within doctests, as these quickly
|
||||||
|
reference the unpopulated zeek::id namespace to construct Val objects
|
||||||
|
of various types, making it hard write basic tests without completely
|
||||||
|
re-organizing.
|
||||||
|
|
||||||
|
Move running of the unit tests after parsing the scripts, so it is possible
|
||||||
|
for some basic exercising of File objects within tests.
|
||||||
|
|
||||||
6.0.0-dev.344 | 2023-04-11 15:23:42 -0700
|
6.0.0-dev.344 | 2023-04-11 15:23:42 -0700
|
||||||
|
|
||||||
* RDP: Instantiate SSL analyzer instead of PIA (Tim Wojtulewicz, Corelight)
|
* RDP: Instantiate SSL analyzer instead of PIA (Tim Wojtulewicz, Corelight)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
6.0.0-dev.344
|
6.0.0-dev.348
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
#include "zeek/zeek-config.h"
|
#include "zeek/zeek-config.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "zeek/Desc.h"
|
#include "zeek/Desc.h"
|
||||||
|
#include "zeek/Reporter.h"
|
||||||
|
|
||||||
using std::min;
|
using std::min;
|
||||||
|
|
||||||
|
@ -322,6 +324,17 @@ void Reassembler::CheckOverlap(const DataBlockList& list, uint64_t seq, uint64_t
|
||||||
|
|
||||||
void Reassembler::NewBlock(double t, uint64_t seq, uint64_t len, const u_char* data)
|
void Reassembler::NewBlock(double t, uint64_t seq, uint64_t len, const u_char* data)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Check for overflows - this should be handled by the caller
|
||||||
|
// and possibly reported as a weird or violation if applicable.
|
||||||
|
if ( std::numeric_limits<uint64_t>::max() - seq < len )
|
||||||
|
{
|
||||||
|
zeek::reporter->InternalWarning("Reassembler::NewBlock() truncating block at seq %" PRIx64
|
||||||
|
" from length %" PRIu64 " to %" PRIu64,
|
||||||
|
seq, len, std::numeric_limits<uint64_t>::max() - seq);
|
||||||
|
len = std::numeric_limits<uint64_t>::max() - seq;
|
||||||
|
}
|
||||||
|
|
||||||
if ( len == 0 )
|
if ( len == 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -535,9 +535,6 @@ TEST_CASE("construction")
|
||||||
CHECK_EQ(s7.Len(), 6);
|
CHECK_EQ(s7.Len(), 6);
|
||||||
CHECK_EQ(s7.Bytes(), text2);
|
CHECK_EQ(s7.Bytes(), text2);
|
||||||
|
|
||||||
// Construct a temporary reporter object for the next two tests
|
|
||||||
zeek::reporter = new zeek::Reporter(false);
|
|
||||||
|
|
||||||
zeek::byte_vec text3 = new u_char[7];
|
zeek::byte_vec text3 = new u_char[7];
|
||||||
memcpy(text3, text.c_str(), 7);
|
memcpy(text3, text.c_str(), 7);
|
||||||
zeek::String s8{false, text3, 6};
|
zeek::String s8{false, text3, 6};
|
||||||
|
@ -549,8 +546,6 @@ TEST_CASE("construction")
|
||||||
zeek::String s9{false, text4, 6};
|
zeek::String s9{false, text4, 6};
|
||||||
CHECK_EQ(std::string(s9.CheckString()), "<string-with-NUL>");
|
CHECK_EQ(std::string(s9.CheckString()), "<string-with-NUL>");
|
||||||
|
|
||||||
delete zeek::reporter;
|
|
||||||
|
|
||||||
zeek::byte_vec text5 = (zeek::byte_vec)malloc(7);
|
zeek::byte_vec text5 = (zeek::byte_vec)malloc(7);
|
||||||
memcpy(text5, text.c_str(), 7);
|
memcpy(text5, text.c_str(), 7);
|
||||||
zeek::String s10{true, text5, 6};
|
zeek::String s10{true, text5, 6};
|
||||||
|
|
|
@ -371,6 +371,7 @@ void TCP_Reassembler::BlockInserted(DataBlockMap::const_iterator it)
|
||||||
{
|
{
|
||||||
const auto& start_block = it->second;
|
const auto& start_block = it->second;
|
||||||
|
|
||||||
|
assert(start_block.seq < start_block.upper);
|
||||||
if ( start_block.seq > last_reassem_seq || start_block.upper <= last_reassem_seq )
|
if ( start_block.seq > last_reassem_seq || start_block.upper <= last_reassem_seq )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "zeek/file_analysis/File.h"
|
#include "zeek/file_analysis/File.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "zeek/Event.h"
|
#include "zeek/Event.h"
|
||||||
|
@ -431,6 +432,15 @@ void File::DeliverStream(const u_char* data, uint64_t len)
|
||||||
|
|
||||||
void File::DeliverChunk(const u_char* data, uint64_t len, uint64_t offset)
|
void File::DeliverChunk(const u_char* data, uint64_t len, uint64_t offset)
|
||||||
{
|
{
|
||||||
|
if ( std::numeric_limits<uint64_t>::max() - offset < len )
|
||||||
|
{
|
||||||
|
reporter->Weird(this, "file_offset_overflow",
|
||||||
|
zeek::util::fmt("offset=%" PRIx64 " len=%" PRIx64, offset, len),
|
||||||
|
GetSource().c_str());
|
||||||
|
|
||||||
|
len = std::numeric_limits<uint64_t>::max() - offset;
|
||||||
|
}
|
||||||
|
|
||||||
// Potentially handle reassembly and deliver to the stream analyzers.
|
// Potentially handle reassembly and deliver to the stream analyzers.
|
||||||
if ( file_reassembler )
|
if ( file_reassembler )
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "zeek/file_analysis/FileReassembler.h"
|
#include "zeek/file_analysis/FileReassembler.h"
|
||||||
|
|
||||||
|
#include "zeek/3rdparty/doctest.h"
|
||||||
#include "zeek/file_analysis/File.h"
|
#include "zeek/file_analysis/File.h"
|
||||||
|
|
||||||
namespace zeek::file_analysis
|
namespace zeek::file_analysis
|
||||||
|
@ -46,7 +47,7 @@ uint64_t FileReassembler::FlushTo(uint64_t sequence)
|
||||||
void FileReassembler::BlockInserted(DataBlockMap::const_iterator it)
|
void FileReassembler::BlockInserted(DataBlockMap::const_iterator it)
|
||||||
{
|
{
|
||||||
const auto& start_block = it->second;
|
const auto& start_block = it->second;
|
||||||
|
assert(start_block.seq < start_block.upper);
|
||||||
if ( start_block.seq > last_reassem_seq || start_block.upper <= last_reassem_seq )
|
if ( start_block.seq > last_reassem_seq || start_block.upper <= last_reassem_seq )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -113,3 +114,58 @@ void FileReassembler::Overlap(const u_char* b1, const u_char* b2, uint64_t n)
|
||||||
// Not doing anything here yet.
|
// Not doing anything here yet.
|
||||||
}
|
}
|
||||||
} // end file_analysis
|
} // end file_analysis
|
||||||
|
|
||||||
|
// Test reassembler logic through FileReassembler.
|
||||||
|
TEST_CASE("file reassembler")
|
||||||
|
{
|
||||||
|
// Can not construct due to protected constructor.
|
||||||
|
class TestFile : public zeek::file_analysis::File
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestFile(const std::string& file_id, const std::string& source_name)
|
||||||
|
: zeek::file_analysis::File(file_id, source_name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto f = std::make_unique<TestFile>("test_file_id", "test_source_name");
|
||||||
|
auto r = std::make_unique<zeek::file_analysis::FileReassembler>(f.get(), 0);
|
||||||
|
|
||||||
|
const u_char* data = (u_char*)("0123456789ABCDEF");
|
||||||
|
|
||||||
|
SUBCASE("block overlap and 64bit overflow")
|
||||||
|
{
|
||||||
|
r->NewBlock(0.0, 0xfffffffffffffff7, 3, data);
|
||||||
|
r->NewBlock(0.0, 0xfffffffffffffff7, 15, data);
|
||||||
|
r->NewBlock(0.0, 0xfffffffffffffff3, 15, data);
|
||||||
|
|
||||||
|
// 0xfffffffffffffff3 through 0xffffffffffffffff
|
||||||
|
CHECK_EQ(r->TotalSize(), 12);
|
||||||
|
|
||||||
|
// This previously hung with an endless loop.
|
||||||
|
r->Flush();
|
||||||
|
CHECK_FALSE(r->HasBlocks());
|
||||||
|
CHECK_EQ(r->TotalSize(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("reject NewBlock() at 64 bit limit")
|
||||||
|
{
|
||||||
|
r->NewBlock(0.0, 0xffffffffffffffff, 4, data);
|
||||||
|
CHECK_FALSE(r->HasBlocks());
|
||||||
|
CHECK_EQ(r->TotalSize(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("truncate NewBlock() to upper 64 bit limit")
|
||||||
|
{
|
||||||
|
r->NewBlock(0.0, 0xfffffffffffffffa, 8, data);
|
||||||
|
CHECK(r->HasBlocks());
|
||||||
|
CHECK_EQ(r->TotalSize(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
SUBCASE("no truncation")
|
||||||
|
{
|
||||||
|
r->NewBlock(0.0, 0xfffffffffffffff7, 8, data);
|
||||||
|
CHECK(r->HasBlocks());
|
||||||
|
CHECK_EQ(r->TotalSize(), 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -682,7 +682,7 @@ SetupResult setup(int argc, char** argv, Options* zopts)
|
||||||
if ( options.plugins_to_load.empty() && options.scripts_to_load.empty() &&
|
if ( options.plugins_to_load.empty() && options.scripts_to_load.empty() &&
|
||||||
options.script_options_to_set.empty() && ! options.pcap_file && ! options.interface &&
|
options.script_options_to_set.empty() && ! options.pcap_file && ! options.interface &&
|
||||||
! options.identifier_to_print && ! command_line_policy && ! options.print_plugins &&
|
! options.identifier_to_print && ! command_line_policy && ! options.print_plugins &&
|
||||||
! options.supervisor_mode && ! Supervisor::ThisNode() )
|
! options.supervisor_mode && ! Supervisor::ThisNode() && ! options.run_unit_tests )
|
||||||
add_input_file("-");
|
add_input_file("-");
|
||||||
|
|
||||||
for ( const auto& script_option : options.script_options_to_set )
|
for ( const auto& script_option : options.script_options_to_set )
|
||||||
|
@ -729,17 +729,6 @@ SetupResult setup(int argc, char** argv, Options* zopts)
|
||||||
|
|
||||||
plugin_mgr->ActivateDynamicPlugins(! options.bare_mode);
|
plugin_mgr->ActivateDynamicPlugins(! options.bare_mode);
|
||||||
|
|
||||||
// Delay the unit test until here so that plugins have been loaded.
|
|
||||||
if ( options.run_unit_tests )
|
|
||||||
{
|
|
||||||
set_signal_mask(false); // Allow ctrl-c to abort the tests early
|
|
||||||
doctest::Context context;
|
|
||||||
auto dargs = to_cargs(options.doctest_args);
|
|
||||||
context.applyCommandLine(dargs.size(), dargs.data());
|
|
||||||
ZEEK_LSAN_ENABLE();
|
|
||||||
exit(context.run());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print usage after plugins load so that any path extensions are properly shown.
|
// Print usage after plugins load so that any path extensions are properly shown.
|
||||||
if ( options.print_usage )
|
if ( options.print_usage )
|
||||||
usage(argv[0], 0);
|
usage(argv[0], 0);
|
||||||
|
@ -811,6 +800,18 @@ SetupResult setup(int argc, char** argv, Options* zopts)
|
||||||
init_net_var();
|
init_net_var();
|
||||||
run_bif_initializers();
|
run_bif_initializers();
|
||||||
|
|
||||||
|
// Delay the unit test until here so that plugins and script
|
||||||
|
// types have been fully loaded.
|
||||||
|
if ( options.run_unit_tests )
|
||||||
|
{
|
||||||
|
set_signal_mask(false); // Allow ctrl-c to abort the tests early
|
||||||
|
doctest::Context context;
|
||||||
|
auto dargs = to_cargs(options.doctest_args);
|
||||||
|
context.applyCommandLine(dargs.size(), dargs.data());
|
||||||
|
ZEEK_LSAN_ENABLE();
|
||||||
|
exit(context.run());
|
||||||
|
}
|
||||||
|
|
||||||
// Assign the script_args for command line processing in Zeek scripts.
|
// Assign the script_args for command line processing in Zeek scripts.
|
||||||
if ( ! options.script_args.empty() )
|
if ( ! options.script_args.empty() )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue