Opened 3 years ago

Last modified 18 months ago

#14322 accepted defect

torsocks fails to wrap setcap binaries

Reported by: cypherpunks Owned by: dgoulet
Priority: Medium Milestone:
Component: Core Tor/Torsocks Version:
Severity: Normal Keywords: setcap setuid LD_PRELOAD torsocks
Cc: cacahuatl@… Actual Points:
Parent ID: Points:
Reviewer: Sponsor:

Description

the Linux 'capabilities' library for allowing non-root users to perform tasks which normally require elevated privileges.

at present the torsocks wrappers have checked for setuid and setgid flags on the binaries it executes and failed closed, throwing an error if this occurs, however there is currently no check to see if the binaries have capabilities applied.

in the case where they do, the LD_PRELOAD set by torsocks is stripped and the program will execute with no warning and without the torsocks wrapper.

as an example of this, the current 'ping' command on my Linux is setcap:

$ getcap which ping
/usr/bin/ping = cap_net_raw+ep
$ torsocks ping -c 1 torproject.org
PING torproject.org (82.195.75.101) 56(84) bytes of data.
64 bytes from 82.195.75.101: icmp_seq=1 ttl=50 time=38.1 ms

the install script which does setcap
setuid here:

https://projects.archlinux.org/svntogit/packages.git/tree/trunk/iputils.install?h=packages/iputils

Child Tickets

Attachments (1)

execve.patch (4.1 KB) - added by cypherpunks 3 years ago.
execve.patch

Download all attachments as: .zip

Change History (8)

comment:1 Changed 3 years ago by yawning

Hmmm. The getcap executable isn't present by default on all Linux systems, so the path of least resistance here might just be to bundle our own helper that the wrapper builds/calls on Linux. Not sure how to handle the torified shell use case, since it just sets LD_PRELOAD and spawns sh.

It's relatively easy to check if a given executable has any capabilites set only with libc,
see: https://gist.github.com/Yawning/fda95db37092669958b1

comment:2 in reply to:  1 ; Changed 3 years ago by cypherpunks

Replying to yawning:

Hmmm. The getcap executable isn't present by default on all Linux systems, so the path of least resistance here might just be to bundle our own helper that the wrapper builds/calls on Linux. Not sure how to handle the torified shell use case, since it just sets LD_PRELOAD and spawns sh.

It's relatively easy to check if a given executable has any capabilites set only with libc,
see: https://gist.github.com/Yawning/fda95db37092669958b1

I apologize in advance for everything wrong with this code, but how about wrapping the entire execve call to perform checks before executing it?

https://gist.github.com/0xcaca0/037ab1f7bf4ac290e60a (this bit of code needs *serious* work, purely a PoC, good luck getting it to even compile, etc)

comment:3 in reply to:  2 Changed 3 years ago by yawning

Replying to cypherpunks:

I apologize in advance for everything wrong with this code, but how about wrapping the entire execve call to perform checks before executing it?

https://gist.github.com/0xcaca0/037ab1f7bf4ac290e60a (this bit of code needs *serious* work, purely a PoC, good luck getting it to even compile, etc)

Hmmm, after some more thought, there's two issues here, depending on how torsocks is invoked/used:

  • Invoked as torsocks cmd, the current shell doesn't have the torsocks wrapper loaded, so wrapping execve and friends in libtorsocks does absolutely nothing. We need to use something like my helper (or setcap if we make that a dependency) to check the capabilities of the file before launching it, unless we always want to spawn a wrapped subshell instance. (Do we care about "something called setcap between when we checked the capabilities on a file and when we actually execve()-ed it"? If so always using a wrapped subshell may be the best option.)
  • Invoked as . torsocks on, we need to wrap execve and friends, so that child processes don't end up having the kernel remove LD_PRELOAD.

So, both? The helper can be linked against libtorsocks.so to reduce code duplication as well since we need to include the check there...

Note: On more recent Linuxes (>= 3.5), it's possible to skip the libtorsocks.so suid/sgid/capabilities checks entirely, via prctl() with PR_SET_NO_NEW_PRIVS. The kernel will then entirely ignore capabilities, suid, sgid permanently for the current process and all of it's future children. This would break torified sudo/su/etc though... (Prior versions require the process to have CAP_SETPCAP to clear the bounding set....).

comment:4 Changed 3 years ago by cypherpunks

Created this looking more canonical with the torsocks code base, written as execve.c for src/lib but really, automake, how does it work?

https://gist.github.com/0xcaca0/49190eab0c70280dd133

edit: wrong gist

edit2: updated the gist because I was doing it totally wrong, it should work now. I just shunted execve stuff in after the close stuff because they're both in unistd.h

I've built it locally, and if you set LD_PRELOAD then call the shell you're good to go. the little check function needs to be made available to the torsocks script, to check before execution, it checks for both setcap and set{g,u}id and it gets an ENOPERM if it can't wrap the executable, otherwise it hands it off to execve to work or fail of it's own accord

However just using `. torsocks on` isn't sufficient because adding LD_PRELOAD to the shells environment won't wrap the shells calls to execve, so they'll get stripped before we check, maybe we need to enforce spawning a new shell?

$ getcap `which ping`
/usr/bin/ping = cap_net_raw+ep
$ getcap `which curl`
$ export LD_PRELOAD=$PWD/src/lib/.libs/libtorsocks.so
$ bash
$ ping google.com
bash: /usr/bin/ping: Operation not permitted
$ curl google.com > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   258  100   258    0     0     65      0  0:00:03  0:00:03 --:--:--    65
Last edited 3 years ago by cypherpunks (previous) (diff)

Changed 3 years ago by cypherpunks

Attachment: execve.patch added

execve.patch

comment:5 Changed 19 months ago by cypherpunks

Severity: Normal

It used to be possible to start a process with CLONE_STOPPED which would cause it to start as if it were immediately given SIGSTOP so it would not be able to make even one syscall. From then, it would be possible to check in /proc if LD_PRELOAD is still set in environ, or if AT_SECURE is set in auxv. Unfortunately that capability was removed from Linux's clone() behavior from 2.6.38 on.

The problem with checking for setuid, setgid, and capabilities is that there are a few other ways that secureexec can be enabled, such as through SELinux, RSBAC, grsecurity's RBAC, etc. A robust solution would be able to detect if the process has or would have AT_SECURE in the first place, rather than just checking for a list of blacklisted filesystem attributes.

Perhaps we could ask for the old CLONE_STOPPED behavior to be put back. I believe it was removed due to lack of use, but this may be a sufficiently compelling reason for it to be added back. It really is a very simple feature, so there'd likely be no objection (except perhaps the fact that the bit has been re-used for CLONE_NEWCGROUP as of 4.6, but that's easy to work around). It has other potential debugging uses as well.

comment:6 Changed 18 months ago by dgoulet

Status: newaccepted

Accept a bunch of tickets for torsocks.

comment:7 Changed 18 months ago by dgoulet

I've merged the execve patch but keeping this one open since it's just fixing half of the problem. Thanks for this btw cypherpunks, I've polished it a bit.

Note: See TracTickets for help on using tickets.