CVE-2021-41133: Other (NVD-CWE-Other)
value: Flatpak is a system for building, distributing, and running sandboxed desktop applications on Linux. In versions prior to 1.10.4 and 1.12.0, Flatpak apps with direct access to AF_UNIX sockets such as those used by Wayland, Pipewire or pipewire-pulse can trick portals and other host-OS services into treating the Flatpak app as though it was an ordinary, non-sandboxed host-OS process. They can do this by manipulating the VFS using recent mount-related syscalls that are not blocked by Flatpak's denylist seccomp filter, in order to substitute a crafted `/.flatpak-info` or make that file disappear entirely. Flatpak apps that act as clients for AF_UNIX sockets such as those used by Wayland, Pipewire or pipewire-pulse can escalate the privileges that the corresponding services will believe the Flatpak app has. Note that protocols that operate entirely over the D-Bus session bus (user bus), system bus or accessibility bus are not affected by this. This is due to the use of a proxy process `xdg-dbus-proxy`, whose VFS cannot be manipulated by the Flatpak app, when interacting with these buses. Patches exist for versions 1.10.4 and 1.12.0, and as of time of publication, a patch for version 1.8.2 is being planned. There are no workarounds aside from upgrading to a patched version.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 7.8
Published: 2021-10-08T14:15Z
Repository: https://github.com/flatpak/flatpak
Language: C
Files changed: common/flatpak-run.c
Commit date: 2021-10-08 12:53:20 +0200
Commit message: run: Add an errno value to seccomp filters

At the moment, if we block a syscall we always make it fail with EPERM,
but this is risky: user-space libraries can start to use new replacements
for old syscalls at any time, and will often treat EPERM as a fatal error.
For new syscalls, we should make the syscall fail with ENOSYS, which is
indistinguishable from running on an older kernel and will cause fallback
to an older implementation, for example clone3() to clone().

In future we should probably move from EPERM to ENOSYS for some of the
syscalls we already block, but for now keep the status quo.

This is a prerequisite for fixing the vulnerability tracked as
GHSA-67h7-w3jq-vh4q.

Signed-off-by: Simon McVittie <smcv@collabora.com>

Diff: diff --git a/common/flatpak-run.c b/common/flatpak-run.c
index e93b3d63b..7817ff94f 100644
--- a/common/flatpak-run.c
+++ b/common/flatpak-run.c
@@ -2895,65 +2895,67 @@ setup_seccomp (FlatpakBwrap   *bwrap,
    **** END NOTE ON CODE SHARING
    */
   struct
   {
     int                  scall;
+    int                  errnum;
     struct scmp_arg_cmp *arg;
   } syscall_blocklist[] = {
     /* Block dmesg */
-    {SCMP_SYS (syslog)},
+    {SCMP_SYS (syslog), EPERM},
     /* Useless old syscall */
-    {SCMP_SYS (uselib)},
+    {SCMP_SYS (uselib), EPERM},
     /* Don't allow disabling accounting */
-    {SCMP_SYS (acct)},
+    {SCMP_SYS (acct), EPERM},
     /* 16-bit code is unnecessary in the sandbox, and modify_ldt is a
        historic source of interesting information leaks. */
-    {SCMP_SYS (modify_ldt)},
+    {SCMP_SYS (modify_ldt), EPERM},
     /* Don't allow reading current quota use */
-    {SCMP_SYS (quotactl)},
+    {SCMP_SYS (quotactl), EPERM},
 
     /* Don't allow access to the kernel keyring */
-    {SCMP_SYS (add_key)},
-    {SCMP_SYS (keyctl)},
-    {SCMP_SYS (request_key)},
+    {SCMP_SYS (add_key), EPERM},
+    {SCMP_SYS (keyctl), EPERM},
+    {SCMP_SYS (request_key), EPERM},
 
     /* Scary VM/NUMA ops */
-    {SCMP_SYS (move_pages)},
-    {SCMP_SYS (mbind)},
-    {SCMP_SYS (get_mempolicy)},
-    {SCMP_SYS (set_mempolicy)},
-    {SCMP_SYS (migrate_pages)},
+    {SCMP_SYS (move_pages), EPERM},
+    {SCMP_SYS (mbind), EPERM},
+    {SCMP_SYS (get_mempolicy), EPERM},
+    {SCMP_SYS (set_mempolicy), EPERM},
+    {SCMP_SYS (migrate_pages), EPERM},
 
     /* Don't allow subnamespace setups: */
-    {SCMP_SYS (unshare)},
-    {SCMP_SYS (mount)},
-    {SCMP_SYS (pivot_root)},
+    {SCMP_SYS (unshare), EPERM},
+    {SCMP_SYS (mount), EPERM},
+    {SCMP_SYS (pivot_root), EPERM},
 #if defined(__s390__) || defined(__s390x__) || defined(__CRIS__)
     /* Architectures with CONFIG_CLONE_BACKWARDS2: the child stack
      * and flags arguments are reversed so the flags come second */
-    {SCMP_SYS (clone), &SCMP_A1 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
+    {SCMP_SYS (clone), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
 #else
     /* Normally the flags come first */
-    {SCMP_SYS (clone), &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
+    {SCMP_SYS (clone), EPERM, &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
 #endif
 
     /* Don't allow faking input to the controlling tty (CVE-2017-5226) */
-    {SCMP_SYS (ioctl), &SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) TIOCSTI)},
+    {SCMP_SYS (ioctl), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) TIOCSTI)},
   };
 
   struct
   {
     int                  scall;
+    int                  errnum;
     struct scmp_arg_cmp *arg;
   } syscall_nondevel_blocklist[] = {
     /* Profiling operations; we expect these to be done by tools from outside
      * the sandbox.  In particular perf has been the source of many CVEs.
      */
-    {SCMP_SYS (perf_event_open)},
+    {SCMP_SYS (perf_event_open), EPERM},
     /* Don't allow you to switch to bsd emulation or whatnot */
-    {SCMP_SYS (personality), &SCMP_A0 (SCMP_CMP_NE, allowed_personality)},
-    {SCMP_SYS (ptrace)}
+    {SCMP_SYS (personality), EPERM, &SCMP_A0 (SCMP_CMP_NE, allowed_personality)},
+    {SCMP_SYS (ptrace), EPERM}
   };
   /* Blocklist all but unix, inet, inet6 and netlink */
   struct
   {
     int             family;
@@ -3033,27 +3035,35 @@ setup_seccomp (FlatpakBwrap   *bwrap,
    */
 
   for (i = 0; i < G_N_ELEMENTS (syscall_blocklist); i++)
     {
       int scall = syscall_blocklist[i].scall;
+      int errnum = syscall_blocklist[i].errnum;
+
+      g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);
+
       if (syscall_blocklist[i].arg)
-        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_blocklist[i].arg);
+        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_blocklist[i].arg);
       else
-        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0);
+        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);
       if (r < 0 && r == -EFAULT /* unknown syscall */)
         return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d"), scall);
     }
 
   if (!devel)
     {
       for (i = 0; i < G_N_ELEMENTS (syscall_nondevel_blocklist); i++)
         {
           int scall = syscall_nondevel_blocklist[i].scall;
+          int errnum = syscall_nondevel_blocklist[i].errnum;
+
+          g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);
+
           if (syscall_nondevel_blocklist[i].arg)
-            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_nondevel_blocklist[i].arg);
+            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_nondevel_blocklist[i].arg);
           else
