Skip to content

Commit

Permalink
Fingerprint fuzzer (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Anilm3 authored Jul 17, 2024
1 parent bac2aca commit 9b8029a
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion fuzzer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()

56 changes: 56 additions & 0 deletions fuzzer/common/common.hpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
#include <cstdlib>
#include <string_view>

class random_buffer {
public:
random_buffer(const uint8_t *bytes, size_t size) : bytes_(bytes), size_(size) {}

template <typename T> T get()
{
if ((index_ + sizeof(T)) > size_) {
return {};
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
const T *value = reinterpret_cast<const T *>(&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<size_t>(get<uint16_t>()) % 4096, size_ - index_);
if (size == 0) {
return "";
}

// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
const auto *ptr = reinterpret_cast<const char *>(&bytes_[index_]);
index_ += size + size % 2;
return {ptr, size};
}

protected:
const uint8_t *bytes_;
size_t size_;
size_t index_{0};
};
4 changes: 4 additions & 0 deletions fuzzer/http_endpoint_fingerprint/corpus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
55 changes: 55 additions & 0 deletions fuzzer/http_endpoint_fingerprint/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

#include "common.hpp"
#include <processor/fingerprint.hpp>

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<uint8_t>();
for (uint8_t i = 0; i < query_size; ++i) {
auto key = buffer.get<std::string_view>();
auto value = buffer.get<std::string_view>();

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<uint8_t>();
for (uint8_t i = 0; i < body_size; ++i) {
auto key = buffer.get<std::string_view>();
auto value = buffer.get<std::string_view>();

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<std::string_view>()},
{{}, {}, false, buffer.get<std::string_view>()}, {{}, {}, false, &query},
{{}, {}, false, &body}, deadline);

ddwaf_object_free(&query);
ddwaf_object_free(&body);
ddwaf_object_free(&output);

return 0;
}
4 changes: 4 additions & 0 deletions fuzzer/http_header_fingerprint/corpus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
51 changes: 51 additions & 0 deletions fuzzer/http_header_fingerprint/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

#include "common.hpp"
#include <processor/fingerprint.hpp>

using namespace ddwaf;
using namespace std::literals;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
{
static std::array<std::string_view, 17> 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<uint8_t>();
for (uint8_t i = 0; i < header_size; ++i) {
auto value = buffer.get<std::string_view>();

std::string_view key;
if (buffer.get<bool>()) { // Known header
key = headers[buffer.get<uint8_t>() % headers.size()];
} else {
key = buffer.get<std::string_view>();
}
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;
}
4 changes: 4 additions & 0 deletions fuzzer/http_network_fingerprint/corpus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
50 changes: 50 additions & 0 deletions fuzzer/http_network_fingerprint/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

#include "common.hpp"
#include <processor/fingerprint.hpp>

using namespace ddwaf;
using namespace std::literals;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *bytes, size_t size)
{
static std::array<std::string_view, 8> 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<uint8_t>();
for (uint8_t i = 0; i < header_size; ++i) {
auto value = buffer.get<std::string_view>();

std::string_view key;
if (buffer.get<bool>()) { // Known header
key = headers[buffer.get<uint8_t>() % headers.size()];
} else {
key = buffer.get<std::string_view>();
}
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;
}
4 changes: 4 additions & 0 deletions fuzzer/session_fingerprint/corpus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
43 changes: 43 additions & 0 deletions fuzzer/session_fingerprint/src/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdint>

#include "common.hpp"
#include <processor/fingerprint.hpp>

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<uint8_t>();
for (uint8_t i = 0; i < cookies_size; ++i) {
auto key = buffer.get<std::string_view>();
auto value = buffer.get<std::string_view>();

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<std::string_view>()},
{{}, {}, false, buffer.get<std::string_view>()}, deadline);

ddwaf_object_free(&cookies);
ddwaf_object_free(&output);

return 0;
}

0 comments on commit 9b8029a

Please sign in to comment.