mirror of
https://github.com/zeek/zeek.git
synced 2025-10-11 11:08:20 +00:00
generic-analyzer-fuzzer: Support NextPacket() fuzzing
A number of analyzers that we've been fuzzing with the generic-analyzer-fuzzer setup do not implement DeliverStream() and instead only work with DeliverPacket() (ntp, syslog, sip, radius, ...). Calling DeliverStream() on those is pretty much a noop and fuzzing not effective. This change adds support to fuzz DeliverPacket(). Whether to use packet or stream fuzzing is configured through a define via CMake. This is still a bit limited in that for analyzers that support both, DeliverPacket() and DeliverStream(), only one code path is fuzzed. Closed #3398
This commit is contained in:
parent
4d3e9de3d3
commit
5d781e54bd
2 changed files with 186 additions and 62 deletions
|
@ -55,10 +55,18 @@ macro (ADD_FUZZ_TARGET _name)
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
macro (ADD_GENERIC_ANALYZER_FUZZ_TARGET _name)
|
macro (ADD_GENERIC_ANALYZER_FUZZ_TARGET _name)
|
||||||
|
set(_transport "tcp")
|
||||||
|
set(extra_args "${ARGN}")
|
||||||
|
list(LENGTH extra_args extra_args_n)
|
||||||
|
if (${extra_args_n} GREATER 0)
|
||||||
|
list(GET extra_args 0 _transport)
|
||||||
|
endif ()
|
||||||
|
|
||||||
set(_fuzz_target zeek-${_name}-fuzzer)
|
set(_fuzz_target zeek-${_name}-fuzzer)
|
||||||
set(_fuzz_source generic-analyzer-fuzzer.cc)
|
set(_fuzz_source generic-analyzer-fuzzer.cc)
|
||||||
setup_fuzz_target(${_fuzz_target} ${_fuzz_source})
|
setup_fuzz_target(${_fuzz_target} ${_fuzz_source})
|
||||||
target_compile_definitions(${_fuzz_target} PUBLIC ZEEK_FUZZ_ANALYZER=${_name})
|
target_compile_definitions(${_fuzz_target} PUBLIC ZEEK_FUZZ_ANALYZER=${_name})
|
||||||
|
target_compile_definitions(${_fuzz_target} PUBLIC ZEEK_FUZZ_ANALYZER_TRANSPORT=${_transport})
|
||||||
endmacro ()
|
endmacro ()
|
||||||
|
|
||||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -94,27 +102,28 @@ add_generic_analyzer_fuzz_target(pop3)
|
||||||
add_generic_analyzer_fuzz_target(smtp)
|
add_generic_analyzer_fuzz_target(smtp)
|
||||||
|
|
||||||
add_generic_analyzer_fuzz_target(dce_rpc)
|
add_generic_analyzer_fuzz_target(dce_rpc)
|
||||||
add_generic_analyzer_fuzz_target(dhcp)
|
add_generic_analyzer_fuzz_target(dhcp udp)
|
||||||
add_generic_analyzer_fuzz_target(dnp3_tcp)
|
add_generic_analyzer_fuzz_target(dnp3_tcp)
|
||||||
add_generic_analyzer_fuzz_target(irc)
|
add_generic_analyzer_fuzz_target(irc)
|
||||||
add_generic_analyzer_fuzz_target(modbus)
|
add_generic_analyzer_fuzz_target(modbus)
|
||||||
add_generic_analyzer_fuzz_target(mqtt)
|
add_generic_analyzer_fuzz_target(mqtt)
|
||||||
add_generic_analyzer_fuzz_target(mysql)
|
add_generic_analyzer_fuzz_target(mysql)
|
||||||
add_generic_analyzer_fuzz_target(ncp)
|
add_generic_analyzer_fuzz_target(ncp)
|
||||||
add_generic_analyzer_fuzz_target(ntp)
|
add_generic_analyzer_fuzz_target(ntp udp)
|
||||||
add_generic_analyzer_fuzz_target(radius)
|
add_generic_analyzer_fuzz_target(radius udp)
|
||||||
add_generic_analyzer_fuzz_target(rdp)
|
add_generic_analyzer_fuzz_target(rdp)
|
||||||
add_generic_analyzer_fuzz_target(rdpeudp)
|
add_generic_analyzer_fuzz_target(rdpeudp udp)
|
||||||
add_generic_analyzer_fuzz_target(rfb)
|
add_generic_analyzer_fuzz_target(rfb)
|
||||||
|
# The rpc based analyzer work with udp and tcp.
|
||||||
add_generic_analyzer_fuzz_target(mount) # rpc
|
add_generic_analyzer_fuzz_target(mount) # rpc
|
||||||
add_generic_analyzer_fuzz_target(nfs) # rpc
|
add_generic_analyzer_fuzz_target(nfs) # rpc
|
||||||
add_generic_analyzer_fuzz_target(portmapper) # rpc
|
add_generic_analyzer_fuzz_target(portmapper) # rpc
|
||||||
add_generic_analyzer_fuzz_target(sip)
|
add_generic_analyzer_fuzz_target(sip udp)
|
||||||
add_generic_analyzer_fuzz_target(smb)
|
add_generic_analyzer_fuzz_target(smb)
|
||||||
add_generic_analyzer_fuzz_target(snmp)
|
add_generic_analyzer_fuzz_target(snmp udp)
|
||||||
add_generic_analyzer_fuzz_target(ssh)
|
add_generic_analyzer_fuzz_target(ssh)
|
||||||
add_generic_analyzer_fuzz_target(ssl)
|
add_generic_analyzer_fuzz_target(ssl)
|
||||||
add_generic_analyzer_fuzz_target(syslog)
|
add_generic_analyzer_fuzz_target(syslog udp)
|
||||||
|
|
||||||
# add_generic_analyzer_fuzz_target(finger) # no pcap files
|
# add_generic_analyzer_fuzz_target(finger) # no pcap files
|
||||||
# add_generic_analyzer_fuzz_target(gssapi) # only samples are embedded in smb
|
# add_generic_analyzer_fuzz_target(gssapi) # only samples are embedded in smb
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
// Generic protocol analyzer fuzzer.
|
||||||
|
//
|
||||||
|
// Expects ZEEK_FUZZ_ANALYZER and ZEEK_FUZZ_ANALYZER_TRANSPORT to be set.
|
||||||
|
//
|
||||||
|
// ZEEK_FUZZER_ANALYZER_TRANSPORT can be "tcp" or "udp" and determines if
|
||||||
|
// fuzzing happens via NextStream() or NextPacket().
|
||||||
|
//
|
||||||
|
// Fuzzing both codepaths at the same time isn't currently supported.
|
||||||
|
// Further note that TCP analyzers may use DeliverPacket() as well.
|
||||||
#include <binpac.h>
|
#include <binpac.h>
|
||||||
|
|
||||||
#include "zeek/Conn.h"
|
#include "zeek/Conn.h"
|
||||||
|
@ -8,14 +17,29 @@
|
||||||
#include "zeek/analyzer/protocol/tcp/TCP.h"
|
#include "zeek/analyzer/protocol/tcp/TCP.h"
|
||||||
#include "zeek/fuzzers/FuzzBuffer.h"
|
#include "zeek/fuzzers/FuzzBuffer.h"
|
||||||
#include "zeek/fuzzers/fuzzer-setup.h"
|
#include "zeek/fuzzers/fuzzer-setup.h"
|
||||||
|
#include "zeek/packet_analysis/protocol/ip/SessionAdapter.h"
|
||||||
#include "zeek/packet_analysis/protocol/tcp/TCPSessionAdapter.h"
|
#include "zeek/packet_analysis/protocol/tcp/TCPSessionAdapter.h"
|
||||||
|
#include "zeek/packet_analysis/protocol/udp/UDPSessionAdapter.h"
|
||||||
#include "zeek/session/Manager.h"
|
#include "zeek/session/Manager.h"
|
||||||
|
|
||||||
// Simple macros for converting a compiler define into a string.
|
// Simple macros for converting a compiler define into a string.
|
||||||
#define VAL(str) #str
|
#define VAL(str) #str
|
||||||
#define TOSTRING(str) VAL(str)
|
#define TOSTRING(str) VAL(str)
|
||||||
|
|
||||||
static zeek::Connection* add_connection()
|
static const char* FUZZ_ANALYZER_NAME = TOSTRING(ZEEK_FUZZ_ANALYZER);
|
||||||
|
static const char* FUZZ_ANALYZER_TRANSPORT = TOSTRING(ZEEK_FUZZ_ANALYZER_TRANSPORT);
|
||||||
|
|
||||||
|
class Fuzzer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Fuzzer(TransportProto proto, const zeek::Tag& analyzer_tag)
|
||||||
|
: proto{proto}, analyzer_tag{analyzer_tag}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Fuzzer(){};
|
||||||
|
|
||||||
|
zeek::Connection* AddConnection()
|
||||||
{
|
{
|
||||||
static constexpr double network_time_start = 1439471031;
|
static constexpr double network_time_start = 1439471031;
|
||||||
zeek::run_state::detail::update_network_time(network_time_start);
|
zeek::run_state::detail::update_network_time(network_time_start);
|
||||||
|
@ -27,42 +51,38 @@ static zeek::Connection* add_connection()
|
||||||
conn_id.src_port = htons(23132);
|
conn_id.src_port = htons(23132);
|
||||||
conn_id.dst_port = htons(80);
|
conn_id.dst_port = htons(80);
|
||||||
conn_id.is_one_way = false;
|
conn_id.is_one_way = false;
|
||||||
conn_id.proto = TRANSPORT_TCP;
|
conn_id.proto = proto;
|
||||||
zeek::detail::ConnKey key(conn_id);
|
zeek::detail::ConnKey key(conn_id);
|
||||||
zeek::Connection* conn = new zeek::Connection(key, network_time_start, &conn_id, 1, &p);
|
zeek::Connection* conn = new zeek::Connection(key, network_time_start, &conn_id, 1, &p);
|
||||||
conn->SetTransport(TRANSPORT_TCP);
|
conn->SetTransport(proto);
|
||||||
zeek::session_mgr->Insert(conn);
|
zeek::session_mgr->Insert(conn);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<zeek::analyzer::Analyzer*, zeek::packet_analysis::TCP::TCPSessionAdapter*>
|
std::tuple<zeek::analyzer::Analyzer*, zeek::packet_analysis::IP::SessionAdapter*,
|
||||||
add_analyzer(zeek::Connection* conn, zeek::Tag tag)
|
zeek::Connection*>
|
||||||
|
Setup()
|
||||||
{
|
{
|
||||||
auto* tcp = new zeek::packet_analysis::TCP::TCPSessionAdapter(conn);
|
auto* conn = AddConnection();
|
||||||
auto* pia = new zeek::analyzer::pia::PIA_TCP(conn);
|
auto* analyzer = zeek::analyzer_mgr->InstantiateAnalyzer(analyzer_tag, conn);
|
||||||
auto a = zeek::analyzer_mgr->InstantiateAnalyzer(tag, conn);
|
if ( ! analyzer )
|
||||||
if ( ! a )
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Unknown or unsupported analyzer %s found\n", TOSTRING(ZEEK_FUZZ_ANALYZER));
|
fprintf(stderr, "Unknown or unsupported analyzer %s\n",
|
||||||
|
analyzer_tag.AsString().c_str());
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp->AddChildAnalyzer(a);
|
auto* adapter = BuildAnalyzerTree(conn, analyzer);
|
||||||
tcp->AddChildAnalyzer(pia->AsAnalyzer());
|
|
||||||
conn->SetSessionAdapter(tcp, pia);
|
return {analyzer, adapter, conn};
|
||||||
return {a, tcp};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
void Process(zeek::detail::FuzzBuffer& fb)
|
||||||
{
|
{
|
||||||
zeek::detail::FuzzBuffer fb{data, size};
|
auto [analyzer, adapter, conn] = Setup();
|
||||||
|
|
||||||
if ( ! fb.Valid() )
|
if ( new_connection )
|
||||||
return 0;
|
conn->Event(new_connection, nullptr);
|
||||||
|
|
||||||
auto tag = zeek::analyzer_mgr->GetComponentTag(TOSTRING(ZEEK_FUZZ_ANALYZER));
|
|
||||||
auto conn = add_connection();
|
|
||||||
auto [a, tcp] = add_analyzer(conn, tag);
|
|
||||||
|
|
||||||
for ( ;; )
|
for ( ;; )
|
||||||
{
|
{
|
||||||
|
@ -73,20 +93,115 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
a->NextStream(chunk->size, chunk->data.get(), chunk->is_orig);
|
NextChunk(analyzer, *chunk);
|
||||||
}
|
}
|
||||||
catch ( const binpac::Exception& e )
|
catch ( const binpac::Exception& e )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk = {};
|
chunk = {}; // Release buffer before draining events.
|
||||||
zeek::event_mgr.Drain();
|
zeek::event_mgr.Drain();
|
||||||
|
|
||||||
// Has the analyzer been disabled during event processing?
|
// Has the analyzer been disabled during event processing?
|
||||||
if ( ! tcp->HasChildAnalyzer(tag) )
|
if ( ! adapter->HasChildAnalyzer(analyzer_tag) )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook methods to be implemented by specific fuzzers.
|
||||||
|
virtual zeek::packet_analysis::IP::SessionAdapter*
|
||||||
|
BuildAnalyzerTree(zeek::Connection* conn, zeek::analyzer::Analyzer* analyzer) = 0;
|
||||||
|
virtual void NextChunk(zeek::analyzer::Analyzer* analyzer,
|
||||||
|
zeek::detail::FuzzBuffer::Chunk& chunk) = 0;
|
||||||
|
|
||||||
|
void Cleanup() { zeek::detail::fuzzer_cleanup_one_input(); }
|
||||||
|
|
||||||
|
// Create a Fuzzer given FUZZ_ANALYZER_NAME and FUZZ_ANALYZER_TRANSPORT globals.
|
||||||
|
static std::unique_ptr<Fuzzer> Create();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TransportProto proto;
|
||||||
|
zeek::Tag analyzer_tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TCPFuzzer : public Fuzzer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TCPFuzzer(const zeek::Tag& analyzer_tag) : Fuzzer(TRANSPORT_TCP, analyzer_tag) { }
|
||||||
|
|
||||||
|
zeek::packet_analysis::IP::SessionAdapter*
|
||||||
|
BuildAnalyzerTree(zeek::Connection* conn, zeek::analyzer::Analyzer* analyzer) override
|
||||||
|
{
|
||||||
|
auto* tcp = new zeek::packet_analysis::TCP::TCPSessionAdapter(conn);
|
||||||
|
auto* pia = new zeek::analyzer::pia::PIA_TCP(conn);
|
||||||
|
tcp->AddChildAnalyzer(analyzer);
|
||||||
|
tcp->AddChildAnalyzer(pia->AsAnalyzer());
|
||||||
|
conn->SetSessionAdapter(tcp, pia);
|
||||||
|
return tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NextChunk(zeek::analyzer::Analyzer* analyzer,
|
||||||
|
zeek::detail::FuzzBuffer::Chunk& chunk) override
|
||||||
|
{
|
||||||
|
analyzer->NextStream(chunk.size, chunk.data.get(), chunk.is_orig);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UDPFuzzer : public Fuzzer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UDPFuzzer(const zeek::Tag& analyzer_tag) : Fuzzer(TRANSPORT_UDP, analyzer_tag) { }
|
||||||
|
|
||||||
|
zeek::packet_analysis::IP::SessionAdapter*
|
||||||
|
BuildAnalyzerTree(zeek::Connection* conn, zeek::analyzer::Analyzer* analyzer) override
|
||||||
|
{
|
||||||
|
auto* udp = new zeek::packet_analysis::UDP::UDPSessionAdapter(conn);
|
||||||
|
auto* pia = new zeek::analyzer::pia::PIA_UDP(conn);
|
||||||
|
udp->AddChildAnalyzer(analyzer);
|
||||||
|
udp->AddChildAnalyzer(pia->AsAnalyzer());
|
||||||
|
conn->SetSessionAdapter(udp, pia);
|
||||||
|
return udp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NextChunk(zeek::analyzer::Analyzer* analyzer,
|
||||||
|
zeek::detail::FuzzBuffer::Chunk& chunk) override
|
||||||
|
{
|
||||||
|
analyzer->NextPacket(chunk.size, chunk.data.get(), chunk.is_orig);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a Fuzzer given FUZZ_ANALYZER_NAME and FUZZ_ANALYZER_TRANSPORT globals.
|
||||||
|
std::unique_ptr<Fuzzer> Fuzzer::Create()
|
||||||
|
{
|
||||||
|
const auto& tag = zeek::analyzer_mgr->GetComponentTag(FUZZ_ANALYZER_NAME);
|
||||||
|
if ( ! tag )
|
||||||
|
{
|
||||||
|
std::fprintf(stderr, "Unable to find component tag for '%s'", FUZZ_ANALYZER_NAME);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( strcmp(FUZZ_ANALYZER_TRANSPORT, "tcp") == 0 )
|
||||||
|
return std::make_unique<TCPFuzzer>(tag);
|
||||||
|
else if ( strcmp(FUZZ_ANALYZER_TRANSPORT, "udp") == 0 )
|
||||||
|
return std::make_unique<UDPFuzzer>(tag);
|
||||||
|
|
||||||
|
std::fprintf(stderr, "Unexpected FUZZ_ANALYZER_TRANSPORT '%s'", FUZZ_ANALYZER_TRANSPORT);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuzzing entry point.
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
zeek::detail::FuzzBuffer fb{data, size};
|
||||||
|
|
||||||
|
if ( ! fb.Valid() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::unique_ptr<Fuzzer> fuzzer = Fuzzer::Create();
|
||||||
|
|
||||||
|
fuzzer->Process(fb);
|
||||||
|
|
||||||
|
fuzzer->Cleanup();
|
||||||
|
|
||||||
zeek::detail::fuzzer_cleanup_one_input();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue