Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add set_fs_pwd event #3919

Merged
merged 6 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to put the script name in the comm filter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cd command which is used to trigger the event is a built-in bash command and not a new executable, so it runs from the script's process

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but we can't get the script name set_fs_pwd.sh in the comm field, right?
in the comm field you will only see the executable name (e.g. bash), not the script name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the script is run directly (which it is), the script name will be in the comm field:
image

--dnscache enable \
--grpc-listen-addr unix:/tmp/tracee.sock \
--events "$TEST" &
Expand Down