/setuid-sandbox

Unofficial mirror of setuid-sandbox

Primary LanguageCApache License 2.0Apache-2.0

This setuid helper will allow a process to execute a target executable that
will be able to drop privileges:

- the setuid sandbox will create a new PID namespace or will switch uid/gid to
  isolate the process
- a helper process, sharing the filesystem view of the existing process, will
  be created. It will accept a request to chroot() the process to an empty
  directory

This is convenient because an executable can be launched, load libraries and
open files and get chroot()-ed to an empty directory when it wants to drop
filesystem access.

Be sure to check the limitations below, as this can be dangerous.

The Chrome/Chromium Linux sandbox is based on this design.

Build instructions
------------------

1. Install libcap2 and the appropriate headers (most likely, your distributions
   will offer a libcap-dev or libcap-devel package)
2. Use "make"
3. Install sandboxme as setuid root (chown root:root sandboxme && chmod 4511 sandboxme)

Dumpable
--------

A non dumpable process cannot be ptraced() by a process without CAP_SYS_PTRACE
  (usually limited to root).

There are a couple of ways for a process to become non dumpable:
  - use a prctl() with PR_SET_DUMPABLE
  - execve() an executable that can't be read by the current process
  - Switch from uid 0 to a non 0 uid

Please note that the "dumpable" state will be reset on execve() depending on
whether or not the target can be read.

UID isolation
-------------

There are two main checks to take into consideration to assess the security of
the pid isolation techniques:

- The signal check in Linux is pretty relaxed. You can send a signal to any
  process with the same uid as you. For SIGCONT and SIGSTOP this is even more
  relaxed
- The ptrace() check in Linux is pretty strict. You need to have the same
  privileges. Privileges include uid, gid and capabilities. If you don't have
  the CAP_SYS_PTRACE capability, the target also needs to be "dumpable"

With this in mind, here are a few assessments:

- If you get a new PID namespace, processes inside the sandbox cannot send
  signals or ptrace processes outside of the sandbox. However, processes
  outside of the sandbox can send signals to and ptrace processes inside the
  sandbox.
  Processes inside the sandbox can send signals or ptrace processes inside the
  the sandbox. It's up to processes inside the sandbox to protect themselves
  from ptrace() by becoming non dumpable if they want to.

- If only the gid has been changed (-u1), sandboxed processes cannot ptrace
  unsandboxed processes, but they can send signals to them. Reciprocally,
  unsandboxed processes can send signals to sandboxed process but cannot ptrace
  them.

  If multiple users on the system are using sandboxme, their sandboxed
  processes will only share the same gid.

- If both gid and uid have been changed (-u2), sandboxed processes from two
  different users on the system can ptrace and send signals to each other.
  ptrace() can be restricted by becoming non dumpable.

Help:
-----

 Possible actions:
 chroot:
  - No chroot() helper (-c)

 uids/gids:
  - Do not switch uid or gid (-u0)
  - Switch gid to the one of "suidsandbox", but not the uid (-u1)
  - Switch uid and gid to "suidsandbox" (-u2)
  - Get a unique uid/gid and switch to it (-u3)
  - Like -u1 but accept failure as long as we have a new PID namespace
    (-u4, default)

 CLONE_NEWNET (-N)
 CLONE_NEWPID:
  - Fail if CLONE_NEWPID is not available (-P)
  - Do not try CLONE_NEWPID (-p)

 Example :

   $ /usr/sbin/sandboxme -- /bin/sh
   Helper: write to 4 ($SBX_D) to chroot the sandboxed process
   Could not find user suidsandbox
   Hi from the sandbox! I'm pid=1, uid=90422, gid=5000, dumpable=N
   Executing /bin/sh
   Warning: we will become dumpable after execve()!
     please make /bin/sh non readable
   sh-4.1$ pwd
   /home/jln
   sh-4.1$ echo C>&$SBX_D
   sh-4.1$ Helper: I chrooted you
   sh-4.1$ pwd
   /

Limitations:
------------

 - allows to escape from chroots  if available in a chroot!
   (keep a FD to /, get chrooted to /something, fchdir to fd, open with relative
   path)
 - by design, can allow a process to become impossible to kill by a user (if the
   administrator created SANDBOXUSER)
 - sending signals is not prohibited from same uids if no new PID namespace was
   created
 - doesn't drop supplementary groups (we lack Windows' DENYONLY SIDs)
 - clone NEWPID: your process becomes the child reaper (init) of the new
   namespace. If this process exits, the namespace dies.
 - environment is not cleaned
 
TODO:
-----

 - check permissions / ownership on chroot directory. Use /tmp (and chroot to
   the fd in /proc) ?
 - support a SAFE_BUNDLED mode with hardcoded target as a security-in-depth
   measure.
 - use prctl SECURE_NOROOT ? Note: the setuid bit will not be honored in what we
   execute next if we use the chroot() trick anyway.
 - cleanup the environment (XDG_SESSION_COOKIE and other sensitive information)
 - optimise do_setuid0 so that it doesn't iterate through all used pids first.
 - Patch ARGV0 so that the process explicitely shows up as sandboxed
 - reset all signal handlers?