google / santa

A binary authorization and monitoring system for macOS
https://santa.dev
Apache License 2.0
4.44k stars 297 forks source link

Transitive Rules with Golang #561

Closed Ryxias closed 1 year ago

Ryxias commented 3 years ago

Hello, I'm attempting to use Santa in lockdown mode with golang.

Problem Description

I've marked go, and the go tools compile, asm, and link as ALLOWLIST_COMPILER and this is working wonderfully. However I've hit a snag with test files.

When I run go test ./my/pkg golang compiles a bunch of binaries into /var/... and unfortunately these binaries do not seem to pick up Transitive rules and get blocked. This forces me to manually add binary rules for those test files on the sync server every time my golang code changes, which isn't desirable for obvious reasons.

The weird thing is when I run go test ./my/pkg -o my.test, this also compiles a binary into /var/... and then moves it into my local directory. It then runs the test and in this case the test file runs with no problems.

The ... "Workaround"

Instead of running go test ./... to test all of my files, my current workaround recursively iterates through all pkg directories in my go project, and repeatedly runs go test . -o temp.test and then deletes the temp.test file. This... "works". It's just not pretty, and I'm really hoping for a more elegant solution.

What I've Tried

I initially considered a race condition between go compiling the test file, generating a transitive rule, and go running the tests. This doesn't seem to be the case, as the in the santa.log file, there doesn't seem to be any ALLOWLIST event for the compiled binary go go test ./my/pkg, but for some reason there is for go test ./my/pkg -o my.test.

I then considered perhaps go test -o uses a different underlying binary for compilation, and perhaps go test is missing a compiler rule. I did some process analysis and found that all subprocesses of my go test used the go compile, asm, and link tools. All of which have compiler rules.

I'm kind of at a loss... any ideas?

pmarkowsky commented 3 years ago

Hi,

First off thanks for the bug report. I'm trying to reproduce and running into similar things. Just running go test inside a simple package and with compiler rules for

I'm seeing the following, where the transitive rule doesn't seem to be getting generated after link produces the binary, but if you set the -o flag the rename is what seems to produce the transitive rule.

For example just running go test inside of a package that basically testing hello world.

[2021-07-16T18:07:19.274Z] I santad: action=EXEC|decision=ALLOW|reason=COMPILER|sha256=764f3c1eb0b427eec9ce7b7c72c2efea9b15b1cd4e7ada362a8315e986f18a89|cert_sha256=345a8e098bd04794aaeefda8c9ef56a0bf3d3706d67d35bc0e23f11bb3bffce5|cert_cn=Developer ID Application: Google, Inc. (EQHXZ8M8AV)|pid=5869|pidversion=13367|ppid=5847|uid=501|user=peterm|gid=20|group=staff|mode=M|path=/usr/local/go/pkg/tool/darwin_amd64/link|args=/usr/local/go/pkg/tool/darwin_amd64/link -o /var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build1081522501/b001/greeter.test -importcfg /var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build1081522501/b001/importcfg.link -s -w -buildmode=exe -buildid=GiUCRo2qdtRhfTweCgCI/daytiD6FgjELFO0IgYyP/I9LRcYN5s7KYFpEfVL-C/GiUCRo2qdtRhfTweCgCI -extld=clang /Users/peterm/Library/Caches/go-build/06/06a06fdfda303a2566f5587663c04d45744a7f4f107674a7ce837912d60fe583-d
[2021-07-16T18:07:19.402Z] I santad: action=EXEC|decision=ALLOW|reason=UNKNOWN|explain=Signature ignored due to error: -67062|sha256=faf08ecdd7926f63903dce7b46e558158e8725f5f6aa20342051a7798d9e2840|pid=5870|pidversion=13369|ppid=5847|uid=501|user=peterm|gid=20|group=staff|mode=M|path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build1081522501/b001/greeter.test|args=/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build1081522501/b001/greeter.test -test.paniconexit0 -test.timeout=10m0s

E.g. when I run go test -o xxx in a simple hello world package called greeter you'll see that greeter.test gets renamed by the go tool to xxx and then xxx is allowed to execute via a transitive rule.

[2021-07-16T17:33:54.759Z] I santad: action=RENAME|path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build652253310/b001/greeter.test|newpath=/Users/peterm/src/protect_workspace/src/github.com/pmarkowsky/greeter/xxx|pid=4968|pidversion=11298|ppid=971|process=go|processpath=/usr/local/go/bin/go|uid=501|user=peterm|gid=20|group=staff
[2021-07-16T18:51:22.793Z] I santad: action=EXEC|decision=ALLOW|reason=TRANSITIVE|explain=Signature ignored due to error: -67062|sha256=05c37dd436baaba142a3e620216a5485d1d2a16dea7d4513a9e12788d3501fec|pid=7064|pidversion=16104|ppid=971|uid=501|user=peterm|gid=20|group=staff|mode=M|path=/Users/peterm/src/protect_workspace/src/github.com/pmarkowsky/greeter/xxx|args=./xxx
pmarkowsky commented 3 years ago

