Join our Newsletter — 33% off our NHI Course
Home› Guides› Linux File Permissions, SUID and ACLs Guide: Read,…
Guide Endpoint Security

Linux File Permissions, SUID and ACLs Guide: Read, Audit and Fix Permissions Safely

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 28 October 2026 16 min read
On this page

Most Linux privilege escalation and data exposure starts with a file that has the wrong permissions: a world-writable script run by root, a setuid binary nobody needs, or a private key readable by every account on the host. Linux decides access with three layers that interact in ways that surprise even experienced administrators: the classic owner, group and other mode bits, the special setuid, setgid and sticky bits, and POSIX access control lists (ACLs) with their mask. This guide explains how each layer works according to the kernel and coreutils documentation, why chmod 777 is almost never the answer, how to audit setuid and world-writable files, how to fix permissions without breaking services, and what to monitor. It is for Linux administrators, platform engineers and security teams hardening servers, containers and build hosts.

Key takeaways

  • Mode bits are the baseline. Owner, group and other each get read, write and execute, set with octal (0640) or symbolic (u=rw,g=r,o=) modes; new files start from the requested mode with the umask removed, usually 022.
  • Setuid is a privilege grant. A setuid-root executable runs with root's effective user ID for whoever starts it, so every one is part of your attack surface and should be justified, patched and inventoried.
  • ACLs change the meaning of the group bits. Once an ACL has a mask, chmod g=... changes the mask, not the owning group's entry, which silently widens or narrows access for every named user and group.
  • chmod 777 hides a design problem. Fix the owner, group or service account instead of letting every local account and compromised process write to the file.
  • Prevent, do not just find. Mount data and temporary filesystems nosuid, set NoNewPrivileges= and RestrictSUIDSGID= on systemd services, and alert on permission changes with auditd.

How Linux permission checks work

Every file has an owner (UID), a group (GID) and a 12-bit mode. The kernel checks permissions against the process's effective user and group IDs and its supplementary groups, in a fixed order: if the process owns the file, only the owner bits apply; if not, and it is in the file's group, only the group bits apply; otherwise the other bits apply. The first matching class decides, which is why a file with mode 0604 can deny its own group read access while allowing everyone else.

BitOn a regular fileOn a directory
r (4)Read the contentsList the names in the directory
w (2)Change the contentsCreate, delete and rename entries (with x), regardless of the files' own permissions
x (1)Execute as a programEnter the directory and reach files inside it by name
setuid (4000)Program runs with the file owner's effective UIDNo standard effect on Linux; chmod keeps it unless explicitly cleared
setgid (2000)Program runs with the file group's effective GID; without group execute it marks mandatory lockingNew files inherit the directory's group, and new subdirectories inherit setgid
sticky (1000)No effect on modern LinuxOnly the file's owner, the directory's owner or a privileged process can delete or rename entries (as on /tmp)

The directory write bit is the one most often misunderstood: write on a directory lets a user delete or replace a file they cannot read or write. A root-owned script with mode 0755 inside a directory writable by a service account can be replaced by that account. Always check the whole path, not only the file.

Reading modes and changing them safely

  • Octal modes: one to four digits; the optional first digit sets setuid (4), setgid (2) and sticky (1), and the last three set owner, group and other. 0640 is owner read and write, group read, other nothing.
  • Symbolic modes: u, g, o and a with +, - or =. Capital X adds execute only to directories and files that already have execute for someone, which makes chmod -R u=rwX,g=rX,o= safe for trees that mix scripts and data.
  • Clearing setuid and setgid on directories: GNU chmod keeps those bits on directories unless you clear them explicitly, for example with a leading zero (00755), a leading minus (-6000) or a leading equals sign (=755).
  • Symbolic links: chmod never changes a symlink's own mode; a link named on the command line changes its target, and recursive runs ignore links by default.
  • Recursive changes: GNU chmod defaults to --no-preserve-root; add --preserve-root in scripts and aliases so a mistyped path cannot recurse over /.

umask: the permissions files are born with

