Skip to content

F-7.2: Privileged Port Restriction Bypass

Classification

  • Severity: Medium
  • CVSS Vector: Network / Low Complexity / Requires Network Access
  • Affected Versions: NFSv2, NFSv3 (servers using secure export option)
  • Prerequisite: Access to a trusted host (SSH, container runtime, or CAP_NET_BIND_SERVICE)

Summary

NFS servers can restrict connections to privileged source ports (<1024) via the secure export option (Linux default) or nfs_portmon (Solaris). The assumption is that only root can bind to these ports, preventing unprivileged users from running rogue NFS clients. This assumption is false on modern systems — multiple bypass vectors exist.

Technical detail

The security model

/etc/exports:
/srv/data   192.168.1.0/24(rw,secure)   # 'secure' is default on Linux

The server rejects RPC calls from source ports >= 1024. The theory: a non-root user on a trusted machine cannot forge NFS credentials because they can't bind to a privileged port.

Bypass vector 1: CAP_NET_BIND_SERVICE

Linux capabilities allow granting port-binding privilege to any binary without full root:

# Grant bind capability to a custom NFS client
sudo setcap 'CAP_NET_BIND_SERVICE+ep' ./nfs_client

# Now any user can run it and bind to privileged ports
./nfs_client --uid 0 --gid 0 target:/export

Any binary with this capability can bind to port <1024. If the attacker finds any such binary on a trusted host, they can use it as a springboard.

Bypass vector 2: SSH port forwarding

A non-root user with SSH access to a trusted host can forward a privileged port:

# On attacker machine (where you are root):
# Forward local port 800 -> trusted_host -> nfs_server:2049
ssh -L 800:nfs_server:2049 user@trusted_host

# Now mount via the tunnel — traffic arrives at NFS server from
# trusted_host's IP on a privileged port
mount -t nfs -o port=800,mountport=800 localhost:/export /mnt

The NFS server sees the connection from trusted_host on a privileged port — both the IP check and the port check pass.

Bypass vector 3: Docker containers

Since Docker 20.03, unprivileged ports start at 0 inside containers. Any containerized process can bind to ports <1024:

# Inside a Docker container on a trusted host
docker run --rm -it --net=host alpine
# Port restriction is meaningless — bind to any port

Bypass vector 4: Non-Unix clients

Windows has no concept of privileged ports. Any Windows program can bind to any port. The secure option is meaningless against Windows NFS clients.

Bypass vector 5: insecure export option

Some administrators explicitly set insecure to support broken clients:

/etc/exports:
/images *(rw,insecure,no_root_squash)   # FOG Project default (CVE)

This disables the port check entirely. Combined with no_root_squash, this grants full root access to anyone who can reach the port.

Impact

  • Unprivileged users on trusted hosts can forge NFS credentials
  • The privileged port restriction provides a false sense of security
  • Administrators may skip Kerberos deployment believing port checks are sufficient

Detection

  • Monitor for insecure in /etc/exports
  • Audit binaries with CAP_NET_BIND_SERVICE: getcap -r / 2>/dev/null | grep net_bind
  • Monitor SSH port forwards on trusted NFS clients
  • Check Docker daemon availability on NFS clients

Remediation

  1. Don't rely on port restrictions — they are a speed bump, not a barrier
  2. Use Kerberos (sec=krb5p) — makes port checks irrelevant
  3. Never combine insecure with no_root_squash
  4. Audit trusted hosts for Docker, capabilities, and SSH access