Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement LFI #2770

Merged
merged 32 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f62a362
Wrap lfi functions
estringana Jul 23, 2024
789a952
Test LFi functions
estringana Aug 2, 2024
0deec48
Avoid unnecesary new zvals
estringana Aug 21, 2024
b13e385
Add rasp duration tag
estringana Aug 26, 2024
2d56e97
Add optional rasp metrics
estringana Aug 27, 2024
93fd454
Add duration_ext tag
estringana Aug 28, 2024
ab85684
Add DD_APPSEC_RASP_ENABLED configuration
estringana Aug 29, 2024
6428f36
Add lfi capability
estringana Aug 30, 2024
6683312
Amend issue with rasp events tags
estringana Aug 30, 2024
f57a0ba
Move rasp.duration_ext to appsec extension
estringana Sep 3, 2024
17565f2
Ensure rasp tags are created
estringana Sep 4, 2024
fb751a4
Ensure duration_ext is added
estringana Sep 5, 2024
f94e637
Avoid loaded wrappers when appsec no loaded
estringana Sep 11, 2024
5fb4b3c
Change comment
estringana Sep 12, 2024
f80d189
Disable rasp by default
estringana Sep 20, 2024
a8b8281
Fix error loading filesytem integration on test_c tests
estringana Sep 30, 2024
4671edc
Add more rasp tests
estringana Sep 30, 2024
8a3dccf
Stop creating not needed spans
estringana Sep 30, 2024
22840b3
Lint
estringana Sep 30, 2024
e248102
Lint
estringana Oct 2, 2024
48bc7f9
Enable rasp manually on integration tests
estringana Oct 3, 2024
c419a52
Amend test
estringana Oct 8, 2024
28a590a
Lint
estringana Oct 8, 2024
b2407b7
Change approach to calculate elapsed
estringana Oct 14, 2024
7340c8d
Reduce calls to waf on LFI functions
estringana Oct 14, 2024
d674b8e
Changes suggested on PR
estringana Oct 15, 2024
ec7f6a2
Remove stat and lstat
estringana Oct 21, 2024
d0fb2b9
Return not loaded when filesytem integration is wrapping no functions
estringana Oct 21, 2024
5231521
Avoid reporting filesytem integration on telemetry
estringana Oct 21, 2024
8330471
Test filesystem integration
estringana Nov 26, 2024
2375169
Fix tests adding new filesystem integration
estringana Nov 26, 2024
1526965
Adapt lfi to new telemetry approach
estringana Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,8 @@ test_integrations_amqp35: global_test_run_dependencies tests/Integrations/AMQP/V
$(call run_tests_debug,tests/Integrations/AMQP/V3_5)
test_integrations_deferred_loading: global_test_run_dependencies tests/Integrations/DeferredLoading/composer.lock-php$(PHP_MAJOR_MINOR)
$(call run_tests_debug,tests/Integrations/DeferredLoading)
test_integrations_filesystem: global_test_run_dependencies
$(call run_tests_debug,tests/Integrations/Filesystem)
test_integrations_curl: global_test_run_dependencies
$(call run_tests_debug,tests/Integrations/Curl)
test_integrations_elasticsearch1: global_test_run_dependencies tests/Integrations/Elasticsearch/V1/composer.lock-php$(PHP_MAJOR_MINOR)
Expand Down
9 changes: 4 additions & 5 deletions appsec/src/extension/backtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "backtrace.h"
#include "compatibility.h"
#include "configuration.h"
#include "ddappsec.h"
#include "ddtrace.h"
#include "logging.h"
#include "php_compat.h"
Expand Down Expand Up @@ -246,13 +247,11 @@ bool dd_report_exploit_backtrace(zend_string *nullable id)
zval *dd_stack = zend_hash_find(Z_ARR_P(meta_struct), _dd_stack_key);
zval *exploit = NULL;
if (!dd_stack || Z_TYPE_P(dd_stack) == IS_NULL) {
zval new_dd_stack;
zval new_exploit;
dd_stack = zend_hash_add_new(
Z_ARR_P(meta_struct), _dd_stack_key, &new_dd_stack);
Z_ARR_P(meta_struct), _dd_stack_key, &EG(uninitialized_zval));
array_init(dd_stack);
exploit =
zend_hash_add_new(Z_ARR_P(dd_stack), _exploit_key, &new_exploit);
exploit = zend_hash_add_new(
Z_ARR_P(dd_stack), _exploit_key, &EG(uninitialized_zval));
array_init(exploit);
} else if (Z_TYPE_P(dd_stack) != IS_ARRAY) {
return false;
Expand Down
8 changes: 5 additions & 3 deletions appsec/src/extension/commands/request_exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

struct ctx {
struct req_info req_info; // dd_command_proc_resp_verd_span_data expect it
bool rasp;
zval *nonnull data;
};

Expand All @@ -22,21 +23,21 @@ static dd_result _pack_command(mpack_writer_t *nonnull w, void *nonnull ctx);
static const dd_command_spec _spec = {
.name = "request_exec",
.name_len = sizeof("request_exec") - 1,
.num_args = 1, // a single map
.num_args = 2,
.outgoing_cb = _pack_command,
.incoming_cb = dd_command_proc_resp_verd_span_data,
.config_features_cb = dd_command_process_config_features_unexpected,
};

dd_result dd_request_exec(dd_conn *nonnull conn, zval *nonnull data)
dd_result dd_request_exec(dd_conn *nonnull conn, zval *nonnull data, bool rasp)
{
if (Z_TYPE_P(data) != IS_ARRAY) {
mlog(dd_log_debug, "Invalid data provided to command request_exec, "
"expected hash table.");
return dd_error;
}

struct ctx ctx = {.data = data};
struct ctx ctx = {.rasp = rasp, .data = data};

return dd_command_exec_req_info(conn, &_spec, &ctx.req_info);
}
Expand All @@ -46,6 +47,7 @@ static dd_result _pack_command(mpack_writer_t *nonnull w, void *nonnull _ctx)
assert(_ctx != NULL);
struct ctx *ctx = _ctx;

mpack_write(w, ctx->rasp);
dd_mpack_write_zval(w, ctx->data);

return dd_success;
Expand Down
2 changes: 1 addition & 1 deletion appsec/src/extension/commands/request_exec.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
#include <SAPI.h>
#include <php.h>

dd_result dd_request_exec(dd_conn *nonnull conn, zval *nonnull data);
dd_result dd_request_exec(dd_conn *nonnull conn, zval *nonnull data, bool rasp);
1 change: 1 addition & 0 deletions appsec/src/extension/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extern bool runtime_config_first_init;
SYSCFG(BOOL, DD_APPSEC_HELPER_LAUNCH, "true") \
CONFIG(STRING, DD_APPSEC_HELPER_PATH, DD_BASE("bin/libddappsec-helper.so")) \
SYSCFG(BOOL, DD_APPSEC_STACK_TRACE_ENABLED, "true") \
SYSCFG(BOOL, DD_APPSEC_RASP_ENABLED , "false") \
estringana marked this conversation as resolved.
Show resolved Hide resolved
SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACE_DEPTH, "32") \
SYSCFG(INT, DD_APPSEC_MAX_STACK_TRACES, "2") \
CONFIG(STRING, DD_APPSEC_HELPER_RUNTIME_PATH, "/tmp", .ini_change = dd_on_runtime_path_update) \
Expand Down
30 changes: 27 additions & 3 deletions appsec/src/extension/ddappsec.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ static PHP_FUNCTION(datadog_appsec_testing_request_exec)
RETURN_FALSE;
}

