~/aymane
← cd .. Footprinting Services

Footprinting NFS

2026-10-14 · #htb #footprinting #nfs #enumeration #privesc

▸ Cheat sheet — useful commands
nmap -p111,2049 -sV -sC <target> Footprint NFS / RPC
nmap --script nfs* -p111,2049 <target> NFS NSE scripts (ls, showmount, statfs)
showmount -e <target> List the exported shares
mkdir t && sudo mount -t nfs <target>:/ ./t/ -o nolock Mount a share
ls -l t/ / ls -n t/ View owners / raw UIDs & GIDs
sudo umount ./t Unmount when done

NFS -> Network File System : is a network file system and has the same purpose as SMB.

Its purpose is to access file systems over a network as if they were local. However, it uses an entirely different protocol. NFS is used between Linux and Unix systems. This means that NFS clients cannot communicate directly with SMB servers. NFS is an Internet standard that governs the procedures in a distributed file system. While NFS protocol version 3.0 (NFSv3), which has been in use for many years, authenticates the client computer, this changes with NFSv4. Here, as with the Windows SMB protocol, the user must authenticate.

Versions :

Ports :

How it's built

The security problem :


Default Configuration :

The /etc/exports file contains a table of physical filesystems on an NFS server accessible by the clients. The NFS Exports Table shows which options it accepts and thus indicates which options are available to us.

Exports File :

aymane0x90@htb[/htb]$ cat /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)

-> The folder is specified and made available to others, and then the rights they will have on this NFS share are connected to a host or a subnet. Finally, additional options can be added to the hosts or subnets.

Option Description
rw Read and write permissions.
ro Read only permissions.
sync Synchronous data transfer. (A bit slower)
async Asynchronous data transfer. (A bit faster)
secure Ports above 1024 will not be used.
insecure Ports above 1024 will be used.
no_subtree_check This option disables the checking of subdirectory trees.
root_squash Assigns all permissions to files of root UID/GID 0 to the UID/GID of anonymous, which prevents root from accessing files on an NFS mount.

Let us create such an entry for test purposes and play around with the settings.

ExportsFS :

root@nfs:~# echo '/mnt/nfs  10.129.14.0/24(sync,no_subtree_check)' >> /etc/exports
root@nfs:~# systemctl restart nfs-kernel-server 
root@nfs:~# exportfs

/mnt/nfs        10.129.14.0/24

We have shared the folder /mnt/nfs to the subnet 10.129.14.0/24 with the setting shown above.
All hosts on the netwok can mount the NFS share and inspect the contents of the folder .

Dangerous Settings :

Option Description
rw Read and write permissions.
insecure Ports above 1024 will be used.
nohide If another file system was mounted below an exported directory, this directory is exported by its own exports entry.
no_root_squash All files created by root are kept with the UID/GID 0.

Footprinting the Service :

When footprinting NFS, the TCP ports 111 and 2049 are essential. We can also get information about the NFS service and the host via RPC.

aymane0x90@htb[/htb]$ sudo nmap 10.129.14.128 -p111,2049 -sV -sC

