/tmp Hardening: noexec Mount Against WebShells
Malicious files dropped via web vulns usually land in /tmp or /dev/shm; once executable they yield reverse shells and lateral movement. noexec/nodev/nosuid mounts, systemd tmp.mount tmpfs, /dev/shm hardening, MySQL/PHP dependency checks, false-positive handling, fstab recovery.
Applicable scenarios
- Prior incident where a WebShell landed and executed via /tmp
- Compliance assessment or security-baseline scan requires /tmp mounted noexec
- Cut the common chain: upload to temp dir → execute → reverse shell
Prerequisites
- Linux server (CentOS 7/8, Ubuntu 20.04 examples), root
- Current /tmp mount known (separate partition vs root)
- Console or out-of-band management so a bad fstab is recoverable
How it works
In many intrusions, attackers write a malicious ELF or script to globally writable dirs (/tmp, /dev/shm) via upload, command injection, or deserialization, then execute for a reverse shell. These dirs default to 1777 (sticky bit); any user can write; most distros mount without exec restriction. noexec blocks direct execution of binaries on that mount; nosuid ignores SUID; nodev forbids device nodes. Combined, write-to-temp drops from high-risk to mostly harmless — files land, they don't run.
Note: noexec only blocks direct binary execution. Indirect execution via the dynamic linker (e.g. `/lib64/ld-linux-x86-64.so.2 ./payload`) can still bypass. Defense in depth, not a substitute for privilege reduction and detection.
Procedure
1. Confirm current mount
`findmnt /tmp
df -hT /tmp /dev/shm`
Record whether /tmp is a separate partition or part of root; that picks the path below.
2. Path 1: /tmp is a separate partition — edit fstab
`cp /etc/fstab /etc/fstab.bak.$(date +%F)
# Find the /tmp line; append to the fourth-column mount options:
# defaults,noexec,nodev,nosuid`
If the line already points at a disk partition (e.g. `LABEL=xxx /tmp xfs defaults 0 0`), only append ,noexec,nodev,nosuid. Do not replace the whole line with tmpfs — that can fill RAM.
3. Path 2: /tmp is on root — enable systemd tmp.mount
Simplest: make /tmp a tmpfs:
`systemctl unmask tmp.mount
systemctl enable --now tmp.mount
# Confirm Options in /usr/lib/systemd/system/tmp.mount include:
# mode=1777,strictatime,noexec,nodev,nosuid`
Also harden /dev/shm. Append to /etc/fstab:
`tmpfs /dev/shm tmpfs defaults,noexec,nodev,nosuid 0 0
mount -o remount /dev/shm`
4. Apply disk-partition options immediately
`mount -o remount,noexec,nodev,nosuid /tmp`
5. Verify execution is blocked
`cp /bin/ls /tmp/ls_test
/tmp/ls_test
# Expected: bash: /tmp/ls_test: Permission denied`
Verification
`findmnt -no OPTIONS /tmp # expect noexec,nodev,nosuid
findmnt -no OPTIONS /dev/shm`
Reboot once. Confirm fstab or tmp.mount still applies after reboot — that's the baseline acceptance check.
FAQ
FAQ 1: Services break after noexec?
Common hits: MySQL temp tables (set `tmpdir = /var/lib/mysql-tmp` in my.cnf, create and chmod the dir), PHP session and upload temp dirs (confirm `session.save_path` and `upload_tmp_dir` do not point at /tmp), install scripts that unpack to /tmp then execute. Relocate those dirs; do not drop noexec to accommodate a service.
FAQ 2: How much RAM does tmpfs use?
tmp.mount defaults to half of physical RAM, charged by actual use. On small boxes, cap it: `mount -o remount,size=512M /tmp`, and persist size in tmp.mount or fstab.
FAQ 3: Bad fstab, reboot won't boot?
systemd drops to emergency shell on mount failure. Root is read-only; run `mount -o remount,rw /`, comment or fix the bad /etc/fstab line, reboot. That's why OOB console is required before this change.
Summary
Hardening /tmp and /dev/shm cuts the temp-dir execution path at negligible cost: separate partition — append noexec,nodev,nosuid in fstab; root partition — enable systemd tmp.mount. Pick one. /dev/shm must be done in either case. Inventory MySQL tmpdir, PHP session, etc. before go-live; accept with findmnt and an exec test; fold into periodic baseline checks.