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