From 9b8029a79cdd6aea7a759778e87e05c6d0f7ee46 Mon Sep 17 00:00:00 2001 From: Anil Mahtani <929854+Anilm3@users.noreply.github.com> Date: Wed, 17 Jul 2024 22:27:38 +0100 Subject: [PATCH] Fingerprint fuzzer (#323) --- .github/workflows/fuzz.yml | 9 +++ fuzzer/CMakeLists.txt | 2 +- fuzzer/common/common.hpp | 56 +++++++++++++++++++ .../corpus/.gitignore | 4 ++ fuzzer/http_endpoint_fingerprint/src/main.cpp | 55 ++++++++++++++++++ .../http_header_fingerprint/corpus/.gitignore | 4 ++ fuzzer/http_header_fingerprint/src/main.cpp | 51 +++++++++++++++++ .../corpus/.gitignore | 4 ++ fuzzer/http_network_fingerprint/src/main.cpp | 50 +++++++++++++++++ fuzzer/session_fingerprint/corpus/.gitignore | 4 ++ fuzzer/session_fingerprint/src/main.cpp | 43 ++++++++++++++ 11 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 fuzzer/common/common.hpp create mode 100644 fuzzer/http_endpoint_fingerprint/corpus/.gitignore create mode 100644 fuzzer/http_endpoint_fingerprint/src/main.cpp create mode 100644 fuzzer/http_header_fingerprint/corpus/.gitignore create mode 100644 fuzzer/http_header_fingerprint/src/main.cpp create mode 100644 fuzzer/http_network_fingerprint/corpus/.gitignore create mode 100644 fuzzer/http_network_fingerprint/src/main.cpp create mode 100644 fuzzer/session_fingerprint/corpus/.gitignore create mode 100644 fuzzer/session_fingerprint/src/main.cpp diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 112c21ac4..3e598d636 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -92,6 +92,15 @@ jobs: params: "" - fuzzer: sha256 params: "" + - fuzzer: http_endpoint_fingerprint + params: "" + - fuzzer: http_header_fingerprint + params: "" + - fuzzer: http_network_fingerprint + params: "" + - fuzzer: session_fingerprint + params: "" + steps: - uses: actions/checkout@v4 with: diff --git a/fuzzer/CMakeLists.txt b/fuzzer/CMakeLists.txt index bd03981b2..fe0e3e6f9 100644 --- a/fuzzer/CMakeLists.txt +++ b/fuzzer/CMakeLists.txt @@ -27,7 +27,7 @@ foreach(dir ${subdirs}) COMPILE_FLAGS ${LINK_COMPILE_FLAGS} LINK_FLAGS ${LINK_COMPILE_FLAGS}) - target_include_directories(${FUZZER_NAME} PRIVATE ${LIBDDWAF_PUBLIC_INCLUDES} ${LIBDDWAF_PRIVATE_INCLUDES}) + target_include_directories(${FUZZER_NAME} PRIVATE ${LIBDDWAF_PUBLIC_INCLUDES} ${LIBDDWAF_PRIVATE_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR}/common/) target_link_libraries(${FUZZER_NAME} PRIVATE fuzzer-common lib_yamlcpp) endforeach() diff --git a/fuzzer/common/common.hpp b/fuzzer/common/common.hpp new file mode 100644 index 000000000..9cffc6497 --- /dev/null +++ b/fuzzer/common/common.hpp @@ -0,0 +1,56 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include +#include +#include + +class random_buffer { +public: + random_buffer(const uint8_t *bytes, size_t size) : bytes_(bytes), size_(size) {} + + template T get() + { + if ((index_ + sizeof(T)) > size_) { + return {}; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const T *value = reinterpret_cast(&bytes_[index_]); + index_ += sizeof(T) + (sizeof(T) % 2); + return *value; + } + + template <> bool get() + { + if (index_ >= size_) { + return false; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + bool value = bytes_[index_] > 0; + index_ += 2; + return value; + } + + template <> std::string_view get() + { + auto size = std::min(static_cast(get()) % 4096, size_ - index_); + if (size == 0) { + return ""; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto *ptr = reinterpret_cast(&bytes_[index_]); + index_ += size + size % 2; + return {ptr, size}; + } + +protected: + const uint8_t *bytes_; + size_t size_; + size_t index_{0}; +}; diff --git a/fuzzer/http_endpoint_fingerprint/corpus/.gitignore b/fuzzer/http_endpoint_fingerprint/corpus/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/fuzzer/http_endpoint_fingerprint/corpus/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/fuzzer/http_endpoint_fingerprint/src/main.cpp b/fuzzer/http_endpoint_fingerprint/src/main.cpp new file mode 100644 index 000000000..29c9402c1 --- /dev/null +++ b/fuzzer/http_endpoint_fingerprint/src/main.cpp @@ -0,0 +1,55 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include + +#include "common.hpp" +#include + +using namespace ddwaf; +using namespace std::literals; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size) +{ + random_buffer buffer{bytes, size}; + + ddwaf_object tmp; + + ddwaf_object query; + ddwaf_object_map(&query); + auto query_size = buffer.get(); + for (uint8_t i = 0; i < query_size; ++i) { + auto key = buffer.get(); + auto value = buffer.get(); + + ddwaf_object_map_addl( + &query, key.data(), key.size(), ddwaf_object_stringl(&tmp, value.data(), value.size())); + } + + ddwaf_object body; + ddwaf_object_map(&body); + auto body_size = buffer.get(); + for (uint8_t i = 0; i < body_size; ++i) { + auto key = buffer.get(); + auto value = buffer.get(); + + ddwaf_object_map_addl( + &body, key.data(), key.size(), ddwaf_object_stringl(&tmp, value.data(), value.size())); + } + + http_endpoint_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + auto [output, attr] = gen.eval_impl({{}, {}, false, buffer.get()}, + {{}, {}, false, buffer.get()}, {{}, {}, false, &query}, + {{}, {}, false, &body}, deadline); + + ddwaf_object_free(&query); + ddwaf_object_free(&body); + ddwaf_object_free(&output); + + return 0; +} diff --git a/fuzzer/http_header_fingerprint/corpus/.gitignore b/fuzzer/http_header_fingerprint/corpus/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/fuzzer/http_header_fingerprint/corpus/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/fuzzer/http_header_fingerprint/src/main.cpp b/fuzzer/http_header_fingerprint/src/main.cpp new file mode 100644 index 000000000..f1bdcc370 --- /dev/null +++ b/fuzzer/http_header_fingerprint/src/main.cpp @@ -0,0 +1,51 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include + +#include "common.hpp" +#include + +using namespace ddwaf; +using namespace std::literals; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size) +{ + static std::array headers{"referer", "connection", "accept-encoding", + "content-encoding", "cache-control", "accept-charset", "content-type", "accept-language", + "x-forwarded-for", "x-real-ip", "x-client-ip", "forwarded-for", "x-cluster-client-ip", + "fastly-client-ip", "cf-connecting-ip", "cf-connecting-ipv6", "user-agent"}; + + random_buffer buffer{bytes, size}; + + ddwaf_object tmp; + + ddwaf_object header; + ddwaf_object_map(&header); + auto header_size = buffer.get(); + for (uint8_t i = 0; i < header_size; ++i) { + auto value = buffer.get(); + + std::string_view key; + if (buffer.get()) { // Known header + key = headers[buffer.get() % headers.size()]; + } else { + key = buffer.get(); + } + ddwaf_object_map_addl(&header, key.data(), key.size(), + ddwaf_object_stringl(&tmp, value.data(), value.size())); + } + + http_header_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + auto [output, attr] = gen.eval_impl({{}, {}, false, &header}, deadline); + + ddwaf_object_free(&header); + ddwaf_object_free(&output); + + return 0; +} diff --git a/fuzzer/http_network_fingerprint/corpus/.gitignore b/fuzzer/http_network_fingerprint/corpus/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/fuzzer/http_network_fingerprint/corpus/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/fuzzer/http_network_fingerprint/src/main.cpp b/fuzzer/http_network_fingerprint/src/main.cpp new file mode 100644 index 000000000..d61f8a065 --- /dev/null +++ b/fuzzer/http_network_fingerprint/src/main.cpp @@ -0,0 +1,50 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include + +#include "common.hpp" +#include + +using namespace ddwaf; +using namespace std::literals; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size) +{ + static std::array headers{"x-forwarded-for", "x-real-ip", "x-client-ip", + "forwarded-for", "x-cluster-client-ip", "fastly-client-ip", "cf-connecting-ip", + "cf-connecting-ipv6"}; + + random_buffer buffer{bytes, size}; + + ddwaf_object tmp; + + ddwaf_object header; + ddwaf_object_map(&header); + auto header_size = buffer.get(); + for (uint8_t i = 0; i < header_size; ++i) { + auto value = buffer.get(); + + std::string_view key; + if (buffer.get()) { // Known header + key = headers[buffer.get() % headers.size()]; + } else { + key = buffer.get(); + } + ddwaf_object_map_addl(&header, key.data(), key.size(), + ddwaf_object_stringl(&tmp, value.data(), value.size())); + } + + http_network_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + auto [output, attr] = gen.eval_impl({{}, {}, false, &header}, deadline); + + ddwaf_object_free(&header); + ddwaf_object_free(&output); + + return 0; +} diff --git a/fuzzer/session_fingerprint/corpus/.gitignore b/fuzzer/session_fingerprint/corpus/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/fuzzer/session_fingerprint/corpus/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/fuzzer/session_fingerprint/src/main.cpp b/fuzzer/session_fingerprint/src/main.cpp new file mode 100644 index 000000000..a4441f81b --- /dev/null +++ b/fuzzer/session_fingerprint/src/main.cpp @@ -0,0 +1,43 @@ +// Unless explicitly stated otherwise all files in this repository are +// dual-licensed under the Apache-2.0 License or BSD-3-Clause License. +// +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021 Datadog, Inc. + +#include + +#include "common.hpp" +#include + +using namespace ddwaf; +using namespace std::literals; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size) +{ + random_buffer buffer{bytes, size}; + + ddwaf_object tmp; + + ddwaf_object cookies; + ddwaf_object_map(&cookies); + auto cookies_size = buffer.get(); + for (uint8_t i = 0; i < cookies_size; ++i) { + auto key = buffer.get(); + auto value = buffer.get(); + + ddwaf_object_map_addl(&cookies, key.data(), key.size(), + ddwaf_object_stringl(&tmp, value.data(), value.size())); + } + + session_fingerprint gen{"id", {}, {}, false, true}; + + ddwaf::timer deadline{2s}; + auto [output, attr] = + gen.eval_impl({{}, {}, false, &cookies}, {{}, {}, false, buffer.get()}, + {{}, {}, false, buffer.get()}, deadline); + + ddwaf_object_free(&cookies); + ddwaf_object_free(&output); + + return 0; +}