Firejail: Insecure Use of OverlayFS as Sandbox File System
==========================================================
Advisory: UNPAR-2021-0
Component: Firejail
Vendor: https://firejail.wordpress.com
Version(s): Starting 0.9.30 (or earlier) till 0.9.64.2
Weakness(es): CWE-61: UNIX Symbolic Link (Symlink) Following
CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
CVE: None assigned
CVSSv3.1: Base score 7.8: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Author: Roman Fiedler
Summary:
========
Firejail is a SUID security sandbox program that reduces the
risk of security breaches by restricting the running environment
of untrusted applications using Linux namespaces and seccomp-bpf.
Therefore it may span an OverlayFS over the complete file system,
redirecting any file system modifications to a temporary space.
Thus malicious sandboxed processes cannot change files on the
"real" active file system. To create such a sandbox elevated
privileges are required.
When setting up the sandbox Firejail tries to ensure, that the
location for temporary OverlayFS file root is not writable by
any unprivileged user as that may allow privilege escalation.
Due to a race condition this check may report a wrong status
and thus both follow symbolic links and use an untrusted directory
as source. With crafted content using the untrusted directory
allows creation of or granting write access to arbitrary files.
Timeline:
* 20201208: Reported to Linux distribution shipping firejail
as optional packet (not in default install)
* 20201214: Triaged as (critical) without timeline
* 20210121: Vulnerability out of scope for Linux distributon,
distributor contacted upstream.
* 20210125: Firejail security contact notified by researcher
* 20210131: Firejail security contact initial response
* 20210208: Firejail patch released
Details:
========
OverlayFS Basics:
Firejail supports persistent overlays on the file system when
running an untrusted process. Thus the sandboxed process can
store information in files but these modifications are not
visible to other processes outside the sandbox. This feature
is provided by the "overlay" Linux file system type. It uses
an "upper" directory that contains changes put over the data
in "lower" before presenting the modified file system tree to
the user side. Therefore data in "upper" shadows what was there
on "lower".
When putting an overlay on top of the root file system, putting
a symbolic link as "etc" will effectively hide the real "etc"
directory and show the link instead. A user writable "upper"
directory therefore allows the user to add, replace or remove
any elements within the file system tree. Within the mounted
overlay all elements from "upper" will be shown with their
respective attributes. The unprivileged user therefore may
replace privileged files with unprivileged versions but not
the other way round (substitute root.root /usr/bin/ping with
one owned by user.users). See [2] for more information.
Firejail OverlayFS Use:
For Firejail this means that a writable "upper" directory will
modify the file system tree later to be presented to the sandboxed
process. This alone would not be problematic as the file system
is mounted in a separate namespace, where SUID binaries do not
work anymore (nonewprivs). Therefore the sandboxed process cannot
gain privileges e.g. due to a modified lib-c.
Even so Firejail wants to prevent any of those changes as a
writable upper directory would also void any assumptions on
the directory structure inside the sandbox. Firejail relies on
a sane directory structure as it will hide blacklisted files
and directories, that may allow leaking general system information
to the sandboxed process. With Firejail using an untrusted
"upper", sandbox setup will continue on the rogue overlay of
the real root file system.
The relevant source code is function "void fs_overlayfs(void)"
in [3], line 943. The code performing the checks and the overlay
mount was introduced 20150816 with commit
980979d77ebd30ca1d6b518d48068a1e3660a4e8 named --overlay
rework, adding a persistent directory; implemented --overlay-tmpfs
option, see [4]. The code was first released 20150914 as
version 0.9.30. Due to changes in OverlayFS behavior functionality
should be disabled on kernels newer than 4.19, but the check
is broken, thus OverlayFS is again available on 5.x kernels.
943 void fs_overlayfs(void) {
...
969 // we disable overlayfs for now, pending fixing
970 if (major >= 4 &&minor >= 19) {
971 fprintf(stderr, "Error: OverlayFS disabled for Linux kernels 4.19 and newer, pending fixing.\n");
972 exit(1);
973 }
...
1033 if (mkdirat(basefd, "odiff", 0755) == -1 && errno != EEXIST) {
1034 perror("mkdir");
1035 fprintf(stderr, "Error: cannot create overlay directory %s\n", odiff);
1036 exit(1);
1037 }
1038 ASSERT_PERMS(odiff, 0, 0, 0755);
...
1065 else { // kernel 3.18 or newer
1066 if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s ", odiff, owork) == -1)
1067 errExit("asprintf");
1068 if (mount("overlay", oroot, "overlay", MS_MGC_VAL, optio n) < 0) {
...
Triggering the Vulnerability:
When Firejail is setting up the OverlayFS for use, it locates
the target directory starting from user home directory (which
cannot be replaced by the user itself) and then walks to the
storage location ".firejail/[name]" by opening only one path
component at a time. It also verifies that "[name]" is root
owned and not user/group writable. But instead of mounting the
overlay using the "/proc/self/fd/[num]" references to the
directory, Firejail will use the absolute path there.
Thus there is a short window of opportunity between the "stat"
call and the "mount" to move the whole directory and replace
it with one containing a user-writable and prefilled ".firejail/[name]"
directory. The following trace shows the Firejail process with
pid 465 losing the race versus the exploit [1] running as pid 446:
# Firejail (465) securely moving to the overlay source directories:
465 openat(AT_FDCWD, "/", O_RDONLY|O_CLOEXEC|O_PATH|O_DIRECTORY ...
465 openat(4, "home", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY ...
465 openat(5, "test", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY ...
465 openat(4, ".firejail", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY ...
465 mkdirat(5, "test", 0755 ...
465 openat(5, "test", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_DIRECTORY ...
465 mkdirat(4, "odiff", 0755 ...
465 stat("/home/test/.firejail/test/odiff", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
465 stat("/home/test/.firejail/test/owork", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
...
446 renameat2(7, ".firejail", 7, ".firejail-b", RENAME_EXCHANGE) = 0
...
465 mount("overlay", "/run/firejail/mnt/oroot", "overlay",
. MS_MGC_VAL, "lowerdir=/,upperdir=/home/test/.firejail/test/odiff,
. workdir=/home/test/.firejail/test/owork"
Afterwards Firejail continues with sandbox setup and copies data
to untrusted locations on the mounted overlay. These operations
are redirected via other symlinks through /proc/self/cwd back
to the real file system outside the OverlayFS, thus modifying
files on the machine with full privileges.
Impact:
=======
The vulnerability gives full privileges to any user that has
an existing user-writable home directory set in "/etc/passwd"
and is able to invoke Firejail.
Mitigation:
===========
The maintainers of Firejail provided version 0.9.64.4 [5] that
disables Overlayfs code completely. Patches fixing the vulnerability
and still retaining overlay filesystem functionality are not
available yet.
Proof of Concept:
=================
The PoC "UnjailMyHeart.c" [1] was tested against Debian Bullseye
firejail package 0.9.64-1. See PoC file header for compile instructions.
References:
===========
[1] https://unparalleled.eu/blog/2021/20210208-rigged-race-against-firejail-for-local-root/UnjailMyHeart.c (to be published later)
[2] https://www.kernel.org/doc/html/latest/filesystems/overlayfs.html
[3] https://raw.githubusercontent.com/netblue30/firejail/da53c4ebf0b7f5c6d07cb14dd7ec3ff3910fe180/src/firejail/fs.c
[4] https://github.com/netblue30/firejail/commit/980979d77ebd30ca1d6b518d48068a1e3660a4e8
[5] https://github.com/netblue30/firejail/releases/tag/0.9.64.4