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

Added trace, build allowlist, and enforce modes #2

Merged
merged 29 commits into from
Oct 28, 2024

Conversation

carminecesarano
Copy link
Collaborator

Todo:

  • test three modes on real packages

Copy link
Member

@algomaster99 algomaster99 left a comment

Choose a reason for hiding this comment

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

I suggested one change and some comments. Do answer them when you have time.

Comment on lines 29 to 35
if sym.Value != 0 {
Cache = append(Cache, SymbolInfo{
Name: sym.Name,
Start: sym.Value,
End: sym.Value + sym.Size,
})
}
Copy link
Member

Choose a reason for hiding this comment

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

What symbols from elf file are you storing here and for what?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since goleash retrieves only the addresses of invoked functions from the stack, we need to translate these addresses into function symbols from the elf binary. Function symbols from the binary come along with package names, which is how goleash can infer the caller dependency.

note: i don't resolve symbols at runtime during enforcement; instead, I resolve and cache all the function symbols once during initialization, for performance reasons.

func runTraceMode(binaryPath string) {
setupAndRun(binaryPath, func(event ebpfEvent, stackTrace []uint64, objs *ebpfObjects) {
// Just log the event
logEvent(event, stackTrace, objs)
Copy link
Member

Choose a reason for hiding this comment

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

We would only want to log the violation of the event, eventually right? This would log complete stacktrace based on all eBPF comments, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For now, for each execution mode, I am logging everything for debugging and testing purpose. We can eventually add a debug mode flag for this.

But yes, in general, we want to log only violation events for the enforcement mode.
We will log everything, as logEvent does, for a "monitoring" mode (now called 'trace'), if we stille need this mode.

syscalls := make(map[string]map[int]bool)
setupAndRun(binaryPath, func(event ebpfEvent, stackTrace []uint64, objs *ebpfObjects) {
callerPackage := stackanalyzer.GetCallerPackage(stackTrace)
if callerPackage != "" {
Copy link
Member

Choose a reason for hiding this comment

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

In what case callerPackage is ""?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The callerPackage can be empty in cases where the package name doesn't have the prefix 'github.com'. The logic is implemented into stackanalyzer.GetCallerPackages()

In this current implementation, I was allowlisting (and also enforcing, in runEnforceMode) only third-party packages hosted on github. This implementation skips the generation and enforcement of the allowlist for packages from the Go runtime, the main package, and other local custom packages.

TODO: we should work on a more robust logic for package tracing. For example, third-party packages can be hosted elsewhere (e.g., k8s.io, google.golang.org, etc).

Copy link
Member

@algomaster99 algomaster99 Oct 10, 2024

Choose a reason for hiding this comment

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

elsewhere (e.g., k8s.io, google.golang.org, etc).

Is there an exhausitve list? Could you host packages on custom URL and download go packages from there?

log.Println("Received signal, exiting..")
return
func runBuildMode(binaryPath string) {
syscalls := make(map[string]map[int]bool)
Copy link
Member

Choose a reason for hiding this comment

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

I am bit confused about what this make function does. Could you explain? 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

make in Go create a slice, a dynamic data structure.

We are creating a map of maps. The key of the outer map is the callerPackage name (string). For each callerPackage, we have an inner map, which the keys are syscall numbers (int). Each syscall number have an associated boolean value representing "allowed" or "denied". This structure allows for efficient allowlist check.

Comment on lines 37 to 38
case "build":
runBuildMode(*binaryPath)
Copy link
Member

Choose a reason for hiding this comment

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

Let's rename this to augment because we take input as allowlist from capslock and we will augment it as we discussed here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree and i like this new name.

@algomaster99
Copy link
Member

@carminecesarano I tried adding a test resource in order to test augment mode. The test resource lies in testpks/basiccgo. The allowlist.json is created using capslock. The augment mode should ideally add CAPABILITY_FILES to the allowlist and that is what we want to test for.
However, running sudo ./bpf_loader -binary $(realpath ../testpkgs/basiccgo/basiccgo) -mode build returns an empty allowlist.

{
  "dependencies": {}
}

What changes should I make to tracer to achieve this?

@carminecesarano
Copy link
Collaborator Author

@algomaster99 there is one more hard-coded parameter to set, which is target_comm in the eBPF program.

This is the name of the command to trace. In your case, it should be basiccgo. We should make this configurable from the CLI.

@algomaster99
Copy link
Member

Still unable to get a non-empty allowlist. Let's sit on Friday to pair program. I get some output from the eBPF program but nowhere it prints the stack trace that has go function for read system call.

@algomaster99
Copy link
Member

The problems with test as of now:

  1. Does not capture after "Tracking syscalls".
  2. We need "sudo" to run the tests.

@algomaster99
Copy link
Member

algomaster99 commented Oct 21, 2024

I managed to solve 1 will push test soon :) The fix was putting time.Sleep(2 * time.Second) before executing basiccgo.

@algomaster99
Copy link
Member

Now we need CI

@algomaster99
Copy link
Member

We have a working CI now. I will fix the functionality for building allowlist and then merge.

@algomaster99
Copy link
Member

Gives a good failure message:

Expected output to contain Go caller function: executeMaliciousCGO, got 12:12:36 Tracking syscalls...
        
        12:12:38 Invoked syscall: 12	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60ea8b
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 158	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e602cb4
        0x1
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 21	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60f9ab
        0x7fc55e60883c
        0x40
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 257	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fb18
        0x7fc55e5f3982
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 262	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60f8de
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 17	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fb9e
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 10	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fd2b
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 3	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60f9db
        0x7fc55e5f3601
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
        
        12:12:38 Invoked syscall: 9	pid: 5069	comm: basiccgo
        12:12:38 Stack Trace:
        0x7fc55e60fcb7
        12:12:38 Go caller function: 
        12:12:38 Go caller package: 
FAIL
exit status 1
FAIL	github.com/chains-project/goleash/track_syscalls	2.007s

@algomaster99
Copy link
Member

@carminecesarano This is the structure of our local go package

├── basiccgo
│   ├── allowlist.json
│   ├── basiccgo
│   ├── go.mod
│   └── main.go
├── example.com
│   ├── filereader
│   │   ├── filereader.go
│   │   ├── hello.c
│   │   └── hello.h
│   └── go.mod
└── Makefile

go.mod file for module basiccgo looks like this now:

module basiccgo

go 1.23.2

require "example.com" v0.0.0
replace (
	"example.com" v0.0.0 => "../example.com"
)

StopTracer(tracer)

var expectedCallerFunction = "Go caller function: example.com/filereader.ExecuteMaliciousCGO"
var expectedCallerPackage = "Go caller package: example.com"
Copy link
Member

@algomaster99 algomaster99 Oct 27, 2024

Choose a reason for hiding this comment

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

@carminecesarano should this not be "Go caller module: example.com"?

The package would be 'Go caller package: example.com/filreader".

@algomaster99 algomaster99 merged commit 1fcd1aa into main Oct 28, 2024
2 checks passed
@algomaster99 algomaster99 deleted the syscall-enforcement branch October 28, 2024 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants