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 Feb 22, 2024
1 parent e9094a9 commit 9fc0ae6
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 37 deletions.
23 changes: 20 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,29 @@ while providing as much as possible the arguments as used by the kernel.
#### Type
kprobe
#### Purpose
Fetch the arguments of exec_binprm
Fetch the arguments of exec_binprm.
Used for kernels older than 5.8.

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

### security_bprm_creds_for_exec
#### Type
kprobe
#### Purpose
Fetch the arguments for the event.
Used starting from kernel 5.8 has it is not available before.

### sys_enter
#### Type
tracepoint
#### Purpose
Fetch the return code of the execution, to determine if to produce the event.
Used starting from kernel 5.8 as in the `security_bprm_creds_for_exec` hook we are not aware of the execution status.

## Example Use Case

Expand All @@ -43,7 +59,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` symbols is not available in some systems, which might result that the event will fail to load in kernel older than 5.8.
For kernel older than 5.8, the event only covers failed executions that are happening within exec_binprm. Other failures may occur at an earlier stage. Newer versions are not aware of fails before the `security_bprm_creds_for_exec`, which is a bit prior to `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
98 changes: 73 additions & 25 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,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 @@ -4917,19 +4917,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 @@ -4940,11 +4929,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 @@ -4972,12 +4957,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 @@ -4995,12 +4979,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 @@ -5016,8 +4999,73 @@ 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);
}

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

SEC("kretprobe/exec_binprm")
int BPF_KPROBE(trace_ret_exec_binprm)
{
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)
{
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("raw_tracepoint/execute_finished")
int execute_finished(struct bpf_raw_tracepoint_args *ctx)
{
program_data_t p = {};
if (!init_program_data(&p, ctx))
return -1;

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

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

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

// clang-format off
Expand Down
2 changes: 2 additions & 0 deletions pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,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
1 change: 1 addition & 0 deletions pkg/ebpf/probes/probe_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool, kSyms *helpers.Ke
BpfCheck: NewTraceProbe(KProbe, "bpf_check", "trace_bpf_check"),
ExecBinprm: NewTraceProbe(KProbe, "exec_binprm", "trace_exec_binprm"),
ExecBinprmRet: NewTraceProbe(KretProbe, "exec_binprm", "trace_ret_exec_binprm"),
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 Down
1 change: 1 addition & 0 deletions pkg/ebpf/probes/probes.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const (
BpfCheck
ExecBinprm
ExecBinprmRet
SecurityBprmCredsForExec
HiddenKernelModuleSeeker
TpProbeRegPrioMayExist
HiddenKernelModuleVerifier
Expand Down
20 changes: 19 additions & 1 deletion pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,12 @@ func (t *Tracee) initDerivationTable() error {
return func() bool { return t.eventsState[id].Submit > 0 }
}

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 @@ -675,6 +681,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 Expand Up @@ -1083,7 +1101,7 @@ func (t *Tracee) attachProbes() error {
}
eventDefinition := events.Core.GetDefinitionByID(id)
for _, probeDep := range eventDefinition.GetDependencies().GetProbes() {
isRelevant, err := probeDep.IsRelevant(t.config.OSInfo)
isRelevant, err := probeDep.IsOsCompatible(t.config.OSInfo)
if err != nil {
logger.Errorw("Event's probe relevance check failed, assuming not relevant",
"event", eventDefinition.GetName(), "probe", probeDep.GetHandle(),
Expand Down
62 changes: 57 additions & 5 deletions pkg/events/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ const (
HiddenKernelModuleSeeker
ModuleLoad
ModuleFree
ExecuteFinished
SecurityBprmCredsForExec
MaxCommonID
)

Expand Down Expand Up @@ -12878,21 +12880,71 @@ 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.SyscallExit__Internal, required: true},
},
tailCalls: []TailCall{
{"sys_exit_init_tail", "syscall__execveat__execute_failed", []uint32{uint32(Execve), uint32(Execveat)}},
},
},
},
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: true,
relevantKernels: []KernelDependency{{version: "5.8", comparison: NewerEquals}}},
},
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: true,
relevantKernels: []KernelDependency{{version: "5.8", comparison: Older}}},
{handle: probes.ExecBinprmRet, required: true,
relevantKernels: []KernelDependency{{version: "5.8", comparison: Older}}},
},
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
7 changes: 5 additions & 2 deletions pkg/events/definition_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package events

import (
"fmt"

"kernel.org/pub/linux/libs/security/libcap/cap"

"github.com/aquasecurity/libbpfgo/helpers"
Expand Down Expand Up @@ -138,8 +139,8 @@ func (p Probe) IsRequired() bool {
// attempt to attach it won't be initiated in the first place.
func (p Probe) IsOsCompatible(osInfo *helpers.OSInfo) (bool, error) {
for _, kernelDep := range p.relevantKernels {
compatibility, err := kernelDep.isKernelCompatible(osInfo)
if err != nil || compatibility == false {
isCompatible, err := kernelDep.isKernelCompatible(osInfo)
if err != nil || !isCompatible {
return false, err
}
}
Expand Down Expand Up @@ -199,6 +200,8 @@ const (
TailHiddenKernelModuleKset
TailHiddenKernelModuleModTree
TailHiddenKernelModuleNewModOnly
TailSecurityBprmCredsForExec1
TailSecurityBprmCredsForExec2
MaxTail
)

Expand Down

0 comments on commit 9fc0ae6

Please sign in to comment.