Programs request a mode when they create a file, commonly 0666 for files and 0777 for directories, and the kernel clears any bits set in the process umask. With the usual 022, a requested 0666 becomes 0644. systemd sets UMask=0022 by default for system units. For services that write secrets, logs or uploads, set UMask=0077 or 0027 in the unit so new files are private by default. Two details matter: if the parent directory has a default ACL, the umask is ignored and the default ACL decides; and since Linux 4.7 the current umask is readable in the Umask field of /proc/<pid>/status, which is useful when auditing running services.

Setuid and setgid binaries

When a process runs a program with the setuid bit set, execve(2) changes the process's effective UID to the file's owner. A setuid-root program therefore runs as root no matter who starts it, and any bug in it, or in how it handles its environment, arguments and file descriptors, is a local root bug. The same page documents when the bits are ignored, which is the basis for most defences:

  • nosuid mounts: the bits have no effect on a filesystem mounted nosuid (some kernels return EPERM instead of ignoring them).
  • no_new_privs: the bits, and file capabilities, have no effect when the calling thread has the no_new_privs attribute, which systemd sets with NoNewPrivileges=yes.
  • Tracing: the bits are ignored when the process is being ptraced.
  • Scripts: Linux ignores setuid and setgid on interpreter scripts, so a setuid shell script is not a way to delegate privilege. Use sudo rules or a small, reviewed program instead.

The risk is concrete. CVE-2021-4034 ("PwnKit") was a memory corruption flaw in polkit's pkexec, which is setuid-root and installed by default on major distributions; Qualys reported it had existed since pkexec was created in 2009 and let any unprivileged local user gain root. It is in the CISA Known Exploited Vulnerabilities catalog with known ransomware use. Qualys's temporary mitigation, until a patched package was installed, was to remove the setuid bit (chmod 0755 /usr/bin/pkexec). Sudo's heap overflow CVE-2021-3156 is also in the KEV catalog. Each setuid binary you remove is one fewer place where the next flaw of that kind matters.

Auditing the setuid inventory

  1. List them: find / -xdev -type f -perm -4000 for setuid and -perm -2000 for setgid, run per filesystem, and record path, owner, package and hash.
  2. Map each to a package: rpm -qf or dpkg -S. A setuid file owned by no package, or in a home, web or temporary directory, is an incident until proven otherwise.
  3. Decide per binary: keep it (needed, patched), remove the bit (feature unused, for example pkexec on a server with no polkit use) or replace it with a capability (for example cap_net_raw for ping, where the distribution supports it). See the Linux Capabilities Guide.
  4. Make the decision stick: package updates can restore a removed bit. Enforce the expected list with configuration management or a compliance profile and re-check after updates. See the OpenSCAP and Linux Compliance Scanning Guide.
  5. Baseline and diff: compare the list against the golden image weekly; any new setuid file outside a package update is an alert.

POSIX ACLs and the mask

ACLs (managed with getfacl and setfacl) let you grant access to named users and groups beyond the single owning group. The acl(5) manual defines six entry types: the owner (ACL_USER_OBJ), named users (ACL_USER), the owning group (ACL_GROUP_OBJ), named groups (ACL_GROUP), the mask (ACL_MASK) and other (ACL_OTHER). An ACL with any named entries must have exactly one mask.

ACL behaviourWhat it means in practiceCommon mistake
The mask caps named users, the owning group and named groupsEffective rights are the entry ANDed with the mask; getfacl prints #effective: where they differGranting rw to a named user and wondering why writes fail
The mask does not cap the owner or otherOther access is still governed by the other entry and mode bitsAssuming an ACL makes a world-readable file private
The group bits shown by ls -l are the mask when one existschmod g-w removes write from every named user and groupA routine chmod 640 cutting off a backup service account
Default ACLs on directories are inherited by new files and subdirectoriesNew objects take the default ACL, trimmed by the creating program's requested mode; the umask is ignoredSetting an access ACL on a directory and expecting new files to inherit it
First matching class decidesAn owner match never falls through to named entriesUsing a named user entry to restrict the file's owner
Deny-by-group is unreliableThe manual warns that unprivileged processes in user namespaces can drop supplementary groups with setgroups(2)Using an ACL that removes rights from a group as a security boundary

