CWE-732: Incorrect Permission Assignment for Critical Resource
Overview
A file, directory, or service is created with permissions broader than it needs, so accounts that should have no business with the resource can read or modify it.
Relationship to Other CWEs
If the defect is that nothing checked a permission, the finding is CWE-862 (Missing Authorization) or CWE-863 (Incorrect Authorization), not this page. MITRE marks CWE-732 ALLOWED-WITH-REVIEW for exactly that reason - it is "often misused for vulnerabilities in which 'permissions' are not checked". This page is for a permission that was assigned too broadly, whether or not anything later consults it. Its parents are CWE-285 (Improper Authorization) and CWE-668 (Exposure of Resource to Wrong Sphere).
The pages around it differ by what the wrong permission is attached to:
- CWE-732 (this page) - the mode or ACL on a specific named resource grants more than it needs
- CWE-276 (Incorrect Default Permissions) - the wrong permission is the one new resources are created with rather than one assigned to a named resource afterwards. Fix it by changing the default; fix CWE-732 by fixing the assignment
- CWE-560 (Use of umask() with chmod-style Argument) - the concrete mistake that most often produces that bad default:
umask()names the bits to withhold, so achmod-style argument grants exactly what it looks like it denies - CWE-282 (Improper Ownership Management) - the owner is wrong rather than the mode. The two land on the same resource often enough that ownership and permissions are worth checking as two separate questions
- CWE-269 (Improper Privilege Management) - the privilege of the process doing the accessing, rather than the permissions of the resource being accessed
MITRE gives CWE-732 six further children with no page here: CWE-277, CWE-278, CWE-279 and CWE-281 cover inherited, preserved and execution-assigned permissions, which the guidance below treats the same way as any other over-broad assignment; CWE-766 is a data element declared public; and CWE-1004 is a session cookie without HttpOnly, which belongs to the cookie-attribute family CWE-614 covers rather than to anything on this page.
OWASP Classification
A01:2025 - Broken Access Control
Risk
High: Any account holding the granted access can read, modify, or delete the resource. For a credentials file that means disclosure; for a config file or an executable it usually means privilege escalation.
Remediation Steps
Core Principle: Apply least privilege to every permission and ACL: default-deny, and grant read, write, or execute only where an operation needs it.
Locate the incorrect permission assignment
- Identify the file, directory, or resource the finding names
- Work out who legitimately needs access and which operations they need
- Read the current permissions with
ls -lon Unix orGet-Aclon Windows - Establish what the resource holds - config, database, logs, a private key, user data - because the correct mode follows from that
Apply least privilege to all resources (Primary Defense)
- Grant only the permissions the legitimate operations need
- Remove write and execute unless something needs them. A config file needs owner access only:
400if nothing rewrites it at runtime,600if something does, and never the execute bit - Set the mode explicitly. These are POSIX modes; Windows is covered below
- 400: Owner read-only (sensitive config, private keys)
- 600: Owner read/write (database files, user data)
- 640: Owner read/write, service group read (config read by one daemon)
- 644: Owner read/write, group/world read (deliberately public content)
- 755: Owner all, group/world read/execute (scripts and binaries that everyone runs)
- Avoid world-writable modes: 777 and 666 let any local account rewrite the resource, and nothing legitimate needs them
- Never give a secret a world-readable mode. 644 and 755 both grant read to every account on the host, so they are wrong for a private key or a credentials file and right for a binary or a public asset. The mode is only correct relative to what the file holds
Shared directories are the exception to "never 777". A directory that several users write to needs the sticky bit as well - mode 1777, which is what /tmp carries - so that a user can delete only their own entries. Without it, any local account can remove or replace another user's file in that directory, which is how a correctly-permissioned file gets swapped out from underneath its owner. See CWE-377 for the temporary-file case.
On Windows there are no numeric modes. Access is an ACL of individual entries, chmod has no equivalent, and Python's os.chmod toggles only the read-only attribute there. Set the DACL explicitly - icacls file /inheritance:r /grant:r "%USERNAME%:(R)", or Set-Acl with a FileSecurity object built from scratch - and breaking inheritance is the step that matters: a file created in a directory that grants Users read inherits that grant no matter what else is added to it.
Use secure defaults and explicit permissions
- Set
umask 027orumask 077so new files start restrictive. In code rather than in a shell, note thatumask()takes the bits to withhold - achmod-style0600passed to it grants what it appears to deny, which is CWE-560 - Set permissions explicitly in code with
chmod,os.chmod(), orFile.setReadable() - Do not rely on system defaults, which are commonly 644 or 755. A resource left at whatever the default produced is CWE-276 rather than this page, and the fix is the default rather than the individual resource
- Set the mode before the sensitive data is written, not after - the secure pattern below covers why the order matters
Apply additional permission controls
- Set the owning user and group with
chownor the platform equivalent - Use filesystem ACLs where the owner/group/other model is too coarse
- Encrypt keys, credentials, and PII at rest as well as restricting the mode
- Create temporary files with
tempfile.NamedTemporaryFile()orFiles.createTempFile(), which create the file exclusively and, on POSIX, at mode0600. On Windows the protection comes from%TEMP%sitting inside the user's profile rather than from the call, so it is lost the moment a path is hardcoded to something likeC:\Temp- see CWE-377
Regularly review and audit permissions
- Sweep for unnecessary access periodically;
find / -perm -002lists what is world-writable - Run a configuration scanner such as Lynis or OpenSCAP, or the equivalent cloud posture tool
- Audit-log access to and modification of sensitive resources
- Alert on failed permission checks and on privilege escalation attempts
Test the permission changes
- Confirm the corrected mode with
ls -l,Get-Acl, orstat() - Try the resource as a different user and a different group, and confirm the access is refused
- Confirm the application still works: it can read its config and write its logs
- Cover file creation, log rotation, and backup and restore, where the mode is easily lost
- Re-scan to confirm the finding is resolved
Common Vulnerable Patterns
- Granting world-writable or world-readable permissions
- Using default or inherited permissions without review
World-Writable Permissions on Critical File (Bash)
# VULNERABLE - World-Writable Permissions on Critical File
# Creates file with world-writable permissions
chmod 777 /etc/critical.conf
Why this is vulnerable: Mode 777 lets any local account rewrite the file, and a configuration file is instructions - so whoever can write it decides what the service does on its next restart: where it connects, which certificate it trusts, whether a check runs at all. That is privilege escalation by way of a text editor, with no exploit involved.
Two things make it worse than the mode alone suggests. The execute bit has no bearing on a config file and is granted here anyway, which is a sign the value was chosen to make an error go away rather than to describe an intent; and if the containing directory is also world-writable, the file can be replaced rather than edited, so restoring its contents does not restore its ownership. Set the narrowest mode that works - 640 with a service group is the usual answer for a config file read by one daemon - and check the directory as well as the file.
Secure Patterns
Owner-Only Read/Write Permissions (Bash)
# Tightening a file that already exists
chmod 600 /etc/critical.conf # owner read/write
chmod 400 /etc/critical.conf # owner read-only
# Creating one: set the mode before the content goes in, not after. chmod only
# changes a file that already exists, so writing first and chmod-ing second
# leaves the contents readable in between
install -m 600 /dev/null /etc/critical.conf # empty file, correct mode
printf '%s\n' "$secret" > /etc/critical.conf
# Same idea with umask, where the redirect itself does the creating
( umask 077; printf '%s\n' "$secret" > /etc/critical.conf )
Why this works:
600gives the owner read and write,400read alone, and group and other nothing in either case. No other local account can read the secret, or edit the file to change what the service does on its next start- Order matters, and
chmodalone does not fix a creation path. A script that writes a key and then callschmod 600created that file under the process umask - commonly022, so world-readable - and the secret was on disk at that mode until thechmodreturned. The window is short, and short is not the same as closed: another local process only has to open the file once, and it keeps the descriptor at the old access afterwards. Where the code creates the file, set the mode at creation (install -m,umask, or the platform's create-with-mode call) and treat a followingchmodas a repair for files that already exist.