Skip to content

Commit

Permalink
fix(events): change process_execute_failed probes
Browse files Browse the repository at this point in the history
The previous probe was missing from different distros and kernels.
The new probes are safer, but only exist starting from v5.8 of the kernel.
  • Loading branch information
AlonZivony committed Apr 28, 2024
1 parent cc1eea7 commit a496a9e
Show file tree
Hide file tree
Showing 15 changed files with 456 additions and 45 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ env:
DNS
HTTP
INSTTESTS: >
PROCESS_EXECUTE_FAILED
VFS_WRITE
FILE_MODIFICATION
HOOKED_SYSCALL
Expand Down
24 changes: 21 additions & 3 deletions docs/docs/events/builtin/extra/process_execute_failed.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,30 @@ while providing as much as possible the arguments as used by the kernel.
#### Type
kprobe
#### Purpose
Fetch the arguments of exec_binprm
To retrieve the arguments of exec_binprm.
Used for kernels older than 5.8.

### exec_binprm
#### Type
kretprobe
#### Purpose
Fetch the return value of exec_binprm
To retrieve the return value of exec_binprm and generate the event.
Used for kernels older than 5.8.

### security_bprm_creds_for_exec
#### Type
kprobe
#### Purpose
To retrieve the arguments for the event.
Relevant from kernel version 5.8 onwards, as the function was added in that kernel.

### sys_enter
#### Type
tracepoint
#### Purpose
To obtain the return code of the execution, determining whether to generate the event.
For a failed execution, an event will be generated using the information from the `security_bprm_creds_for_exec` hook.
Relevant from kernel version 5.8 onwards, matching the `security_bprm_creds_for_exec` hook.

## Example Use Case

