Skip to content

Commit

Permalink
Add set_fs_pwd event (#3919)
Browse files Browse the repository at this point in the history
* Added set_fs_pwd event

* Added test for set_fs_pwd

* Run make fix-fmt

* Added doc for set_fs_pwd event

* Added test to PR workflow

* Fix test for systems without bpf_probe_read_user_str
The bpf_probe_read_user_str helper was added in kernel 5.5, and before that
reads from user space were not guaranteed to work if the architecture doesn't support it.
In practice, ARM systems couldn't read from user space before this helper was introduced.
The test now doesn't expect the unresolved_path (which comes from a userspace read)
to conatin anything if the helper doesn't exist.
  • Loading branch information
oshaked1 authored May 12, 2024
1 parent 5b227f8 commit a67662e
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ env:
PROCTREE_DATA_SOURCE
DNS_DATA_SOURCE
WRITABLE_DATA_SOURCE
SET_FS_PWD
jobs:
#
# DOC VERIFICATION
Expand Down
34 changes: 34 additions & 0 deletions docs/docs/events/builtin/extra/set_fs_pwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# set_fs_pwd

## Intro

set_fs_pwd - An event capturing changes to the current working directory.

## Description

This event captures any changes to the current working directory (typically by using the `chdir` and `fchdir` syscalls).

## Arguments

* `unresolved_pathname`:`const char*`[K,TOCTOU,OPT] - unresolved, user-supplied path which the current working directory is being changed to (only relevant to directory changes using the `chdir` syscall).
* `resolved_pathname`:`const char*`[K] - the fully resolved filesystem path which the current working directory is being changed to.

## Hooks

### set_fs_pwd

#### Type

kprobe

#### Purpose

Catch changes to the current working directory.

## Example Use Case

## Issues

## Related Events

`chdir`, `fchdir`
2 changes: 2 additions & 0 deletions pkg/ebpf/c/common/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ statfunc struct pt_regs *get_task_pt_regs(struct task_struct *task)
#define SYSCALL_FDATASYNC 75
#define SYSCALL_FTRUNCATE 77
#define SYSCALL_GETDENTS 78
#define SYSCALL_CHDIR 80
#define SYSCALL_FCHDIR 81
#define SYSCALL_FCHMOD 91
#define SYSCALL_FCHOWN 93
Expand Down Expand Up @@ -221,6 +222,7 @@ statfunc struct pt_regs *get_task_pt_regs(struct task_struct *task)
#define SYSCALL_FDATASYNC 83
#define SYSCALL_FTRUNCATE 46
#define SYSCALL_GETDENTS UNDEFINED_SYSCALL
#define SYSCALL_CHDIR 49
#define SYSCALL_FCHDIR 50
#define SYSCALL_FCHMOD 52
#define SYSCALL_FCHOWN 55
Expand Down
24 changes: 24 additions & 0 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -5072,6 +5072,30 @@ int BPF_KPROBE(trace_security_path_notify)
return events_perf_submit(&p, 0);
}

SEC("kprobe/set_fs_pwd")
int BPF_KPROBE(trace_set_fs_pwd)
{
program_data_t p = {};
if (!init_program_data(&p, ctx, SET_FS_PWD))
return 0;

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

syscall_data_t *sys = &p.task_info->syscall_data;

void *unresolved_path = NULL;
if (sys->id == SYSCALL_CHDIR)
unresolved_path = (void *) sys->args.args[0];

void *resolved_path = get_path_str((struct path *) PT_REGS_PARM2(ctx));

save_str_to_buf(&p.event->args_buf, unresolved_path, 0);
save_str_to_buf(&p.event->args_buf, resolved_path, 1);

return events_perf_submit(&p, 0);
}

// clang-format off

