How to Audit Linux File Permissions and Find Security Holes

By banditz

Monday, January 5, 2026 • 5 min read

Linux terminal showing find command results for SUID files

There’s a move every sysadmin has seen at least once. The application throws a permission denied error. Somebody runs chmod 777 on the directory. The error goes away. Everyone moves on.

Except now that directory is world-writable. Every user, every service, every process — they can all read, modify, and execute everything inside it. If that directory has config files, any compromised service can rewrite them. If it has scripts, anyone can inject code.

File permissions are boring. Nobody gets excited about rwxr-xr--. But misconfigured permissions are consistently in the top vectors for Linux privilege escalation. Not because the attacks are sophisticated — because the mistakes are simple.

The Permission Model in 60 Seconds

Run ls -la and you see:

-rw-r--r--  1 root root  1542 Mar 15 09:20 /etc/nginx/nginx.conf

drwx------  2 deploy deploy  4096 Mar 15 09:22 /home/deploy/.ssh

Breaking down -rw-r--r--:

First character = file type (- file, d directory, l symlink). Then three groups: owner (rw-), group (r--), others (r--).

  • r = read (4), w = write (2), x = execute (1), - = none (0)

So -rw-r--r-- in numeric = 644: owner reads/writes, everyone else reads.

For directories, x means you can enter it and access files. A directory with r-- lets you list filenames but not read files. With --x you can access files if you know their names but not list them.

Sane defaults: 644 for files, 755 for directories.

Step 1: Find World-Writable Files

World-writable = anyone on the system can modify the file. Lowest-hanging fruit for attackers.

Find every world-writable file:

sudo find / -type f -perm -o+w \

    -not -path "/proc/*" \

    -not -path "/sys/*" \

    -not -path "/dev/*" \

    2>/dev/null

For directories:

sudo find / -type d -perm -o+w \

    -not -path "/proc/*" \

    -not -path "/sys/*" \

    -not -path "/tmp" \

    -not -path "/var/tmp" \

    2>/dev/null

/tmp and /var/tmp are supposed to be world-writable. Everything else needs investigation.

Red flags:

  • Config files (.conf, .ini, .env) — anyone can change app behavior
  • Scripts (.sh, .py, .php) — code injection vector
  • Cron files — writable cron scripts run as the cron user
  • Web app files — writable PHP = direct code execution

Fix:

sudo chmod o-w /path/to/file

# Or set correct permissions

sudo chmod 644 /path/to/file

Step 2: Hunt for SUID and SGID Binaries

SUID means when you run this binary, it executes with the file owner’s permissions — not yours.

If root owns a SUID binary, anyone who runs it gets root-level execution. That’s by design for passwd (needs root for /etc/shadow) and sudo. The problem is unexpected SUID binaries.

Find all SUID:

sudo find / -type f -perm -4000 2>/dev/null

Find all SGID:

sudo find / -type f -perm -2000 2>/dev/null

A clean Linux server should have:

/usr/bin/passwd

/usr/bin/sudo

/usr/bin/su

/usr/bin/mount

/usr/bin/umount

/usr/bin/chfn

/usr/bin/chsh

/usr/bin/newgrp

/usr/bin/gpasswd

/usr/bin/pkexec

Everything else is suspicious. GTFOBins lists Linux binaries exploitable with SUID — find, vim, python, bash, nmap, and many more.

If python3 had SUID set (it never should):

python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

Instant root shell.

Remove unnecessary SUID:

sudo chmod u-s /path/to/binary

Step 3: Check Sensitive Files

SSH files:

sudo stat -c '%a %U %G %n' /home/*/.ssh /home/*/.ssh/* 2>/dev/null

Required:

  • ~/.ssh/700
  • ~/.ssh/authorized_keys644 or 600
  • ~/.ssh/id_ed25519 (private key) — 600 (SSH refuses looser permissions)
  • ~/.ssh/config600

System credentials:

sudo stat -c '%a %U %G %n' /etc/passwd /etc/shadow /etc/group /etc/gshadow
  • /etc/passwd644 root:root
  • /etc/shadow640 root:shadow (contains password hashes)
  • /etc/group644 root:root
  • /etc/gshadow640 root:shadow

If /etc/shadow is world-readable, any user can read hashes and attempt offline cracking.

Web server:

Web directories should be 755, files 644. Config files owned by root at 640. Never make web files writable by the web server user unless absolutely necessary (uploads directory only).

Step 4: Find Orphaned Files

Files without valid owners = deleted user accounts or improper management:

sudo find / -nouser -o -nogroup 2>/dev/null | grep -v '/proc\|/sys'

Risk: if a new user gets the recycled UID, they inherit all orphaned files.

Fix:

sudo chown root:root /path/to/orphaned-file

Step 5: Set Proper Defaults with umask

Check current:

umask

Common values:

  • 022 — new files 644, directories 755 (standard)
  • 027 — new files 640, directories 750 (others can’t read)
  • 077 — new files 600, directories 700 (only owner)

For servers, 027 is ideal. Set in /etc/login.defs:

UMASK 027

For systemd services:

[Service]

UMask=0027

The Permission Audit Checklist

Run quarterly on every production server:

  1. World-writable filesfind / -type f -perm -o+w — fix everything outside /tmp
  2. SUID/SGID binariesfind / -perm -4000 — compare against known-good list
  3. SSH permissions — directories 700, private keys 600
  4. /etc/shadow — must be 640, never world-readable
  5. Web files — directories 755, files 644, config owned by root
  6. Orphaned filesfind / -nouser -o -nogroup — reassign
  7. Verify umask — should be 022 or 027

For automated auditing, Lynis does comprehensive permission checks. Install with sudo apt install lynis and run sudo lynis audit system.

Permissions aren’t glamorous. Nobody puts “I audited file permissions” on conference slides. But I’ve seen production databases exposed because /etc/shadow was 644. I’ve seen web servers owned because deploy scripts left everything world-writable. I’ve seen root shells from SUID on python3.

The boring stuff prevents the exciting breaches.

Step-by-Step Guide

1

Understand the Linux permission model

Every file has three permission sets for owner group and others. Each has read write and execute bits. Use ls -la to see them. Numeric equivalents are read 4 write 2 execute 1. Standard permissions are 644 for files and 755 for directories. Understanding this is essential before auditing. Step 2: Name: Find all world-writable files and directories | Text: Run sudo find / -type f -perm -o+w excluding /proc /sys /dev. Any world-writable file outside /tmp is suspicious. Configuration files scripts and binaries should never be world-writable. Fix with chmod o-w.

2

Find all SUID and SGID binaries

Run sudo find / -type f -perm -4000 for SUID and -2000 for SGID. Compare against known-good list. Standard SUID includes passwd sudo mount su. Anything unexpected is a potential backdoor. Remove SUID with chmod u-s if not needed. Check GTFOBins for exploitable binaries.

3

Check sensitive file permissions

SSH directory must be 700 with private keys at 600. /etc/shadow must be 640 not world-readable. Web files should be 755 directories 644 files owned by appropriate user. Use stat -c '%a %U %G %n' to check.

4

Find all world-writable files and directories

Run sudo find / -type f -perm -o+w excluding /proc /sys /dev. Any world-writable file outside /tmp is suspicious. Configuration files scripts and binaries should never be world-writable. Fix with chmod o-w.

5

Set proper umask defaults

Check umask value. Set 027 for servers so new files get 640 and directories 750. Configure in /etc/login.defs or /etc/profile for login shells. For systemd services set UMask=0027 in unit files.

Frequently Asked Questions

Why is chmod 777 dangerous?
It gives read write and execute to every user. Any compromised service can modify the file. If it is a script an attacker injects code. The correct fix is identifying which user needs access and granting only that.
What is the SUID bit and why is it a risk?
SUID means the program runs with the file owner permissions. If root owns a SUID binary anyone running it gets root execution. Attackers target SUID binaries for privilege escalation. Only essential system binaries should have it.
How often should I audit permissions?
Monthly on production servers and after major deployments. Use Lynis or AIDE for continuous monitoring and automated alerts on permission changes.
What is the sticky bit?
On directories it means only the file owner can delete files even if others have write access. Classic example is /tmp with 1777 permissions. Without it any user could delete others files.
banditz

Research Bug bounty at javahack team

Freeland Reseacrh Bug Bounty

View all articles →