Starting Nmap 7.80 ( https://nmap.org ) at 2021-09-19 17:12 CEST
Nmap scan report for 10.129.14.128
Host is up (0.00018s latency).

PORT    STATE SERVICE VERSION
111/tcp open  rpcbind 2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3           2049/udp   nfs
|   100003  3           2049/udp6  nfs
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      41982/udp6  mountd
|   100005  1,2,3      45837/tcp   mountd
|   100005  1,2,3      47217/tcp6  mountd
|   100005  1,2,3      58830/udp   mountd
|   100021  1,3,4      39542/udp   nlockmgr
|   100021  1,3,4      44629/tcp   nlockmgr
|   100021  1,3,4      45273/tcp6  nlockmgr
|   100021  1,3,4      47524/udp6  nlockmgr
|   100227  3           2049/tcp   nfs_acl
|   100227  3           2049/tcp6  nfs_acl
|   100227  3           2049/udp   nfs_acl
|_  100227  3           2049/udp6  nfs_acl
2049/tcp open  nfs_acl 3 (RPC #100227)
MAC Address: 00:00:00:00:00:00 (VMware)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.58 seconds

The rpcinfo NSE script retrieves a list of all currently running RPC services, their names and descriptions, and the ports they use.

aymane0x90@htb[/htb]$ sudo nmap --script nfs* 10.129.14.128 -sV -p111,2049

Starting Nmap 7.80 ( https://nmap.org ) at 2021-09-19 17:37 CEST
Nmap scan report for 10.129.14.128
Host is up (0.00021s latency).

PORT     STATE SERVICE VERSION
111/tcp  open  rpcbind 2-4 (RPC #100000)
| nfs-ls: Volume /mnt/nfs
|   access: Read Lookup NoModify NoExtend NoDelete NoExecute
| PERMISSION  UID    GID    SIZE  TIME                 FILENAME
| rwxrwxrwx   65534  65534  4096  2021-09-19T15:28:17  .
| ??????????  ?      ?      ?     ?                    ..
| rw-r--r--   0      0      1872  2021-09-19T15:27:42  id_rsa
| rw-r--r--   0      0      348   2021-09-19T15:28:17  id_rsa.pub
| rw-r--r--   0      0      0     2021-09-19T15:22:30  nfs.share
|_
| nfs-showmount: 
|_  /mnt/nfs 10.129.14.0/24
| nfs-statfs: 
|   Filesystem  1K-blocks   Used       Available   Use%  Maxfilesize  Maxlink
|_  /mnt/nfs    30313412.0  8074868.0  20675664.0  29%   16.0T        32000
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100003  3           2049/udp   nfs
|   100003  3           2049/udp6  nfs
|   100003  3,4         2049/tcp   nfs
|   100003  3,4         2049/tcp6  nfs
|   100005  1,2,3      41982/udp6  mountd
|   100005  1,2,3      45837/tcp   mountd
|   100005  1,2,3      47217/tcp6  mountd
|   100005  1,2,3      58830/udp   mountd
|   100021  1,3,4      39542/udp   nlockmgr
|   100021  1,3,4      44629/tcp   nlockmgr
|   100021  1,3,4      45273/tcp6  nlockmgr
|   100021  1,3,4      47524/udp6  nlockmgr
|   100227  3           2049/tcp   nfs_acl
|   100227  3           2049/tcp6  nfs_acl
|   100227  3           2049/udp   nfs_acl
|_  100227  3           2049/udp6  nfs_acl
2049/tcp open  nfs_acl 3 (RPC #100227)
MAC Address: 00:00:00:00:00:00 (VMware)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.45 seconds

We can create a new empty folder to which the NFS share will be mounted. Once mounted, we can navigate it and view the contents just like our local system.

Show Available NFS Shares :

aymane0x90@htb[/htb]$ showmount -e 10.129.14.128

Export list for 10.129.14.128:
/mnt/nfs 10.129.14.0/24

Mounting NFS Share :

aymane0x90@htb[/htb]$ mkdir target-NFS
aymane0x90@htb[/htb]$ sudo mount -t nfs 10.129.14.128:/ ./target-NFS/ -o nolock
aymane0x90@htb[/htb]$ cd target-NFS
aymane0x90@htb[/htb]$ tree .

.
└── mnt
    └── nfs
        ├── id_rsa
        ├── id_rsa.pub
        └── nfs.share

2 directories, 3 files

List Contents with Usernames & Group Names :

aymane0x90@htb[/htb]$ ls -l mnt/nfs/

total 16
-rw-r--r-- 1 cry0l1t3 cry0l1t3 1872 Sep 25 00:55 cry0l1t3.priv
-rw-r--r-- 1 cry0l1t3 cry0l1t3  348 Sep 25 00:55 cry0l1t3.pub
-rw-r--r-- 1 root     root     1872 Sep 19 17:27 id_rsa
-rw-r--r-- 1 root     root      348 Sep 19 17:28 id_rsa.pub
-rw-r--r-- 1 root     root        0 Sep 19 17:22 nfs.share

List Contents with UIDs & GUID :

aymane0x90@htb[/htb]$ ls -n mnt/nfs/

total 16
-rw-r--r-- 1 1000 1000 1872 Sep 25 00:55 cry0l1t3.priv
-rw-r--r-- 1 1000 1000  348 Sep 25 00:55 cry0l1t3.pub
-rw-r--r-- 1    0 1000 1221 Sep 19 18:21 backup.sh
-rw-r--r-- 1    0    0 1872 Sep 19 17:27 id_rsa
-rw-r--r-- 1    0    0  348 Sep 19 17:28 id_rsa.pub
-rw-r--r-- 1    0    0    0 Sep 19 17:22 nfs.share

The SUID bit

Normally, when you run a program, it runs as you — with your user’s privileges.

But a file can have a special flag called the SUID bit (Set User ID). When a program has SUID set, it runs as the file’s owner, not as the person who launched it.

The classic real example is /usr/bin/passwd. Changing your password means writing to /etc/shadow, a file only root can touch. Yet a normal user can run passwd and change their password. How? passwd is SUID root — so even when you run it, it executes with root’s powers for the moment it runs. You can spot it in ls -l by the s where the owner’s x normally is:

-rwsr-xr-x 1 root root ... /usr/bin/passwd
   ^ that 's' is the SUID bit

So the one-line takeaway: a SUID binary owned by bob = “anyone who runs this becomes bob while it runs.”

If that binary happens to be a shell, then running it drops you into a shell as bob. That’s the whole weapon.

The goal of the attack :

You have SSH access to a target as a low-privilege user — call them user1 (you).
There’s another user, user2, who can read files you can’t (or it’s root — same idea).
You want user2’s access.

Your plan: get a shell binary that is SUID user2 sitting on the target. Then you run it from your SSH session as user1, and because of the SUID bit, the shell runs as user2. Now you can read user2’s files.

Why you can’t just do this normally :

As user1 on the target, you’re stuck:

You can’t chown a file to user2 — only root is allowed to give files away to another user.
You can’t create a SUID binary owned by user2 — same reason.

So locally, the attack is impossible. You need some way to plant a file on the target’s disk that’s already owned by user2 and already has SUID set.

This is exactly what NFS hands you :

Here’s where the UID/GID trust gap you just learned about pays off. The NFS share is a folder that physically lives on the target’s disk — but you’re writing to it from your own attacker machine, where you are root. And NFS just trusts whatever ownership and permissions you set from the client side.

So from your attacker box:

On the attacker machine, as root, with the NFS share mounted :

cp /bin/bash /mnt/nfs/rootshell     # drop a copy of a shell onto the share
chown user2 /mnt/nfs/rootshell      # make user2 the owner
chmod u+s /mnt/nfs/rootshell        # set the SUID bit

Because NFS didn’t stop you, that file now sits on the target’s disk, owned by user2, with SUID set — something you could never have created from inside your SSH session.

Then, back in your SSH session as user1, you run it:

/path/to/share/rootshell -p

(-p tells bash to keep the elevated privileges instead of dropping them.) The SUID bit makes that shell run as user2, and you’ve escalated.

The chain in one view :
1- SSH into the target as low-priv user1.
2- From your attacker box (where you’re root), write a shell to the NFS share.
3- chown it to user2 and set SUID — NFS lets you because it trusts your client-side identity.
4- The file lands on the target’s disk with those exact properties.
5- Back in the SSH session, user1 runs it → the shell runs as user2 → access gained.

Unmounting :

aymane0x90@htb[/htb]$ cd ..
aymane0x90@htb[/htb]$ sudo umount ./target-NFS