diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index db605a8ae02e..1a05e400babc 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -61,6 +61,7 @@ env: DNS HTTP INSTTESTS: > + PROCESS_EXECUTE_FAILED VFS_WRITE FILE_MODIFICATION HOOKED_SYSCALL diff --git a/docs/docs/events/builtin/extra/process_execute_failed.md b/docs/docs/events/builtin/extra/process_execute_failed.md index 58241f661133..988fdb271f18 100644 --- a/docs/docs/events/builtin/extra/process_execute_failed.md +++ b/docs/docs/events/builtin/extra/process_execute_failed.md @@ -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 @@ -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 \ No newline at end of file diff --git a/pkg/ebpf/c/maps.h b/pkg/ebpf/c/maps.h index 69dac1628982..733963fff454 100644 --- a/pkg/ebpf/c/maps.h +++ b/pkg/ebpf/c/maps.h @@ -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 }; diff --git a/pkg/ebpf/c/tracee.bpf.c b/pkg/ebpf/c/tracee.bpf.c index f1232b891e13..644fa89bbe77 100644 --- a/pkg/ebpf/c/tracee.bpf.c +++ b/pkg/ebpf/c/tracee.bpf.c @@ -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)) @@ -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; @@ -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; } @@ -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)) @@ -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)) @@ -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") diff --git a/pkg/ebpf/c/types.h b/pkg/ebpf/c/types.h index 186d4fa99a10..9d0ad9361418 100644 --- a/pkg/ebpf/c/types.h +++ b/pkg/ebpf/c/types.h @@ -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, }; @@ -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 diff --git a/pkg/ebpf/probes/probe_group.go b/pkg/ebpf/probes/probe_group.go index f1de5897d108..22a47ffdddf2 100644 --- a/pkg/ebpf/probes/probe_group.go +++ b/pkg/ebpf/probes/probe_group.go @@ -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"), @@ -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 { diff --git a/pkg/ebpf/probes/probes.go b/pkg/ebpf/probes/probes.go index d673c22f4580..3d21df9a5281 100644 --- a/pkg/ebpf/probes/probes.go +++ b/pkg/ebpf/probes/probes.go @@ -128,6 +128,7 @@ const ( ExecBinprm ExecBinprmRet SecurityPathNotify + SecurityBprmCredsForExec HiddenKernelModuleSeeker TpProbeRegPrioMayExist HiddenKernelModuleVerifier @@ -138,4 +139,6 @@ const ( SignalSchedProcessFork SignalSchedProcessExec SignalSchedProcessExit + ExecuteFinisehd + ExecuteAtFinisehd ) diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 83af6a95f970..1ed46ce26122 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -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: { @@ -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 // diff --git a/pkg/events/core.go b/pkg/events/core.go index 18842ae4f895..6afb0ca467ec 100644 --- a/pkg/events/core.go +++ b/pkg/events/core.go @@ -106,6 +106,8 @@ const ( HiddenKernelModuleSeeker ModuleLoad ModuleFree + ExecuteFinished + SecurityBprmCredsForExec MaxCommonID ) @@ -12885,6 +12887,51 @@ 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, @@ -12892,14 +12939,14 @@ var CoreEvents = map[ID]Definition{ 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{ diff --git a/pkg/events/definition_dependencies.go b/pkg/events/definition_dependencies.go index 4be3186a99e9..044c639e7116 100644 --- a/pkg/events/definition_dependencies.go +++ b/pkg/events/definition_dependencies.go @@ -141,6 +141,8 @@ const ( TailHiddenKernelModuleKset TailHiddenKernelModuleModTree TailHiddenKernelModuleNewModOnly + TailSecurityBprmCredsForExec1 + TailSecurityBprmCredsForExec2 MaxTail ) diff --git a/pkg/events/derive/process_execute_failed.go b/pkg/events/derive/process_execute_failed.go new file mode 100644 index 000000000000..352f36fd8f30 --- /dev/null +++ b/pkg/events/derive/process_execute_failed.go @@ -0,0 +1,127 @@ +package derive + +import ( + "fmt" + + lru "github.com/hashicorp/golang-lru/v2" + + "github.com/aquasecurity/tracee/pkg/events" + "github.com/aquasecurity/tracee/types/trace" +) + +// ExecFailedGenerator is the object which implement the ProcessExecuteFailed event derivation +type ExecFailedGenerator struct { + execEndInfo *lru.Cache[int, execEndInfo] + baseEvents *lru.Cache[int, *trace.Event] + deriveBase deriveBase +} + +// InitProcessExecuteFailedGenerator initialize a new generator for the ProcessExecuteFailed event. +func InitProcessExecuteFailedGenerator() (*ExecFailedGenerator, error) { + // The cache is only between mid-execution to its end, so we only need cache of about 1 per core. + // For now, we assume that the current value is sufficient + const executionEventsCacheSize = 16 + + executeProcsCache, err := lru.New[int, execEndInfo](executionEventsCacheSize) + if err != nil { + return nil, err + } + executeParamsCache, err := lru.New[int, *trace.Event](executionEventsCacheSize) + if err != nil { + return nil, err + } + return &ExecFailedGenerator{ + execEndInfo: executeProcsCache, + baseEvents: executeParamsCache, + deriveBase: makeDeriveBase(events.ProcessExecuteFailed), + }, nil +} + +// ProcessExecuteFailed return the DeriveFunction for the "process_execute_failed" event. +func (gen *ExecFailedGenerator) ProcessExecuteFailed() DeriveFunction { + return func(event trace.Event) ([]trace.Event, []error) { + var errs []error + var derivedEvents []trace.Event + derivedEvent, err := gen.deriveEvent(&event) + if err != nil { + errs = append(errs, err) + } + if derivedEvent != nil { + derivedEvents = append(derivedEvents, *derivedEvent) + } + return derivedEvents, errs + } +} + +// execEndInfo stores information about the end of process execution operation. +type execEndInfo struct { + returnCode int + timestamp int +} + +// deriveEvent is the main logic, which will try to derive the event from the given event. +func (gen *ExecFailedGenerator) deriveEvent(event *trace.Event) ( + *trace.Event, error, +) { + switch events.ID(event.EventID) { + case events.SecurityBprmCredsForExec: + return gen.handleExecBaseEvent(event) + case events.ExecuteFinished: + return gen.handleExecFinished(event) + default: + return nil, fmt.Errorf("unsupported event %s", event.EventName) + } +} + +// handleExecFinished will derive the event if all the event parts were received. +// Else it will cache the finished exec info for future use. +func (gen *ExecFailedGenerator) handleExecFinished(event *trace.Event) (*trace.Event, error) { + execInfo := execEndInfo{ + returnCode: event.ReturnValue, + timestamp: event.Timestamp, + } + securityExecEvent, ok := gen.baseEvents.Get(event.HostProcessID) + if ok { + gen.execEndInfo.Remove(event.HostProcessID) + if !isFailedExec(execInfo.returnCode) { + return nil, nil + } + return gen.generateEvent(securityExecEvent, execInfo) + } + gen.execEndInfo.Add(event.HostProcessID, execInfo) + return nil, nil +} + +// handleExecBaseEvent will derive the event if the event parts were received, else will cache +// the base event for future use +func (gen *ExecFailedGenerator) handleExecBaseEvent(event *trace.Event) (*trace.Event, error) { + execInfo, ok := gen.execEndInfo.Get(event.HostProcessID) + // We don't have the execution end info - cache current event and wait for it to be received + // This is the expected flow, as the execution finished event come chronology after + if !ok { + gen.baseEvents.Add(event.HostProcessID, event) + return nil, nil + } + gen.execEndInfo.Remove(event.HostProcessID) + if !isFailedExec(execInfo.returnCode) { + return nil, nil + } + return gen.generateEvent(event, execInfo) +} + +// generateEvent create the ProcessExecuteFailed event from its parts +func (gen *ExecFailedGenerator) generateEvent( + baseEvent *trace.Event, + execInfo execEndInfo, +) (*trace.Event, error) { + newEvent := *baseEvent + newEvent.Timestamp = execInfo.timestamp + newEvent.EventID = gen.deriveBase.ID + newEvent.EventName = gen.deriveBase.Name + newEvent.ReturnValue = execInfo.returnCode + return &newEvent, nil +} + +func isFailedExec(returnCode int) bool { + return returnCode < 0 +} diff --git a/tests/e2e-inst-signatures/e2e-process_execute_failed.go b/tests/e2e-inst-signatures/e2e-process_execute_failed.go new file mode 100644 index 000000000000..fbab3405687f --- /dev/null +++ b/tests/e2e-inst-signatures/e2e-process_execute_failed.go @@ -0,0 +1,109 @@ +package main + +import ( + "fmt" + "strings" + "sync" + + bpfhelpers "github.com/aquasecurity/libbpfgo/helpers" + + "github.com/aquasecurity/tracee/signatures/helpers" + "github.com/aquasecurity/tracee/types/detect" + "github.com/aquasecurity/tracee/types/protocol" + "github.com/aquasecurity/tracee/types/trace" +) + +type e2eProcessExecuteFailed struct { + cb detect.SignatureHandler + osInfo *bpfhelpers.OSInfo + markUnsupportedKernel sync.Once +} + +func (sig *e2eProcessExecuteFailed) Init(ctx detect.SignatureContext) error { + sig.cb = ctx.Callback + var err error + sig.osInfo, err = bpfhelpers.GetOSInfo() + if err != nil { + return err + } + return nil +} + +func (sig *e2eProcessExecuteFailed) GetMetadata() (detect.SignatureMetadata, error) { + return detect.SignatureMetadata{ + ID: "PROCESS_EXECUTE_FAILED", + EventName: "PROCESS_EXECUTE_FAILED", + Version: "0.1.0", + Name: "Process Execute Failed Test", + Description: "Instrumentation events E2E Tests: Process Execute Failed", + Tags: []string{"e2e", "instrumentation"}, + }, nil +} + +func (sig *e2eProcessExecuteFailed) GetSelectedEvents() ([]detect.SignatureEventSelector, error) { + return []detect.SignatureEventSelector{ + {Source: "tracee", Name: "process_execute_failed"}, + {Source: "tracee", Name: "init_namespaces"}, + }, nil +} + +func (sig *e2eProcessExecuteFailed) OnEvent(event protocol.Event) error { + eventObj, ok := event.Payload.(trace.Event) + if !ok { + return fmt.Errorf("failed to cast event's payload") + } + + switch eventObj.EventName { + case "init_namespaces": + // The event is not guaranteed to work for kernel version 5.7 or older, making the test + // unreliable. + // Ensure that the tests will pass (unless an error occur). + var err error + sig.markUnsupportedKernel.Do( + func() { + var comp bpfhelpers.KernelVersionComparison + comp, err = sig.osInfo.CompareOSBaseKernelRelease("5.7") + if err != nil { + return + } + if comp == bpfhelpers.KernelVersionNewer { // < V5.8 + m, _ := sig.GetMetadata() + + sig.cb(&detect.Finding{ + SigMetadata: m, + Event: event, + }) + } + }) + if err != nil { + return err + } + case "process_execute_failed": + filePath, err := helpers.GetTraceeStringArgumentByName(eventObj, "path") + if err != nil { + return err + } + + // check expected values from test for detection + + if !strings.HasSuffix(filePath, "test.sh") { + return nil + } + + m, _ := sig.GetMetadata() + + sig.cb(&detect.Finding{ + SigMetadata: m, + Event: event, + Data: map[string]interface{}{}, + }) + } + + return nil +} + +func (sig *e2eProcessExecuteFailed) OnSignal(s detect.Signal) error { + return nil +} + +func (sig *e2eProcessExecuteFailed) Close() {} diff --git a/tests/e2e-inst-signatures/export.go b/tests/e2e-inst-signatures/export.go index e7771f89952b..c35999d95771 100644 --- a/tests/e2e-inst-signatures/export.go +++ b/tests/e2e-inst-signatures/export.go @@ -7,6 +7,7 @@ import ( var ExportedSignatures = []detect.Signature{ // Instrumentation e2e signatures + &e2eProcessExecuteFailed{}, &e2eVfsWrite{}, &e2eFileModification{}, &e2eSecurityInodeRename{}, diff --git a/tests/e2e-inst-signatures/scripts/process_execute_failed.sh b/tests/e2e-inst-signatures/scripts/process_execute_failed.sh new file mode 100755 index 000000000000..8b349b14d46d --- /dev/null +++ b/tests/e2e-inst-signatures/scripts/process_execute_failed.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +exit_err() { + echo "ERROR: $@" + exit 1 +} + +TEST_SCRIPT=/tmp/test.sh + +echo "echo hello > /dev/null" > "$TEST_SCRIPT" +chmod +x "$TEST_SCRIPT" +ls . > /dev/null # necessary for the execution path to work +# Executing the script will fail as this is not an ELF and does not start with `#!` +# This will produce the `process_execute_failed` event. +# Afterwards it will try to run it with bash +exec "$TEST_SCRIPT" & +sleep 2 +rm "$TEST_SCRIPT" \ No newline at end of file diff --git a/tests/e2e-inst-test.sh b/tests/e2e-inst-test.sh index a4e9b53ee9fc..a7b744834e7f 100755 --- a/tests/e2e-inst-test.sh +++ b/tests/e2e-inst-test.sh @@ -129,7 +129,7 @@ for TEST in $TESTS; do --output option:parse-arguments \ --log file:$SCRIPT_TMP_DIR/tracee-log-$$ \ --signatures-dir "$SIG_DIR" \ - --scope comm=echo,mv,ls,tracee,proctreetester,ping,ds_writer,fsnotify_tester \ + --scope comm=echo,mv,ls,tracee,proctreetester,ping,ds_writer,fsnotify_tester,process_execute,tracee-ebpf \ --dnscache enable \ --grpc-listen-addr unix:/tmp/tracee.sock \ --events "$TEST" &