Mount options: noexec, nosuid, nodev, the secure way
The attacker's first move after a web shell is almost always the same: drop a binary in /tmp and run it. It is a little like leaving the workshop unlocked and then acting surprised about the new furniture.
The short answer
Mount every filesystem that users or services can write to, such as /tmp, /var/tmp, /dev/shm and /home, with nosuid and nodev, and with noexec where nothing needs to run from it. Set it in /etc/fstab, apply with mount -o remount, and check with findmnt. noexec stops direct execution only; scripts run through an interpreter still work.
On this page
What goes wrong
Every Linux host has directories that anyone can write to. /tmp,
/var/tmp and /dev/shm are writable by every user and every service. Home
directories are writable by their owners, which includes a compromised web
application running as its own user.
By default, a file in those places can be executed, can carry a setuid bit that the kernel honors, and can be a device node that gives raw access to hardware. An attacker who can write a file can often run it too.
Three mount options close these doors for a whole filesystem at once:
noexec: the kernel refuses to run programs directly from it.nosuid: setuid and setgid bits and file capabilities are ignored.nodev: device files on it cannot be opened.
What the docs say
Do not permit direct execution of any binaries on the mounted filesystem.
Source: mount(8), noexec
Do not honor set-user-ID and set-group-ID bits or file capabilities when executing programs from this filesystem.
Source: mount(8), nosuid
Do not interpret character or block special devices on the filesystem.
Source: mount(8), nodev
The important word is "direct". noexec does not stop sh /tmp/x.sh,
python3 /tmp/x.py or perl /tmp/x.pl, because the program that runs is the
interpreter, which lives on another filesystem. The man page does not spell
this out, so many people think noexec blocks all code.
The secure configuration
# /etc/fstab (add or adjust these lines; keep your existing / and /boot entries)
# /tmp in RAM, nothing runs from it, no setuid, no devices.
tmpfs /tmp tmpfs rw,nosuid,nodev,noexec,mode=1777,size=2G 0 0
# /var/tmp survives reboots, so bind it to its own disk path with the same rules.
/srv/vartmp /var/tmp none rw,bind,nosuid,nodev,noexec 0 0
# Shared memory: nothing should be executed from here.
tmpfs /dev/shm tmpfs rw,nosuid,nodev,noexec 0 0
# Home directories: users may run their own scripts, but setuid and devices are ignored.
UUID=0a1b2c3d-0000-4000-8000-000000000001 /home ext4 defaults,nosuid,nodev 0 2
# /boot is only read by the bootloader and the kernel update scripts.
UUID=0a1b2c3d-0000-4000-8000-000000000002 /boot ext4 defaults,nosuid,nodev,noexec 0 2# Create the backing directory for /var/tmp once.
install -d -m 1777 /srv/vartmp
findmnt --verify # checks /etc/fstab for errors before you reboot
systemctl daemon-reload # systemd reads fstab into mount units
mount -o remount /tmp # re-reads the options from fstab
mount -o remount /dev/shm
mount /var/tmp
findmnt -o TARGET,OPTIONS /tmp /var/tmp /dev/shm /homeProve it
The test mounts three tmpfs filesystems: /mnt/plain with no options,
/mnt/nosuid-only which keeps exec so the setuid bit can be tested, and
/mnt/hardened with all three options.
findmnt -o TARGET,FSTYPE,OPTIONS /mnt/plain
findmnt -n -o TARGET,FSTYPE,OPTIONS /mnt/nosuid-only
findmnt -n -o TARGET,FSTYPE,OPTIONS /mnt/hardenedTARGET FSTYPE OPTIONS
/mnt/plain tmpfs rw,relatime,size=16384k
/mnt/nosuid-only tmpfs rw,nosuid,nodev,relatime,size=16384k
/mnt/hardened tmpfs rw,nosuid,nodev,noexec,relatime,size=16384knoexec stops a copied binary, and a script run by its path:
/mnt/plain/id -un
/mnt/hardened/id -un
/mnt/hardened/job.shroot
bash: line 17: /mnt/hardened/id: Permission denied
bash: line 19: /mnt/hardened/job.sh: Permission deniedIt does not stop the same script given to its interpreter. It does stop the old trick of starting a binary through the dynamic loader:
sh /mnt/hardened/job.sh
/lib64/ld-linux-x86-64.so.2 /mnt/hardened/id -unscript ran
/mnt/hardened/id: error while loading shared libraries: /mnt/hardened/id: failed to map segment from shared objectnosuid: a setuid-root copy of bash, run by the unprivileged user nobody:
printf "plain: "; su -s /bin/sh nobody -c "/mnt/plain/rootbash -p -c \"id -un\""
printf "nosuid-only: "; su -s /bin/sh nobody -c "/mnt/nosuid-only/rootbash -p -c \"id -un\""plain: root
nosuid-only: nobodynodev: a character device node created on each mount (major 1, minor 5, the
same device as /dev/zero):
head -c 4 /mnt/plain/zero | od -An -tx1 | sed "s/^/plain: read bytes/"
head -c 4 /mnt/hardened/zeroplain: read bytes 00 00 00 00
head: cannot open '/mnt/hardened/zero' for reading: Permission deniedThe test script is secure-tests/mount-options-noexec-nosuid-nodev/run.sh.
Mistakes people make
Believing noexec blocks all code
It blocks execve() of files on that filesystem. Interpreters, bash script,
and code loaded by a program that is already running are not blocked. Treat
noexec as one layer that removes the easy path, not as a sandbox.
Forgetting /var/tmp and /dev/shm
Hardening /tmp alone moves the attacker one directory over. /var/tmp and
/dev/shm are just as writable.
Editing fstab and never checking
Options you wrote are not options the kernel uses until the filesystem is
remounted. findmnt shows what is in force; /etc/fstab shows what you hoped.
A typo that stops the boot
A broken line in /etc/fstab can drop a host into emergency mode. Run
findmnt --verify after every edit, and keep console access before you
reboot a remote host.
Installers that run from /tmp
Some installers unpack a helper into /tmp and run it. Point them at a
private directory with TMPDIR=/root/tmp for that one command instead of
removing noexec for everyone.
Checklist
/tmpis mounted withnosuid,nodev,noexec./var/tmpis mounted withnosuid,nodev,noexec./dev/shmis mounted withnosuid,nodev,noexec./homeis mounted withnosuid,nodev.- Any removable or data filesystem is mounted with
nosuid,nodev. findmnt --verifyreports no errors.findmnt -o TARGET,OPTIONSshows the options on the running host.- A test binary copied to
/tmpfails withPermission denied.
Three words in fstab will not stop a determined attacker. They will stop the lazy ones, and the lazy ones are most of them.
FND
Learn it on a live range
Linux 3: securing Linux, in Foundation: a real host in your browser, and every objective checked on the machine.
Start freeThe Secure Way
More on linux hosts
SSH, sudo, firewalls, mount options, updates and the host basics every engineer should get right.
All linux hosts guides