From 1bb72641ce7ffba820256f775847da3052b9df4d Mon Sep 17 00:00:00 2001 From: Rafael David Tinoco Date: Fri, 21 Oct 2022 23:41:08 -0300 Subject: [PATCH] capabilities: fix: raise caps ring for privileged operations (#2280) --- pkg/ebpf/events_processor.go | 75 +++++++++++++++++------------ pkg/ebpf/initialization/ksymbols.go | 14 ++++-- pkg/ebpf/tracee.go | 74 +++++++++++++++++----------- 3 files changed, 99 insertions(+), 64 deletions(-) diff --git a/pkg/ebpf/events_processor.go b/pkg/ebpf/events_processor.go index 248064d6755..039a5e42f38 100644 --- a/pkg/ebpf/events_processor.go +++ b/pkg/ebpf/events_processor.go @@ -55,7 +55,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { switch eventId { case events.VfsWrite, events.VfsWritev, events.KernelWrite: - //capture written files + // capture written files if t.config.Capture.FileWrite { filePath, err := parse.ArgStringVal(event, "pathname") if err != nil { @@ -90,7 +90,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { } case events.SchedProcessExec: - //update the process tree with correct comm name + // update the process tree with correct command name if t.config.ProcessInfo { processData, err := t.procInfo.GetElement(event.HostProcessID) if err == nil { @@ -98,25 +98,25 @@ func (t *Tracee) processEvent(event *trace.Event) error { t.procInfo.UpdateElement(event.HostProcessID, processData) } } - - //cache this pid by it's mnt ns + // cache this pid by it's mnt ns if event.ProcessID == 1 { t.pidsInMntns.ForceAddBucketItem(uint32(event.MountNS), uint32(event.HostProcessID)) } else { t.pidsInMntns.AddBucketItem(uint32(event.MountNS), uint32(event.HostProcessID)) } - //capture executed files + // capture executed files if t.config.Capture.Exec || t.config.Output.ExecHash { filePath, err := parse.ArgStringVal(event, "pathname") if err != nil { return fmt.Errorf("error parsing sched_process_exec args: %v", err) } - // path should be absolute, except for e.g memfd_create files + // Should be absolute path, except for e.g memfd_create files if filePath == "" || filePath[0] != '/' { return nil } - - // try to access the root fs via another process in the same mount namespace (since the current process might have already died) + // try to access the root fs via another process in the same mount + // namespace (as the process from the current event might have + // already died) pids := t.pidsInMntns.GetBucket(uint32(event.MountNS)) for _, pid := range pids { // will break on success err = nil @@ -132,51 +132,61 @@ func (t *Tracee) processEvent(event *trace.Event) error { containerId = "host" } capturedFileID := fmt.Sprintf("%s:%s", containerId, sourceFilePath) + // capture exec'ed files ? if t.config.Capture.Exec { destinationDirPath := containerId if err := utils.MkdirAtExist(t.outDir, destinationDirPath, 0755); err != nil { return err } destinationFilePath := filepath.Join(destinationDirPath, fmt.Sprintf("exec.%d.%s", event.Timestamp, filepath.Base(filePath))) - // create an in-memory profile if t.config.Capture.Profile { t.updateProfile(fmt.Sprintf("%s:%d", filepath.Join(destinationDirPath, fmt.Sprintf("exec.%s", filepath.Base(filePath))), castedSourceFileCtime), uint64(event.Timestamp)) } - - //don't capture same file twice unless it was modified + // don't capture same file twice unless it was modified lastCtime, ok := t.capturedFiles[capturedFileID] if !ok || lastCtime != castedSourceFileCtime { - //capture - err = utils.CopyRegularFileByRelativePath(sourceFilePath, t.outDir, destinationFilePath) + + // capture (ring1) + err = t.capabilities.Required(func() error { + return utils.CopyRegularFileByRelativePath( + sourceFilePath, + t.outDir, + destinationFilePath, + ) + }) if err != nil { return err } - //mark this file as captured + + // mark this file as captured t.capturedFiles[capturedFileID] = castedSourceFileCtime } } - + // check exec'ed hash ? if t.config.Output.ExecHash { var hashInfoObj fileExecInfo var currentHash string hashInfoInterface, ok := t.fileHashes.Get(capturedFileID) - - // cast to fileExecInfo if ok { hashInfoObj = hashInfoInterface.(fileExecInfo) } - // Check if cache can be used + // check if cache can be used if ok && hashInfoObj.LastCtime == castedSourceFileCtime { currentHash = hashInfoObj.Hash } else { - currentHash, err = computeFileHashAtPath(sourceFilePath) - if err == nil { - hashInfoObj = fileExecInfo{castedSourceFileCtime, currentHash} - t.fileHashes.Add(capturedFileID, hashInfoObj) - } - } + // ring1 + t.capabilities.Required(func() error { + currentHash, err = computeFileHashAtPath(sourceFilePath) + if err == nil { + hashInfoObj = fileExecInfo{castedSourceFileCtime, currentHash} + t.fileHashes.Add(capturedFileID, hashInfoObj) + } + return nil + }) + + } event.Args = append(event.Args, trace.Argument{ ArgMeta: trace.ArgMeta{Name: "sha256", Type: "const char*"}, Value: currentHash, @@ -189,6 +199,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { } return err } + case events.SchedProcessExit: if t.config.ProcessInfo { if t.config.Capture.NetPerProcess { @@ -200,6 +211,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { go t.deleteProcInfoDelayed(event.HostThreadID) } + case events.SchedProcessFork: if t.config.ProcessInfo { hostTid, err := parse.ArgInt32Val(event, "child_tid") @@ -246,6 +258,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { } t.procInfo.UpdateElement(int(hostTid), processData) } + case events.CgroupMkdir: cgroupId, err := parse.ArgUint64Val(event, "cgroup_id") if err != nil { @@ -286,8 +299,9 @@ func (t *Tracee) processEvent(event *trace.Event) error { } t.containers.CgroupRemove(cgroupId, hId) - // in case FinitModule and InitModule occurs it means that a kernel module was loaded - // and we will want to check if it hooked the syscall table and seq_ops + // In case FinitModule and InitModule occurs, it means that a kernel module + // was loaded and tracee needs to check if it hooked the syscall table and + // seq_ops case events.DoInitModule: _, ok1 := t.events[events.HookedSyscalls] _, ok2 := t.events[events.HookedSeqOps] @@ -330,6 +344,7 @@ func (t *Tracee) processEvent(event *trace.Event) error { } } event.Args[0].Value = hookedFops + case events.PrintNetSeqOps, events.PrintSyscallTable: // Initial event - no need to process if event.Timestamp == 0 { @@ -339,10 +354,10 @@ func (t *Tracee) processEvent(event *trace.Event) error { if err != nil { return fmt.Errorf("failed to apply invoke context on %s event: %s", event.EventName, err) } - - // this was previously event = &withInvokingContext - // however, if applied as such, withInvokingContext will go out of scope and the reference will be moved back - // as such we apply the value internally and not through a referene switch + // This was previously event = &withInvokingContext. However, if applied + // as such, withInvokingContext will go out of scope and the reference + // will be moved back as such we apply the value internally and not + // through a referene switch (*event) = withInvokingContext } diff --git a/pkg/ebpf/initialization/ksymbols.go b/pkg/ebpf/initialization/ksymbols.go index 9f6f103559d..3f4b7189c93 100644 --- a/pkg/ebpf/initialization/ksymbols.go +++ b/pkg/ebpf/initialization/ksymbols.go @@ -1,9 +1,10 @@ package initialization import ( + "unsafe" + "github.com/aquasecurity/libbpfgo" "github.com/aquasecurity/libbpfgo/helpers" - "unsafe" ) var maxKsymNameLen = 64 // Most match the constant in the bpf code @@ -33,11 +34,14 @@ func SendKsymbolsToMap(bpfKsymsMap *libbpfgo.BPFMap, ksymbols map[string]*helper return nil } -// ValidateKsymbolsTable check if the addresses in the table are valid by checking a specific symbol address. -// The reason for the addresses to be invalid is if the capabilities required to read the kallsyms file are not given. -// The chosen symbol used here is "security_file_open" because it is a must-have symbol for tracee to run. +// ValidateKsymbolsTable checks if the addresses in the table are valid by +// checking a specific symbol address. The reason for the addresses to be +// invalid is if the capabilities required to read the kallsyms file are not +// given. The chosen symbol used here is "security_file_open" because it is a +// must-have symbol for tracee to run. func ValidateKsymbolsTable(ksyms *helpers.KernelSymbolTable) bool { - if sym, err := ksyms.GetSymbolByName(globalSymbolOwner, "security_file_open"); err != nil || sym.Address == 0 { + sym, err := ksyms.GetSymbolByName(globalSymbolOwner, "security_file_open") + if err != nil || sym.Address == 0 { return false } return true diff --git a/pkg/ebpf/tracee.go b/pkg/ebpf/tracee.go index 12763904225..0f7673a7940 100644 --- a/pkg/ebpf/tracee.go +++ b/pkg/ebpf/tracee.go @@ -11,6 +11,7 @@ import ( "io" "net" "os" + "runtime/debug" "strconv" "strings" "syscall" @@ -375,20 +376,21 @@ func (t *Tracee) Init() error { // Init kernel symbols map - err = t.capabilities.Requested(func() error { - if initReq.kallsyms { + if initReq.kallsyms { + err = t.capabilities.Requested(func() error { // ring2 + t.kernelSymbols, err = helpers.NewKernelSymbolsMap() if err != nil { t.Close() return fmt.Errorf("error creating symbols map: %v", err) } if !initialization.ValidateKsymbolsTable(t.kernelSymbols) { - return fmt.Errorf("kernel symbols were not loaded currectly. " + - "Make sure tracee-ebpf has the CAP_SYSLOG capability") + return fmt.Errorf("kernel symbols were not loaded currectly") } - } - return nil - }, cap.SYSLOG) + return nil + + }, cap.SYSLOG) + } if err != nil { return err } @@ -1313,24 +1315,33 @@ func (t *Tracee) Run(ctx gocontext.Context) error { // Close cleans up created resources func (t *Tracee) Close() { - if t.probes != nil { - err := t.probes.DetachAll() - if err != nil { - fmt.Fprintf(os.Stderr, "failed to detach probes when closing tracee: %s", err) + err := t.capabilities.Required(func() error { // ring1 + + if t.probes != nil { + err := t.probes.DetachAll() + if err != nil { + return fmt.Errorf("failed to detach probes when closing tracee: %s", err) + } } - } - if t.bpfModule != nil { - t.bpfModule.Close() - } + if t.bpfModule != nil { + t.bpfModule.Close() + } - if t.containers != nil { - err := t.containers.Close() - if err != nil { - fmt.Fprintf(os.Stderr, "failed to clean containers module when closing tracee: %s", err) + if t.containers != nil { + err := t.containers.Close() + if err != nil { + return fmt.Errorf("failed to clean containers module when closing tracee: %s", err) + } } + t.running = false + + return nil + }) + + if err != nil { + fmt.Fprintf(os.Stderr, "%v", err) } - t.running = false } func (t *Tracee) Running() bool { @@ -1458,14 +1469,19 @@ func (t *Tracee) triggerSeqOpsIntegrityCheckCall( } func (t *Tracee) updateKallsyms() error { - kernelSymbols, err := helpers.NewKernelSymbolsMap() - if err != nil { - return err - } - if !initialization.ValidateKsymbolsTable(kernelSymbols) { - return errors.New("invalid ksymbol table") - } + return t.capabilities.Requested(func() error { // ring2 - t.kernelSymbols = kernelSymbols - return nil + kernelSymbols, err := helpers.NewKernelSymbolsMap() + if err != nil { + return err + } + if !initialization.ValidateKsymbolsTable(kernelSymbols) { + debug.PrintStack() + return errors.New("invalid ksymbol table") + } + t.kernelSymbols = kernelSymbols + + return nil + + }, cap.SYSLOG) }