Evaluating the Security Efficacy of noexec on /tmp: Practical Considerations and Workarounds


2 views

Mounting /tmp with noexec has been a longstanding security recommendation in Unix-like systems. The principle is straightforward: by preventing direct execution of binaries in the world-writable temporary directory, you create an additional barrier against privilege escalation attacks. This is particularly relevant in:

  • Shared hosting environments
  • Multi-user systems
  • Services that process untrusted uploads

While noexec provides a security boundary, determined attackers can bypass it through several methods:

# Method 1: Using the dynamic loader directly
/lib64/ld-linux-x86-64.so.2 /tmp/malicious-binary

# Method 2: Script execution via interpreters
python3 /tmp/exploit.py
perl /tmp/exploit.pl

These techniques effectively nullify the protection for many attack scenarios while introducing management overhead.

Several common applications expect executable /tmp space:

# Debian's debconf example
DEBIAN_FRONTEND=noninteractive apt-get install -y some-package
# May fail with:
# Error: Could not create temporary directory for execution

Workarounds often involve creating dedicated executable spaces:

# Alternative tmp directory solution
mkdir -p /var/tmp-exec
mount -o bind,exec /var/tmp-exec /tmp/.exec
chmod 1777 /tmp/.exec

The security value of /tmp hardening varies by use case:

Scenario noexec Benefit Bypass Difficulty
Automated mass exploitation High - blocks many ready-to-run exploits Medium
Targeted attacks by skilled adversaries Low - easily bypassed Low
Misconfigured services Medium - prevents some RCE vectors High

For systems where noexec causes compatibility issues, consider:

# Namespace isolation with PrivateTmp in systemd
[Service]
PrivateTmp=true

# Mounting tmpfs with restricted permissions
mount -t tmpfs -o size=1G,nosuid,nodev,noexec,mode=1777 tmpfs /tmp

For containerized environments, combine with:

# Docker example
docker run --read-only --tmpfs /tmp:noexec,nosuid,size=1g

Historically, mounting /tmp with noexec was considered a security best practice to prevent execution of malicious binaries in world-writable directories. The Debian Security Manual explicitly recommends:

# /etc/fstab example
tmpfs /tmp tmpfs nosuid,nodev,noexec 0 0

Colin Watson and other security researchers have demonstrated practical bypass methods:

# Method 1: Using ld-linux directly
/lib64/ld-linux-x86-64.so.2 ./malicious-binary

# Method 2: Script execution via interpreters
#!/bin/bash
echo "Malicious payload" > /tmp/script.sh
/bin/bash /tmp/script.sh

In containerized environments, the effectiveness diminishes further. Consider this Docker scenario:

FROM alpine
RUN mkdir -p /tmp/exploit && \
    echo -e '#!/bin/sh\nid > /tmp/output' > /tmp/exploit/poc && \
    chmod +x /tmp/exploit/poc && \
    /bin/sh /tmp/exploit/poc

More effective approaches include:

  • Implementing filesystem namespaces
  • Using SELinux/AppArmor policies
  • Applying mount point restrictions

Example AppArmor profile snippet:

# /etc/apparmor.d/deny-tmp-exec
/tmp/** ix,

Despite limitations, noexec remains valuable for:

  • Blocking naive exploit attempts
  • Compliance requirements
  • Defense-in-depth strategies

For systems using debconf, configure exceptions:

# /etc/debconf.conf
TmpFileMode: 0755

Modern tmpfs implementations show negligible overhead from noexec:

$ time dd if=/dev/zero of=/tmp/test bs=1M count=1000
# Compare with and without noexec