Ok, digging into this more. Looking at the logs with /usr/bin/log --predicate 'sender=="com.google.santa.daemon" to see if we're creating the transitive rules is showing that in the first case we're only making rules.

With go test we seem to be missing the close calls to the temp files files generated by go's linker:

bash-3.2# log stream --predicate 'sender=="com.google.santa.daemon"'
Filtering the log data using "sender == "com.google.santa.daemon""
Timestamp                       Thread     Type        Activity             PID    TTL  
2021-07-16 15:51:10.736766-0400 0x1977c    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2708919370/b001/_testmain.go pid=7899
2021-07-16 15:51:12.059364-0400 0x198ef    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2708919370/b055/vet.cfg pid=7899
2021-07-16 15:51:12.121263-0400 0x19911    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2708919370/b001/importcfg.link pid=7899

With go test -o xxx2 we seem to be getting all of the close calls from the linker :

bash-3.2# log stream --predicate 'sender="com.google.santa.daemon"'
Filtering the log data using "sender == "com.google.santa.daemon""
Timestamp                       Thread     Type        Activity             PID    TTL  
2021-07-16 16:00:28.445026-0400 0x1aa49    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2233019274/b001/_testmain.go pid=8222
2021-07-16 16:00:29.785935-0400 0x1ae34    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2233019274/b055/vet.cfg pid=8222
2021-07-16 16:00:29.845599-0400 0x1aa49    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2233019274/b001/importcfg.link pid=8222
2021-07-16 16:00:30.078705-0400 0x1aa49    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: CLOSE: creating a transitive rule: path=/private/var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2233019274/b001/greeter.test pid=8222
2021-07-16 16:00:30.120360-0400 0x1ae34    Default     0x0                  743    0    com.google.santa.daemon: I com.google.santa.daemon: RENAME: creating a transitive rule: path=/Users/peterm/src/protect_workspace/src/github.com/pmarkowsky/greeter/xxx2 pid=8222

While running a custom dtrace script in the background we can see that pid 8222 is the go tool and pid 8241 is the linker (link). It's the linker in both cases that closes the file as expected.

 0    170                      close:entry PPID: 8222 PID: 8241  link closed /var/folders/dy/t_lqgz5d3f37375w_8gxzpn00000gn/T/go-build2233019274/b001/greeter.test

Custom dtrace script attached. watch_calls.d.gz

Ryxias commented 3 years ago

Do you think this is a fixable issue with Santa? Or does it sound like a fundamental issue with go's linker?

If it's the latter I'm content with deploying a workaround using the AllowedPathRegex. We've just been using something like (^\/var\/folders\/.*go\-build.*\.test$)|....

pmarkowsky commented 3 years ago

@Ryxias for right now I'd say your fastest work around would be to use the AllowedPathRegex.

I've been seeing some of the close events showing up as not-modified from the Endpoint Security Framework client. So I'm going to continue to triage this.

Could you let me know what version of macOS you're using?

Ryxias commented 3 years ago

During the duration of this issue: 11.4 and 11.5

pmarkowsky commented 3 years ago

Ok, tracked this down. Endpoint security framework incorrectly sets the modified flag for es_event_close_t events to false for files that were written via mmap/msync as unmodified.

test.c.gz See the attached program to reproduce the behavior.

pmarkowsky commented 3 years ago

I've filed an issue with Apple's Feedback assistant they've given it identifier, FB9535577.

eopeter commented 1 year ago

What is the Status of the Apple Feedback Assistant issue FB9535577? Thank you

pmarkowsky commented 1 year ago

@eopeter Sadly the FB9535577 is still listed as open. However as of macOS 13, there's a new field on file close events called was_mapped_writeable that we're going to look into.

pmarkowsky commented 1 year ago

We had some time to look into this on Ventura we noticed that the flag wasn't being populated if the mmap'd region was unmapped. We've opened FB12094635 with Apple about this.

pmarkowsky commented 1 year ago

Tested this with 13.5. The was_mapped_writeable flag is correctly being set on Close events.

eopeter commented 1 year ago

This is great!!

eopeter commented 1 year ago

I have 2023.8 installed but binaries generated from golang are still blocked. Should the following work with this release or is there something I need to do to make it work.

$ go build .
$ santactl fileinfo guardin 
Path                   : /Users/eoche/Projects/tests/testoutput
SHA-256                : dc215149cf728e068691b02fc55253575047e207bb1b057e2eda781450bb5bce
SHA-1                  : edad158327dfde46143136207ee187d87e25161c
Type                   : Executable (x86_64)
Code-signed            : No
Rule                   : Blocked (Unknown)