-            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0);
+            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);
 
           if (r < 0 && r == -EFAULT /* unknown syscall */)
             return flatpak_fail_error (error, FLATPAK_ERROR_SETUP_FAILED, _("Failed to block syscall %d"), scall);
         }
     }

Vulnerable code:     {SCMP_SYS (syslog)},
    {SCMP_SYS (uselib)},
    {SCMP_SYS (acct)},
    {SCMP_SYS (modify_ldt)},
    {SCMP_SYS (quotactl)},
    {SCMP_SYS (add_key)},
    {SCMP_SYS (keyctl)},
    {SCMP_SYS (request_key)},
    {SCMP_SYS (move_pages)},
    {SCMP_SYS (mbind)},
    {SCMP_SYS (get_mempolicy)},
    {SCMP_SYS (set_mempolicy)},
    {SCMP_SYS (migrate_pages)},
    {SCMP_SYS (unshare)},
    {SCMP_SYS (mount)},
    {SCMP_SYS (pivot_root)},
    {SCMP_SYS (clone), &SCMP_A1 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
    {SCMP_SYS (clone), &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
    {SCMP_SYS (ioctl), &SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) TIOCSTI)},
    {SCMP_SYS (perf_event_open)},
    {SCMP_SYS (personality), &SCMP_A0 (SCMP_CMP_NE, allowed_personality)},
    {SCMP_SYS (ptrace)}
        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_blocklist[i].arg);
        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0);
            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 1, *syscall_nondevel_blocklist[i].arg);
            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (EPERM), scall, 0);
Fixed code:     int                  errnum;
    {SCMP_SYS (syslog), EPERM},
    {SCMP_SYS (uselib), EPERM},
    {SCMP_SYS (acct), EPERM},
    {SCMP_SYS (modify_ldt), EPERM},
    {SCMP_SYS (quotactl), EPERM},
    {SCMP_SYS (add_key), EPERM},
    {SCMP_SYS (keyctl), EPERM},
    {SCMP_SYS (request_key), EPERM},
    {SCMP_SYS (move_pages), EPERM},
    {SCMP_SYS (mbind), EPERM},
    {SCMP_SYS (get_mempolicy), EPERM},
    {SCMP_SYS (set_mempolicy), EPERM},
    {SCMP_SYS (migrate_pages), EPERM},
    {SCMP_SYS (unshare), EPERM},
    {SCMP_SYS (mount), EPERM},
    {SCMP_SYS (pivot_root), EPERM},
    {SCMP_SYS (clone), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
    {SCMP_SYS (clone), EPERM, &SCMP_A0 (SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)},
    {SCMP_SYS (ioctl), EPERM, &SCMP_A1 (SCMP_CMP_MASKED_EQ, 0xFFFFFFFFu, (int) TIOCSTI)},
    int                  errnum;
    {SCMP_SYS (perf_event_open), EPERM},
    {SCMP_SYS (personality), EPERM, &SCMP_A0 (SCMP_CMP_NE, allowed_personality)},
    {SCMP_SYS (ptrace), EPERM}
      int errnum = syscall_blocklist[i].errnum;

      g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);

        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_blocklist[i].arg);
        r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);
          int errnum = syscall_nondevel_blocklist[i].errnum;

          g_return_val_if_fail (errnum == EPERM || errnum == ENOSYS, FALSE);

            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 1, *syscall_nondevel_blocklist[i].arg);
            r = seccomp_rule_add (seccomp, SCMP_ACT_ERRNO (errnum), scall, 0);