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

Fix security hole checking unpacked kernel headers #3033

Merged
merged 1 commit into from
Mar 6, 2024

Conversation

jordalgo
Copy link
Contributor

@jordalgo jordalgo commented Mar 6, 2024

Make sure to check that the unpacked kheaders tar
is owned by root to prevent bpftrace from loading
compromised linux headers.

Borrowed from @brendangregg here: iovisor/bcc#4928

Tested:

$ sudo chown 1000 /tmp/kheaders-6.7.4-200.fc39.aarch64
$ sudo ./src/bpftrace test-script.bt 
ERROR: header file ownership expected to be root: /tmp/kheaders-6.7.4-200.fc39.aarch64
Checklist
  • Language changes are updated in man/adoc/bpftrace.adoc
  • User-visible and non-trivial changes updated in CHANGELOG.md
  • The new behaviour is covered by tests

@danobi
Copy link
Member

danobi commented Mar 6, 2024

Possible to add a unit test for file_exists_and_ownedby_root()? Can easily create a non-root owned file. And could probably use /proc/1/.. for a root owned file?

@jordalgo
Copy link
Contributor Author

jordalgo commented Mar 6, 2024

Possible to add a unit test for file_exists_and_ownedby_root()? Can easily create a non-root owned file. And could probably use /proc/1/.. for a root owned file?

@danobi Good call. Added.

tests/utils.cpp Outdated
std::string tmpdir = "/tmp/bpftrace-test-utils-XXXXXX";
std::string file1 = "/ownedby-user";
std::string file2 = "/no-exists";
if (::mkdtemp(&tmpdir[0]) == nullptr) {
Copy link
Member

Choose a reason for hiding this comment

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

nit

Suggested change
if (::mkdtemp(&tmpdir[0]) == nullptr) {
if (::mkdtemp(tmpdir.data()) == nullptr) {

}

int fd;
fd = open((tmpdir + file1).c_str(), O_CREAT, S_IRUSR);
Copy link
Member

Choose a reason for hiding this comment

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

ASSERT_GE(fd, 0) ?

Make sure to check that the unpacked kheaders tar
is owned by root to prevent bpftrace from loading
compromised linux headers.
@jordalgo jordalgo merged commit 4be4b71 into bpftrace:master Mar 6, 2024
19 checks passed
@brendangregg
Copy link
Contributor

Thanks!

danobi pushed a commit to danobi/bpftrace that referenced this pull request Mar 7, 2024
Make sure to check that the unpacked kheaders tar
is owned by root to prevent bpftrace from loading
compromised linux headers.

Co-authored-by: Jordan Rome <jordalgo@fedoraproject.org>
danobi pushed a commit that referenced this pull request Mar 7, 2024
Make sure to check that the unpacked kheaders tar
is owned by root to prevent bpftrace from loading
compromised linux headers.

Co-authored-by: Jordan Rome <jordalgo@fedoraproject.org>
@jeromemarchand
Copy link
Contributor

I'm afraid this is not fixed yet. If a user build a valid kheaders tree in /tmp/kheaders-$(uname -r) bpftrace will show an error message, but but proceed to use use the possibly compromised headers none the less:

# /usr/share/bpftrace/tools/execsnoop.bt 
ERROR: header file ownership expected to be root: /tmp/kheaders-5.14.0-431.rh29064.el9.x86_64
Attaching 3 probes...
TIME            PID     PPID    ARGS
18:15:38.492036 3116    1934    ls --color=auto -l --color=auto

If the user has an empty keaders-$(uname -r) directory it seems that bpftrace will overwrite it, but not if it isn't empty.
I think the problem is we don't check whether moving tpmdir to shared_path succeeds at the end of unpack_kheaders_tar_xz():

  int rc = ::pclose(tar);
  if (rc == 0) {
    tmpdir.move_to(shared_path);
    return shared_path;
  }

Or maybe we should exit as soon as wrong ownership is detected.

@jordalgo
Copy link
Contributor Author

jordalgo commented May 6, 2024

@jeromemarchand Good catch! I think maybe there is confusion around rename as it seems to be implementation specific if the new file name exists. On my machine, it's doing what you observed in that it doesn't replace the compromised headers path and just re-uses. I'll add some deletion and extra checking (alternatively we can do what you said and just exit if the permissions are not root).

jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 6, 2024
The potentially compromised temporary kheaders path
was not being overwritten by `rename`. This meant that
the compromised path was still being used and read from.

Instead of continuing just abort and direct the user
to delete this compromised path.

Reported here: bpftrace#3033
@jordalgo
Copy link
Contributor Author

jordalgo commented May 6, 2024

Second attempt to fix here: #3154

jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 7, 2024
The potentially compromised temporary kheaders path
was not being overwritten by `rename`. This meant that
the compromised path was still being used and read from.

Instead of continuing just abort and direct the user
to delete this compromised path.

Reported here: bpftrace#3033
jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 7, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
bpftrace#3033
bpftrace#3154
jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 13, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
bpftrace#3033
bpftrace#3154
jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 13, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
bpftrace#3033
bpftrace#3154
jordalgo pushed a commit to jordalgo/bpftrace that referenced this pull request May 13, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
bpftrace#3033
bpftrace#3154
jordalgo added a commit that referenced this pull request May 15, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
#3033
#3154

Co-authored-by: Jordan Rome <jordalgo@fedoraproject.org>
viktormalik pushed a commit to viktormalik/bpftrace that referenced this pull request May 20, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
bpftrace#3033
bpftrace#3154

Co-authored-by: Jordan Rome <jordalgo@fedoraproject.org>
viktormalik pushed a commit that referenced this pull request May 21, 2024
Looking in shared writeable locations for kernel
headers is inherently risky even bpftrace does
the unpacking. Remove this functionality and let
the user specify the path to these headers if
we can't find them in known locations.

References:
#3033
#3154

Co-authored-by: Jordan Rome <jordalgo@fedoraproject.org>
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.

None yet

4 participants