// Network Packets (works from ~5.2 and beyond)
Expand Down
1 change: 1 addition & 0 deletions pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ enum event_id_e
SECURITY_BPF_PROG,
PROCESS_EXECUTION_FAILED,
SECURITY_PATH_NOTIFY,
SET_FS_PWD,
HIDDEN_KERNEL_MODULE_SEEKER,
MODULE_LOAD,
MODULE_FREE,
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 @@ -222,6 +222,7 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool, kSyms *helpers.Ke
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"),
SetFsPwd: NewTraceProbe(KProbe, "set_fs_pwd", "trace_set_fs_pwd"),
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 @@ -129,6 +129,7 @@ const (
ExecBinprmRet
SecurityPathNotify
SecurityBprmCredsForExec
SetFsPwd
HiddenKernelModuleSeeker
TpProbeRegPrioMayExist
HiddenKernelModuleVerifier
Expand Down
26 changes: 26 additions & 0 deletions pkg/events/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const (
SecurityBpfProg
ProcessExecuteFailed
SecurityPathNotify
SetFsPwd
HiddenKernelModuleSeeker
ModuleLoad
ModuleFree
Expand Down Expand Up @@ -13010,6 +13011,31 @@ var CoreEvents = map[ID]Definition{
{Type: "unsigned int", Name: "obj_type"},
},
},
SetFsPwd: {
id: SetFsPwd,
id32Bit: Sys32Undefined,
name: "set_fs_pwd",
dependencies: Dependencies{
probes: []Probe{
{handle: probes.SetFsPwd, required: true},
{handle: probes.SyscallEnter__Internal, required: true},
},
tailCalls: []TailCall{
{
"sys_enter_init_tail",
"sys_enter_init",
[]uint32{
uint32(Chdir),
},
},
},
},
sets: []string{"syscalls"},
params: []trace.ArgMeta{
{Type: "const char*", Name: "unresolved_path"},
{Type: "const char*", Name: "resolved_path"},
},
},
//
// Begin of Signal Events (Control Plane)
//
Expand Down
96 changes: 96 additions & 0 deletions tests/e2e-inst-signatures/e2e-set_fs_pwd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package main

import (
"fmt"
"strings"

libbfgo "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 e2eSetFsPwd struct {
cb detect.SignatureHandler
hasReadUser bool
}

func (sig *e2eSetFsPwd) Init(ctx detect.SignatureContext) error {
sig.cb = ctx.Callback

// Find if this system has the bpf_probe_read_user_str helper.
// If it doesn't we won't expect the unresolved path to contain anything
ksyms, err := libbfgo.NewKernelSymbolTable()
if err != nil {
return err
}
_, err = ksyms.GetSymbolByName("bpf_probe_read_user_str")
if err != nil {
sig.hasReadUser = false
} else {
sig.hasReadUser = true
}

return nil
}

func (sig *e2eSetFsPwd) GetMetadata() (detect.SignatureMetadata, error) {
return detect.SignatureMetadata{
ID: "SET_FS_PWD",
EventName: "SET_FS_PWD",
Version: "0.1.0",
Name: "set_fs_pwd Test",
Description: "Instrumentation events E2E Tests: set_fs_pwd",
Tags: []string{"e2e", "instrumentation"},
}, nil
}

func (sig *e2eSetFsPwd) GetSelectedEvents() ([]detect.SignatureEventSelector, error) {
return []detect.SignatureEventSelector{
{Source: "tracee", Name: "set_fs_pwd"},
}, nil
}

func (sig *e2eSetFsPwd) 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 "set_fs_pwd":
unresolvedPath, err := helpers.GetTraceeStringArgumentByName(eventObj, "unresolved_path")
if sig.hasReadUser && err != nil {
return err
}

resolvedPath, err := helpers.GetTraceeStringArgumentByName(eventObj, "resolved_path")
if err != nil {
return err
}

// check expected values from test for detection

if (sig.hasReadUser && !strings.HasSuffix(unresolvedPath, "/test_link")) || !strings.HasSuffix(resolvedPath, "/test_dir") {
return nil
}

m, _ := sig.GetMetadata()

sig.cb(&detect.Finding{
SigMetadata: m,
Event: event,
Data: map[string]interface{}{},
})
}

return nil
}

func (sig *e2eSetFsPwd) OnSignal(s detect.Signal) error {
return nil
}

func (sig *e2eSetFsPwd) Close() {}
1 change: 1 addition & 0 deletions tests/e2e-inst-signatures/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var ExportedSignatures = []detect.Signature{
&e2eDnsDataSource{},
&e2eWritableDatasourceSig{},
&e2eSecurityPathNotify{},
&e2eSetFsPwd{},
}

var ExportedDataSources = []detect.DataSource{
Expand Down
14 changes: 14 additions & 0 deletions tests/e2e-inst-signatures/scripts/set_fs_pwd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash

exit_err() {
echo -n "ERROR: "
echo "$@"
exit 1
}

mkdir test_dir || exit_err "failed creating dir"
ln -s test_dir test_link || exit_err "failed creating link"
cd test_link || exit_err "failed changing directory"
cd .. || exit_err "failed changing directory back"
rm test_link || exit_err "failed removing link"
rm -r test_dir || exit_err "failed removing dir"
2 changes: 1 addition & 1 deletion tests/e2e-inst-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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,process_execute,tracee-ebpf,writev \
--scope comm=echo,mv,ls,tracee,proctreetester,ping,ds_writer,fsnotify_tester,process_execute,tracee-ebpf,writev,set_fs_pwd.sh \
--dnscache enable \
--grpc-listen-addr unix:/tmp/tracee.sock \
--events "$TEST" &
Expand Down

0 comments on commit a67662e

Please sign in to comment.