Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20181212133639.GA8474@f195.suse.de>
Date: Wed, 12 Dec 2018 14:36:39 +0100
From: Matthias Gerstner <mgerstner@...e.de>
To: oss-security@...ts.openwall.com
Subject: Singularity: CVE-2018-19295: local root exploit - unprivileged users
 can join arbitrary mnt, net, pid and ipc namespaces

Hello,

following is a report about security issues found in Singularity [1].

Introduction
============

Singularity is a Linux namespace based container solution often used
in HPC (high performance computing) environments. In the course of a
code review [4] conducted for inclusion of Singularity version 2.6.0 in
SUSE enterprise products I found a couple of security issues.

According to upstream this affects Singularity versions 2.4.0 through
2.6.0. A security bugfix release 2.6.1 has been published to address the
issues [2]. Please note that starting with major version 3.0.0
Singularity consists of a complete rewrite in the Go programming
language that is not affected by these issues.

Issue Details
=============

Singularity ships a couple of setuid root enabled binaries for setting
up the container environment for unprivileged users. These binaries are
where the security issues are originating. Note that on SUSE
distributions by default only members of the singularity group can
access these setuid root binaries. Following is my initial report
describing the security issues based on version 2.6.0.

A) One issue is found in mount-setuid in `src/mount.c:75` where
`singularity_runtime_ns(SR_NS_MNT)` is called. This function interprets
an environment variable `SINGULARITY_DAEMON_JOIN`. If it is set then
`_singularity_runtime_ns_mnt_join()` is called which in turn evaluates
the environment variable `SINGULARITY_NS_FD`. This environment variable
is supposed to specify the number of an inherited file descriptor that
refers to some `/proc/<pid>/ns` directory. The function then calls with
effective uid 0 `openat(ns_fd, "mnt", O_RDONLY);` to open the mount
namespace file descriptor and later on joins it via `setns()`.

This logic causes the following security issues:

1) A regular user can use it to join more or less arbitrary mount
  namespaces in the system. The `/proc/<pid>/ns` directories have mode
  0511 which allows any user to open a file descriptor for it when
  specifying the `O_PATH` open flag. Joining other users' mount
  namespaces is normally not possible for regular users as is stated in
  the `man 2 setns` man page:

  ```Changing the mount namespace requires that the caller possess both
  CAP_SYS_CHROOT and CAP_SYS_ADMIN capabilities in its own user
  namespace and CAP_SYS_ADMIN in the target mount namespace.```

  This can therefore also be used to cause the image mount in
  /var/singularity/mnt/final to occur in the root mount namespace and
  therefore make it visible to other processes in the root namespace of
  the system. By using the directory container format arbitrary
  user-reachable directories can be bind-mounted to
  /var/singularity/mnt/final. (which, by itself, is probably not
  security relevant).
2) The logic can also be exploited to test for existence of arbitrary
  paths, by passing an FD refering to a user controlled directory that
  contains a symlink `mnt` to e.g. /root/.bash_history. The mount-setuid
  logging will show either EINVAL or ENOENT depending on whether the file
  exists.
3) The same approach as in 2) can be used to cause an arbitrary file to
  be opened by root, which could have side-effects depending on file
  system or device files etc.
4) When causing mount-suid to join the root mount namespace this way,
  the mount of the image specified via the `SINGULARITY_IMAGE`
  environment variable is persistent and not unmounted. When specifying a
  system directory like /usr/bin in `SINGULARITY_IMAGE` then a bind
  mount of /usr/bin will be performed in /var/singularity/mnt/final. A
  following mount of an actual file based image will cause the image
  contents to be bind-mounted on top of /var/singularity/mnt/final, and
  therefore become visible in /usr/bin. This can be considered a local
  root exploit, since user controlled files can be put in system path
  locations.

Regarding 1) and 4) you can find a PoC program attached (attach_ns.cpp).
Instructions on how to use it are found inside the source file comments.

B) Another similar issue is found in the start-suid program in
`src/start.c:97` where `singularity_runtime_ns(SR_NS_ALL)` is called.
This allows a regular user to join more or less arbitrary mnt, net, pid
and ipc namespaces. The same defects as listed in A) apply here.

The attached PoC program (join_ns.py) demonstrates how to join selected
namespaces by exploiting this issue.

C) The third setuid program, action-suid does apparently not directly
suffer from the issue above, because before `singularity_runtime_ns()`
is called, the function `singularity_daemon_init()` is called which in
join mode opens a `DAEMON_NS_FD` on its own and overrides the user
supplied one. However, the `daemon_init_join()` function which is
responsible for doing so checks whether /proc/<pid>/ns of the target
process is owned by the calling user. After that `open()` is called on
/proc/<pid>/ns. This approach is still subject to a race condition,
because the calling user could try to just in time replace this PID by a
different process that runs in namespaces not normally accessible to
him.

In the code it is a bit confusing that the following check and ERROR
message is found in various places:

```
if ( singularity_registry_get("DAEMON_JOIN") ) {
        singularity_message(ERROR, "Internal Error - This function should not be called when joining an instance\n");
    }
```

The execution is not aborted, however.

Timeline
========

2018-11-09: I privately reported the issues described above to the
Singularity project lead.

2018-11-12: Upstream opened an internal issue for it and confirmed the
issue in the following days. They started handling the issue according
to their own security protocol [3].

2018-11-21: Upstream communicated CVE-2018-19295 to me for these issues.

2018-11-30: Upstream provided patches to their PRO customers.

2018-12-11: Upstream released security bugfix release 2.6.1.

References
==========

[1]: https://www.sylabs.io/
[2]: https://github.com/sylabs/singularity/releases/tag/2.6.1
[3]: https://www.sylabs.io/2018/06/sylabs-security-vulnerability-protocol
[4]: https://bugzilla.suse.com/show_bug.cgi?id=1111411

Regards

Matthias

-- 
Matthias Gerstner <matthias.gerstner@...e.de>
Dipl.-Wirtsch.-Inf. (FH), Security Engineer
https://www.suse.com/security
Telefon: +49 911 740 53 290
GPG Key ID: 0x14C405C971923553

SUSE Linux GmbH
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nuernberg)

View attachment "join_ns.py" of type "text/x-python" (5734 bytes)

View attachment "attach_ns.cpp" of type "text/x-c" (3096 bytes)

Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.