if (dd_request_exec(conn, data) != dd_success) {
if (dd_request_exec(conn, data, false) != dd_success) {
RETURN_FALSE;
}

Expand All @@ -476,6 +476,10 @@ static PHP_FUNCTION(datadog_appsec_testing_request_exec)

static PHP_FUNCTION(datadog_appsec_push_address)
{
struct timespec start;
struct timespec end;
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
long elapsed = 0;
UNUSED(return_value);
if (!DDAPPSEC_G(active)) {
mlog(dd_log_debug, "Trying to access to push_address "
Expand All @@ -485,10 +489,16 @@ static PHP_FUNCTION(datadog_appsec_push_address)

zend_string *key = NULL;
zval *value = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz", &key, &value) == FAILURE) {
bool rasp = false;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|b", &key, &value, &rasp) ==
FAILURE) {
RETURN_FALSE;
}

if (rasp && !get_global_DD_APPSEC_RASP_ENABLED()) {
return;
}

zval parameters_zv;
zend_array *parameters_arr = zend_new_array(1);
ZVAL_ARR(&parameters_zv, parameters_arr);
Expand All @@ -502,9 +512,22 @@ static PHP_FUNCTION(datadog_appsec_push_address)
return;
}

dd_result res = dd_request_exec(conn, &parameters_zv);
dd_result res = dd_request_exec(conn, &parameters_zv, rasp);
zval_ptr_dtor(&parameters_zv);

if (rasp) {
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
elapsed =
((int64_t)end.tv_sec - (int64_t)start.tv_sec) *
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
(int64_t)1000000000 +
((int64_t)end.tv_nsec - (int64_t)start.tv_nsec);
zend_object *span = dd_trace_get_active_root_span();
if (span) {
dd_tags_add_rasp_duration_ext(span, elapsed);
}
}

if (dd_req_is_user_req()) {
if (res == dd_should_block || res == dd_should_redirect) {
dd_req_call_blocking_function(res);
Expand All @@ -529,6 +552,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(push_address_arginfo, 0, 0, IS_VOID, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, rasp)
ZEND_END_ARG_INFO()

// clang-format off
Expand Down
17 changes: 17 additions & 0 deletions appsec/src/extension/tags.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"_dd.appsec.events.users.login.success.sdk"
#define DD_EVENTS_USER_LOGIN_FAILURE_SDK \
"_dd.appsec.events.users.login.failure.sdk"
#define DD_EVENTS_RASP_DURATION_EXT "_dd.appsec.rasp.duration_ext"

static zend_string *_dd_tag_data_zstr;
static zend_string *_dd_tag_event_zstr;
Expand All @@ -78,6 +79,7 @@ static zend_string *_dd_tag_rh_content_encoding; // response
static zend_string *_dd_tag_rh_content_language; // response
static zend_string *_dd_tag_user_id;
static zend_string *_dd_metric_enabled;
static zend_string *_dd_rasp_duration_ext;
static zend_string *_dd_signup_event;
static zend_string *_dd_login_success_event;
static zend_string *_dd_login_failure_event;
Expand Down Expand Up @@ -173,6 +175,9 @@ void dd_tags_startup()
_key_remote_addr_zstr =
zend_string_init_interned(LSTRARG("REMOTE_ADDR"), 1);

_dd_rasp_duration_ext =
zend_string_init_interned(LSTRARG(DD_EVENTS_RASP_DURATION_EXT), 1);

// Event related strings
_track_zstr =
zend_string_init_interned(LSTRARG("track"), 1 /* permanent */);
Expand Down Expand Up @@ -298,6 +303,18 @@ void dd_tags_set_event_user_id(zend_string *nonnull zstr)
_event_user_id = zend_string_copy(zstr);
}

void dd_tags_add_rasp_duration_ext(
zend_object *nonnull span, zend_long duration)
{
zval *metrics_zv = dd_trace_span_get_metrics(span);
if (!metrics_zv) {
return;
}
zval zv;
ZVAL_LONG(&zv, duration);
zend_hash_add(Z_ARRVAL_P(metrics_zv), _dd_rasp_duration_ext, &zv);
}

void dd_tags_rshutdown()
{
zend_llist_clean(&_appsec_json_frags);
Expand Down
3 changes: 2 additions & 1 deletion appsec/src/extension/tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ void dd_tags_set_event_user_id(zend_string *nonnull zstr);
// does not increase the refcount on zstr
void dd_tags_add_appsec_json_frag(zend_string *nonnull zstr);


void dd_tags_add_rasp_duration_ext(
zend_object *nonnull span, zend_long duration);
2 changes: 1 addition & 1 deletion appsec/src/extension/user_tracking.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ void dd_find_and_apply_verdict_for_user(zend_string *nonnull user_id)
zend_hash_str_add_new(
Z_ARRVAL(data_zv), "usr.id", sizeof("usr.id") - 1, &user_id_zv);

dd_result res = dd_request_exec(conn, &data_zv);
dd_result res = dd_request_exec(conn, &data_zv, false);
zval_ptr_dtor(&data_zv);

dd_tags_set_event_user_id(user_id);
Expand Down
6 changes: 3 additions & 3 deletions appsec/src/helper/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@ template <typename T> bool client::service_guard()

template <typename T>
std::shared_ptr<typename T::response> client::publish(
typename T::request &command)
typename T::request &command, bool rasp)
{
SPDLOG_DEBUG("received command {}", T::name);

auto response = std::make_shared<typename T::response>();
try {
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
auto res = context_->publish(std::move(command.data));
auto res = context_->publish(std::move(command.data), rasp);
if (res) {
for (auto &act : res->actions) {
dds::network::action_struct new_action;
Expand Down Expand Up @@ -316,7 +316,7 @@ bool client::handle_command(network::request_exec::request &command)
context_.emplace(*service_->get_engine());
}

auto response = publish<network::request_exec>(command);
auto response = publish<network::request_exec>(command, command.rasp);
return send_message<network::request_exec>(response);
}

Expand Down
3 changes: 2 additions & 1 deletion appsec/src/helper/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class client {

protected:
template <typename T>
std::shared_ptr<typename T::response> publish(typename T::request &command);
std::shared_ptr<typename T::response> publish(
typename T::request &command, bool rasp = false);
template <typename T> bool service_guard();
template <typename T, bool actions = true>
bool send_message(const std::shared_ptr<typename T::response> &message);
Expand Down
5 changes: 3 additions & 2 deletions appsec/src/helper/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ void engine::update(
std::atomic_store_explicit(&common_, new_common, std::memory_order_release);
}

std::optional<engine::result> engine::context::publish(parameter &&param)
std::optional<engine::result> engine::context::publish(
parameter &&param, bool rasp)
{
// Once the parameter reaches this function, it is guaranteed to be
// owned by the engine.
Expand Down Expand Up @@ -82,7 +83,7 @@ std::optional<engine::result> engine::context::publish(parameter &&param)
}
try {
const auto &listener = it->second;
listener->call(data, event_);
listener->call(data, event_, rasp);
} catch (std::exception &e) {
SPDLOG_ERROR("subscriber failed: {}", e.what());
}
Expand Down
2 changes: 1 addition & 1 deletion appsec/src/helper/engine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class engine {
context &operator=(context &&) = delete;
~context() = default;

std::optional<result> publish(parameter &&param);
std::optional<result> publish(parameter &&param, bool rasp = false);
// NOLINTNEXTLINE(google-runtime-references)
void get_metrics(metrics::telemetry_submitter &msubmitter);

Expand Down
5 changes: 5 additions & 0 deletions appsec/src/helper/metrics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ constexpr std::string_view event_rules_version =
constexpr std::string_view waf_version = "_dd.appsec.waf.version";
constexpr std::string_view waf_duration = "_dd.appsec.waf.duration";

// rasp
constexpr std::string_view rasp_duration = "_dd.appsec.rasp.duration";
constexpr std::string_view rasp_rule_eval = "_dd.appsec.rasp.rule.eval";
constexpr std::string_view rasp_timeout = "_dd.appsec.rasp.timeout";

} // namespace dds::metrics

template <>
Expand Down
3 changes: 2 additions & 1 deletion appsec/src/helper/network/proto.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ struct request_exec {
static constexpr const char *name = request_exec::name;
static constexpr request_id id = request_id::request_exec;

bool rasp = false;
dds::parameter data;

request() = default;
Expand All @@ -196,7 +197,7 @@ struct request_exec {
request &operator=(request &&) = default;
~request() override = default;

MSGPACK_DEFINE(data)
MSGPACK_DEFINE(rasp, data)
};

struct response : base_response_generic<response> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class engine_listener : public listener_base {
[[nodiscard]] std::unordered_set<product> get_supported_products() override
{
return {known_products::ASM, known_products::ASM_DD,
known_products::ASM_DATA};
known_products::ASM_DATA, known_products::ASM_RASP_LFI};
}

protected:
Expand Down
5 changes: 5 additions & 0 deletions appsec/src/helper/remote_config/product.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ struct known_products {
static inline constexpr product ASM_DATA{std::string_view{"ASM_DATA"}};
static inline constexpr product ASM_FEATURES{
std::string_view{"ASM_FEATURES"}};
static inline constexpr product ASM_RASP_LFI{
std::string_view{"ASM_RASP_LFI"}};
static inline constexpr product UNKNOWN{std::string_view{"UNKOWN"}};

static product for_name(std::string_view name)
Expand All @@ -45,6 +47,9 @@ struct known_products {
if (name == ASM_FEATURES.name()) {
return ASM_FEATURES;
}
if (name == ASM_RASP_LFI.name()) {
return ASM_RASP_LFI;
}

return UNKNOWN;
}
Expand Down
3 changes: 2 additions & 1 deletion appsec/src/helper/subscriber/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class subscriber {

virtual ~listener() = default;
// NOLINTNEXTLINE(google-runtime-references)
virtual void call(parameter_view &data, event &event) = 0;
virtual void call(
parameter_view &data, event &event, bool rasp = false) = 0;

// NOLINTNEXTLINE(google-runtime-references)
virtual void submit_metrics(
Expand Down
20 changes: 19 additions & 1 deletion appsec/src/helper/subscriber/waf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ instance::listener::~listener()
}
}

void instance::listener::call(dds::parameter_view &data, event &event)
void instance::listener::call(
dds::parameter_view &data, event &event, bool rasp)
{
ddwaf_result res;
DDWAF_RET_CODE code;
Expand Down Expand Up @@ -345,6 +346,14 @@ void instance::listener::call(dds::parameter_view &data, event &event)
break;
}
}
if (rasp) {
// NOLINTNEXTLINE
rasp_runtime_ += res.total_runtime / 1000.0;
rasp_calls_++;
if (res.timeout) {
rasp_timeouts_ += 1;
}
}

const parameter_view derivatives{res.derivatives};
for (const auto &derivative : derivatives) {
Expand Down Expand Up @@ -403,6 +412,15 @@ void instance::listener::submit_metrics(
metrics::event_rules_version, std::string{ruleset_version_});
msubmitter.submit_span_metric(metrics::waf_duration, total_runtime_);

if (rasp_calls_ > 0) {
msubmitter.submit_span_metric(metrics::rasp_duration, rasp_runtime_);
msubmitter.submit_span_metric(metrics::rasp_rule_eval, rasp_calls_);
if (rasp_timeouts_ > 0) {
msubmitter.submit_span_metric(
metrics::rasp_timeout, rasp_timeouts_);
}
}

for (const auto &[key, value] : derivatives_) {
std::string derivative = value;
if (value.length() > max_plain_schema_allowed &&
Expand Down
Loading
Loading