diff --git a/src/fuzzers/CMakeLists.txt b/src/fuzzers/CMakeLists.txt index ef9012b9c1..41a46dc64a 100644 --- a/src/fuzzers/CMakeLists.txt +++ b/src/fuzzers/CMakeLists.txt @@ -94,6 +94,7 @@ target_link_libraries(zeek_fuzzer_shared PUBLIC ${zeek_fuzzer_shared_deps} add_fuzz_target(packet) add_fuzz_target(dns) +add_fuzz_target(websocket) add_generic_analyzer_fuzz_target(ftp) add_generic_analyzer_fuzz_target(http) diff --git a/src/fuzzers/corpora/websocket-corpus.zip b/src/fuzzers/corpora/websocket-corpus.zip new file mode 100644 index 0000000000..d2f3b23363 Binary files /dev/null and b/src/fuzzers/corpora/websocket-corpus.zip differ diff --git a/src/fuzzers/websocket-fuzzer.cc b/src/fuzzers/websocket-fuzzer.cc new file mode 100644 index 0000000000..1d1b47d39e --- /dev/null +++ b/src/fuzzers/websocket-fuzzer.cc @@ -0,0 +1,107 @@ +#include + +#include "zeek/Conn.h" +#include "zeek/ID.h" +#include "zeek/RunState.h" +#include "zeek/analyzer/Analyzer.h" +#include "zeek/analyzer/Manager.h" +#include "zeek/analyzer/protocol/pia/PIA.h" +#include "zeek/analyzer/protocol/tcp/TCP.h" +#include "zeek/analyzer/protocol/websocket/WebSocket.h" +#include "zeek/fuzzers/FuzzBuffer.h" +#include "zeek/fuzzers/fuzzer-setup.h" +#include "zeek/packet_analysis/protocol/tcp/TCPSessionAdapter.h" +#include "zeek/session/Manager.h" + +static constexpr auto FUZZ_ANALYZER_NAME = "websocket"; + +static zeek::Connection* add_connection() { + static constexpr double network_time_start = 1439471031; + zeek::run_state::detail::update_network_time(network_time_start); + + zeek::Packet p; + zeek::ConnTuple conn_id; + conn_id.src_addr = zeek::IPAddr("1.2.3.4"); + conn_id.dst_addr = zeek::IPAddr("5.6.7.8"); + conn_id.src_port = htons(23132); + conn_id.dst_port = htons(80); + conn_id.is_one_way = false; + conn_id.proto = TRANSPORT_TCP; + zeek::detail::ConnKey key(conn_id); + zeek::Connection* conn = new zeek::Connection(key, network_time_start, &conn_id, 1, &p); + conn->SetTransport(TRANSPORT_TCP); + zeek::session_mgr->Insert(conn); + return conn; +} + +static std::tuple add_analyzer( + zeek::Connection* conn, const zeek::Tag& analyzer_tag) { + auto* tcp = new zeek::packet_analysis::TCP::TCPSessionAdapter(conn); + auto* pia = new zeek::analyzer::pia::PIA_TCP(conn); + auto a = zeek::analyzer_mgr->InstantiateAnalyzer(analyzer_tag, conn); + tcp->AddChildAnalyzer(a); + tcp->AddChildAnalyzer(pia->AsAnalyzer()); + conn->SetSessionAdapter(tcp, pia); + + return {a, tcp}; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + static auto analyzer_tag = zeek::analyzer_mgr->GetComponentTag(FUZZ_ANALYZER_NAME); + if ( ! analyzer_tag ) { + std::fprintf(stderr, "Unable to find component tag for '%s'", FUZZ_ANALYZER_NAME); + abort(); + } + + // Would be nice to have that in LLVMFuzzerInitialize, oh well... + static bool one_time_setup = false; + if ( ! one_time_setup ) { + zeek::analyzer_mgr->DisableAllAnalyzers(); + zeek::analyzer_mgr->EnableAnalyzer(analyzer_tag); + const auto& pia_tcp_tag = zeek::analyzer_mgr->GetComponentTag("PIA_TCP"); + zeek::analyzer_mgr->EnableAnalyzer(pia_tcp_tag); + one_time_setup = true; + } + + zeek::detail::FuzzBuffer fb{data, size}; + + if ( ! fb.Valid() ) + return 0; + + auto conn = add_connection(); + if ( new_connection ) + conn->Event(new_connection, nullptr); + + auto [a, adapter] = add_analyzer(conn, analyzer_tag); + + // WebSocket specific initialization. May also want to fuzz + // this in the future. + static const auto& config_type = zeek::id::find_type("WebSocket::AnalyzerConfig"); + static const auto& config_rec = zeek::make_intrusive(config_type); + auto wsa = static_cast(a); + wsa->Configure(config_rec); + + for ( ;; ) { + auto chunk = fb.Next(); + + if ( ! chunk ) + break; + + try { + a->NextStream(chunk->size, chunk->data.get(), chunk->is_orig); + } catch ( const binpac::Exception& e ) { + } + + chunk = {}; // Release buffer before draining events. + zeek::event_mgr.Drain(); + + // Has the analyzer been disabled during event processing? + if ( ! adapter->HasChildAnalyzer(analyzer_tag) ) + break; + } + + zeek::event_mgr.Drain(); + zeek::detail::fuzzer_cleanup_one_input(); + + return 0; +}