A + after the mode in ls -l output (-rw-r-----+) is the only hint that an ACL exists, which is why ACL-granted access is often missed in reviews. Include getfacl -R output in any permission audit of sensitive paths, and make sure backup and restore tools preserve ACLs (for example tar --acls or rsync -A), otherwise a restore silently changes who has access.

Choosing the right control

NeedUseAvoid
One service writes, one team readsOwner = service account, group = team, mode 06400666 or 0777
Several teams need different access to one treeNamed group ACL entries plus a default ACL on the directoryAdding everyone to one shared group
Shared drop directorysetgid directory with sticky bit (3770) and a default ACLWorld-writable directory without sticky bit
A program needs one root power (bind a low port, raw sockets)File or ambient capability for that one powersetuid root
An administrator needs to run a command as rootA specific sudo rule, loggedA setuid copy of a shell or interpreter
A file must not change even for root processesImmutable attribute (chattr +i) plus file integrity monitoringRelying on mode bits, which root bypasses
Confine what a root process can touchSELinux or AppArmor policy; systemd ProtectSystem= and ReadOnlyPaths=Discretionary permissions alone

Discretionary permissions do not constrain root. Where a compromised root process is in your threat model, mandatory access control is the next layer. See the Reference Monitors and Mandatory Access Control Guide.

  • Small estates: a short list of approved setuid binaries, nosuid on data mounts and a weekly find diff covers most of the risk.
  • Large estates: enforce permissions with configuration management, scan images in the build pipeline, and feed auditd perm_mod events into the SIEM.

Why chmod 777 is the wrong fix

chmod 777 usually appears when an application cannot write a file and someone wants the error to go away. It grants read, write and execute to every account on the host, including every service account and anything that compromises one of them. On a directory it lets any user delete or replace every file inside. On an executable run by root or by a scheduled job it is a privilege escalation path.

  1. Find the real writer: check which user the process runs as (ps -o user,pid,cmd, or User= in the systemd unit).
  2. Give that account ownership or group access: chown app:app and mode 0750 for directories or 0640 for files, or a named ACL entry if several accounts need access.
  3. Check the parent directories: the process needs execute on every directory in the path and write on the directory where it creates files.
  4. Check MAC denials: a "permission denied" with correct mode bits is often SELinux or AppArmor; look in the audit log for AVC or apparmor denials rather than widening the mode.
  5. Fix the creation path: if files keep appearing with the wrong mode, fix the service's UMask= or the directory's default ACL, not each file.

Where identity fits

File permissions are how Linux protects the credentials that non-human identities use: SSH private keys, Kerberos keytabs, cloud credential files, API tokens in environment files and TLS private keys. A readable keytab or .aws/credentials file is a stolen identity. OpenSSH defends its own files: with StrictModes yes (the default), sshd checks the modes and ownership of the user's files and home directory before accepting a login, because world-writable files are a common novice mistake.

  • Private keys and keytabs: owner-only (0600 or 0400), owned by the service account that uses them, in a directory that is not group or world writable. See the Linux Service Accounts and Keytab Management Guide.
  • Service accounts: give each service its own account rather than sharing one, so permissions and audit records point to one workload.
  • Environment and config files with secrets: 0640 at most, group limited to the service, and better still moved to a secrets manager.

Detection and measurement

  • auditd permission changes: the audit-userspace project's sample STIG rules record chmod, fchmod, fchmodat, chown family and extended attribute calls (setxattr, removexattr and variants, which is how ACLs and file capabilities are written) by interactive users under the key perm_mod. Alert on a new setuid or setgid bit, on world-write being added, and on any change under /etc, /usr or credential directories. See the Linux Logging and auditd for Defenders Guide.
  • File integrity monitoring: track mode, owner, ACL and hash of setuid binaries, sudoers files and key files. See the Linux File Integrity Monitoring Guide.
  • Scheduled scans: world-writable files (-perm -0002 -type f), world-writable directories without the sticky bit, files with no valid owner (-nouser, -nogroup) and setuid files, diffed against the baseline.