Expand All @@ -43,7 +60,8 @@ Fetch the return value of exec_binprm
```

## Issues
Currently, only covers failed executions that are happening within exec_binprm. Other failures may occur at an earlier stage.
The `exec_binprm` symbol is not available in some systems, potentially resulting in the failure to load the event in kernels older than 5.8.
For kernels older than 5.8, the event only encompasses failed executions occurring within `exec_binprm`. Other failures may occur at an earlier stage. Newer versions do not account for failures before `security_bprm_creds_for_exec`, which precedes `exec_binprm`.

## Related Events
execve,execveat,bprm_check,sched_process_exec
2 changes: 2 additions & 0 deletions pkg/ebpf/c/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ enum tail_call_id_e
TAIL_HIDDEN_KERNEL_MODULE_KSET,
TAIL_HIDDEN_KERNEL_MODULE_MOD_TREE,
TAIL_HIDDEN_KERNEL_MODULE_NEW_MOD_ONLY,
TAIL_SECURITY_BPRM_CREDS_FOR_EXEC1,
TAIL_SECURITY_BPRM_CREDS_FOR_EXEC2,
MAX_TAIL_CALL
};

Expand Down
126 changes: 90 additions & 36 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4890,64 +4890,44 @@ int BPF_KPROBE(trace_ret_inotify_find_inode)
return events_perf_submit(&p, 0);
}

SEC("kprobe/exec_binprm")
TRACE_ENT_FUNC(exec_binprm, EXEC_BINPRM);

SEC("kretprobe/exec_binprm")
int BPF_KPROBE(trace_ret_exec_binprm)
statfunc int execute_failed_tail0(struct pt_regs *ctx, program_data_t *p, u32 tail_call_id)
{
args_t saved_args;
if (load_args(&saved_args, EXEC_BINPRM) != 0) {
// missed entry or not traced
if (!evaluate_scope_filters(p))
return 0;
}
del_args(EXEC_BINPRM);

program_data_t p = {};
if (!init_program_data(&p, ctx, PROCESS_EXECUTION_FAILED))
return 0;

if (!evaluate_scope_filters(&p))
return 0;

int ret_val = PT_REGS_RC(ctx);
if (ret_val == 0)
return 0; // not interested of successful execution - for that we have sched_process_exec

struct linux_binprm *bprm = (struct linux_binprm *) saved_args.args[0];
struct linux_binprm *bprm = (struct linux_binprm *) PT_REGS_PARM1(ctx);
if (bprm == NULL) {
return -1;
}

struct file *file = get_file_ptr_from_bprm(bprm);

const char *path = get_binprm_filename(bprm);
save_str_to_buf(&p.event->args_buf, (void *) path, 0);
save_str_to_buf(&p->event->args_buf, (void *) path, 0);

void *binary_path = get_path_str(__builtin_preserve_access_index(&file->f_path));
save_str_to_buf(&p.event->args_buf, binary_path, 1);
save_str_to_buf(&p->event->args_buf, binary_path, 1);

dev_t binary_device_id = get_dev_from_file(file);
save_to_submit_buf(&p.event->args_buf, &binary_device_id, sizeof(dev_t), 2);
save_to_submit_buf(&p->event->args_buf, &binary_device_id, sizeof(dev_t), 2);

unsigned long binary_inode_number = get_inode_nr_from_file(file);
save_to_submit_buf(&p.event->args_buf, &binary_inode_number, sizeof(unsigned long), 3);
save_to_submit_buf(&p->event->args_buf, &binary_inode_number, sizeof(unsigned long), 3);

u64 binary_ctime = get_ctime_nanosec_from_file(file);
save_to_submit_buf(&p.event->args_buf, &binary_ctime, sizeof(u64), 4);
save_to_submit_buf(&p->event->args_buf, &binary_ctime, sizeof(u64), 4);

umode_t binary_inode_mode = get_inode_mode_from_file(file);
save_to_submit_buf(&p.event->args_buf, &binary_inode_mode, sizeof(umode_t), 5);
save_to_submit_buf(&p->event->args_buf, &binary_inode_mode, sizeof(umode_t), 5);

const char *interpreter_path = get_binprm_interp(bprm);
save_str_to_buf(&p.event->args_buf, (void *) interpreter_path, 6);
save_str_to_buf(&p->event->args_buf, (void *) interpreter_path, 6);

bpf_tail_call(ctx, &prog_array, TAIL_EXEC_BINPRM1);
bpf_tail_call(ctx, &prog_array, tail_call_id);
return -1;
}

SEC("kretprobe/trace_ret_exec_binprm1")
int BPF_KPROBE(trace_ret_exec_binprm1)
statfunc int execute_failed_tail1(struct pt_regs *ctx, u32 tail_call_id)
{
program_data_t p = {};
if (!init_tailcall_program_data(&p, ctx))
Expand All @@ -4965,12 +4945,11 @@ int BPF_KPROBE(trace_ret_exec_binprm1)
int kernel_invoked = (get_task_parent_flags(task) & PF_KTHREAD) ? 1 : 0;
save_to_submit_buf(&p.event->args_buf, &kernel_invoked, sizeof(int), 9);

bpf_tail_call(ctx, &prog_array, TAIL_EXEC_BINPRM2);
bpf_tail_call(ctx, &prog_array, tail_call_id);
return -1;
}

SEC("kretprobe/trace_ret_exec_binprm2")
int BPF_KPROBE(trace_ret_exec_binprm2)
statfunc int execute_failed_tail2(struct pt_regs *ctx)
{
program_data_t p = {};
if (!init_tailcall_program_data(&p, ctx))
Expand All @@ -4986,10 +4965,85 @@ int BPF_KPROBE(trace_ret_exec_binprm2)
}

int ret = PT_REGS_RC(ctx); // needs to be int

return events_perf_submit(&p, ret);
}

bool use_security_bprm_creds_for_exec = false;

SEC("kprobe/exec_binprm")
TRACE_ENT_FUNC(exec_binprm, EXEC_BINPRM);

SEC("kretprobe/exec_binprm")
int BPF_KPROBE(trace_ret_exec_binprm)
{
if (use_security_bprm_creds_for_exec) {
return 0;
}
args_t saved_args;
if (load_args(&saved_args, EXEC_BINPRM) != 0) {
// missed entry or not traced
return 0;
}
del_args(EXEC_BINPRM);

int ret_val = PT_REGS_RC(ctx);
if (ret_val == 0)
return 0; // not interested of successful execution - for that we have sched_process_exec

program_data_t p = {};
if (!init_program_data(&p, ctx, PROCESS_EXECUTION_FAILED))
return 0;
return execute_failed_tail0(ctx, &p, TAIL_EXEC_BINPRM1);
}

SEC("kretprobe/trace_execute_failed1")
int BPF_KPROBE(trace_execute_failed1)
{
return execute_failed_tail1(ctx, TAIL_EXEC_BINPRM2);
}

SEC("kretprobe/trace_execute_failed2")
int BPF_KPROBE(trace_execute_failed2)
{
return execute_failed_tail2(ctx);
}

SEC("kprobe/security_bprm_creds_for_exec")
int BPF_KPROBE(trace_security_bprm_creds_for_exec)
{
use_security_bprm_creds_for_exec = true;
program_data_t p = {};
if (!init_program_data(&p, ctx, SECURITY_BPRM_CREDS_FOR_EXEC))
return 0;
return execute_failed_tail0(ctx, &p, TAIL_SECURITY_BPRM_CREDS_FOR_EXEC1);
}

SEC("kretprobe/trace_security_bprm_creds_for_exec1")
int BPF_KPROBE(trace_security_bprm_creds_for_exec1)
{
return execute_failed_tail1(ctx, TAIL_SECURITY_BPRM_CREDS_FOR_EXEC2);
}

SEC("kretprobe/trace_security_bprm_creds_for_exec2")
int BPF_KPROBE(trace_security_bprm_creds_for_exec2)
{
return execute_failed_tail2(ctx);
}

SEC("tracepoint/execute_finished")
int execute_finished(struct sys_exit_tracepoint_args *args)
{
program_data_t p = {};
if (!init_program_data(&p, args, EXECUTE_FINISHED))
return -1;

if (!evaluate_scope_filters(&p))
return 0;

long exec_ret = args->ret;
return events_perf_submit(&p, exec_ret);
}

SEC("kprobe/security_path_notify")
int BPF_KPROBE(trace_security_path_notify)
{
Expand Down
8 changes: 8 additions & 0 deletions pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ enum event_id_e
HIDDEN_KERNEL_MODULE_SEEKER,
MODULE_LOAD,
MODULE_FREE,
EXECUTE_FINISHED,
SECURITY_BPRM_CREDS_FOR_EXEC,
MAX_EVENT_ID,
NO_EVENT_SUBMIT,
};
Expand Down Expand Up @@ -546,4 +548,10 @@ enum file_modification_op
typedef __u64 stack_trace_t[MAX_STACK_DEPTH];
typedef u32 file_type_t;

struct sys_exit_tracepoint_args {
u64 __pad;
int __syscall_nr;
long ret;
};

#endif
3 changes: 3 additions & 0 deletions pkg/ebpf/probes/probe_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool, kSyms *helpers.Ke
ExecBinprm: NewTraceProbe(KProbe, "exec_binprm", "trace_exec_binprm"),
ExecBinprmRet: NewTraceProbe(KretProbe, "exec_binprm", "trace_ret_exec_binprm"),
SecurityPathNotify: NewTraceProbe(KProbe, "security_path_notify", "trace_security_path_notify"),
SecurityBprmCredsForExec: NewTraceProbe(KProbe, "security_bprm_creds_for_exec", "trace_security_bprm_creds_for_exec"),
TpProbeRegPrioMayExist: NewTraceProbe(KProbe, "tracepoint_probe_register_prio_may_exist", "trace_tracepoint_probe_register_prio_may_exist"),
ModuleLoad: NewTraceProbe(RawTracepoint, "module:module_load", "tracepoint__module__module_load"),
ModuleFree: NewTraceProbe(RawTracepoint, "module:module_free", "tracepoint__module__module_free"),
Expand All @@ -229,6 +230,8 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool, kSyms *helpers.Ke
SignalSchedProcessFork: NewTraceProbe(RawTracepoint, "sched:sched_process_fork", "sched_process_fork_signal"),
SignalSchedProcessExec: NewTraceProbe(RawTracepoint, "sched:sched_process_exec", "sched_process_exec_signal"),
SignalSchedProcessExit: NewTraceProbe(RawTracepoint, "sched:sched_process_exit", "sched_process_exit_signal"),
ExecuteFinished: NewTraceProbe(Tracepoint, "syscalls:sys_exit_execve", "execute_finished"),
ExecuteAtFinished: NewTraceProbe(Tracepoint, "syscalls:sys_exit_execveat", "execute_finished"),
}

if !netEnabled {
Expand Down
3 changes: 3 additions & 0 deletions pkg/ebpf/probes/probes.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const (
ExecBinprm
ExecBinprmRet
SecurityPathNotify
SecurityBprmCredsForExec
HiddenKernelModuleSeeker
TpProbeRegPrioMayExist
HiddenKernelModuleVerifier
Expand All @@ -138,4 +139,6 @@ const (
SignalSchedProcessFork
SignalSchedProcessExec
SignalSchedProcessExit
ExecuteFinished
ExecuteAtFinished
)
18 changes: 18 additions & 0 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,12 @@ func (t *Tracee) initDerivationTable() error {
}
symbolsCollisions := derive.SymbolsCollision(t.contSymbolsLoader, t.config.Policies)

executeFailedGen, err := derive.InitProcessExecuteFailedGenerator()
if err != nil {
logger.Errorw("failed to init derive function for ProcessExecuteFiled", "error", err)
return nil
}

t.eventDerivations = derive.Table{
events.CgroupMkdir: {
events.ContainerCreate: {
Expand Down Expand Up @@ -692,6 +698,18 @@ func (t *Tracee) initDerivationTable() error {
),
},
},
events.ExecuteFinished: {
events.ProcessExecuteFailed: {
Enabled: shouldSubmit(events.ProcessExecuteFailed),
DeriveFunction: executeFailedGen.ProcessExecuteFailed(),
},
},
events.SecurityBprmCredsForExec: {
events.ProcessExecuteFailed: {
Enabled: shouldSubmit(events.ProcessExecuteFailed),
DeriveFunction: executeFailedGen.ProcessExecuteFailed(),
},
},
//
// Network Packet Derivations
//
Expand Down
Loading

0 comments on commit a496a9e

Please sign in to comment.