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 17, 2024
1 parent 9542061 commit 0774240
Show file tree
Hide file tree
Showing 15 changed files with 445 additions and 34 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pr.yaml
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
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
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
104 changes: 79 additions & 25 deletions pkg/ebpf/c/tracee.bpf.c
Expand Up @@ -1544,7 +1544,7 @@ int BPF_KPROBE(trace_filldir64)
if (!init_program_data(&p, ctx))
return 0;

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

if (!should_submit(HIDDEN_INODES, p.event))
Expand Down Expand Up @@ -5058,19 +5058,8 @@ int BPF_KPROBE(trace_ret_inotify_find_inode)
return events_perf_submit(&p, INOTIFY_WATCH, 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, u32 tail_call_id)
{
args_t saved_args;
if (load_args(&saved_args, EXEC_BINPRM) != 0) {
// missed entry or not traced
return 0;
}
del_args(EXEC_BINPRM);

program_data_t p = {};
if (!init_program_data(&p, ctx))
return 0;
Expand All @@ -5081,11 +5070,7 @@ int BPF_KPROBE(trace_ret_exec_binprm)
if (!should_submit(PROCESS_EXECUTION_FAILED, p.event))
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;
}
Expand Down Expand Up @@ -5113,12 +5098,11 @@ int BPF_KPROBE(trace_ret_exec_binprm)
const char *interpreter_path = get_binprm_interp(bprm);
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 @@ -5136,12 +5120,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, u32 event_id)
{
program_data_t p = {};
if (!init_tailcall_program_data(&p, ctx))
Expand All @@ -5157,8 +5140,79 @@ int BPF_KPROBE(trace_ret_exec_binprm2)
}

int ret = PT_REGS_RC(ctx); // needs to be int
return events_perf_submit(&p, event_id, 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
return execute_failed_tail0(ctx, 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, PROCESS_EXECUTION_FAILED);
}

SEC("kprobe/security_bprm_creds_for_exec")
int BPF_KPROBE(trace_security_bprm_creds_for_exec)
{
use_security_bprm_creds_for_exec = true;
return execute_failed_tail0(ctx, 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, SECURITY_BPRM_CREDS_FOR_EXEC);
}

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

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

if (!should_submit(EXECUTE_FINISHED, p.event))
return 0;

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

SEC("kprobe/security_path_notify")
Expand Down
8 changes: 8 additions & 0 deletions pkg/ebpf/c/types.h
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,
};

Expand Down Expand Up @@ -545,4 +547,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
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"),
ExecuteFinisehd: NewTraceProbe(Tracepoint, "syscalls:sys_exit_execve", "execute_finished"),
ExecuteAtFinisehd: NewTraceProbe(Tracepoint, "syscalls:sys_exit_execveat", "execute_finished"),
}

if !netEnabled {
Expand Down
3 changes: 3 additions & 0 deletions pkg/ebpf/probes/probes.go
Expand Up @@ -128,6 +128,7 @@ const (
ExecBinprm
ExecBinprmRet
SecurityPathNotify
SecurityBprmCredsForExec
HiddenKernelModuleSeeker
TpProbeRegPrioMayExist
HiddenKernelModuleVerifier
Expand All @@ -138,4 +139,6 @@ const (
SignalSchedProcessFork
SignalSchedProcessExec
SignalSchedProcessExit
ExecuteFinisehd
ExecuteAtFinisehd
)
18 changes: 18 additions & 0 deletions pkg/ebpf/tracee.go
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
57 changes: 52 additions & 5 deletions pkg/events/core.go
Expand Up @@ -106,6 +106,8 @@ const (
HiddenKernelModuleSeeker
ModuleLoad
ModuleFree
ExecuteFinished
SecurityBprmCredsForExec
MaxCommonID
)

Expand Down Expand Up @@ -12885,21 +12887,66 @@ var CoreEvents = map[ID]Definition{
{Type: "bool", Name: "load"},
},
},
ExecuteFinished: {
id: ExecuteFinished,
id32Bit: Sys32Undefined,
name: "execute_finished",
version: NewVersion(1, 0, 0),
sets: []string{"proc"},
internal: true,
dependencies: Dependencies{
probes: []Probe{
{handle: probes.ExecuteFinisehd, required: false}, // TODO: Change to required once fallback are supported
{handle: probes.ExecuteAtFinisehd, required: false}, // TODO: Change to required once fallback are supported
},
},
},
SecurityBprmCredsForExec: {
id: SecurityBprmCredsForExec,
id32Bit: Sys32Undefined,
name: "security_bprm_creds_for_exec",
version: NewVersion(1, 0, 0),
sets: []string{"proc"},
internal: true,
dependencies: Dependencies{
probes: []Probe{
{handle: probes.SecurityBprmCredsForExec, required: false}, // TODO: Change to required once fallback are supported
},
tailCalls: []TailCall{
{"prog_array", "trace_security_bprm_creds_for_exec1", []uint32{TailSecurityBprmCredsForExec1}},
{"prog_array", "trace_security_bprm_creds_for_exec2", []uint32{TailSecurityBprmCredsForExec2}},
},
},
params: []trace.ArgMeta{
{Type: "const char*", Name: "path"},
{Type: "const char*", Name: "binary.path"},
{Type: "dev_t", Name: "binary.device_id"},
{Type: "unsigned long", Name: "binary.inode_number"},
{Type: "unsigned long", Name: "binary.ctime"},
{Type: "umode_t", Name: "binary.inode_mode"},
{Type: "const char*", Name: "interpreter_path"},
{Type: "umode_t", Name: "stdin_type"},
{Type: "char*", Name: "stdin_path"},
{Type: "int", Name: "kernel_invoked"},
{Type: "const char*const*", Name: "binary.arguments"},
{Type: "const char*const*", Name: "environment"},
},
},
ProcessExecuteFailed: {
id: ProcessExecuteFailed,
id32Bit: Sys32Undefined,
name: "process_execute_failed",
version: NewVersion(1, 0, 0),
sets: []string{"proc"},
dependencies: Dependencies{
ids: []ID{ExecuteFinished, SecurityBprmCredsForExec}, // For kernel version >= 5.8
probes: []Probe{
{handle: probes.ExecBinprm, required: true},
{handle: probes.ExecBinprmRet, required: true},
{handle: probes.ExecBinprm, required: false},
{handle: probes.ExecBinprmRet, required: false},
},
tailCalls: []TailCall{
{"sys_enter_init_tail", "sys_enter_init", []uint32{uint32(Execve), uint32(Execveat)}},
{"prog_array", "trace_ret_exec_binprm1", []uint32{TailExecBinprm1}},
{"prog_array", "trace_ret_exec_binprm2", []uint32{TailExecBinprm2}},
{"prog_array", "trace_execute_failed1", []uint32{TailExecBinprm1}},
{"prog_array", "trace_execute_failed2", []uint32{TailExecBinprm2}},
},
},
params: []trace.ArgMeta{
Expand Down
2 changes: 2 additions & 0 deletions pkg/events/definition_dependencies.go
Expand Up @@ -141,6 +141,8 @@ const (
TailHiddenKernelModuleKset
TailHiddenKernelModuleModTree
TailHiddenKernelModuleNewModOnly
TailSecurityBprmCredsForExec1
TailSecurityBprmCredsForExec2
MaxTail
)

Expand Down

0 comments on commit 0774240

Please sign in to comment.