MetricWhat it showsTarget
Setuid and setgid files per host versus approved listAttack surfaceNo unapproved entries; list shrinking
World-writable files outside /tmp-style directoriesUnsafe sharingZero
Writable mounts without nosuid (/tmp, /var/tmp, /home, data volumes)Preventive coverageZero
systemd services without NoNewPrivileges=yesPrivilege containmentFalling; exceptions documented
Credential files readable by group or otherNHI credential exposureZero
Unexpected perm_mod events per weekDrift and possible tamperingEach one explained

Implementation and rollback

  1. Measure first: run the scans above on a representative set of hosts and images; do not change anything yet.
  2. Agree an approved setuid list per build: start from the distribution default and remove what is unused.
  3. Pilot in a test ring: remove setuid bits, add nosuid to /tmp, /var/tmp, /dev/shm and data mounts, set NoNewPrivileges=yes and RestrictSUIDSGID=yes on services, and run functional tests. Watch for tools that genuinely need setuid, such as passwd, su and sudo.
  4. Roll out by ring with a recorded rollback: keep the previous mode, owner and ACL of every file you change (getfacl -R output restores with setfacl --restore), and keep mount changes in version-controlled configuration.
  5. Enforce and monitor: configuration management restores the approved state after package updates; auditd and FIM catch drift.

Common failure patterns

  • chmod 777 left in place: a quick fix during an outage becomes permanent, and a web upload directory or cron script becomes writable by every account.
  • Writable directory, protected file: the file is root-owned 0755, but its directory is writable by a service account that can replace it.
  • Setuid bits restored by updates: a mitigation such as removing the bit from pkexec is undone by the next package update and nobody notices.
  • Invisible ACLs: reviews read ls -l, miss the + and never see the named user entry that grants a contractor account write access.
  • Restores that drop or change ACLs: a backup tool without ACL support restores files with only mode bits, removing or widening access.
  • Credential files with default modes: a deployment script writes a keytab or token file with the default 0644 because the service's umask was never set.

Framework mapping

FrameworkReferenceHow it applies
NIST SP 800-53 Rev. 5AC-3 Access Enforcement; AC-6 Least Privilege; CM-6 Configuration Settings; CM-7 Least Functionality; SI-7 Software, Firmware, and Information IntegrityCorrect modes and ACLs, minimal setuid, enforced baselines, integrity monitoring
NIST CSF 2.0PR.AA-05; PR.PS-01Least-privilege permissions; configuration management
MITRE ATT&CK v20T1548.001 Setuid and Setgid; T1222.002 Linux and Mac PermissionsAbuse of setuid binaries; permission changes to enable execution or persistence
MITRE D3FENDD3-FAPA File Access Pattern AnalysisDetecting unusual access to key and credential files
CISA KEVCVE-2021-4034 (polkit pkexec); CVE-2021-3156 (sudo)Exploited flaws in setuid-root programs

Practitioner checklist

  • Inventory setuid and setgid files on every build and image, map each to a package and approve or remove it.
  • Mount /tmp, /var/tmp, /dev/shm, home and data volumes with nosuid (and nodev, and noexec where workloads allow).
  • Set NoNewPrivileges=yes, RestrictSUIDSGID=yes and a restrictive UMask= on systemd services.
  • Replace setuid-root with capabilities or specific sudo rules where possible.
  • Remove world-write from everything except sticky temporary directories, and ban chmod 777 in runbooks and code review.
  • Include getfacl output in permission reviews and confirm backups preserve ACLs.
  • Make SSH keys, keytabs and credential files owner-only and owned by a dedicated service account.
  • Deploy auditd perm_mod rules and file integrity monitoring on setuid binaries and credential paths, and alert on new setuid files.
  • Re-run the baseline after every package update and image rebuild.

Standards and references

Related NHI Mgmt Group resources: Linux Capabilities Guide · Linux Privilege Escalation Techniques Guide · Linux Fundamentals for Defenders Guide · Linux Server Hardening Guide · Linux Service Accounts and Keytab Management Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 28 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.