Skip to content

Commit

Permalink
capabilities: fix: raise caps ring for privileged operations (#2280)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaeldtinoco committed Oct 22, 2022
1 parent 69c472b commit 1bb7264
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 64 deletions.
75 changes: 45 additions & 30 deletions pkg/ebpf/events_processor.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -90,33 +90,33 @@ 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 {
processData.Comm = event.ProcessName
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
Expand All @@ -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,
Expand All @@ -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 {
Expand All @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}

Expand Down
14 changes: 9 additions & 5 deletions 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
Expand Down Expand Up @@ -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
Expand Down
74 changes: 45 additions & 29 deletions pkg/ebpf/tracee.go
Expand Up @@ -11,6 +11,7 @@ import (
"io"
"net"
"os"
"runtime/debug"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}

0 comments on commit 1bb7264

Please sign in to comment.